use tardis::basic::dto::TardisContext;
use tardis::basic::field::TrimString;
use tardis::basic::result::TardisResult;
use tardis::db::reldb_client::TardisActiveModel;
use tardis::{TardisFuns, TardisFunsInst};
use crate::rbum::dto::rbum_domain_dto::RbumDomainAddReq;
use crate::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumKindFilterReq};
use crate::rbum::dto::rbum_kind_dto::RbumKindAddReq;
use crate::rbum::rbum_enumeration::RbumScopeLevelKind;
use crate::rbum::serv::rbum_crud_serv::RbumCrudOperation;
use crate::rbum::serv::rbum_domain_serv::RbumDomainServ;
use crate::rbum::serv::rbum_kind_serv::RbumKindServ;
use super::domain::spi_bs;
pub async fn init(code: &str, funs: &TardisFunsInst) -> TardisResult<TardisContext> {
let ctx = TardisContext {
own_paths: "".to_string(),
ak: "_".to_string(),
roles: vec![],
groups: vec![],
owner: "".to_string(),
..Default::default()
};
if RbumDomainServ::get_rbum_domain_id_by_code(code, funs).await?.is_some() {
return Ok(ctx);
}
funs.db().init(spi_bs::ActiveModel::init(TardisFuns::reldb().backend(), None, TardisFuns::reldb().compatible_type())).await?;
RbumDomainServ::add_rbum(
&mut RbumDomainAddReq {
code: TrimString(code.to_string()),
name: TrimString(code.to_string()),
note: None,
icon: None,
sort: None,
scope_level: Some(RbumScopeLevelKind::Root),
},
funs,
&ctx,
)
.await?;
Ok(ctx)
}
pub async fn add_kind(scheme: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
if !RbumKindServ::exist_rbum(
&RbumKindFilterReq {
basic: RbumBasicFilterReq {
code: Some(scheme.to_string()),
..Default::default()
},
..Default::default()
},
funs,
ctx,
)
.await?
{
RbumKindServ::add_rbum(
&mut RbumKindAddReq {
code: TrimString(scheme.to_string()),
name: TrimString(scheme.to_string()),
note: None,
icon: None,
sort: None,
module: None,
ext_table_name: Some("spi_bs".to_lowercase()),
scope_level: Some(RbumScopeLevelKind::Root),
},
funs,
ctx,
)
.await?;
}
Ok(())
}
pub mod common {
use std::collections::HashMap;
use tardis::{basic::dto::TardisContext, TardisFuns};
use crate::spi::spi_constants;
pub fn get_isolation_flag_from_context(ctx: &TardisContext) -> String {
format!("spi{}", TardisFuns::crypto.hex.encode(&ctx.ak))
}
pub fn set_isolation_flag_to_ext(isolation_flag: &str, ext: &mut HashMap<String, String>) {
ext.insert(spi_constants::SPI_ISOLATION_FLAG.to_string(), isolation_flag.to_string());
}
pub fn get_isolation_flag_from_ext(ext: &HashMap<String, String>) -> Option<String> {
ext.get(spi_constants::SPI_ISOLATION_FLAG).map(|s| s.to_string())
}
}
pub mod common_pg {
use std::collections::HashMap;
use tardis::{
basic::{dto::TardisContext, error::TardisError, result::TardisResult},
config::config_dto::DBModuleConfig,
db::{
reldb_client::{TardisRelDBClient, TardisRelDBlConnection},
sea_orm::Value,
},
TardisFuns,
};
use crate::spi::{
dto::spi_bs_dto::SpiBsCertResp,
spi_constants::GLOBAL_STORAGE_FLAG,
spi_funs::{SpiBsInst, TypedSpiBsInst},
};
use super::common;
pub fn get_schema_name_from_context(ctx: &TardisContext) -> String {
common::get_isolation_flag_from_context(ctx)
}
pub fn set_schema_name_to_ext(schema_name: &str, ext: &mut HashMap<String, String>) {
common::set_isolation_flag_to_ext(schema_name, ext);
}
pub fn get_schema_name_from_ext(ext: &HashMap<String, String>) -> Option<String> {
common::get_isolation_flag_from_ext(ext)
}
pub fn get_table_full_name(ext: &HashMap<String, String>, table_flag: String, tag: String) -> String {
let schema_name = get_schema_name_from_ext(ext).expect("ignore");
format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}_{tag}")
}
pub async fn check_schema_exit(client: &TardisRelDBClient, ctx: &TardisContext) -> TardisResult<bool> {
let schema_name = get_schema_name_from_context(ctx);
let schema = client.conn().count_by_sql("SELECT 1 FROM information_schema.schemata WHERE schema_name = $1", vec![Value::from(schema_name.as_str())]).await?;
Ok(schema != 0)
}
pub async fn create_schema(client: &TardisRelDBClient, ctx: &TardisContext) -> TardisResult<String> {
let schema_name = get_schema_name_from_context(ctx);
if !check_schema_exit(client, ctx).await? {
client.conn().execute_one(&format!("CREATE SCHEMA {schema_name}"), vec![]).await?;
}
Ok(schema_name)
}
pub async fn check_table_exit(table_name: &str, conn: &TardisRelDBlConnection, ctx: &TardisContext) -> TardisResult<bool> {
let schema_name = get_schema_name_from_context(ctx);
let table = conn
.count_by_sql(
"SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2",
vec![Value::from(schema_name.as_str()), Value::from(format!("{GLOBAL_STORAGE_FLAG}_{table_name}"))],
)
.await?;
Ok(table != 0)
}
pub async fn set_schema_to_session(schema_name: &str, conn: &mut TardisRelDBlConnection) -> TardisResult<()> {
conn.begin().await?;
conn.execute_one(&format!("SET SCHEMA '{schema_name}'"), vec![]).await?;
Ok(())
}
pub fn package_table_name(table_name: &str, ctx: &TardisContext) -> String {
let schema_name = get_schema_name_from_context(ctx);
format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_name}")
}
pub async fn init(bs_cert: &SpiBsCertResp, ctx: &TardisContext, mgr: bool) -> TardisResult<SpiBsInst> {
let ext = TardisFuns::json.str_to_json(&bs_cert.ext)?;
let compatible_type = TardisFuns::json.json_to_obj(ext.get("compatible_type").unwrap_or(&tardis::serde_json::Value::String("None".to_string())).clone())?;
let client = TardisRelDBClient::init(&DBModuleConfig {
url: bs_cert.conn_uri.parse().expect("invalid url"),
max_connections: ext.get("max_connections").and_then(|c| c.as_u64()).unwrap_or(5) as u32,
min_connections: ext.get("min_connections").and_then(|c| c.as_u64()).unwrap_or(1) as u32,
connect_timeout_sec: None,
idle_timeout_sec: None,
compatible_type,
})
.await?;
let mut ext = HashMap::new();
let schema_name = if bs_cert.private {
"public".to_string()
} else if mgr {
create_schema(&client, ctx).await?
} else if check_schema_exit(&client, ctx).await? {
get_schema_name_from_context(ctx)
} else {
return Err(TardisError::bad_request("The requested schema does not exist", ""));
};
set_schema_name_to_ext(&schema_name, &mut ext);
Ok(SpiBsInst { client: Box::new(client), ext })
}
pub async fn init_table_and_conn(
bs_inst: TypedSpiBsInst<'_, TardisRelDBClient>,
ctx: &TardisContext,
mgr: bool,
tag: Option<&str>,
table_flag: &str,
table_create_content: &str,
table_inherits: Option<String>,
indexes: Vec<(&str, &str)>,
primary_keys: Option<Vec<&str>>,
update_time_field: Option<&str>,
) -> TardisResult<(TardisRelDBlConnection, String)> {
let tag = tag.map(|t| format!("_{t}")).unwrap_or_default();
let conn = bs_inst.0.conn();
let schema_name = get_schema_name_from_ext(bs_inst.1).expect("ignore");
if check_table_exit(&format!("{table_flag}{tag}"), &conn, ctx).await? {
return Ok((conn, format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag}")));
} else if !mgr {
return Err(TardisError::bad_request("The requested tag does not exist", ""));
}
do_init_table(
&schema_name,
&conn,
&tag,
table_flag,
table_create_content,
table_inherits,
indexes,
primary_keys,
update_time_field,
)
.await?;
Ok((conn, format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag}")))
}
pub async fn init_conn(bs_inst: TypedSpiBsInst<'_, TardisRelDBClient>) -> TardisResult<(TardisRelDBlConnection, String)> {
let conn = bs_inst.0.conn();
let schema_name = get_schema_name_from_ext(bs_inst.1).expect("ignore");
Ok((conn, schema_name))
}
pub async fn init_table(
conn: &TardisRelDBlConnection,
tag: Option<&str>,
table_flag: &str,
table_create_content: &str,
indexes: Vec<(&str, &str)>,
primary_keys: Option<Vec<&str>>,
update_time_field: Option<&str>,
ctx: &TardisContext,
) -> TardisResult<()> {
let tag = tag.map(|t| format!("_{t}")).unwrap_or_default();
let schema_name = get_schema_name_from_context(ctx);
do_init_table(&schema_name, conn, &tag, table_flag, table_create_content, None, indexes, primary_keys, update_time_field).await
}
async fn do_init_table(
schema_name: &str,
conn: &TardisRelDBlConnection,
tag: &str,
table_flag: &str,
table_create_content: &str,
table_inherits: Option<String>,
indexes: Vec<(&str, &str)>,
primary_keys: Option<Vec<&str>>,
update_time_field: Option<&str>,
) -> TardisResult<()> {
conn.execute_one(
&format!(
r#"CREATE TABLE {schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag}
(
{table_create_content}
){}"#,
if let Some(inherits) = table_inherits {
format!(" INHERITS ({inherits})")
} else {
"".to_string()
}
),
vec![],
)
.await?;
for (idx, (field_name_or_fun, index_type)) in indexes.into_iter().enumerate() {
#[inline]
fn truncate_str(s: &str, max_size: usize) -> &str {
&s[..max_size.min(s.len())]
}
let index_name = format!(
"idx_{schema_name}{tag}_{table_flag}_{idx}",
schema_name = truncate_str(schema_name, 18),
tag = truncate_str(tag, 11),
table_flag = truncate_str(table_flag, 25),
idx = truncate_str(idx.to_string().as_str(), 3),
);
conn.execute_one(
&format!("CREATE INDEX {index_name} ON {schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag} USING {index_type}({field_name_or_fun})"),
vec![],
)
.await?;
}
if let Some(primary_keys) = primary_keys {
let pks = primary_keys.join(", ");
conn.execute_one(
&format!(r#"ALTER TABLE {schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag} ADD PRIMARY KEY ({pks})"#),
vec![],
)
.await?;
}
if let Some(update_time_field) = update_time_field {
conn.execute_one(
&format!(
r###"CREATE OR REPLACE FUNCTION TARDIS_AUTO_UPDATE_TIME_{}()
RETURNS TRIGGER AS $$
BEGIN
NEW.{} = now();
RETURN NEW;
END;
$$ language 'plpgsql';"###,
update_time_field.replace('-', "_"),
update_time_field
),
vec![],
)
.await?;
conn.execute_one(
&format!(
r###"CREATE OR REPLACE TRIGGER TARDIS_AUTO_UPDATE_TIME_ON
BEFORE UPDATE
ON
{}.{GLOBAL_STORAGE_FLAG}_{}{}
FOR EACH ROW
EXECUTE PROCEDURE TARDIS_AUTO_UPDATE_TIME_{}();"###,
schema_name,
table_flag,
tag,
update_time_field.replace('-', "_")
),
vec![],
)
.await?;
}
Ok(())
}
}