use std::{ops::Deref, sync::Arc};
use crate::{
data_definition::table::{DatabaseTableDefinition, Identifier, TableColumn},
AsSql, BuildSql,
};
#[derive(PartialEq, Eq, Debug, Default)]
pub struct PrimaryKeyColumnConstraint {
index_parameters: Option<IndexParameters>,
}
impl BuildSql for PrimaryKeyColumnConstraint {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
let mut statement = "PRIMARY KEY".to_string();
sql.push("PRIMARY KEY ");
if let Some(params) = &self.index_parameters {
params.build_sql(sql);
}
}
}
struct CheckExpressionConstraint {}
impl BuildSql for CheckExpressionConstraint {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
todo!()
}
}
#[derive(PartialEq, Eq, Debug, Default)]
pub struct IndexParameters {}
impl BuildSql for IndexParameters {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
todo!()
}
}
impl BuildSql for Vec<Identifier> {
fn build_sql(
&self,
builder: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
let mut idents = self.iter().peekable();
while let Some(col) = idents.next() {
builder.push_bind(col.to_string());
if idents.peek().is_some() {
builder.push(", ");
}
}
}
}
#[derive(PartialEq, Eq, Debug, Default)]
pub struct UniqueColumnConstraint {
is_null_distinct: bool,
index_parameters: Option<IndexParameters>,
}
impl BuildSql for UniqueColumnConstraint {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
sql.push("UNIQUE");
if self.is_null_distinct {
sql.push(" NULLS DISTINCT");
}
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct ReferencesConstraint {
pub ref_table: Identifier,
pub ref_column: Option<Identifier>,
pub match_type: Option<ReferencesConstraintMatchType>,
pub on_delete_action: Option<ReferentialAction>,
pub on_update_action: Option<ReferentialAction>,
}
impl ReferencesConstraint {
fn new(
ref_table: Identifier,
ref_column: Identifier,
) -> Self {
Self {
ref_table,
ref_column: Some(ref_column),
match_type: None,
on_delete_action: None,
on_update_action: None,
}
}
fn _compare(
_old: Self,
_new: Self,
) -> Option<String> {
todo!()
}
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum ReferentialAction {
NoAction,
Restrict,
Cascade,
SetNull(Vec<Identifier>), SetDefault(Vec<Identifier>),
}
impl BuildSql for ReferentialAction {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
match self {
ReferentialAction::NoAction => sql.push("NO ACTION"),
ReferentialAction::Restrict => sql.push("RESTRICT"),
ReferentialAction::Cascade => sql.push("CASCADE"),
ReferentialAction::SetNull(column_names) => {
sql.push("SET NULL ( ");
column_names.build_sql(sql);
sql.push(" )")
},
ReferentialAction::SetDefault(column_names) => {
sql.push(" SET DEFAULT (");
column_names.build_sql(sql);
sql.push(" )")
},
};
}
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum ReferencesConstraintMatchType {
Full,
Partial,
Simple,
}
impl BuildSql for ReferencesConstraintMatchType {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
sql.push(match self {
ReferencesConstraintMatchType::Full => "MATCH FULL",
ReferencesConstraintMatchType::Partial => "MATCH PARTIAL",
ReferencesConstraintMatchType::Simple => "MATCH SIMPLE",
});
}
}
impl BuildSql for ReferencesConstraint {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
sql.push("REFERENCES ");
sql.push(self.ref_table.to_string());
sql.push(' ');
if let Some(column) = &self.ref_column {
sql.push('(');
sql.push(column);
sql.push(')');
}
if let Some(match_type) = &self.match_type {
match_type.build_sql(sql);
}
if let Some(ref_action) = &self.on_delete_action {
sql.push(" ON DELETE ");
ref_action.build_sql(sql);
}
if let Some(ref_action) = &self.on_delete_action {
sql.push(" ON UPDATE ");
ref_action.build_sql(sql);
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TableColumnConstraint {
pub name: Option<Identifier>,
pub detail: Arc<TableColumnConstraintDetail>,
}
impl Deref for TableColumnConstraint {
type Target = TableColumnConstraintDetail;
fn deref(&self) -> &Self::Target {
&self.detail
}
}
impl TableColumnConstraint {
pub fn new(constraint: TableColumnConstraintDetail) -> Self {
Self {
name: None,
detail: Arc::new(constraint),
}
}
pub fn unique() -> Self {
Self {
name: None,
detail: Arc::new(
TableColumnConstraintDetail::Unique(UniqueColumnConstraint::default()),
),
}
}
pub fn foreign_key<T>(
ref_table: DatabaseTableDefinition<T>,
ref_column: TableColumn,
) -> Self {
Self {
name: None,
detail: Arc::new(TableColumnConstraintDetail::References(ReferencesConstraint::new(
ref_table.table_name.clone(),
ref_column.column_name.clone(),
))),
}
}
pub fn primary_key() -> Self {
Self {
name: None,
detail: Arc::new(TableColumnConstraintDetail::PrimaryKey(
PrimaryKeyColumnConstraint::default(),
)),
}
}
pub fn non_null() -> Self {
Self {
name: None,
detail: Arc::new(TableColumnConstraintDetail::NotNull),
}
}
}
impl BuildSql for TableColumnConstraint {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
sql.push(" ");
if let Some(name) = &self.name {
sql.push("CONSTRAINT ");
sql.push_bind(name.to_string());
sql.push(" ");
}
self.detail.build_sql(sql)
}
}
#[derive(PartialEq, Eq, Debug)]
pub enum TableColumnConstraintDetail {
NotNull,
Null,
Unique(UniqueColumnConstraint),
PrimaryKey(PrimaryKeyColumnConstraint),
References(ReferencesConstraint),
}
impl BuildSql for TableColumnConstraintDetail {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
match self {
TableColumnConstraintDetail::NotNull => {
sql.push("NOT NULL".to_owned());
},
TableColumnConstraintDetail::Null => {
sql.push("NULL".to_owned());
},
TableColumnConstraintDetail::Unique(unique) => unique.build_sql(sql),
TableColumnConstraintDetail::PrimaryKey(pk) => pk.build_sql(sql),
TableColumnConstraintDetail::References(fk) => fk.build_sql(sql),
}
}
}