use async_trait::async_trait;
use canyon_crud::DatabaseType;
use regex::Regex;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Not;
use crate::canyon_crud::{crud::Transaction, DatasourceConfig};
use crate::constants::regex_patterns;
use crate::QUERIES_TO_EXECUTE;
use super::information_schema::{ColumnMetadata, TableMetadata};
use super::memory::CanyonMemory;
use super::register_types::{CanyonRegisterEntity, CanyonRegisterEntityField};
#[derive(Debug, Default)]
pub struct MigrationsProcessor {
operations: Vec<Box<dyn DatabaseOperation>>,
set_primary_key_operations: Vec<Box<dyn DatabaseOperation>>,
drop_primary_key_operations: Vec<Box<dyn DatabaseOperation>>,
constraints_operations: Vec<Box<dyn DatabaseOperation>>,
}
impl Transaction<Self> for MigrationsProcessor {}
impl MigrationsProcessor {
pub async fn process<'a>(
&'a mut self,
canyon_memory: CanyonMemory,
canyon_entities: Vec<CanyonRegisterEntity<'a>>,
database_tables: Vec<&'a TableMetadata>,
datasource: &'_ DatasourceConfig<'static>,
) {
let db_type = datasource.properties.db_type;
for canyon_register_entity in canyon_entities {
let entity_name = canyon_register_entity.entity_name.to_lowercase();
self.create_or_rename_tables(
&canyon_memory,
entity_name.as_str(),
canyon_register_entity.entity_fields.clone(),
&database_tables,
);
let current_table_metadata = MigrationsHelper::get_current_table_metadata(
&canyon_memory,
entity_name.as_str(),
&database_tables,
);
self.delete_fields(
entity_name.as_str(),
canyon_register_entity.entity_fields.clone(),
current_table_metadata,
db_type,
);
for canyon_register_field in canyon_register_entity.entity_fields {
let current_column_metadata = MigrationsHelper::get_current_column_metadata(
canyon_register_field.field_name.clone(),
current_table_metadata,
);
if current_table_metadata.is_some() {
self.create_or_modify_field(
entity_name.as_str(),
db_type,
canyon_register_field.clone(),
current_column_metadata,
)
}
if (current_table_metadata.is_none()
&& !canyon_register_field.annotations.is_empty())
|| (current_table_metadata.is_some() && current_column_metadata.is_none())
{
self.add_constraints(entity_name.as_str(), canyon_register_field.clone())
}
if current_table_metadata.is_some() && current_column_metadata.is_some() {
self.add_modify_or_remove_constraints(
entity_name.as_str(),
canyon_register_field,
current_column_metadata.unwrap(),
)
}
}
}
for operation in &self.operations {
operation.generate_sql(datasource).await; }
for operation in &self.drop_primary_key_operations {
operation.generate_sql(datasource).await; }
for operation in &self.set_primary_key_operations {
operation.generate_sql(datasource).await; }
for operation in &self.constraints_operations {
operation.generate_sql(datasource).await; }
}
fn create_or_rename_tables<'a>(
&mut self,
canyon_memory: &'_ CanyonMemory,
entity_name: &'a str,
entity_fields: Vec<CanyonRegisterEntityField>,
database_tables: &'a [&'a TableMetadata],
) {
if !MigrationsHelper::entity_already_on_database(entity_name, database_tables) {
if canyon_memory.renamed_entities.contains_key(entity_name) {
self.table_rename(
canyon_memory
.renamed_entities
.get(entity_name) .unwrap()
.to_owned(),
entity_name.to_string(), )
} else {
self.create_table(entity_name.to_string(), entity_fields)
}
}
}
fn create_table(&mut self, table_name: String, entity_fields: Vec<CanyonRegisterEntityField>) {
self.operations.push(Box::new(TableOperation::CreateTable(
table_name,
entity_fields,
)));
}
fn table_rename(&mut self, old_table_name: String, new_table_name: String) {
self.operations
.push(Box::new(TableOperation::AlterTableName(
old_table_name,
new_table_name,
)));
}
fn delete_fields<'a>(
&mut self,
entity_name: &'a str,
entity_fields: Vec<CanyonRegisterEntityField>,
current_table_metadata: Option<&'a TableMetadata>,
db_type: DatabaseType,
) {
if current_table_metadata.is_none() {
return;
}
let columns_name_to_delete: Vec<&ColumnMetadata> = current_table_metadata
.unwrap()
.columns
.iter()
.filter(|db_column| {
entity_fields
.iter()
.map(|canyon_field| canyon_field.field_name.to_string())
.any(|canyon_field| canyon_field == db_column.column_name)
.not()
})
.collect();
for column_metadata in columns_name_to_delete {
if db_type == DatabaseType::SqlServer && !column_metadata.is_nullable {
self.drop_column_not_null(
entity_name,
column_metadata.column_name.clone(),
MigrationsHelper::get_datatype_from_column_metadata(column_metadata),
)
}
self.delete_column(entity_name, column_metadata.column_name.clone());
}
}
fn create_or_modify_field(
&mut self,
entity_name: &str,
db_type: DatabaseType,
canyon_register_entity_field: CanyonRegisterEntityField,
current_column_metadata: Option<&ColumnMetadata>,
) {
if current_column_metadata.is_none() {
self.create_column(entity_name.to_string(), canyon_register_entity_field)
} else if !MigrationsHelper::is_same_datatype(
db_type,
&canyon_register_entity_field,
current_column_metadata.unwrap(),
) {
self.change_column_datatype(entity_name.to_string(), canyon_register_entity_field)
}
}
fn delete_column(&mut self, table_name: &str, column_name: String) {
self.operations.push(Box::new(ColumnOperation::DeleteColumn(
table_name.to_string(),
column_name,
)));
}
fn drop_column_not_null(
&mut self,
table_name: &str,
column_name: String,
column_datatype: String,
) {
self.operations
.push(Box::new(ColumnOperation::DropNotNullBeforeDropColumn(
table_name.to_string(),
column_name,
column_datatype,
)));
}
fn create_column(&mut self, table_name: String, field: CanyonRegisterEntityField) {
self.operations
.push(Box::new(ColumnOperation::CreateColumn(table_name, field)));
}
fn change_column_datatype(&mut self, table_name: String, field: CanyonRegisterEntityField) {
self.operations
.push(Box::new(ColumnOperation::AlterColumnType(
table_name, field,
)));
}
fn add_constraints(
&mut self,
entity_name: &str,
canyon_register_entity_field: CanyonRegisterEntityField,
) {
for attr in &canyon_register_entity_field.annotations {
if attr.starts_with("Annotation: ForeignKey") {
let annotation_data = MigrationsHelper::extract_foreign_key_annotation(
&canyon_register_entity_field.annotations,
);
let table_to_reference = annotation_data.0;
let column_to_reference = annotation_data.1;
let foreign_key_name = format!(
"{entity_name}_{}_fkey",
&canyon_register_entity_field.field_name
);
Self::add_foreign_key(
self,
entity_name,
foreign_key_name,
table_to_reference,
column_to_reference,
&canyon_register_entity_field,
);
}
if attr.starts_with("Annotation: PrimaryKey") {
Self::add_primary_key(self, entity_name, canyon_register_entity_field.clone());
if canyon_register_entity_field.is_autoincremental() {
Self::add_identity(self, entity_name, canyon_register_entity_field.clone());
}
}
}
}
fn add_foreign_key(
&mut self,
entity_name: &'_ str,
foreign_key_name: String,
table_to_reference: String,
column_to_reference: String,
canyon_register_entity_field: &CanyonRegisterEntityField,
) {
self.constraints_operations
.push(Box::new(TableOperation::AddTableForeignKey(
entity_name.to_string(),
foreign_key_name,
canyon_register_entity_field.field_name.clone(),
table_to_reference,
column_to_reference,
)));
}
fn add_primary_key(
&mut self,
entity_name: &str,
canyon_register_entity_field: CanyonRegisterEntityField,
) {
self.set_primary_key_operations
.push(Box::new(TableOperation::AddTablePrimaryKey(
entity_name.to_string(),
canyon_register_entity_field,
)));
}
fn add_identity(&mut self, entity_name: &str, field: CanyonRegisterEntityField) {
self.constraints_operations
.push(Box::new(ColumnOperation::AlterColumnAddIdentity(
entity_name.to_string(),
field.clone(),
)));
self.constraints_operations
.push(Box::new(SequenceOperation::ModifySequence(
entity_name.to_string(),
field,
)));
}
fn add_modify_or_remove_constraints(
&mut self,
entity_name: &str,
canyon_register_entity_field: CanyonRegisterEntityField,
current_column_metadata: &ColumnMetadata,
) {
let field_is_primary_key = canyon_register_entity_field
.annotations
.iter()
.any(|anno| anno.starts_with("Annotation: PrimaryKey"));
let field_is_foreign_key = canyon_register_entity_field
.annotations
.iter()
.any(|anno| anno.starts_with("Annotation: ForeignKey"));
if field_is_primary_key && current_column_metadata.primary_key_info.is_none() {
Self::add_primary_key(self, entity_name, canyon_register_entity_field.clone());
if canyon_register_entity_field.is_autoincremental() {
Self::add_identity(self, entity_name, canyon_register_entity_field.clone());
}
}
else if field_is_primary_key && current_column_metadata.primary_key_info.is_some() {
let is_autoincr_rust = canyon_register_entity_field.is_autoincremental();
let is_autoincr_in_db = current_column_metadata.is_identity;
if !is_autoincr_rust && is_autoincr_in_db {
Self::drop_identity(self, entity_name, canyon_register_entity_field.clone())
} else if is_autoincr_rust && !is_autoincr_in_db {
Self::add_identity(self, entity_name, canyon_register_entity_field.clone())
}
}
else if !field_is_primary_key && current_column_metadata.primary_key_info.is_some() {
Self::drop_primary_key(
self,
entity_name,
current_column_metadata
.primary_key_name
.as_ref()
.expect("PrimaryKey constrain name not found")
.to_string(),
);
if current_column_metadata.is_identity {
Self::drop_identity(self, entity_name, canyon_register_entity_field.clone());
}
}
if field_is_foreign_key && current_column_metadata.foreign_key_name.is_none() {
if current_column_metadata.foreign_key_name.is_none() {
let annotation_data = MigrationsHelper::extract_foreign_key_annotation(
&canyon_register_entity_field.annotations,
);
let foreign_key_name = format!(
"{entity_name}_{}_fkey",
&canyon_register_entity_field.field_name
);
Self::add_foreign_key(
self,
entity_name,
foreign_key_name,
annotation_data.0,
annotation_data.1,
&canyon_register_entity_field,
);
}
}
else if field_is_foreign_key && current_column_metadata.foreign_key_name.is_some() {
let annotation_data = MigrationsHelper::extract_foreign_key_annotation(
&canyon_register_entity_field.annotations,
);
let foreign_key_name = format!(
"{entity_name}_{}_fkey",
&canyon_register_entity_field.field_name
);
let references_regex = Regex::new(regex_patterns::EXTRACT_FOREIGN_KEY_INFO).unwrap();
let captures_references = references_regex
.captures(
current_column_metadata
.foreign_key_info
.as_ref()
.expect("Regex - foreign key info"),
)
.expect("Regex - foreign key info not found");
let current_column = captures_references
.name("current_column")
.expect("Regex - Current column not found")
.as_str()
.to_string();
let ref_table = captures_references
.name("ref_table")
.expect("Regex - Ref tablenot found")
.as_str()
.to_string();
let ref_column = captures_references
.name("ref_column")
.expect("Regex - Ref column not found")
.as_str()
.to_string();
if canyon_register_entity_field.field_name != current_column
|| annotation_data.0 != ref_table
|| annotation_data.1 != ref_column
{
Self::delete_foreign_key(
self,
entity_name,
current_column_metadata
.foreign_key_name
.as_ref()
.expect("Annotation foreign key constrain name not found")
.to_string(),
);
Self::add_foreign_key(
self,
entity_name,
foreign_key_name,
annotation_data.0,
annotation_data.1,
&canyon_register_entity_field,
)
}
} else if !field_is_foreign_key && current_column_metadata.foreign_key_name.is_some() {
Self::delete_foreign_key(
self,
entity_name,
current_column_metadata
.foreign_key_name
.as_ref()
.expect("ForeignKey constrain name not found")
.to_string(),
);
}
}
fn drop_primary_key(&mut self, entity_name: &str, primary_key_name: String) {
self.drop_primary_key_operations
.push(Box::new(TableOperation::DeleteTablePrimaryKey(
entity_name.to_string(),
primary_key_name,
)));
}
fn drop_identity(
&mut self,
entity_name: &str,
canyon_register_entity_field: CanyonRegisterEntityField,
) {
self.constraints_operations
.push(Box::new(ColumnOperation::AlterColumnDropIdentity(
entity_name.to_string(),
canyon_register_entity_field,
)));
}
fn delete_foreign_key(&mut self, entity_name: &str, constrain_name: String) {
self.constraints_operations
.push(Box::new(TableOperation::DeleteTableForeignKey(
entity_name.to_string(),
constrain_name,
)));
}
#[allow(clippy::await_holding_lock)]
pub async fn from_query_register(queries_to_execute: &HashMap<&str, Vec<&str>>) {
for datasource in queries_to_execute.iter() {
for query_to_execute in datasource.1 {
let res = Self::query(query_to_execute, [], datasource.0).await;
match res {
Ok(_) => println!(
"\t[OK] - {:?} - Query: {:?}",
datasource.0, &query_to_execute
),
Err(e) => println!(
"\t[ERR] - {:?} - Query: {:?}\nCause: {:?}",
datasource.0, &query_to_execute, e
),
}
}
}
}
}
struct MigrationsHelper;
impl MigrationsHelper {
fn entity_already_on_database<'a>(
entity_name: &'a str,
database_tables: &'a [&'_ TableMetadata],
) -> bool {
database_tables
.iter()
.any(|v| v.table_name.to_lowercase() == entity_name.to_lowercase())
}
fn get_current_table_metadata<'a>(
canyon_memory: &'_ CanyonMemory,
entity_name: &'a str,
database_tables: &'a [&'_ TableMetadata],
) -> Option<&'a TableMetadata> {
let correct_entity_name = canyon_memory
.renamed_entities
.get(&entity_name.to_lowercase())
.map(|e| e.to_owned())
.unwrap_or_else(|| entity_name.to_string());
database_tables
.iter()
.find(|table_metadata| {
table_metadata.table_name.to_lowercase() == *correct_entity_name.to_lowercase()
})
.map(|e| e.to_owned())
}
fn get_current_column_metadata(
column_name: String,
current_table_metadata: Option<&TableMetadata>,
) -> Option<&ColumnMetadata> {
if let Some(metadata_table) = current_table_metadata {
metadata_table
.columns
.iter()
.find(|column| column.column_name == column_name)
} else {
None
}
}
fn get_datatype_from_column_metadata(current_column_metadata: &ColumnMetadata) -> String {
if vec!["nvarchar", "varchar"]
.contains(¤t_column_metadata.datatype.to_lowercase().as_str())
{
let varchar_len = match ¤t_column_metadata.character_maximum_length {
Some(v) => v.to_string(),
None => "max".to_string(),
};
format!("{}({})", current_column_metadata.datatype, varchar_len)
} else {
current_column_metadata.datatype.to_string()
}
}
fn is_same_datatype(
db_type: DatabaseType,
canyon_register_entity_field: &CanyonRegisterEntityField,
current_column_metadata: &ColumnMetadata,
) -> bool {
if db_type == DatabaseType::PostgreSql {
canyon_register_entity_field
.to_postgres_alter_syntax()
.to_lowercase()
== current_column_metadata.datatype
} else if db_type == DatabaseType::SqlServer {
canyon_register_entity_field
.to_sqlserver_alter_syntax()
.to_lowercase()
== current_column_metadata.datatype
} else {
todo!()
}
}
fn extract_foreign_key_annotation(field_annotations: &[String]) -> (String, String) {
let opt_fk_annotation = field_annotations
.iter()
.find(|anno| anno.starts_with("Annotation: ForeignKey"));
if let Some(fk_annotation) = opt_fk_annotation {
let annotation_data = fk_annotation
.split(',')
.filter(|x| !x.contains("Annotation: ForeignKey")) .map(|x| {
x.split(':')
.collect::<Vec<&str>>()
.get(1)
.expect("Error. Unable to split annotations")
.trim()
.to_string()
})
.collect::<Vec<String>>();
let table_to_reference = annotation_data
.get(0)
.expect("Error extracting table ref from FK annotation")
.to_string();
let column_to_reference = annotation_data
.get(1)
.expect("Error extracting column ref from FK annotation")
.to_string();
(table_to_reference, column_to_reference)
} else {
panic!("Detected a Foreign Key attribute when does not exists on the user's code");
}
}
}
#[cfg(test)]
mod migrations_helper_tests {
use super::*;
use crate::constants;
const MOCKED_ENTITY_NAME: &str = "League";
#[test]
fn test_entity_already_on_database() {
let parse_result_empty_db_tables =
MigrationsHelper::entity_already_on_database(MOCKED_ENTITY_NAME, &[]);
assert!(!parse_result_empty_db_tables);
let mocked_league_entity_on_database = MigrationsHelper::entity_already_on_database(
MOCKED_ENTITY_NAME,
&[&constants::mocked_data::TABLE_METADATA_LEAGUE_EX],
);
assert!(mocked_league_entity_on_database);
let mocked_league_entity_on_database = MigrationsHelper::entity_already_on_database(
MOCKED_ENTITY_NAME,
&[&constants::mocked_data::NON_MATCHING_TABLE_METADATA],
);
assert!(!mocked_league_entity_on_database)
}
}
#[async_trait]
trait DatabaseOperation: Debug {
async fn generate_sql(&self, datasource: &DatasourceConfig<'static>);
}
#[derive(Debug)]
#[allow(dead_code)]
enum TableOperation {
CreateTable(String, Vec<CanyonRegisterEntityField>),
AlterTableName(String, String),
AddTableForeignKey(String, String, String, String, String),
DeleteTableForeignKey(String, String),
AddTablePrimaryKey(String, CanyonRegisterEntityField),
DeleteTablePrimaryKey(String, String),
}
impl<T: Debug> Transaction<T> for TableOperation {}
#[async_trait]
impl DatabaseOperation for TableOperation {
async fn generate_sql(&self, datasource: &DatasourceConfig<'static>) {
let db_type = datasource.properties.db_type;
let stmt = match self {
TableOperation::CreateTable(table_name, table_fields) => {
if db_type == DatabaseType::PostgreSql {
format!(
"CREATE TABLE {:?} ({:?});",
table_name,
table_fields
.iter()
.map(|entity_field| format!(
"{} {}",
entity_field.field_name,
entity_field.to_postgres_syntax()
))
.collect::<Vec<String>>()
.join(", ")
)
.replace('"', "")
} else if db_type == DatabaseType::SqlServer {
format!(
"CREATE TABLE {:?} ({:?});",
table_name,
table_fields
.iter()
.map(|entity_field| format!(
"{} {}",
entity_field.field_name,
entity_field.to_sqlserver_syntax()
))
.collect::<Vec<String>>()
.join(", ")
)
.replace('"', "")
} else {
todo!()
}
}
TableOperation::AlterTableName(old_table_name, new_table_name) => {
if db_type == DatabaseType::PostgreSql {
format!("ALTER TABLE {old_table_name} RENAME TO {new_table_name};")
} else if db_type == DatabaseType::SqlServer {
format!("exec sp_rename '{old_table_name}', '{new_table_name}';")
} else {
todo!()
}
}
TableOperation::AddTableForeignKey(
table_name,
foreign_key_name,
column_foreign_key,
table_to_reference,
column_to_reference,
) => {
if db_type == DatabaseType::PostgreSql {
format!(
"ALTER TABLE {table_name} ADD CONSTRAINT {foreign_key_name} \
FOREIGN KEY ({column_foreign_key}) REFERENCES {table_to_reference} ({column_to_reference});"
)
} else if db_type == DatabaseType::SqlServer {
todo!("[MS-SQL -> Operation still won't supported by Canyon for Sql Server]")
} else {
todo!()
}
}
TableOperation::DeleteTableForeignKey(table_with_foreign_key, constraint_name) => {
if db_type == DatabaseType::PostgreSql {
format!(
"ALTER TABLE {table_with_foreign_key} DROP CONSTRAINT {constraint_name};",
)
} else if db_type == DatabaseType::SqlServer {
todo!("[MS-SQL -> Operation still won't supported by Canyon for Sql Server]")
} else {
todo!()
}
}
TableOperation::AddTablePrimaryKey(table_name, entity_field) => {
if db_type == DatabaseType::PostgreSql {
format!(
"ALTER TABLE {table_name} ADD PRIMARY KEY (\"{}\");",
entity_field.field_name
)
} else if db_type == DatabaseType::SqlServer {
todo!("[MS-SQL -> Operation still won't supported by Canyon for Sql Server]")
} else {
todo!()
}
}
TableOperation::DeleteTablePrimaryKey(table_name, primary_key_name) => {
if db_type == DatabaseType::PostgreSql || db_type == DatabaseType::SqlServer {
format!("ALTER TABLE {table_name} DROP CONSTRAINT {primary_key_name} CASCADE;")
} else {
todo!()
}
}
};
if QUERIES_TO_EXECUTE
.lock()
.unwrap()
.contains_key(datasource.name)
{
QUERIES_TO_EXECUTE
.lock()
.unwrap()
.get_mut(datasource.name)
.unwrap()
.push(stmt);
} else {
QUERIES_TO_EXECUTE
.lock()
.unwrap()
.insert(datasource.name, vec![stmt]);
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
enum ColumnOperation {
CreateColumn(String, CanyonRegisterEntityField),
DeleteColumn(String, String),
AlterColumnType(String, CanyonRegisterEntityField),
AlterColumnDropNotNull(String, CanyonRegisterEntityField),
DropNotNullBeforeDropColumn(String, String, String),
AlterColumnSetNotNull(String, CanyonRegisterEntityField),
AlterColumnAddIdentity(String, CanyonRegisterEntityField),
AlterColumnDropIdentity(String, CanyonRegisterEntityField),
}
impl Transaction<Self> for ColumnOperation {}
#[async_trait]
impl DatabaseOperation for ColumnOperation {
async fn generate_sql(&self, datasource: &DatasourceConfig<'static>) {
let db_type = datasource.properties.db_type;
let stmt = match self {
ColumnOperation::CreateColumn(table_name, entity_field) =>
if db_type == DatabaseType::PostgreSql {
format!(
"ALTER TABLE {} ADD COLUMN \"{}\" {};",
table_name,
entity_field.field_name,
entity_field.to_postgres_syntax())
} else if db_type == DatabaseType::SqlServer {
format!(
"ALTER TABLE {} ADD \"{}\" {};",
table_name,
entity_field.field_name,
entity_field.to_sqlserver_syntax()
)
} else {
todo!()
},
ColumnOperation::DeleteColumn(table_name, column_name) => {
format!("ALTER TABLE {table_name} DROP COLUMN {column_name};")
},
ColumnOperation::AlterColumnType(table_name, entity_field) =>
if db_type == DatabaseType::PostgreSql {
format!(
"ALTER TABLE {table_name} ALTER COLUMN \"{}\" TYPE {};",
entity_field.field_name,
entity_field.to_postgres_alter_syntax())
} else if db_type == DatabaseType::SqlServer {
todo!("[MS-SQL -> Operation still won't supported by Canyon for Sql Server]")
} else {
todo!()
}
,
ColumnOperation::AlterColumnDropNotNull(table_name, entity_field) =>
if db_type == DatabaseType::PostgreSql {
format!(
"ALTER TABLE {:?} ALTER COLUMN \"{}\" DROP NOT NULL;",
table_name, entity_field.field_name
)
} else if db_type == DatabaseType::SqlServer {
format!(
"ALTER TABLE {} ALTER COLUMN {} {} NULL",
table_name, entity_field.field_name, entity_field.to_sqlserver_alter_syntax()
)
} else {
todo!()
}
ColumnOperation::DropNotNullBeforeDropColumn(table_name, column_name, column_datatype) =>
format!(
"ALTER TABLE {table_name} ALTER COLUMN {column_name} {column_datatype} NULL; DECLARE @tableName VARCHAR(MAX) = '{table_name}'
DECLARE @columnName VARCHAR(MAX) = '{column_name}'
DECLARE @ConstraintName nvarchar(200)
SELECT @ConstraintName = Name
FROM SYS.DEFAULT_CONSTRAINTS
WHERE PARENT_OBJECT_ID = OBJECT_ID(@tableName)
AND PARENT_COLUMN_ID = (
SELECT column_id FROM sys.columns
WHERE NAME = @columnName AND object_id = OBJECT_ID(@tableName))
IF @ConstraintName IS NOT NULL
EXEC('ALTER TABLE '+@tableName+' DROP CONSTRAINT ' + @ConstraintName);"
),
ColumnOperation::AlterColumnSetNotNull(table_name, entity_field) => format!(
"ALTER TABLE {table_name} ALTER COLUMN \"{}\" SET NOT NULL;", entity_field.field_name
),
ColumnOperation::AlterColumnAddIdentity(table_name, entity_field) => format!(
"ALTER TABLE {table_name} ALTER COLUMN \"{}\" ADD GENERATED ALWAYS AS IDENTITY;", entity_field.field_name
),
ColumnOperation::AlterColumnDropIdentity(table_name, entity_field) => format!(
"ALTER TABLE {table_name} ALTER COLUMN \"{}\" DROP IDENTITY;", entity_field.field_name
),
};
if QUERIES_TO_EXECUTE
.lock()
.unwrap()
.contains_key(datasource.name)
{
QUERIES_TO_EXECUTE
.lock()
.unwrap()
.get_mut(datasource.name)
.unwrap()
.push(stmt);
} else {
QUERIES_TO_EXECUTE
.lock()
.unwrap()
.insert(datasource.name, vec![stmt]);
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
enum SequenceOperation {
ModifySequence(String, CanyonRegisterEntityField),
}
impl Transaction<Self> for SequenceOperation {}
#[async_trait]
impl DatabaseOperation for SequenceOperation {
async fn generate_sql(&self, datasource: &DatasourceConfig<'static>) {
let db_type = datasource.properties.db_type;
let stmt = match self {
SequenceOperation::ModifySequence(table_name, entity_field) => {
if db_type == DatabaseType::PostgreSql {
format!(
"SELECT setval(pg_get_serial_sequence('{:?}', '{}'), max(\"{}\")) from {:?};",
table_name, entity_field.field_name, entity_field.field_name, table_name
)
} else if db_type == DatabaseType::SqlServer {
todo!("[MS-SQL -> Operation still won't supported by Canyon for Sql Server]")
} else {
todo!()
}
}
};
if QUERIES_TO_EXECUTE
.lock()
.unwrap()
.contains_key(datasource.name)
{
QUERIES_TO_EXECUTE
.lock()
.unwrap()
.get_mut(datasource.name)
.unwrap()
.push(stmt);
} else {
QUERIES_TO_EXECUTE
.lock()
.unwrap()
.insert(datasource.name, vec![stmt]);
}
}
}