Skip to content

Commit 280f518

Browse files
Moved CreateTrigger and DropTrigger out of Statement enum (#2026)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent b8539a5 commit 280f518

File tree

7 files changed

+270
-244
lines changed

7 files changed

+270
-244
lines changed

src/ast/ddl.rs

Lines changed: 228 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
2121
#[cfg(not(feature = "std"))]
2222
use alloc::{boxed::Box, string::String, vec::Vec};
23-
use core::fmt::{self, Write};
23+
use core::fmt::{self, Display, Write};
2424

2525
#[cfg(feature = "serde")]
2626
use serde::{Deserialize, Serialize};
@@ -30,14 +30,15 @@ use sqlparser_derive::{Visit, VisitMut};
3030

3131
use crate::ast::value::escape_single_quote_string;
3232
use crate::ast::{
33-
display_comma_separated, display_separated, ArgMode, CommentDef, CreateFunctionBody,
34-
CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, DataType, Expr, FileFormat,
35-
FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel,
36-
HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident, InitializeKind,
37-
MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg,
38-
OrderByExpr, ProjectionSelect, Query, RefreshModeKind, RowAccessPolicy, SequenceOptions,
39-
Spanned, SqlOption, StorageSerializationPolicy, TableVersion, Tag, Value, ValueWithSpan,
40-
WrappedCollection,
33+
display_comma_separated, display_separated, ArgMode, CommentDef, ConditionalStatements,
34+
CreateFunctionBody, CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, DataType,
35+
Expr, FileFormat, FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier,
36+
FunctionParallel, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
37+
InitializeKind, MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens,
38+
OperateFunctionArg, OrderByExpr, ProjectionSelect, Query, RefreshModeKind, RowAccessPolicy,
39+
SequenceOptions, Spanned, SqlOption, StorageSerializationPolicy, TableVersion, Tag,
40+
TriggerEvent, TriggerExecBody, TriggerObject, TriggerPeriod, TriggerReferencing, Value,
41+
ValueWithSpan, WrappedCollection,
4142
};
4243
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
4344
use crate::keywords::Keyword;
@@ -3176,3 +3177,221 @@ impl Spanned for RenameTableNameKind {
31763177
}
31773178
}
31783179
}
3180+
3181+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3182+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3183+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3184+
/// CREATE TRIGGER
3185+
///
3186+
/// Examples:
3187+
///
3188+
/// ```sql
3189+
/// CREATE TRIGGER trigger_name
3190+
/// BEFORE INSERT ON table_name
3191+
/// FOR EACH ROW
3192+
/// EXECUTE FUNCTION trigger_function();
3193+
/// ```
3194+
///
3195+
/// Postgres: <https://www.postgresql.org/docs/current/sql-createtrigger.html>
3196+
/// SQL Server: <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql>
3197+
pub struct CreateTrigger {
3198+
/// True if this is a `CREATE OR ALTER TRIGGER` statement
3199+
///
3200+
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver16#arguments)
3201+
pub or_alter: bool,
3202+
/// The `OR REPLACE` clause is used to re-create the trigger if it already exists.
3203+
///
3204+
/// Example:
3205+
/// ```sql
3206+
/// CREATE OR REPLACE TRIGGER trigger_name
3207+
/// AFTER INSERT ON table_name
3208+
/// FOR EACH ROW
3209+
/// EXECUTE FUNCTION trigger_function();
3210+
/// ```
3211+
pub or_replace: bool,
3212+
/// The `CONSTRAINT` keyword is used to create a trigger as a constraint.
3213+
pub is_constraint: bool,
3214+
/// The name of the trigger to be created.
3215+
pub name: ObjectName,
3216+
/// Determines whether the function is called before, after, or instead of the event.
3217+
///
3218+
/// Example of BEFORE:
3219+
///
3220+
/// ```sql
3221+
/// CREATE TRIGGER trigger_name
3222+
/// BEFORE INSERT ON table_name
3223+
/// FOR EACH ROW
3224+
/// EXECUTE FUNCTION trigger_function();
3225+
/// ```
3226+
///
3227+
/// Example of AFTER:
3228+
///
3229+
/// ```sql
3230+
/// CREATE TRIGGER trigger_name
3231+
/// AFTER INSERT ON table_name
3232+
/// FOR EACH ROW
3233+
/// EXECUTE FUNCTION trigger_function();
3234+
/// ```
3235+
///
3236+
/// Example of INSTEAD OF:
3237+
///
3238+
/// ```sql
3239+
/// CREATE TRIGGER trigger_name
3240+
/// INSTEAD OF INSERT ON table_name
3241+
/// FOR EACH ROW
3242+
/// EXECUTE FUNCTION trigger_function();
3243+
/// ```
3244+
pub period: TriggerPeriod,
3245+
/// Whether the trigger period was specified before the target table name.
3246+
///
3247+
/// ```sql
3248+
/// -- period_before_table == true: Postgres, MySQL, and standard SQL
3249+
/// CREATE TRIGGER t BEFORE INSERT ON table_name ...;
3250+
/// -- period_before_table == false: MSSQL
3251+
/// CREATE TRIGGER t ON table_name BEFORE INSERT ...;
3252+
/// ```
3253+
pub period_before_table: bool,
3254+
/// Multiple events can be specified using OR, such as `INSERT`, `UPDATE`, `DELETE`, or `TRUNCATE`.
3255+
pub events: Vec<TriggerEvent>,
3256+
/// The table on which the trigger is to be created.
3257+
pub table_name: ObjectName,
3258+
/// The optional referenced table name that can be referenced via
3259+
/// the `FROM` keyword.
3260+
pub referenced_table_name: Option<ObjectName>,
3261+
/// This keyword immediately precedes the declaration of one or two relation names that provide access to the transition relations of the triggering statement.
3262+
pub referencing: Vec<TriggerReferencing>,
3263+
/// This specifies whether the trigger function should be fired once for
3264+
/// every row affected by the trigger event, or just once per SQL statement.
3265+
pub trigger_object: TriggerObject,
3266+
/// Whether to include the `EACH` term of the `FOR EACH`, as it is optional syntax.
3267+
pub include_each: bool,
3268+
/// Triggering conditions
3269+
pub condition: Option<Expr>,
3270+
/// Execute logic block
3271+
pub exec_body: Option<TriggerExecBody>,
3272+
/// For MSSQL and dialects where statements are preceded by `AS`
3273+
pub statements_as: bool,
3274+
/// For SQL dialects with statement(s) for a body
3275+
pub statements: Option<ConditionalStatements>,
3276+
/// The characteristic of the trigger, which include whether the trigger is `DEFERRABLE`, `INITIALLY DEFERRED`, or `INITIALLY IMMEDIATE`,
3277+
pub characteristics: Option<ConstraintCharacteristics>,
3278+
}
3279+
3280+
impl Display for CreateTrigger {
3281+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3282+
let CreateTrigger {
3283+
or_alter,
3284+
or_replace,
3285+
is_constraint,
3286+
name,
3287+
period_before_table,
3288+
period,
3289+
events,
3290+
table_name,
3291+
referenced_table_name,
3292+
referencing,
3293+
trigger_object,
3294+
condition,
3295+
include_each,
3296+
exec_body,
3297+
statements_as,
3298+
statements,
3299+
characteristics,
3300+
} = self;
3301+
write!(
3302+
f,
3303+
"CREATE {or_alter}{or_replace}{is_constraint}TRIGGER {name} ",
3304+
or_alter = if *or_alter { "OR ALTER " } else { "" },
3305+
or_replace = if *or_replace { "OR REPLACE " } else { "" },
3306+
is_constraint = if *is_constraint { "CONSTRAINT " } else { "" },
3307+
)?;
3308+
3309+
if *period_before_table {
3310+
write!(f, "{period}")?;
3311+
if !events.is_empty() {
3312+
write!(f, " {}", display_separated(events, " OR "))?;
3313+
}
3314+
write!(f, " ON {table_name}")?;
3315+
} else {
3316+
write!(f, "ON {table_name}")?;
3317+
write!(f, " {period}")?;
3318+
if !events.is_empty() {
3319+
write!(f, " {}", display_separated(events, ", "))?;
3320+
}
3321+
}
3322+
3323+
if let Some(referenced_table_name) = referenced_table_name {
3324+
write!(f, " FROM {referenced_table_name}")?;
3325+
}
3326+
3327+
if let Some(characteristics) = characteristics {
3328+
write!(f, " {characteristics}")?;
3329+
}
3330+
3331+
if !referencing.is_empty() {
3332+
write!(f, " REFERENCING {}", display_separated(referencing, " "))?;
3333+
}
3334+
3335+
if *include_each {
3336+
write!(f, " FOR EACH {trigger_object}")?;
3337+
} else if exec_body.is_some() {
3338+
write!(f, " FOR {trigger_object}")?;
3339+
}
3340+
if let Some(condition) = condition {
3341+
write!(f, " WHEN {condition}")?;
3342+
}
3343+
if let Some(exec_body) = exec_body {
3344+
write!(f, " EXECUTE {exec_body}")?;
3345+
}
3346+
if let Some(statements) = statements {
3347+
if *statements_as {
3348+
write!(f, " AS")?;
3349+
}
3350+
write!(f, " {statements}")?;
3351+
}
3352+
Ok(())
3353+
}
3354+
}
3355+
3356+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3357+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3358+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3359+
/// DROP TRIGGER
3360+
///
3361+
/// ```sql
3362+
/// DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
3363+
/// ```
3364+
///
3365+
pub struct DropTrigger {
3366+
/// Whether to include the `IF EXISTS` clause.
3367+
pub if_exists: bool,
3368+
/// The name of the trigger to be dropped.
3369+
pub trigger_name: ObjectName,
3370+
/// The name of the table from which the trigger is to be dropped.
3371+
pub table_name: Option<ObjectName>,
3372+
/// `CASCADE` or `RESTRICT`
3373+
pub option: Option<ReferentialAction>,
3374+
}
3375+
3376+
impl fmt::Display for DropTrigger {
3377+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3378+
let DropTrigger {
3379+
if_exists,
3380+
trigger_name,
3381+
table_name,
3382+
option,
3383+
} = self;
3384+
write!(f, "DROP TRIGGER")?;
3385+
if *if_exists {
3386+
write!(f, " IF EXISTS")?;
3387+
}
3388+
match &table_name {
3389+
Some(table_name) => write!(f, " {trigger_name} ON {table_name}")?,
3390+
None => write!(f, " {trigger_name}")?,
3391+
};
3392+
if let Some(option) = option {
3393+
write!(f, " {option}")?;
3394+
}
3395+
Ok(())
3396+
}
3397+
}

0 commit comments

Comments
 (0)