mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-12-23 11:12:51 +00:00
Added support for ALTER OPERATOR syntax (#2114)
Some checks failed
license / Release Audit Tool (RAT) (push) Has been cancelled
Rust / codestyle (push) Has been cancelled
Rust / lint (push) Has been cancelled
Rust / benchmark-lint (push) Has been cancelled
Rust / compile (push) Has been cancelled
Rust / docs (push) Has been cancelled
Rust / compile-no-std (push) Has been cancelled
Rust / test (beta) (push) Has been cancelled
Rust / test (nightly) (push) Has been cancelled
Rust / test (stable) (push) Has been cancelled
Some checks failed
license / Release Audit Tool (RAT) (push) Has been cancelled
Rust / codestyle (push) Has been cancelled
Rust / lint (push) Has been cancelled
Rust / benchmark-lint (push) Has been cancelled
Rust / compile (push) Has been cancelled
Rust / docs (push) Has been cancelled
Rust / compile-no-std (push) Has been cancelled
Rust / test (beta) (push) Has been cancelled
Rust / test (nightly) (push) Has been cancelled
Rust / test (stable) (push) Has been cancelled
This commit is contained in:
parent
effaac5c39
commit
326f111808
8 changed files with 585 additions and 142 deletions
133
src/ast/ddl.rs
133
src/ast/ddl.rs
|
|
@ -995,6 +995,103 @@ impl fmt::Display for AlterTypeOperation {
|
|||
}
|
||||
}
|
||||
|
||||
/// `ALTER OPERATOR` statement
|
||||
/// See <https://www.postgresql.org/docs/current/sql-alteroperator.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 AlterOperator {
|
||||
/// Operator name (can be schema-qualified)
|
||||
pub name: ObjectName,
|
||||
/// Left operand type (`None` if no left operand)
|
||||
pub left_type: Option<DataType>,
|
||||
/// Right operand type
|
||||
pub right_type: DataType,
|
||||
/// The operation to perform
|
||||
pub operation: AlterOperatorOperation,
|
||||
}
|
||||
|
||||
/// An [AlterOperator] 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 AlterOperatorOperation {
|
||||
/// `OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
|
||||
OwnerTo(Owner),
|
||||
/// `SET SCHEMA new_schema`
|
||||
SetSchema { schema_name: ObjectName },
|
||||
/// `SET ( options )`
|
||||
Set {
|
||||
/// List of operator options to set
|
||||
options: Vec<OperatorOption>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Option for `ALTER OPERATOR SET` 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 OperatorOption {
|
||||
/// `RESTRICT = { res_proc | NONE }`
|
||||
Restrict(Option<ObjectName>),
|
||||
/// `JOIN = { join_proc | NONE }`
|
||||
Join(Option<ObjectName>),
|
||||
/// `COMMUTATOR = com_op`
|
||||
Commutator(ObjectName),
|
||||
/// `NEGATOR = neg_op`
|
||||
Negator(ObjectName),
|
||||
/// `HASHES`
|
||||
Hashes,
|
||||
/// `MERGES`
|
||||
Merges,
|
||||
}
|
||||
|
||||
impl fmt::Display for AlterOperator {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ALTER OPERATOR {} (", self.name)?;
|
||||
if let Some(left_type) = &self.left_type {
|
||||
write!(f, "{}", left_type)?;
|
||||
} else {
|
||||
write!(f, "NONE")?;
|
||||
}
|
||||
write!(f, ", {}) {}", self.right_type, self.operation)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AlterOperatorOperation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::OwnerTo(owner) => write!(f, "OWNER TO {}", owner),
|
||||
Self::SetSchema { schema_name } => write!(f, "SET SCHEMA {}", schema_name),
|
||||
Self::Set { options } => {
|
||||
write!(f, "SET (")?;
|
||||
for (i, option) in options.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", option)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OperatorOption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Restrict(Some(proc_name)) => write!(f, "RESTRICT = {}", proc_name),
|
||||
Self::Restrict(None) => write!(f, "RESTRICT = NONE"),
|
||||
Self::Join(Some(proc_name)) => write!(f, "JOIN = {}", proc_name),
|
||||
Self::Join(None) => write!(f, "JOIN = NONE"),
|
||||
Self::Commutator(op_name) => write!(f, "COMMUTATOR = {}", op_name),
|
||||
Self::Negator(op_name) => write!(f, "NEGATOR = {}", op_name),
|
||||
Self::Hashes => write!(f, "HASHES"),
|
||||
Self::Merges => write!(f, "MERGES"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
|
@ -1790,7 +1887,7 @@ impl fmt::Display for ColumnOption {
|
|||
GeneratedAs::Always => "ALWAYS",
|
||||
GeneratedAs::ByDefault => "BY DEFAULT",
|
||||
// ExpStored goes with an expression, handled above
|
||||
GeneratedAs::ExpStored => unreachable!(),
|
||||
GeneratedAs::ExpStored => "",
|
||||
};
|
||||
write!(f, "GENERATED {when} AS IDENTITY")?;
|
||||
if sequence_options.is_some() {
|
||||
|
|
@ -3981,18 +4078,8 @@ pub struct CreateOperator {
|
|||
pub left_arg: Option<DataType>,
|
||||
/// RIGHTARG parameter (right operand type)
|
||||
pub right_arg: Option<DataType>,
|
||||
/// COMMUTATOR parameter (commutator operator)
|
||||
pub commutator: Option<ObjectName>,
|
||||
/// NEGATOR parameter (negator operator)
|
||||
pub negator: Option<ObjectName>,
|
||||
/// RESTRICT parameter (restriction selectivity function)
|
||||
pub restrict: Option<ObjectName>,
|
||||
/// JOIN parameter (join selectivity function)
|
||||
pub join: Option<ObjectName>,
|
||||
/// HASHES flag
|
||||
pub hashes: bool,
|
||||
/// MERGES flag
|
||||
pub merges: bool,
|
||||
/// Operator options (COMMUTATOR, NEGATOR, RESTRICT, JOIN, HASHES, MERGES)
|
||||
pub options: Vec<OperatorOption>,
|
||||
}
|
||||
|
||||
/// CREATE OPERATOR FAMILY statement
|
||||
|
|
@ -4044,23 +4131,9 @@ impl fmt::Display for CreateOperator {
|
|||
if let Some(right_arg) = &self.right_arg {
|
||||
params.push(format!("RIGHTARG = {}", right_arg));
|
||||
}
|
||||
if let Some(commutator) = &self.commutator {
|
||||
params.push(format!("COMMUTATOR = {}", commutator));
|
||||
}
|
||||
if let Some(negator) = &self.negator {
|
||||
params.push(format!("NEGATOR = {}", negator));
|
||||
}
|
||||
if let Some(restrict) = &self.restrict {
|
||||
params.push(format!("RESTRICT = {}", restrict));
|
||||
}
|
||||
if let Some(join) = &self.join {
|
||||
params.push(format!("JOIN = {}", join));
|
||||
}
|
||||
if self.hashes {
|
||||
params.push("HASHES".to_string());
|
||||
}
|
||||
if self.merges {
|
||||
params.push("MERGES".to_string());
|
||||
|
||||
for option in &self.options {
|
||||
params.push(option.to_string());
|
||||
}
|
||||
|
||||
write!(f, "{}", params.join(", "))?;
|
||||
|
|
|
|||
|
|
@ -99,15 +99,15 @@ impl fmt::Display for StageParamsObject {
|
|||
|
||||
impl fmt::Display for StageLoadSelectItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.alias.is_some() {
|
||||
write!(f, "{}.", self.alias.as_ref().unwrap())?;
|
||||
if let Some(alias) = &self.alias {
|
||||
write!(f, "{alias}.")?;
|
||||
}
|
||||
write!(f, "${}", self.file_col_num)?;
|
||||
if self.element.is_some() {
|
||||
write!(f, ":{}", self.element.as_ref().unwrap())?;
|
||||
if let Some(element) = &self.element {
|
||||
write!(f, ":{element}")?;
|
||||
}
|
||||
if self.item_as.is_some() {
|
||||
write!(f, " AS {}", self.item_as.as_ref().unwrap())?;
|
||||
if let Some(item_as) = &self.item_as {
|
||||
write!(f, " AS {item_as}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,23 +59,23 @@ pub use self::dcl::{
|
|||
AlterRoleOperation, CreateRole, ResetConfig, RoleOption, SecondaryRoles, SetConfigValue, Use,
|
||||
};
|
||||
pub use self::ddl::{
|
||||
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation,
|
||||
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
|
||||
AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
|
||||
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
|
||||
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
|
||||
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
|
||||
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterOperator,
|
||||
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, OperatorPurpose, Owner, Partition, ProcedureParam,
|
||||
ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind,
|
||||
Truncate, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
|
||||
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
|
||||
UserDefinedTypeStorage, ViewColumnDef,
|
||||
OperatorArgTypes, OperatorClassItem, OperatorOption, OperatorPurpose, Owner, Partition,
|
||||
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
|
||||
TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
|
||||
UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
|
||||
UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
|
||||
};
|
||||
pub use self::dml::{Delete, Insert, Update};
|
||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||
|
|
@ -3396,6 +3396,11 @@ pub enum Statement {
|
|||
/// ```
|
||||
AlterType(AlterType),
|
||||
/// ```sql
|
||||
/// ALTER OPERATOR
|
||||
/// ```
|
||||
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteroperator.html)
|
||||
AlterOperator(AlterOperator),
|
||||
/// ```sql
|
||||
/// ALTER ROLE
|
||||
/// ```
|
||||
AlterRole {
|
||||
|
|
@ -4971,6 +4976,7 @@ impl fmt::Display for Statement {
|
|||
Statement::AlterType(AlterType { name, operation }) => {
|
||||
write!(f, "ALTER TYPE {name} {operation}")
|
||||
}
|
||||
Statement::AlterOperator(alter_operator) => write!(f, "{alter_operator}"),
|
||||
Statement::AlterRole { name, operation } => {
|
||||
write!(f, "ALTER ROLE {name} {operation}")
|
||||
}
|
||||
|
|
@ -9814,8 +9820,8 @@ impl fmt::Display for ShowCharset {
|
|||
} else {
|
||||
write!(f, " CHARACTER SET")?;
|
||||
}
|
||||
if self.filter.is_some() {
|
||||
write!(f, " {}", self.filter.as_ref().unwrap())?;
|
||||
if let Some(filter) = &self.filter {
|
||||
write!(f, " {filter}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ impl Spanned for Values {
|
|||
/// - [Statement::CreateSecret]
|
||||
/// - [Statement::CreateRole]
|
||||
/// - [Statement::AlterType]
|
||||
/// - [Statement::AlterOperator]
|
||||
/// - [Statement::AlterRole]
|
||||
/// - [Statement::AttachDatabase]
|
||||
/// - [Statement::AttachDuckDBDatabase]
|
||||
|
|
@ -401,6 +402,7 @@ impl Spanned for Statement {
|
|||
),
|
||||
// These statements need to be implemented
|
||||
Statement::AlterType { .. } => Span::empty(),
|
||||
Statement::AlterOperator { .. } => Span::empty(),
|
||||
Statement::AlterRole { .. } => Span::empty(),
|
||||
Statement::AlterSession { .. } => Span::empty(),
|
||||
Statement::AttachDatabase { .. } => Span::empty(),
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ macro_rules! define_keywords {
|
|||
pub const ALL_KEYWORDS: &[&str] = &[
|
||||
$($ident),*
|
||||
];
|
||||
|
||||
impl core::fmt::Display for Keyword {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
Keyword::NoKeyword => write!(f, "NoKeyword"),
|
||||
$(Keyword::$ident => write!(f, "{}", $ident),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@
|
|||
// Splitting complex nodes (expressions, statements, types) into separate types
|
||||
// would bloat the API and hide intent. Extra memory is a worthwhile tradeoff.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
#![forbid(clippy::unreachable)]
|
||||
|
||||
// Allow proc-macros to find this crate
|
||||
extern crate self as sqlparser;
|
||||
|
|
|
|||
|
|
@ -1194,7 +1194,11 @@ impl<'a> Parser<'a> {
|
|||
let mut id_parts: Vec<Ident> = vec![match t {
|
||||
Token::Word(w) => w.into_ident(next_token.span),
|
||||
Token::SingleQuotedString(s) => Ident::with_quote('\'', s),
|
||||
_ => unreachable!(), // We matched above
|
||||
_ => {
|
||||
return Err(ParserError::ParserError(
|
||||
"Internal parser error: unexpected token type".to_string(),
|
||||
))
|
||||
}
|
||||
}];
|
||||
|
||||
while self.consume_token(&Token::Period) {
|
||||
|
|
@ -1641,7 +1645,11 @@ impl<'a> Parser<'a> {
|
|||
Token::PGSquareRoot => UnaryOperator::PGSquareRoot,
|
||||
Token::PGCubeRoot => UnaryOperator::PGCubeRoot,
|
||||
Token::AtSign => UnaryOperator::PGAbs,
|
||||
_ => unreachable!(),
|
||||
_ => {
|
||||
return Err(ParserError::ParserError(
|
||||
"Internal parser error: unexpected unary operator token".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(Expr::UnaryOp {
|
||||
op,
|
||||
|
|
@ -1709,18 +1717,22 @@ impl<'a> Parser<'a> {
|
|||
Ok(Expr::Value(self.parse_value()?))
|
||||
}
|
||||
Token::LParen => {
|
||||
let expr = if let Some(expr) = self.try_parse_expr_sub_query()? {
|
||||
expr
|
||||
} else if let Some(lambda) = self.try_parse_lambda()? {
|
||||
return Ok(lambda);
|
||||
} else {
|
||||
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
|
||||
match exprs.len() {
|
||||
0 => unreachable!(), // parse_comma_separated ensures 1 or more
|
||||
1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())),
|
||||
_ => Expr::Tuple(exprs),
|
||||
}
|
||||
};
|
||||
let expr =
|
||||
if let Some(expr) = self.try_parse_expr_sub_query()? {
|
||||
expr
|
||||
} else if let Some(lambda) = self.try_parse_lambda()? {
|
||||
return Ok(lambda);
|
||||
} else {
|
||||
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
|
||||
match exprs.len() {
|
||||
0 => return Err(ParserError::ParserError(
|
||||
"Internal parser error: parse_comma_separated returned empty list"
|
||||
.to_string(),
|
||||
)),
|
||||
1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())),
|
||||
_ => Expr::Tuple(exprs),
|
||||
}
|
||||
};
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(expr)
|
||||
}
|
||||
|
|
@ -3591,7 +3603,9 @@ impl<'a> Parser<'a> {
|
|||
right: Box::new(right),
|
||||
is_some: keyword == Keyword::SOME,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: expected any of {{ALL, ANY, SOME}}, got {unexpected_keyword:?}"),
|
||||
)),
|
||||
})
|
||||
} else {
|
||||
Ok(Expr::BinaryOp {
|
||||
|
|
@ -5590,13 +5604,14 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let option = self
|
||||
.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT])
|
||||
.map(|keyword| match keyword {
|
||||
Keyword::CASCADE => ReferentialAction::Cascade,
|
||||
Keyword::RESTRICT => ReferentialAction::Restrict,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let option = match self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]) {
|
||||
Some(Keyword::CASCADE) => Some(ReferentialAction::Cascade),
|
||||
Some(Keyword::RESTRICT) => Some(ReferentialAction::Restrict),
|
||||
Some(unexpected_keyword) => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: expected any of {{CASCADE, RESTRICT}}, got {unexpected_keyword:?}"),
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
Ok(Statement::DropTrigger(DropTrigger {
|
||||
if_exists,
|
||||
trigger_name,
|
||||
|
|
@ -5646,7 +5661,9 @@ impl<'a> Parser<'a> {
|
|||
match self.expect_one_of_keywords(&[Keyword::ROW, Keyword::STATEMENT])? {
|
||||
Keyword::ROW => TriggerObject::Row,
|
||||
Keyword::STATEMENT => TriggerObject::Statement,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in ROW/STATEMENT"),
|
||||
)),
|
||||
};
|
||||
|
||||
Some(if include_each {
|
||||
|
|
@ -5709,7 +5726,9 @@ impl<'a> Parser<'a> {
|
|||
Keyword::INSTEAD => self
|
||||
.expect_keyword_is(Keyword::OF)
|
||||
.map(|_| TriggerPeriod::InsteadOf)?,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in trigger period"),
|
||||
)),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -5733,7 +5752,9 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
Keyword::DELETE => TriggerEvent::Delete,
|
||||
Keyword::TRUNCATE => TriggerEvent::Truncate,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in trigger event"),
|
||||
)),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -5767,7 +5788,9 @@ impl<'a> Parser<'a> {
|
|||
{
|
||||
Keyword::FUNCTION => TriggerExecBodyType::Function,
|
||||
Keyword::PROCEDURE => TriggerExecBodyType::Procedure,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in trigger exec body"),
|
||||
)),
|
||||
},
|
||||
func_desc: self.parse_function_desc()?,
|
||||
})
|
||||
|
|
@ -6284,7 +6307,9 @@ impl<'a> Parser<'a> {
|
|||
Some(Keyword::CURRENT_USER) => Owner::CurrentUser,
|
||||
Some(Keyword::CURRENT_ROLE) => Owner::CurrentRole,
|
||||
Some(Keyword::SESSION_USER) => Owner::SessionUser,
|
||||
Some(_) => unreachable!(),
|
||||
Some(unexpected_keyword) => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in owner"),
|
||||
)),
|
||||
None => {
|
||||
match self.parse_identifier() {
|
||||
Ok(ident) => Owner::Ident(ident),
|
||||
|
|
@ -6346,7 +6371,9 @@ impl<'a> Parser<'a> {
|
|||
Some(match keyword {
|
||||
Keyword::PERMISSIVE => CreatePolicyType::Permissive,
|
||||
Keyword::RESTRICTIVE => CreatePolicyType::Restrictive,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in policy type"),
|
||||
)),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -6366,7 +6393,9 @@ impl<'a> Parser<'a> {
|
|||
Keyword::INSERT => CreatePolicyCommand::Insert,
|
||||
Keyword::UPDATE => CreatePolicyCommand::Update,
|
||||
Keyword::DELETE => CreatePolicyCommand::Delete,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in policy command"),
|
||||
)),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -6479,12 +6508,7 @@ impl<'a> Parser<'a> {
|
|||
let mut is_procedure = false;
|
||||
let mut left_arg: Option<DataType> = None;
|
||||
let mut right_arg: Option<DataType> = None;
|
||||
let mut commutator: Option<ObjectName> = None;
|
||||
let mut negator: Option<ObjectName> = None;
|
||||
let mut restrict: Option<ObjectName> = None;
|
||||
let mut join: Option<ObjectName> = None;
|
||||
let mut hashes = false;
|
||||
let mut merges = false;
|
||||
let mut options: Vec<OperatorOption> = Vec::new();
|
||||
|
||||
loop {
|
||||
let keyword = self.expect_one_of_keywords(&[
|
||||
|
|
@ -6501,11 +6525,11 @@ impl<'a> Parser<'a> {
|
|||
])?;
|
||||
|
||||
match keyword {
|
||||
Keyword::HASHES if !hashes => {
|
||||
hashes = true;
|
||||
Keyword::HASHES if !options.iter().any(|o| matches!(o, OperatorOption::Hashes)) => {
|
||||
options.push(OperatorOption::Hashes);
|
||||
}
|
||||
Keyword::MERGES if !merges => {
|
||||
merges = true;
|
||||
Keyword::MERGES if !options.iter().any(|o| matches!(o, OperatorOption::Merges)) => {
|
||||
options.push(OperatorOption::Merges);
|
||||
}
|
||||
Keyword::FUNCTION | Keyword::PROCEDURE if function.is_none() => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
|
|
@ -6520,33 +6544,49 @@ impl<'a> Parser<'a> {
|
|||
self.expect_token(&Token::Eq)?;
|
||||
right_arg = Some(self.parse_data_type()?);
|
||||
}
|
||||
Keyword::COMMUTATOR if commutator.is_none() => {
|
||||
Keyword::COMMUTATOR
|
||||
if !options
|
||||
.iter()
|
||||
.any(|o| matches!(o, OperatorOption::Commutator(_))) =>
|
||||
{
|
||||
self.expect_token(&Token::Eq)?;
|
||||
if self.parse_keyword(Keyword::OPERATOR) {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
commutator = Some(self.parse_operator_name()?);
|
||||
let op = self.parse_operator_name()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
options.push(OperatorOption::Commutator(op));
|
||||
} else {
|
||||
commutator = Some(self.parse_operator_name()?);
|
||||
options.push(OperatorOption::Commutator(self.parse_operator_name()?));
|
||||
}
|
||||
}
|
||||
Keyword::NEGATOR if negator.is_none() => {
|
||||
Keyword::NEGATOR
|
||||
if !options
|
||||
.iter()
|
||||
.any(|o| matches!(o, OperatorOption::Negator(_))) =>
|
||||
{
|
||||
self.expect_token(&Token::Eq)?;
|
||||
if self.parse_keyword(Keyword::OPERATOR) {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
negator = Some(self.parse_operator_name()?);
|
||||
let op = self.parse_operator_name()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
options.push(OperatorOption::Negator(op));
|
||||
} else {
|
||||
negator = Some(self.parse_operator_name()?);
|
||||
options.push(OperatorOption::Negator(self.parse_operator_name()?));
|
||||
}
|
||||
}
|
||||
Keyword::RESTRICT if restrict.is_none() => {
|
||||
Keyword::RESTRICT
|
||||
if !options
|
||||
.iter()
|
||||
.any(|o| matches!(o, OperatorOption::Restrict(_))) =>
|
||||
{
|
||||
self.expect_token(&Token::Eq)?;
|
||||
restrict = Some(self.parse_object_name(false)?);
|
||||
options.push(OperatorOption::Restrict(Some(
|
||||
self.parse_object_name(false)?,
|
||||
)));
|
||||
}
|
||||
Keyword::JOIN if join.is_none() => {
|
||||
Keyword::JOIN if !options.iter().any(|o| matches!(o, OperatorOption::Join(_))) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
join = Some(self.parse_object_name(false)?);
|
||||
options.push(OperatorOption::Join(Some(self.parse_object_name(false)?)));
|
||||
}
|
||||
_ => {
|
||||
return Err(ParserError::ParserError(format!(
|
||||
|
|
@ -6575,12 +6615,7 @@ impl<'a> Parser<'a> {
|
|||
is_procedure,
|
||||
left_arg,
|
||||
right_arg,
|
||||
commutator,
|
||||
negator,
|
||||
restrict,
|
||||
join,
|
||||
hashes,
|
||||
merges,
|
||||
options,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -6997,7 +7032,9 @@ impl<'a> Parser<'a> {
|
|||
match keyword {
|
||||
Keyword::WITH => Some(true),
|
||||
Keyword::WITHOUT => Some(false),
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in cursor hold"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
|
|
@ -9764,7 +9801,9 @@ impl<'a> Parser<'a> {
|
|||
Keyword::PART => Ok(Partition::Part(self.parse_expr()?)),
|
||||
Keyword::PARTITION => Ok(Partition::Expr(self.parse_expr()?)),
|
||||
// unreachable because expect_one_of_keywords used above
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => Err(ParserError::ParserError(
|
||||
format!("Internal parser error: expected any of {{PART, PARTITION}}, got {unexpected_keyword:?}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9780,6 +9819,7 @@ impl<'a> Parser<'a> {
|
|||
Keyword::ICEBERG,
|
||||
Keyword::SCHEMA,
|
||||
Keyword::USER,
|
||||
Keyword::OPERATOR,
|
||||
])?;
|
||||
match object_type {
|
||||
Keyword::SCHEMA => {
|
||||
|
|
@ -9812,12 +9852,15 @@ impl<'a> Parser<'a> {
|
|||
operation,
|
||||
})
|
||||
}
|
||||
Keyword::OPERATOR => self.parse_alter_operator(),
|
||||
Keyword::ROLE => self.parse_alter_role(),
|
||||
Keyword::POLICY => self.parse_alter_policy(),
|
||||
Keyword::CONNECTOR => self.parse_alter_connector(),
|
||||
Keyword::USER => self.parse_alter_user(),
|
||||
// unreachable because expect_one_of_keywords used above
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => Err(ParserError::ParserError(
|
||||
format!("Internal parser error: expected any of {{VIEW, TYPE, TABLE, INDEX, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR}}, got {unexpected_keyword:?}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9931,6 +9974,116 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a [Statement::AlterOperator]
|
||||
///
|
||||
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-alteroperator.html)
|
||||
pub fn parse_alter_operator(&mut self) -> Result<Statement, ParserError> {
|
||||
let name = self.parse_operator_name()?;
|
||||
|
||||
// Parse (left_type, right_type)
|
||||
self.expect_token(&Token::LParen)?;
|
||||
|
||||
let left_type = if self.parse_keyword(Keyword::NONE) {
|
||||
None
|
||||
} else {
|
||||
Some(self.parse_data_type()?)
|
||||
};
|
||||
|
||||
self.expect_token(&Token::Comma)?;
|
||||
let right_type = self.parse_data_type()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
// Parse the operation
|
||||
let operation = if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
|
||||
let owner = if self.parse_keyword(Keyword::CURRENT_ROLE) {
|
||||
Owner::CurrentRole
|
||||
} else if self.parse_keyword(Keyword::CURRENT_USER) {
|
||||
Owner::CurrentUser
|
||||
} else if self.parse_keyword(Keyword::SESSION_USER) {
|
||||
Owner::SessionUser
|
||||
} else {
|
||||
Owner::Ident(self.parse_identifier()?)
|
||||
};
|
||||
AlterOperatorOperation::OwnerTo(owner)
|
||||
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
|
||||
let schema_name = self.parse_object_name(false)?;
|
||||
AlterOperatorOperation::SetSchema { schema_name }
|
||||
} else if self.parse_keyword(Keyword::SET) {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
|
||||
let mut options = Vec::new();
|
||||
loop {
|
||||
let keyword = self.expect_one_of_keywords(&[
|
||||
Keyword::RESTRICT,
|
||||
Keyword::JOIN,
|
||||
Keyword::COMMUTATOR,
|
||||
Keyword::NEGATOR,
|
||||
Keyword::HASHES,
|
||||
Keyword::MERGES,
|
||||
])?;
|
||||
|
||||
match keyword {
|
||||
Keyword::RESTRICT => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let proc_name = if self.parse_keyword(Keyword::NONE) {
|
||||
None
|
||||
} else {
|
||||
Some(self.parse_object_name(false)?)
|
||||
};
|
||||
options.push(OperatorOption::Restrict(proc_name));
|
||||
}
|
||||
Keyword::JOIN => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let proc_name = if self.parse_keyword(Keyword::NONE) {
|
||||
None
|
||||
} else {
|
||||
Some(self.parse_object_name(false)?)
|
||||
};
|
||||
options.push(OperatorOption::Join(proc_name));
|
||||
}
|
||||
Keyword::COMMUTATOR => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let op_name = self.parse_operator_name()?;
|
||||
options.push(OperatorOption::Commutator(op_name));
|
||||
}
|
||||
Keyword::NEGATOR => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let op_name = self.parse_operator_name()?;
|
||||
options.push(OperatorOption::Negator(op_name));
|
||||
}
|
||||
Keyword::HASHES => {
|
||||
options.push(OperatorOption::Hashes);
|
||||
}
|
||||
Keyword::MERGES => {
|
||||
options.push(OperatorOption::Merges);
|
||||
}
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in operator option"),
|
||||
)),
|
||||
}
|
||||
|
||||
if !self.consume_token(&Token::Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.expect_token(&Token::RParen)?;
|
||||
AlterOperatorOperation::Set { options }
|
||||
} else {
|
||||
return self.expected_ref(
|
||||
"OWNER TO, SET SCHEMA, or SET after ALTER OPERATOR",
|
||||
self.peek_token_ref(),
|
||||
);
|
||||
};
|
||||
|
||||
Ok(Statement::AlterOperator(AlterOperator {
|
||||
name,
|
||||
left_type,
|
||||
right_type,
|
||||
operation,
|
||||
}))
|
||||
}
|
||||
|
||||
// Parse a [Statement::AlterSchema]
|
||||
// ALTER SCHEMA [ IF EXISTS ] schema_name
|
||||
pub fn parse_alter_schema(&mut self) -> Result<Statement, ParserError> {
|
||||
|
|
@ -14175,7 +14328,9 @@ impl<'a> Parser<'a> {
|
|||
table = match kw {
|
||||
Keyword::PIVOT => self.parse_pivot_table_factor(table)?,
|
||||
Keyword::UNPIVOT => self.parse_unpivot_table_factor(table)?,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in pivot/unpivot"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
return Ok(table);
|
||||
|
|
@ -14433,7 +14588,9 @@ impl<'a> Parser<'a> {
|
|||
table = match kw {
|
||||
Keyword::PIVOT => self.parse_pivot_table_factor(table)?,
|
||||
Keyword::UNPIVOT => self.parse_unpivot_table_factor(table)?,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in pivot/unpivot"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15532,7 +15689,9 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
Some(Keyword::TABLE) | None => Some(GrantObjects::Tables(objects?)),
|
||||
_ => unreachable!(),
|
||||
Some(unexpected_keyword) => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in grant objects"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -16402,7 +16561,9 @@ impl<'a> Parser<'a> {
|
|||
let kind = match self.expect_one_of_keywords(&[Keyword::MIN, Keyword::MAX])? {
|
||||
Keyword::MIN => HavingBoundKind::Min,
|
||||
Keyword::MAX => HavingBoundKind::Max,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in having bound"),
|
||||
)),
|
||||
};
|
||||
clauses.push(FunctionArgumentClause::Having(HavingBound(
|
||||
kind,
|
||||
|
|
@ -16930,7 +17091,9 @@ impl<'a> Parser<'a> {
|
|||
let lock_type = match self.expect_one_of_keywords(&[Keyword::UPDATE, Keyword::SHARE])? {
|
||||
Keyword::UPDATE => LockType::Update,
|
||||
Keyword::SHARE => LockType::Share,
|
||||
_ => unreachable!(),
|
||||
unexpected_keyword => return Err(ParserError::ParserError(
|
||||
format!("Internal parser error: expected any of {{UPDATE, SHARE}}, got {unexpected_keyword:?}"),
|
||||
)),
|
||||
};
|
||||
let of = if self.parse_keyword(Keyword::OF) {
|
||||
Some(self.parse_object_name(false)?)
|
||||
|
|
|
|||
|
|
@ -6715,24 +6715,26 @@ fn parse_create_operator() {
|
|||
length: 255,
|
||||
unit: None
|
||||
}))),
|
||||
commutator: Some(ObjectName::from(vec![
|
||||
Ident::new("schema"),
|
||||
Ident::new(">")
|
||||
])),
|
||||
negator: Some(ObjectName::from(vec![
|
||||
Ident::new("schema"),
|
||||
Ident::new("<=")
|
||||
])),
|
||||
restrict: Some(ObjectName::from(vec![
|
||||
Ident::new("myschema"),
|
||||
Ident::new("sel_func")
|
||||
])),
|
||||
join: Some(ObjectName::from(vec![
|
||||
Ident::new("myschema"),
|
||||
Ident::new("join_func")
|
||||
])),
|
||||
hashes: true,
|
||||
merges: true,
|
||||
options: vec![
|
||||
OperatorOption::Commutator(ObjectName::from(vec![
|
||||
Ident::new("schema"),
|
||||
Ident::new(">")
|
||||
])),
|
||||
OperatorOption::Negator(ObjectName::from(vec![
|
||||
Ident::new("schema"),
|
||||
Ident::new("<=")
|
||||
])),
|
||||
OperatorOption::Restrict(Some(ObjectName::from(vec![
|
||||
Ident::new("myschema"),
|
||||
Ident::new("sel_func")
|
||||
]))),
|
||||
OperatorOption::Join(Some(ObjectName::from(vec![
|
||||
Ident::new("myschema"),
|
||||
Ident::new("join_func")
|
||||
]))),
|
||||
OperatorOption::Hashes,
|
||||
OperatorOption::Merges,
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -6748,12 +6750,7 @@ fn parse_create_operator() {
|
|||
is_procedure: false,
|
||||
left_arg: None,
|
||||
right_arg: None,
|
||||
commutator: None,
|
||||
negator: None,
|
||||
restrict: None,
|
||||
join: None,
|
||||
hashes: false,
|
||||
merges: false,
|
||||
options: vec![],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -6778,13 +6775,9 @@ fn parse_create_operator() {
|
|||
),
|
||||
] {
|
||||
match pg().verified_stmt(&format!("CREATE OPERATOR {name} (FUNCTION = f)")) {
|
||||
Statement::CreateOperator(CreateOperator {
|
||||
name,
|
||||
hashes: false,
|
||||
merges: false,
|
||||
..
|
||||
}) => {
|
||||
Statement::CreateOperator(CreateOperator { name, options, .. }) => {
|
||||
assert_eq!(name, expected_name);
|
||||
assert!(options.is_empty());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
@ -6920,6 +6913,202 @@ fn parse_drop_operator() {
|
|||
assert!(pg().parse_sql_statements(sql).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_alter_operator() {
|
||||
use sqlparser::ast::{AlterOperator, AlterOperatorOperation, OperatorOption, Owner};
|
||||
|
||||
// Test ALTER OPERATOR ... OWNER TO with different owner types
|
||||
for (owner_sql, owner_ast) in [
|
||||
("joe", Owner::Ident(Ident::new("joe"))),
|
||||
("CURRENT_USER", Owner::CurrentUser),
|
||||
("CURRENT_ROLE", Owner::CurrentRole),
|
||||
("SESSION_USER", Owner::SessionUser),
|
||||
] {
|
||||
for (op_name, op_name_ast, left_type_sql, left_type_ast, right_type_sql, right_type_ast) in [
|
||||
(
|
||||
"+",
|
||||
ObjectName::from(vec![Ident::new("+")]),
|
||||
"INTEGER",
|
||||
Some(DataType::Integer(None)),
|
||||
"INTEGER",
|
||||
DataType::Integer(None),
|
||||
),
|
||||
(
|
||||
"~",
|
||||
ObjectName::from(vec![Ident::new("~")]),
|
||||
"NONE",
|
||||
None,
|
||||
"BIT",
|
||||
DataType::Bit(None),
|
||||
),
|
||||
(
|
||||
"@@",
|
||||
ObjectName::from(vec![Ident::new("@@")]),
|
||||
"TEXT",
|
||||
Some(DataType::Text),
|
||||
"TEXT",
|
||||
DataType::Text,
|
||||
),
|
||||
] {
|
||||
let sql = format!(
|
||||
"ALTER OPERATOR {} ({}, {}) OWNER TO {}",
|
||||
op_name, left_type_sql, right_type_sql, owner_sql
|
||||
);
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(&sql),
|
||||
Statement::AlterOperator(AlterOperator {
|
||||
name: op_name_ast.clone(),
|
||||
left_type: left_type_ast.clone(),
|
||||
right_type: right_type_ast.clone(),
|
||||
operation: AlterOperatorOperation::OwnerTo(owner_ast.clone()),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Test ALTER OPERATOR ... SET SCHEMA
|
||||
for (op_name, op_name_ast, schema_name, schema_name_ast) in [
|
||||
(
|
||||
"+",
|
||||
ObjectName::from(vec![Ident::new("+")]),
|
||||
"new_schema",
|
||||
ObjectName::from(vec![Ident::new("new_schema")]),
|
||||
),
|
||||
(
|
||||
"myschema.@@",
|
||||
ObjectName::from(vec![Ident::new("myschema"), Ident::new("@@")]),
|
||||
"other_schema",
|
||||
ObjectName::from(vec![Ident::new("other_schema")]),
|
||||
),
|
||||
] {
|
||||
let sql = format!(
|
||||
"ALTER OPERATOR {} (TEXT, TEXT) SET SCHEMA {}",
|
||||
op_name, schema_name
|
||||
);
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(&sql),
|
||||
Statement::AlterOperator(AlterOperator {
|
||||
name: op_name_ast,
|
||||
left_type: Some(DataType::Text),
|
||||
right_type: DataType::Text,
|
||||
operation: AlterOperatorOperation::SetSchema {
|
||||
schema_name: schema_name_ast,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Test ALTER OPERATOR ... SET with RESTRICT and JOIN
|
||||
for (restrict_val, restrict_ast, join_val, join_ast) in [
|
||||
(
|
||||
"_int_contsel",
|
||||
Some(ObjectName::from(vec![Ident::new("_int_contsel")])),
|
||||
"_int_contjoinsel",
|
||||
Some(ObjectName::from(vec![Ident::new("_int_contjoinsel")])),
|
||||
),
|
||||
(
|
||||
"NONE",
|
||||
None,
|
||||
"my_joinsel",
|
||||
Some(ObjectName::from(vec![Ident::new("my_joinsel")])),
|
||||
),
|
||||
(
|
||||
"my_sel",
|
||||
Some(ObjectName::from(vec![Ident::new("my_sel")])),
|
||||
"NONE",
|
||||
None,
|
||||
),
|
||||
] {
|
||||
let sql = format!(
|
||||
"ALTER OPERATOR && (TEXT, TEXT) SET (RESTRICT = {}, JOIN = {})",
|
||||
restrict_val, join_val
|
||||
);
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(&sql),
|
||||
Statement::AlterOperator(AlterOperator {
|
||||
name: ObjectName::from(vec![Ident::new("&&")]),
|
||||
left_type: Some(DataType::Text),
|
||||
right_type: DataType::Text,
|
||||
operation: AlterOperatorOperation::Set {
|
||||
options: vec![
|
||||
OperatorOption::Restrict(restrict_ast),
|
||||
OperatorOption::Join(join_ast),
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Test ALTER OPERATOR ... SET with COMMUTATOR and NEGATOR
|
||||
for (operator, commutator, negator) in [("&&", "&&", ">"), ("+", "+", "-"), ("<", "<", ">=")] {
|
||||
let sql = format!(
|
||||
"ALTER OPERATOR {} (INTEGER, INTEGER) SET (COMMUTATOR = {}, NEGATOR = {})",
|
||||
operator, commutator, negator
|
||||
);
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(&sql),
|
||||
Statement::AlterOperator(AlterOperator {
|
||||
name: ObjectName::from(vec![Ident::new(operator)]),
|
||||
left_type: Some(DataType::Integer(None)),
|
||||
right_type: DataType::Integer(None),
|
||||
operation: AlterOperatorOperation::Set {
|
||||
options: vec![
|
||||
OperatorOption::Commutator(ObjectName::from(vec![Ident::new(commutator)])),
|
||||
OperatorOption::Negator(ObjectName::from(vec![Ident::new(negator)])),
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Test ALTER OPERATOR ... SET with HASHES and MERGES (individually and combined)
|
||||
for (operator, options_sql, options_ast) in [
|
||||
("=", "HASHES", vec![OperatorOption::Hashes]),
|
||||
("<", "MERGES", vec![OperatorOption::Merges]),
|
||||
(
|
||||
"<=",
|
||||
"HASHES, MERGES",
|
||||
vec![OperatorOption::Hashes, OperatorOption::Merges],
|
||||
),
|
||||
] {
|
||||
let sql = format!(
|
||||
"ALTER OPERATOR {} (INTEGER, INTEGER) SET ({})",
|
||||
operator, options_sql
|
||||
);
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(&sql),
|
||||
Statement::AlterOperator(AlterOperator {
|
||||
name: ObjectName::from(vec![Ident::new(operator)]),
|
||||
left_type: Some(DataType::Integer(None)),
|
||||
right_type: DataType::Integer(None),
|
||||
operation: AlterOperatorOperation::Set {
|
||||
options: options_ast
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Test ALTER OPERATOR ... SET with multiple options combined
|
||||
let sql =
|
||||
"ALTER OPERATOR + (INTEGER, INTEGER) SET (COMMUTATOR = +, NEGATOR = -, HASHES, MERGES)";
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(sql),
|
||||
Statement::AlterOperator(AlterOperator {
|
||||
name: ObjectName::from(vec![Ident::new("+")]),
|
||||
left_type: Some(DataType::Integer(None)),
|
||||
right_type: DataType::Integer(None),
|
||||
operation: AlterOperatorOperation::Set {
|
||||
options: vec![
|
||||
OperatorOption::Commutator(ObjectName::from(vec![Ident::new("+")])),
|
||||
OperatorOption::Negator(ObjectName::from(vec![Ident::new("-")])),
|
||||
OperatorOption::Hashes,
|
||||
OperatorOption::Merges,
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_drop_operator_family() {
|
||||
for if_exists in [true, false] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue