use crate::dialect::{Dialect, DialectKind, SqlType};
use crate::driver::ExecuteResult;
use crate::executor::Executor;
use crate::index::{IndexColumn, IndexDef};
use crate::query::expr::Expr;
use crate::row::Row;
use crate::value::Value;
use super::ddl::{ColumnSpec, DefaultValue, ForeignKeyAction, ForeignKeySpec, TableDef};
use super::{render, BoxFuture};
pub trait DynExecutor: Sync {
fn dialect(&self) -> &dyn Dialect;
fn execute<'a>(
&'a self,
sql: String,
params: Vec<Value>,
) -> BoxFuture<'a, crate::Result<ExecuteResult>>;
fn fetch_all<'a>(
&'a self,
sql: String,
params: Vec<Value>,
) -> BoxFuture<'a, crate::Result<Vec<Row>>>;
}
impl<E: Executor + Sync> DynExecutor for E {
fn dialect(&self) -> &dyn Dialect {
Executor::dialect(self)
}
fn execute<'a>(
&'a self,
sql: String,
params: Vec<Value>,
) -> BoxFuture<'a, crate::Result<ExecuteResult>> {
Box::pin(Executor::execute(self, sql, params))
}
fn fetch_all<'a>(
&'a self,
sql: String,
params: Vec<Value>,
) -> BoxFuture<'a, crate::Result<Vec<Row>>> {
Box::pin(Executor::fetch_all(self, sql, params))
}
}
enum Mode<'e> {
Execute(&'e dyn DynExecutor),
Collect(Vec<String>),
}
pub struct SchemaManager<'e> {
mode: Mode<'e>,
dialect: &'e dyn Dialect,
}
impl<'e> SchemaManager<'e> {
pub(crate) fn executing(executor: &'e dyn DynExecutor) -> Self {
Self {
dialect: executor.dialect(),
mode: Mode::Execute(executor),
}
}
pub fn collect(dialect: &'e dyn Dialect) -> Self {
Self {
mode: Mode::Collect(Vec::new()),
dialect,
}
}
pub fn into_collected(self) -> Vec<String> {
match self.mode {
Mode::Collect(buffer) => buffer,
Mode::Execute(_) => Vec::new(),
}
}
async fn dispatch(&mut self, statements: Vec<String>) -> crate::Result<()> {
let executor = match &mut self.mode {
Mode::Execute(executor) => *executor,
Mode::Collect(buffer) => {
buffer.extend(statements);
return Ok(());
}
};
for statement in statements {
executor.execute(statement, Vec::new()).await?;
}
Ok(())
}
pub fn create_table(&mut self, name: &str) -> CreateTable<'_, 'e> {
CreateTable {
schema: self,
def: TableDef::new(name),
}
}
pub fn drop_table(&mut self, name: &str) -> DropTable<'_, 'e> {
DropTable {
schema: self,
name: name.to_string(),
if_exists: false,
}
}
pub fn create_index(&mut self, name: &str) -> CreateIndex<'_, 'e> {
CreateIndex {
schema: self,
table: String::new(),
def: IndexDef::new(name),
if_not_exists: false,
}
}
pub fn drop_index(&mut self, name: &str) -> DropIndex<'_, 'e> {
DropIndex {
schema: self,
name: name.to_string(),
if_exists: false,
}
}
pub fn create_trigger(&mut self, name: &str) -> CreateTrigger<'_, 'e> {
CreateTrigger {
schema: self,
name: name.to_string(),
timing: TriggerTiming::Before,
event: TriggerEvent::Insert,
table: String::new(),
for_each_row: false,
body: String::new(),
}
}
pub fn drop_trigger(&mut self, name: &str) -> DropTrigger<'_, 'e> {
DropTrigger {
schema: self,
name: name.to_string(),
table: None,
if_exists: false,
}
}
pub async fn raw(&mut self, sql: &str) -> crate::Result<()> {
self.dispatch(vec![sql.to_string()]).await
}
pub async fn raw_for(&mut self, kind: DialectKind, sql: &str) -> crate::Result<()> {
if self.dialect.kind() == kind {
self.dispatch(vec![sql.to_string()]).await
} else {
Ok(())
}
}
}
pub struct CreateTable<'a, 'e> {
schema: &'a mut SchemaManager<'e>,
def: TableDef,
}
impl CreateTable<'_, '_> {
pub fn if_not_exists(mut self) -> Self {
self.def.if_not_exists = true;
self
}
pub fn column(mut self, column: Column) -> Self {
self.def.columns.push(column.into_spec());
self
}
pub fn primary_key(mut self, columns: &[&str]) -> Self {
self.def.primary_key = columns.iter().map(|c| c.to_string()).collect();
self
}
pub fn foreign_key(mut self, foreign_key: ForeignKey) -> Self {
self.def.foreign_keys.push(foreign_key.into_spec());
self
}
pub fn check(mut self, expression: impl Into<String>) -> Self {
self.def.checks.push(expression.into());
self
}
pub fn timestamps(mut self) -> Self {
self.def.columns.push(timestamp_column("created_at"));
self.def.columns.push(timestamp_column("updated_at"));
self
}
pub async fn execute(self) -> crate::Result<()> {
let statements = render::create_table(self.schema.dialect, &self.def)?;
self.schema.dispatch(statements).await
}
}
pub struct DropTable<'a, 'e> {
schema: &'a mut SchemaManager<'e>,
name: String,
if_exists: bool,
}
impl DropTable<'_, '_> {
pub fn if_exists(mut self) -> Self {
self.if_exists = true;
self
}
pub async fn execute(self) -> crate::Result<()> {
let statement = render::drop_table(self.schema.dialect, &self.name, self.if_exists);
self.schema.dispatch(vec![statement]).await
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TriggerTiming {
Before,
After,
}
impl TriggerTiming {
fn as_sql(self) -> &'static str {
match self {
TriggerTiming::Before => "BEFORE",
TriggerTiming::After => "AFTER",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TriggerEvent {
Insert,
Update,
Delete,
}
impl TriggerEvent {
fn as_sql(self) -> &'static str {
match self {
TriggerEvent::Insert => "INSERT",
TriggerEvent::Update => "UPDATE",
TriggerEvent::Delete => "DELETE",
}
}
}
pub struct CreateTrigger<'a, 'e> {
schema: &'a mut SchemaManager<'e>,
name: String,
timing: TriggerTiming,
event: TriggerEvent,
table: String,
for_each_row: bool,
body: String,
}
impl CreateTrigger<'_, '_> {
pub fn timing(mut self, timing: TriggerTiming) -> Self {
self.timing = timing;
self
}
pub fn before(self) -> Self {
self.timing(TriggerTiming::Before)
}
pub fn after(self) -> Self {
self.timing(TriggerTiming::After)
}
pub fn event(mut self, event: TriggerEvent) -> Self {
self.event = event;
self
}
pub fn on(mut self, table: &str) -> Self {
self.table = table.to_string();
self
}
pub fn for_each_row(mut self) -> Self {
self.for_each_row = true;
self
}
pub fn body(mut self, body: &str) -> Self {
self.body = body.to_string();
self
}
pub async fn execute(self) -> crate::Result<()> {
let mut sql = String::from("CREATE TRIGGER ");
self.schema.dialect.quote_identifier(&self.name, &mut sql);
sql.push(' ');
sql.push_str(self.timing.as_sql());
sql.push(' ');
sql.push_str(self.event.as_sql());
sql.push_str(" ON ");
self.schema.dialect.quote_identifier(&self.table, &mut sql);
if self.for_each_row {
sql.push_str(" FOR EACH ROW");
}
if !self.body.is_empty() {
sql.push(' ');
sql.push_str(&self.body);
}
self.schema.dispatch(vec![sql]).await
}
}
pub struct DropTrigger<'a, 'e> {
schema: &'a mut SchemaManager<'e>,
name: String,
table: Option<String>,
if_exists: bool,
}
impl DropTrigger<'_, '_> {
pub fn if_exists(mut self) -> Self {
self.if_exists = true;
self
}
pub fn on(mut self, table: &str) -> Self {
self.table = Some(table.to_string());
self
}
pub async fn execute(self) -> crate::Result<()> {
let mut sql = String::from("DROP TRIGGER ");
if self.if_exists {
sql.push_str("IF EXISTS ");
}
self.schema.dialect.quote_identifier(&self.name, &mut sql);
if self.schema.dialect.kind() == DialectKind::Postgres {
if let Some(table) = &self.table {
sql.push_str(" ON ");
self.schema.dialect.quote_identifier(table, &mut sql);
}
}
self.schema.dispatch(vec![sql]).await
}
}
pub struct CreateIndex<'a, 'e> {
schema: &'a mut SchemaManager<'e>,
table: String,
def: IndexDef,
if_not_exists: bool,
}
impl CreateIndex<'_, '_> {
pub fn on_table(mut self, table: &str) -> Self {
self.table = table.to_string();
self
}
pub fn unique(mut self) -> Self {
self.def.unique = true;
self
}
pub fn column(mut self, column: IndexColumn) -> Self {
self.def.columns.push(column);
self
}
pub fn columns(mut self, columns: impl IntoIterator<Item = IndexColumn>) -> Self {
self.def.columns.extend(columns);
self
}
pub fn method(mut self, method: impl Into<String>) -> Self {
self.def.method = Some(method.into());
self
}
pub fn include<I, S>(mut self, columns: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.def
.include
.extend(columns.into_iter().map(Into::into));
self
}
pub fn where_(mut self, predicate: Expr) -> Self {
self.def.predicate = Some(predicate);
self
}
pub fn if_not_exists(mut self) -> Self {
self.if_not_exists = true;
self
}
pub async fn execute(self) -> crate::Result<()> {
let statement =
render::create_index(self.schema.dialect, &self.table, &self.def, self.if_not_exists)?;
self.schema.dispatch(vec![statement]).await
}
}
pub struct DropIndex<'a, 'e> {
schema: &'a mut SchemaManager<'e>,
name: String,
if_exists: bool,
}
impl DropIndex<'_, '_> {
pub fn if_exists(mut self) -> Self {
self.if_exists = true;
self
}
pub async fn execute(self) -> crate::Result<()> {
let statement = render::drop_index(self.schema.dialect, &self.name, self.if_exists);
self.schema.dispatch(vec![statement]).await
}
}
fn timestamp_column(name: &str) -> ColumnSpec {
ColumnSpec {
name: name.to_string(),
ty: SqlType::Timestamp,
nullable: false,
primary_key: false,
auto_increment: false,
unique: false,
default: Some(DefaultValue::CurrentTimestamp),
}
}
pub struct Column {
spec: ColumnSpec,
}
impl Column {
pub fn new(name: impl Into<String>) -> Self {
let mut spec = ColumnSpec::new(name, SqlType::Text);
spec.nullable = true;
Self { spec }
}
pub fn integer(mut self) -> Self {
self.spec.ty = SqlType::Integer;
self
}
pub fn bigint(mut self) -> Self {
self.spec.ty = SqlType::BigInt;
self
}
pub fn varchar(mut self, length: u32) -> Self {
debug_assert!(length > 0, "varchar length must be > 0");
self.spec.ty = SqlType::Varchar(length);
self
}
pub fn text(mut self) -> Self {
self.spec.ty = SqlType::Text;
self
}
pub fn boolean(mut self) -> Self {
self.spec.ty = SqlType::Boolean;
self
}
pub fn real(mut self) -> Self {
self.spec.ty = SqlType::Real;
self
}
pub fn timestamp(mut self) -> Self {
self.spec.ty = SqlType::Timestamp;
self
}
pub fn blob(mut self) -> Self {
self.spec.ty = SqlType::Blob;
self
}
pub fn json(mut self) -> Self {
self.spec.ty = SqlType::Json;
self
}
pub fn uuid(mut self) -> Self {
self.spec.ty = SqlType::Uuid;
self
}
pub fn enum_type(
mut self,
name: &'static str,
variants: &'static [&'static str],
) -> Self {
self.spec.ty = SqlType::Enum { name, variants };
self
}
pub fn not_null(mut self) -> Self {
self.spec.nullable = false;
self
}
pub fn nullable(mut self) -> Self {
self.spec.nullable = true;
self
}
pub fn primary_key(mut self) -> Self {
self.spec.primary_key = true;
self
}
pub fn auto_increment(mut self) -> Self {
self.spec.auto_increment = true;
self
}
pub fn unique(mut self) -> Self {
self.spec.unique = true;
self
}
pub fn default(mut self, value: impl Into<DefaultValue>) -> Self {
self.spec.default = Some(value.into());
self
}
fn into_spec(self) -> ColumnSpec {
self.spec
}
}
pub struct ForeignKey {
spec: ForeignKeySpec,
}
impl ForeignKey {
pub fn new() -> Self {
Self {
spec: ForeignKeySpec {
columns: Vec::new(),
ref_table: String::new(),
ref_columns: Vec::new(),
on_delete: ForeignKeyAction::NoAction,
on_update: ForeignKeyAction::NoAction,
},
}
}
pub fn from(mut self, table: &str, column: &str) -> Self {
let _ = table;
self.spec.columns.push(column.to_string());
self
}
pub fn to(mut self, table: &str, column: &str) -> Self {
self.spec.ref_table = table.to_string();
self.spec.ref_columns.push(column.to_string());
self
}
pub fn on_delete(mut self, action: ForeignKeyAction) -> Self {
self.spec.on_delete = action;
self
}
pub fn on_update(mut self, action: ForeignKeyAction) -> Self {
self.spec.on_update = action;
self
}
fn into_spec(self) -> ForeignKeySpec {
self.spec
}
}
impl Default for ForeignKey {
fn default() -> Self {
Self::new()
}
}