Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Triggers #824

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,20 @@ mod index_builder;
mod query_builder;
mod table_builder;
mod table_ref_builder;
mod trigger_builder;
// mod trigger_ref_builder;

pub use self::foreign_key_builder::*;
pub use self::index_builder::*;
pub use self::query_builder::*;
pub use self::table_builder::*;
pub use self::table_ref_builder::*;
pub use self::trigger_builder::*;
// pub use self::trigger_ref_builder::*;

pub trait GenericBuilder: QueryBuilder + SchemaBuilder {}

pub trait SchemaBuilder: TableBuilder + IndexBuilder + ForeignKeyBuilder {}
pub trait SchemaBuilder: TableBuilder + IndexBuilder + ForeignKeyBuilder + TriggerBuilder {}

pub trait QuotedBuilder {
/// The type of quote the builder uses.
Expand Down
1 change: 1 addition & 0 deletions src/backend/mysql/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub(crate) mod foreign_key;
pub(crate) mod index;
pub(crate) mod query;
pub(crate) mod table;
pub(crate) mod trigger;

use super::*;

Expand Down
3 changes: 3 additions & 0 deletions src/backend/mysql/trigger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use super::*;

impl TriggerBuilder for MysqlQueryBuilder {}
1 change: 1 addition & 0 deletions src/backend/postgres/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub(crate) mod foreign_key;
pub(crate) mod index;
pub(crate) mod query;
pub(crate) mod table;
pub(crate) mod trigger;
pub(crate) mod types;

use super::*;
Expand Down
3 changes: 3 additions & 0 deletions src/backend/postgres/trigger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use super::*;

impl TriggerBuilder for PostgresQueryBuilder {}
1 change: 1 addition & 0 deletions src/backend/sqlite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub(crate) mod foreign_key;
pub(crate) mod index;
pub(crate) mod query;
pub(crate) mod table;
pub(crate) mod trigger;

use super::*;

Expand Down
3 changes: 3 additions & 0 deletions src/backend/sqlite/trigger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use super::*;

impl TriggerBuilder for SqliteQueryBuilder {}
61 changes: 61 additions & 0 deletions src/backend/trigger_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::*;

pub trait TriggerBuilder: TableRefBuilder + QueryBuilder {
/// Translate [`TriggerCreateStatement`] into SQL statement.
fn prepare_trigger_create_statement(
&self,
create: &TriggerCreateStatement,
sql: &mut dyn SqlWriter,
) {
write!(sql, "CREATE TRIGGER ").unwrap();
self.prepare_create_trigger_if_not_exists(create, sql);

let trigger_ref = match &create.trigger.name {
Some(value) => value,
// auto-generate trigger name
_ => &create.trigger.trigger_ref(),
};
let trigger_ref: TableRef = trigger_ref.into();
self.prepare_table_ref_iden(&trigger_ref, sql);
write!(sql, " {} {} ON ", create.trigger.time, create.trigger.event).unwrap();
self.prepare_table_ref_iden(&create.trigger.table, sql);
write!(sql, " FOR EACH ROW\nBEGIN\n").unwrap();
Copy link

Choose a reason for hiding this comment

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

It would be nice to have statement level triggers although they aren't available in mysql.


self.prepare_trigger_actions(&create.trigger.actions, sql);

write!(sql, "END").unwrap();
}

fn prepare_trigger_actions(&self, actions: &TriggerActions, sql: &mut dyn SqlWriter) {
for action in actions {
self.prepare_simple_expr_common(&action, sql);
write!(sql, ";\n").unwrap();
}
}

/// Translate IF NOT EXISTS expression in [`TriggerCreateStatement`].
fn prepare_create_trigger_if_not_exists(
&self,
create: &TriggerCreateStatement,
sql: &mut dyn SqlWriter,
) {
if create.if_not_exists {
write!(sql, "IF NOT EXISTS ").unwrap();
}
}

// /// Translate [`TriggerRef`] into SQL statement.
// fn prepare_table_ref(&self, trigger_ref: &TableRef, sql: &mut dyn SqlWriter) {
// self.prepare_table_ref_iden(trigger_ref, sql)
// }

/// Translate [`TriggerDropStatement`] into SQL statement.
fn prepare_trigger_drop_statement(&self, drop: &TriggerDropStatement, sql: &mut dyn SqlWriter) {
write!(sql, "DROP TRIGGER ").unwrap();
self.prepare_table_ref_iden(&drop.name.clone().into(), sql);
}

fn prepare_simple_expr_yeah(&self, simple_expr: &SimpleExpr, sql: &mut dyn SqlWriter) {
self.prepare_simple_expr_common(simple_expr, sql);
}
}
12 changes: 12 additions & 0 deletions src/backend/trigger_ref_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::*;

pub trait TriggerRefBuilder: QuotedBuilder {
/// Translate [`TriggerRef`] that without values into SQL statement.
fn prepare_trigger_ref_iden(&self, table_ref: &TriggerRef, sql: &mut dyn SqlWriter) {
match table_ref {
TriggerRef::Trigger(iden) => {
iden.prepare(sql.as_writer(), self.quote());
}
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ pub mod query;
pub mod schema;
pub mod table;
pub mod token;
pub mod trigger;
pub mod types;
pub mod value;

Expand All @@ -841,6 +842,7 @@ pub use query::*;
pub use schema::*;
pub use table::*;
pub use token::*;
pub use trigger::*;
pub use types::*;
pub use value::*;

Expand Down
40 changes: 40 additions & 0 deletions src/trigger/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use super::DefinedTrigger;
use crate::{backend::SchemaBuilder, SchemaStatementBuilder};
use inherent::inherent;

#[derive(Debug, Clone)]
pub struct TriggerCreateStatement {
pub(crate) trigger: DefinedTrigger,
pub(crate) if_not_exists: bool,
}

impl TriggerCreateStatement {
pub fn new(trigger: DefinedTrigger) -> Self {
TriggerCreateStatement {
trigger,
if_not_exists: false,
}
}

pub fn if_not_exists(&mut self) -> &mut Self {
self.if_not_exists = true;
self
}
}

#[inherent]
impl SchemaStatementBuilder for TriggerCreateStatement {
pub fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
let mut sql = String::with_capacity(256);
schema_builder.prepare_trigger_create_statement(self, &mut sql);
sql
}

pub fn build_any(&self, schema_builder: &dyn SchemaBuilder) -> String {
let mut sql = String::with_capacity(256);
schema_builder.prepare_trigger_create_statement(self, &mut sql);
sql
}

pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String;
}
99 changes: 99 additions & 0 deletions src/trigger/drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use super::TriggerRef;
use crate::{backend::SchemaBuilder, SchemaStatementBuilder};
use inherent::inherent;

/// Drop a trigger
///
/// # Examples
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let trigger = NamedTrigger::new("my_trigger")
/// .to_owned();
///
/// let drop_stmt = trigger.drop();
///
/// assert_eq!(
/// drop_stmt.to_string(MysqlQueryBuilder),
/// r#"DROP TRIGGER `my_trigger`"#
/// );
/// assert_eq!(
/// drop_stmt.to_string(PostgresQueryBuilder),
/// r#"DROP TRIGGER "my_trigger""#
/// );
/// assert_eq!(
/// drop_stmt.to_string(SqliteQueryBuilder),
/// r#"DROP TRIGGER "my_trigger""#
/// );
/// ```
///
/// # Trigger names can be derived from table name, action and action time
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let trigger = UnnamedTrigger::new()
/// .before_insert(Glyph::Table);
///
/// let drop_stmt = trigger.drop();
///
/// assert_eq!(
/// drop_stmt.to_string(MysqlQueryBuilder),
/// r#"DROP TRIGGER `t_glyph_before_insert`"#
/// );
/// assert_eq!(
/// drop_stmt.to_string(PostgresQueryBuilder),
/// r#"DROP TRIGGER "t_glyph_before_insert""#
/// );
/// assert_eq!(
/// drop_stmt.to_string(SqliteQueryBuilder),
/// r#"DROP TRIGGER "t_glyph_before_insert""#
/// );
///
/// ```
#[derive(Debug, Clone)]
pub struct TriggerDropStatement {
pub(crate) name: TriggerRef,
pub(crate) if_exists: bool,
}

impl TriggerDropStatement {
/// Construct drop table statement
pub fn new(name: TriggerRef) -> Self {
Self {
name: name,
if_exists: false,
}
}

/// Drop table if exists
pub fn if_exists(&mut self) -> &mut Self {
self.if_exists = true;
self
}

pub fn take(&mut self) -> Self {
Self {
name: std::mem::take(&mut self.name),
if_exists: self.if_exists,
}
}
}

#[inherent]
impl SchemaStatementBuilder for TriggerDropStatement {
pub fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
let mut sql = String::with_capacity(256);
schema_builder.prepare_trigger_drop_statement(self, &mut sql);
sql
}

pub fn build_any(&self, schema_builder: &dyn SchemaBuilder) -> String {
let mut sql = String::with_capacity(256);
schema_builder.prepare_trigger_drop_statement(self, &mut sql);
sql
}

pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String;
}
Loading