mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Add CREATE TRIGGER
support for SQL Server (#1810)
This commit is contained in:
parent
728645fb31
commit
a497358c3a
7 changed files with 255 additions and 41 deletions
|
@ -2380,11 +2380,16 @@ impl fmt::Display for BeginEndStatements {
|
|||
end_token: AttachedToken(end_token),
|
||||
} = self;
|
||||
|
||||
write!(f, "{begin_token} ")?;
|
||||
if begin_token.token != Token::EOF {
|
||||
write!(f, "{begin_token} ")?;
|
||||
}
|
||||
if !statements.is_empty() {
|
||||
format_statement_list(f, statements)?;
|
||||
}
|
||||
write!(f, " {end_token}")
|
||||
if end_token.token != Token::EOF {
|
||||
write!(f, " {end_token}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3729,7 +3734,12 @@ pub enum Statement {
|
|||
/// ```
|
||||
///
|
||||
/// Postgres: <https://www.postgresql.org/docs/current/sql-createtrigger.html>
|
||||
/// SQL Server: <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql>
|
||||
CreateTrigger {
|
||||
/// True if this is a `CREATE OR ALTER TRIGGER` statement
|
||||
///
|
||||
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver16#arguments)
|
||||
or_alter: bool,
|
||||
/// The `OR REPLACE` clause is used to re-create the trigger if it already exists.
|
||||
///
|
||||
/// Example:
|
||||
|
@ -3790,7 +3800,9 @@ pub enum Statement {
|
|||
/// Triggering conditions
|
||||
condition: Option<Expr>,
|
||||
/// Execute logic block
|
||||
exec_body: TriggerExecBody,
|
||||
exec_body: Option<TriggerExecBody>,
|
||||
/// For SQL dialects with statement(s) for a body
|
||||
statements: Option<ConditionalStatements>,
|
||||
/// The characteristic of the trigger, which include whether the trigger is `DEFERRABLE`, `INITIALLY DEFERRED`, or `INITIALLY IMMEDIATE`,
|
||||
characteristics: Option<ConstraintCharacteristics>,
|
||||
},
|
||||
|
@ -4587,6 +4599,7 @@ impl fmt::Display for Statement {
|
|||
}
|
||||
Statement::CreateFunction(create_function) => create_function.fmt(f),
|
||||
Statement::CreateTrigger {
|
||||
or_alter,
|
||||
or_replace,
|
||||
is_constraint,
|
||||
name,
|
||||
|
@ -4599,19 +4612,30 @@ impl fmt::Display for Statement {
|
|||
condition,
|
||||
include_each,
|
||||
exec_body,
|
||||
statements,
|
||||
characteristics,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"CREATE {or_replace}{is_constraint}TRIGGER {name} {period}",
|
||||
"CREATE {or_alter}{or_replace}{is_constraint}TRIGGER {name} ",
|
||||
or_alter = if *or_alter { "OR ALTER " } else { "" },
|
||||
or_replace = if *or_replace { "OR REPLACE " } else { "" },
|
||||
is_constraint = if *is_constraint { "CONSTRAINT " } else { "" },
|
||||
)?;
|
||||
|
||||
if !events.is_empty() {
|
||||
write!(f, " {}", display_separated(events, " OR "))?;
|
||||
if exec_body.is_some() {
|
||||
write!(f, "{period}")?;
|
||||
if !events.is_empty() {
|
||||
write!(f, " {}", display_separated(events, " OR "))?;
|
||||
}
|
||||
write!(f, " ON {table_name}")?;
|
||||
} else {
|
||||
write!(f, "ON {table_name}")?;
|
||||
write!(f, " {period}")?;
|
||||
if !events.is_empty() {
|
||||
write!(f, " {}", display_separated(events, ", "))?;
|
||||
}
|
||||
}
|
||||
write!(f, " ON {table_name}")?;
|
||||
|
||||
if let Some(referenced_table_name) = referenced_table_name {
|
||||
write!(f, " FROM {referenced_table_name}")?;
|
||||
|
@ -4627,13 +4651,19 @@ impl fmt::Display for Statement {
|
|||
|
||||
if *include_each {
|
||||
write!(f, " FOR EACH {trigger_object}")?;
|
||||
} else {
|
||||
} else if exec_body.is_some() {
|
||||
write!(f, " FOR {trigger_object}")?;
|
||||
}
|
||||
if let Some(condition) = condition {
|
||||
write!(f, " WHEN {condition}")?;
|
||||
}
|
||||
write!(f, " EXECUTE {exec_body}")
|
||||
if let Some(exec_body) = exec_body {
|
||||
write!(f, " EXECUTE {exec_body}")?;
|
||||
}
|
||||
if let Some(statements) = statements {
|
||||
write!(f, " AS {statements}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Statement::DropTrigger {
|
||||
if_exists,
|
||||
|
|
|
@ -110,6 +110,7 @@ impl fmt::Display for TriggerEvent {
|
|||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum TriggerPeriod {
|
||||
For,
|
||||
After,
|
||||
Before,
|
||||
InsteadOf,
|
||||
|
@ -118,6 +119,7 @@ pub enum TriggerPeriod {
|
|||
impl fmt::Display for TriggerPeriod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
TriggerPeriod::For => write!(f, "FOR"),
|
||||
TriggerPeriod::After => write!(f, "AFTER"),
|
||||
TriggerPeriod::Before => write!(f, "BEFORE"),
|
||||
TriggerPeriod::InsteadOf => write!(f, "INSTEAD OF"),
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
use crate::ast::helpers::attached_token::AttachedToken;
|
||||
use crate::ast::{
|
||||
BeginEndStatements, ConditionalStatementBlock, ConditionalStatements, IfStatement, Statement,
|
||||
TriggerObject,
|
||||
};
|
||||
use crate::dialect::Dialect;
|
||||
use crate::keywords::{self, Keyword};
|
||||
|
@ -125,6 +126,15 @@ impl Dialect for MsSqlDialect {
|
|||
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
|
||||
if parser.peek_keyword(Keyword::IF) {
|
||||
Some(self.parse_if_stmt(parser))
|
||||
} else if parser.parse_keywords(&[Keyword::CREATE, Keyword::TRIGGER]) {
|
||||
Some(self.parse_create_trigger(parser, false))
|
||||
} else if parser.parse_keywords(&[
|
||||
Keyword::CREATE,
|
||||
Keyword::OR,
|
||||
Keyword::ALTER,
|
||||
Keyword::TRIGGER,
|
||||
]) {
|
||||
Some(self.parse_create_trigger(parser, true))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -215,6 +225,42 @@ impl MsSqlDialect {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Parse `CREATE TRIGGER` for [MsSql]
|
||||
///
|
||||
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
|
||||
fn parse_create_trigger(
|
||||
&self,
|
||||
parser: &mut Parser,
|
||||
or_alter: bool,
|
||||
) -> Result<Statement, ParserError> {
|
||||
let name = parser.parse_object_name(false)?;
|
||||
parser.expect_keyword_is(Keyword::ON)?;
|
||||
let table_name = parser.parse_object_name(false)?;
|
||||
let period = parser.parse_trigger_period()?;
|
||||
let events = parser.parse_comma_separated(Parser::parse_trigger_event)?;
|
||||
|
||||
parser.expect_keyword_is(Keyword::AS)?;
|
||||
let statements = Some(parser.parse_conditional_statements(&[Keyword::END])?);
|
||||
|
||||
Ok(Statement::CreateTrigger {
|
||||
or_alter,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name,
|
||||
period,
|
||||
events,
|
||||
table_name,
|
||||
referenced_table_name: None,
|
||||
referencing: Vec::new(),
|
||||
trigger_object: TriggerObject::Statement,
|
||||
include_each: false,
|
||||
condition: None,
|
||||
exec_body: None,
|
||||
statements,
|
||||
characteristics: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a sequence of statements, optionally separated by semicolon.
|
||||
///
|
||||
/// Stops parsing when reaching EOF or the given keyword.
|
||||
|
|
|
@ -745,19 +745,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let conditional_statements = if self.peek_keyword(Keyword::BEGIN) {
|
||||
let begin_token = self.expect_keyword(Keyword::BEGIN)?;
|
||||
let statements = self.parse_statement_list(terminal_keywords)?;
|
||||
let end_token = self.expect_keyword(Keyword::END)?;
|
||||
ConditionalStatements::BeginEnd(BeginEndStatements {
|
||||
begin_token: AttachedToken(begin_token),
|
||||
statements,
|
||||
end_token: AttachedToken(end_token),
|
||||
})
|
||||
} else {
|
||||
let statements = self.parse_statement_list(terminal_keywords)?;
|
||||
ConditionalStatements::Sequence { statements }
|
||||
};
|
||||
let conditional_statements = self.parse_conditional_statements(terminal_keywords)?;
|
||||
|
||||
Ok(ConditionalStatementBlock {
|
||||
start_token: AttachedToken(start_token),
|
||||
|
@ -767,6 +755,30 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parse a BEGIN/END block or a sequence of statements
|
||||
/// This could be inside of a conditional (IF, CASE, WHILE etc.) or an object body defined optionally BEGIN/END and one or more statements.
|
||||
pub(crate) fn parse_conditional_statements(
|
||||
&mut self,
|
||||
terminal_keywords: &[Keyword],
|
||||
) -> Result<ConditionalStatements, ParserError> {
|
||||
let conditional_statements = if self.peek_keyword(Keyword::BEGIN) {
|
||||
let begin_token = self.expect_keyword(Keyword::BEGIN)?;
|
||||
let statements = self.parse_statement_list(terminal_keywords)?;
|
||||
let end_token = self.expect_keyword(Keyword::END)?;
|
||||
|
||||
ConditionalStatements::BeginEnd(BeginEndStatements {
|
||||
begin_token: AttachedToken(begin_token),
|
||||
statements,
|
||||
end_token: AttachedToken(end_token),
|
||||
})
|
||||
} else {
|
||||
ConditionalStatements::Sequence {
|
||||
statements: self.parse_statement_list(terminal_keywords)?,
|
||||
}
|
||||
};
|
||||
Ok(conditional_statements)
|
||||
}
|
||||
|
||||
/// Parse a `RAISE` statement.
|
||||
///
|
||||
/// See [Statement::Raise]
|
||||
|
@ -4614,9 +4626,9 @@ impl<'a> Parser<'a> {
|
|||
} else if self.parse_keyword(Keyword::FUNCTION) {
|
||||
self.parse_create_function(or_alter, or_replace, temporary)
|
||||
} else if self.parse_keyword(Keyword::TRIGGER) {
|
||||
self.parse_create_trigger(or_replace, false)
|
||||
self.parse_create_trigger(or_alter, or_replace, false)
|
||||
} else if self.parse_keywords(&[Keyword::CONSTRAINT, Keyword::TRIGGER]) {
|
||||
self.parse_create_trigger(or_replace, true)
|
||||
self.parse_create_trigger(or_alter, or_replace, true)
|
||||
} else if self.parse_keyword(Keyword::MACRO) {
|
||||
self.parse_create_macro(or_replace, temporary)
|
||||
} else if self.parse_keyword(Keyword::SECRET) {
|
||||
|
@ -5314,10 +5326,11 @@ impl<'a> Parser<'a> {
|
|||
|
||||
pub fn parse_create_trigger(
|
||||
&mut self,
|
||||
or_alter: bool,
|
||||
or_replace: bool,
|
||||
is_constraint: bool,
|
||||
) -> Result<Statement, ParserError> {
|
||||
if !dialect_of!(self is PostgreSqlDialect | GenericDialect | MySqlDialect) {
|
||||
if !dialect_of!(self is PostgreSqlDialect | GenericDialect | MySqlDialect | MsSqlDialect) {
|
||||
self.prev_token();
|
||||
return self.expected("an object type after CREATE", self.peek_token());
|
||||
}
|
||||
|
@ -5363,6 +5376,7 @@ impl<'a> Parser<'a> {
|
|||
let exec_body = self.parse_trigger_exec_body()?;
|
||||
|
||||
Ok(Statement::CreateTrigger {
|
||||
or_alter,
|
||||
or_replace,
|
||||
is_constraint,
|
||||
name,
|
||||
|
@ -5374,7 +5388,8 @@ impl<'a> Parser<'a> {
|
|||
trigger_object,
|
||||
include_each,
|
||||
condition,
|
||||
exec_body,
|
||||
exec_body: Some(exec_body),
|
||||
statements: None,
|
||||
characteristics,
|
||||
})
|
||||
}
|
||||
|
@ -5382,10 +5397,12 @@ impl<'a> Parser<'a> {
|
|||
pub fn parse_trigger_period(&mut self) -> Result<TriggerPeriod, ParserError> {
|
||||
Ok(
|
||||
match self.expect_one_of_keywords(&[
|
||||
Keyword::FOR,
|
||||
Keyword::BEFORE,
|
||||
Keyword::AFTER,
|
||||
Keyword::INSTEAD,
|
||||
])? {
|
||||
Keyword::FOR => TriggerPeriod::For,
|
||||
Keyword::BEFORE => TriggerPeriod::Before,
|
||||
Keyword::AFTER => TriggerPeriod::After,
|
||||
Keyword::INSTEAD => self
|
||||
|
|
|
@ -273,6 +273,16 @@ fn parse_create_function() {
|
|||
END\
|
||||
";
|
||||
let _ = ms().verified_stmt(create_or_alter_function);
|
||||
|
||||
let create_function_with_return_expression = "\
|
||||
CREATE FUNCTION some_scalar_udf(@foo INT, @bar VARCHAR(256)) \
|
||||
RETURNS INT \
|
||||
AS \
|
||||
BEGIN \
|
||||
RETURN CONVERT(INT, 1) + 2; \
|
||||
END\
|
||||
";
|
||||
let _ = ms().verified_stmt(create_function_with_return_expression);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2199,6 +2209,101 @@ fn parse_mssql_merge_with_output() {
|
|||
ms_and_generic().verified_stmt(stmt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_trigger() {
|
||||
let create_trigger = "\
|
||||
CREATE OR ALTER TRIGGER reminder1 \
|
||||
ON Sales.Customer \
|
||||
AFTER INSERT, UPDATE \
|
||||
AS RAISERROR('Notify Customer Relations', 16, 10);\
|
||||
";
|
||||
let create_stmt = ms().verified_stmt(create_trigger);
|
||||
assert_eq!(
|
||||
create_stmt,
|
||||
Statement::CreateTrigger {
|
||||
or_alter: true,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("reminder1")]),
|
||||
period: TriggerPeriod::After,
|
||||
events: vec![TriggerEvent::Insert, TriggerEvent::Update(vec![]),],
|
||||
table_name: ObjectName::from(vec![Ident::new("Sales"), Ident::new("Customer")]),
|
||||
referenced_table_name: None,
|
||||
referencing: vec![],
|
||||
trigger_object: TriggerObject::Statement,
|
||||
include_each: false,
|
||||
condition: None,
|
||||
exec_body: None,
|
||||
statements: Some(ConditionalStatements::Sequence {
|
||||
statements: vec![Statement::RaisError {
|
||||
message: Box::new(Expr::Value(
|
||||
(Value::SingleQuotedString("Notify Customer Relations".to_string()))
|
||||
.with_empty_span()
|
||||
)),
|
||||
severity: Box::new(Expr::Value(
|
||||
(Value::Number("16".parse().unwrap(), false)).with_empty_span()
|
||||
)),
|
||||
state: Box::new(Expr::Value(
|
||||
(Value::Number("10".parse().unwrap(), false)).with_empty_span()
|
||||
)),
|
||||
arguments: vec![],
|
||||
options: vec![],
|
||||
}],
|
||||
}),
|
||||
characteristics: None,
|
||||
}
|
||||
);
|
||||
|
||||
let multi_statement_as_trigger = "\
|
||||
CREATE TRIGGER some_trigger ON some_table FOR INSERT \
|
||||
AS \
|
||||
DECLARE @var INT; \
|
||||
RAISERROR('Trigger fired', 10, 1);\
|
||||
";
|
||||
let _ = ms().verified_stmt(multi_statement_as_trigger);
|
||||
|
||||
let multi_statement_trigger = "\
|
||||
CREATE TRIGGER some_trigger ON some_table FOR INSERT \
|
||||
AS \
|
||||
BEGIN \
|
||||
DECLARE @var INT; \
|
||||
RAISERROR('Trigger fired', 10, 1); \
|
||||
END\
|
||||
";
|
||||
let _ = ms().verified_stmt(multi_statement_trigger);
|
||||
|
||||
let create_trigger_with_return = "\
|
||||
CREATE TRIGGER some_trigger ON some_table FOR INSERT \
|
||||
AS \
|
||||
BEGIN \
|
||||
RETURN; \
|
||||
END\
|
||||
";
|
||||
let _ = ms().verified_stmt(create_trigger_with_return);
|
||||
|
||||
let create_trigger_with_return = "\
|
||||
CREATE TRIGGER some_trigger ON some_table FOR INSERT \
|
||||
AS \
|
||||
BEGIN \
|
||||
RETURN; \
|
||||
END\
|
||||
";
|
||||
let _ = ms().verified_stmt(create_trigger_with_return);
|
||||
|
||||
let create_trigger_with_conditional = "\
|
||||
CREATE TRIGGER some_trigger ON some_table FOR INSERT \
|
||||
AS \
|
||||
BEGIN \
|
||||
IF 1 = 2 \
|
||||
BEGIN \
|
||||
RAISERROR('Trigger fired', 10, 1); \
|
||||
END; \
|
||||
RETURN; \
|
||||
END\
|
||||
";
|
||||
let _ = ms().verified_stmt(create_trigger_with_conditional);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_drop_trigger() {
|
||||
let sql_drop_trigger = "DROP TRIGGER emp_stamp;";
|
||||
|
|
|
@ -3779,6 +3779,7 @@ fn parse_create_trigger() {
|
|||
assert_eq!(
|
||||
create_stmt,
|
||||
Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
|
||||
|
@ -3790,13 +3791,14 @@ fn parse_create_trigger() {
|
|||
trigger_object: TriggerObject::Row,
|
||||
include_each: true,
|
||||
condition: None,
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
|
||||
args: None,
|
||||
}
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: None,
|
||||
}
|
||||
);
|
||||
|
|
|
@ -5157,6 +5157,7 @@ fn test_escaped_string_literal() {
|
|||
fn parse_create_simple_before_insert_trigger() {
|
||||
let sql = "CREATE TRIGGER check_insert BEFORE INSERT ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_insert";
|
||||
let expected = Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("check_insert")]),
|
||||
|
@ -5168,13 +5169,14 @@ fn parse_create_simple_before_insert_trigger() {
|
|||
trigger_object: TriggerObject::Row,
|
||||
include_each: true,
|
||||
condition: None,
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("check_account_insert")]),
|
||||
args: None,
|
||||
},
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: None,
|
||||
};
|
||||
|
||||
|
@ -5185,6 +5187,7 @@ fn parse_create_simple_before_insert_trigger() {
|
|||
fn parse_create_after_update_trigger_with_condition() {
|
||||
let sql = "CREATE TRIGGER check_update AFTER UPDATE ON accounts FOR EACH ROW WHEN (NEW.balance > 10000) EXECUTE FUNCTION check_account_update";
|
||||
let expected = Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("check_update")]),
|
||||
|
@ -5203,13 +5206,14 @@ fn parse_create_after_update_trigger_with_condition() {
|
|||
op: BinaryOperator::Gt,
|
||||
right: Box::new(Expr::value(number("10000"))),
|
||||
}))),
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("check_account_update")]),
|
||||
args: None,
|
||||
},
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: None,
|
||||
};
|
||||
|
||||
|
@ -5220,6 +5224,7 @@ fn parse_create_after_update_trigger_with_condition() {
|
|||
fn parse_create_instead_of_delete_trigger() {
|
||||
let sql = "CREATE TRIGGER check_delete INSTEAD OF DELETE ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_deletes";
|
||||
let expected = Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("check_delete")]),
|
||||
|
@ -5231,13 +5236,14 @@ fn parse_create_instead_of_delete_trigger() {
|
|||
trigger_object: TriggerObject::Row,
|
||||
include_each: true,
|
||||
condition: None,
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("check_account_deletes")]),
|
||||
args: None,
|
||||
},
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: None,
|
||||
};
|
||||
|
||||
|
@ -5248,6 +5254,7 @@ fn parse_create_instead_of_delete_trigger() {
|
|||
fn parse_create_trigger_with_multiple_events_and_deferrable() {
|
||||
let sql = "CREATE CONSTRAINT TRIGGER check_multiple_events BEFORE INSERT OR UPDATE OR DELETE ON accounts DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_account_changes";
|
||||
let expected = Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: true,
|
||||
name: ObjectName::from(vec![Ident::new("check_multiple_events")]),
|
||||
|
@ -5263,13 +5270,14 @@ fn parse_create_trigger_with_multiple_events_and_deferrable() {
|
|||
trigger_object: TriggerObject::Row,
|
||||
include_each: true,
|
||||
condition: None,
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("check_account_changes")]),
|
||||
args: None,
|
||||
},
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: Some(ConstraintCharacteristics {
|
||||
deferrable: Some(true),
|
||||
initially: Some(DeferrableInitial::Deferred),
|
||||
|
@ -5284,6 +5292,7 @@ fn parse_create_trigger_with_multiple_events_and_deferrable() {
|
|||
fn parse_create_trigger_with_referencing() {
|
||||
let sql = "CREATE TRIGGER check_referencing BEFORE INSERT ON accounts REFERENCING NEW TABLE AS new_accounts OLD TABLE AS old_accounts FOR EACH ROW EXECUTE FUNCTION check_account_referencing";
|
||||
let expected = Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("check_referencing")]),
|
||||
|
@ -5306,13 +5315,14 @@ fn parse_create_trigger_with_referencing() {
|
|||
trigger_object: TriggerObject::Row,
|
||||
include_each: true,
|
||||
condition: None,
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("check_account_referencing")]),
|
||||
args: None,
|
||||
},
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: None,
|
||||
};
|
||||
|
||||
|
@ -5332,7 +5342,7 @@ fn parse_create_trigger_invalid_cases() {
|
|||
),
|
||||
(
|
||||
"CREATE TRIGGER check_update TOMORROW UPDATE ON accounts EXECUTE FUNCTION check_account_update",
|
||||
"Expected: one of BEFORE or AFTER or INSTEAD, found: TOMORROW"
|
||||
"Expected: one of FOR or BEFORE or AFTER or INSTEAD, found: TOMORROW"
|
||||
),
|
||||
(
|
||||
"CREATE TRIGGER check_update BEFORE SAVE ON accounts EXECUTE FUNCTION check_account_update",
|
||||
|
@ -5590,6 +5600,7 @@ fn parse_trigger_related_functions() {
|
|||
assert_eq!(
|
||||
create_trigger,
|
||||
Statement::CreateTrigger {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
is_constraint: false,
|
||||
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
|
||||
|
@ -5601,13 +5612,14 @@ fn parse_trigger_related_functions() {
|
|||
trigger_object: TriggerObject::Row,
|
||||
include_each: true,
|
||||
condition: None,
|
||||
exec_body: TriggerExecBody {
|
||||
exec_body: Some(TriggerExecBody {
|
||||
exec_type: TriggerExecBodyType::Function,
|
||||
func_desc: FunctionDesc {
|
||||
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
|
||||
args: None,
|
||||
}
|
||||
},
|
||||
}),
|
||||
statements: None,
|
||||
characteristics: None
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue