1use tardis::basic::dto::TardisContext;
3use tardis::basic::field::TrimString;
4use tardis::basic::result::TardisResult;
5use tardis::db::reldb_client::TardisActiveModel;
6use tardis::{TardisFuns, TardisFunsInst};
7
8use crate::rbum::dto::rbum_domain_dto::RbumDomainAddReq;
9use crate::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumKindFilterReq};
10use crate::rbum::dto::rbum_kind_dto::RbumKindAddReq;
11use crate::rbum::rbum_enumeration::RbumScopeLevelKind;
12use crate::rbum::serv::rbum_crud_serv::RbumCrudOperation;
13use crate::rbum::serv::rbum_domain_serv::RbumDomainServ;
14use crate::rbum::serv::rbum_kind_serv::RbumKindServ;
15
16use super::domain::spi_bs;
17
18pub async fn init(code: &str, funs: &TardisFunsInst) -> TardisResult<TardisContext> {
24 let ctx = TardisContext {
25 own_paths: "".to_string(),
26 ak: "_".to_string(),
27 roles: vec![],
28 groups: vec![],
29 owner: "".to_string(),
30 ..Default::default()
31 };
32 if RbumDomainServ::get_rbum_domain_id_by_code(code, funs).await?.is_some() {
33 return Ok(ctx);
34 }
35 funs.db().init(spi_bs::ActiveModel::init(TardisFuns::reldb().backend(), None, TardisFuns::reldb().compatible_type())).await?;
37 RbumDomainServ::add_rbum(
39 &mut RbumDomainAddReq {
40 code: TrimString(code.to_string()),
41 name: TrimString(code.to_string()),
42 note: None,
43 icon: None,
44 sort: None,
45 scope_level: Some(RbumScopeLevelKind::Root),
46 },
47 funs,
48 &ctx,
49 )
50 .await?;
51 Ok(ctx)
52}
53
54pub async fn add_kind(scheme: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
57 if !RbumKindServ::exist_rbum(
58 &RbumKindFilterReq {
59 basic: RbumBasicFilterReq {
60 code: Some(scheme.to_string()),
61 ..Default::default()
62 },
63 ..Default::default()
64 },
65 funs,
66 ctx,
67 )
68 .await?
69 {
70 RbumKindServ::add_rbum(
71 &mut RbumKindAddReq {
72 code: TrimString(scheme.to_string()),
73 name: TrimString(scheme.to_string()),
74 note: None,
75 icon: None,
76 sort: None,
77 module: None,
78 ext_table_name: Some("spi_bs".to_lowercase()),
79 scope_level: Some(RbumScopeLevelKind::Root),
80 },
81 funs,
82 ctx,
83 )
84 .await?;
85 }
86 Ok(())
87}
88
89pub mod common {
105 use std::collections::HashMap;
106
107 use tardis::{basic::dto::TardisContext, TardisFuns};
108
109 use crate::spi::spi_constants;
110
111 pub fn get_isolation_flag_from_context(ctx: &TardisContext) -> String {
114 format!("spi{}", TardisFuns::crypto.hex.encode(&ctx.ak))
116 }
117
118 pub fn set_isolation_flag_to_ext(isolation_flag: &str, ext: &mut HashMap<String, String>) {
121 ext.insert(spi_constants::SPI_ISOLATION_FLAG.to_string(), isolation_flag.to_string());
122 }
123
124 pub fn get_isolation_flag_from_ext(ext: &HashMap<String, String>) -> Option<String> {
127 ext.get(spi_constants::SPI_ISOLATION_FLAG).map(|s| s.to_string())
128 }
129}
130
131pub mod common_pg {
134 use std::collections::HashMap;
135
136 use tardis::{
137 basic::{dto::TardisContext, error::TardisError, result::TardisResult},
138 config::config_dto::DBModuleConfig,
139 db::{
140 reldb_client::{TardisRelDBClient, TardisRelDBlConnection},
141 sea_orm::Value,
142 },
143 TardisFuns,
144 };
145
146 use crate::spi::{
147 dto::spi_bs_dto::SpiBsCertResp,
148 spi_constants::GLOBAL_STORAGE_FLAG,
149 spi_funs::{SpiBsInst, TypedSpiBsInst},
150 };
151
152 use super::common;
153
154 pub fn get_schema_name_from_context(ctx: &TardisContext) -> String {
160 common::get_isolation_flag_from_context(ctx)
161 }
162
163 pub fn set_schema_name_to_ext(schema_name: &str, ext: &mut HashMap<String, String>) {
169 common::set_isolation_flag_to_ext(schema_name, ext);
170 }
171
172 pub fn get_schema_name_from_ext(ext: &HashMap<String, String>) -> Option<String> {
178 common::get_isolation_flag_from_ext(ext)
179 }
180
181 pub fn get_table_full_name(ext: &HashMap<String, String>, table_flag: String, tag: String) -> String {
184 let schema_name = get_schema_name_from_ext(ext).expect("ignore");
185 format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}_{tag}")
186 }
187
188 pub async fn check_schema_exit(client: &TardisRelDBClient, ctx: &TardisContext) -> TardisResult<bool> {
191 let schema_name = get_schema_name_from_context(ctx);
192 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?;
193 Ok(schema != 0)
194 }
195
196 pub async fn create_schema(client: &TardisRelDBClient, ctx: &TardisContext) -> TardisResult<String> {
199 let schema_name = get_schema_name_from_context(ctx);
200 if !check_schema_exit(client, ctx).await? {
201 client.conn().execute_one(&format!("CREATE SCHEMA {schema_name}"), vec![]).await?;
202 }
203 Ok(schema_name)
204 }
205
206 pub async fn check_table_exit(table_name: &str, conn: &TardisRelDBlConnection, ctx: &TardisContext) -> TardisResult<bool> {
212 let schema_name = get_schema_name_from_context(ctx);
213 let table = conn
214 .count_by_sql(
215 "SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2",
216 vec![Value::from(schema_name.as_str()), Value::from(format!("{GLOBAL_STORAGE_FLAG}_{table_name}"))],
217 )
218 .await?;
219 Ok(table != 0)
220 }
221
222 pub async fn set_schema_to_session(schema_name: &str, conn: &mut TardisRelDBlConnection) -> TardisResult<()> {
228 conn.begin().await?;
229 conn.execute_one(&format!("SET SCHEMA '{schema_name}'"), vec![]).await?;
230 Ok(())
231 }
232
233 pub fn package_table_name(table_name: &str, ctx: &TardisContext) -> String {
239 let schema_name = get_schema_name_from_context(ctx);
240 format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_name}")
241 }
242
243 pub async fn init(bs_cert: &SpiBsCertResp, ctx: &TardisContext, mgr: bool) -> TardisResult<SpiBsInst> {
246 let ext = TardisFuns::json.str_to_json(&bs_cert.ext)?;
247 let compatible_type = TardisFuns::json.json_to_obj(ext.get("compatible_type").unwrap_or(&tardis::serde_json::Value::String("None".to_string())).clone())?;
248 let client = TardisRelDBClient::init(&DBModuleConfig {
249 url: bs_cert.conn_uri.parse().expect("invalid url"),
250 max_connections: ext.get("max_connections").and_then(|c| c.as_u64()).unwrap_or(5) as u32,
251 min_connections: ext.get("min_connections").and_then(|c| c.as_u64()).unwrap_or(1) as u32,
252 connect_timeout_sec: None,
253 idle_timeout_sec: None,
254 compatible_type,
255 })
256 .await?;
257 let mut ext = HashMap::new();
258 let schema_name = if bs_cert.private {
261 "public".to_string()
262 } else if mgr {
263 create_schema(&client, ctx).await?
266 } else if check_schema_exit(&client, ctx).await? {
267 get_schema_name_from_context(ctx)
268 } else {
269 return Err(TardisError::bad_request("The requested schema does not exist", ""));
270 };
271 set_schema_name_to_ext(&schema_name, &mut ext);
272 Ok(SpiBsInst { client: Box::new(client), ext })
273 }
274
275 pub async fn init_table_and_conn(
278 bs_inst: TypedSpiBsInst<'_, TardisRelDBClient>,
280 ctx: &TardisContext,
281 mgr: bool,
283 tag: Option<&str>,
285 table_flag: &str,
287 table_create_content: &str,
289 table_inherits: Option<String>,
290 indexes: Vec<(&str, &str)>,
293 primary_keys: Option<Vec<&str>>,
295 update_time_field: Option<&str>,
297 ) -> TardisResult<(TardisRelDBlConnection, String)> {
298 let tag = tag.map(|t| format!("_{t}")).unwrap_or_default();
299 let conn = bs_inst.0.conn();
300 let schema_name = get_schema_name_from_ext(bs_inst.1).expect("ignore");
301 if check_table_exit(&format!("{table_flag}{tag}"), &conn, ctx).await? {
302 return Ok((conn, format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag}")));
303 } else if !mgr {
304 return Err(TardisError::bad_request("The requested tag does not exist", ""));
305 }
306 do_init_table(
307 &schema_name,
308 &conn,
309 &tag,
310 table_flag,
311 table_create_content,
312 table_inherits,
313 indexes,
314 primary_keys,
315 update_time_field,
316 )
317 .await?;
318 Ok((conn, format!("{schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag}")))
319 }
320
321 pub async fn init_conn(bs_inst: TypedSpiBsInst<'_, TardisRelDBClient>) -> TardisResult<(TardisRelDBlConnection, String)> {
324 let conn = bs_inst.0.conn();
325 let schema_name = get_schema_name_from_ext(bs_inst.1).expect("ignore");
326 Ok((conn, schema_name))
327 }
328
329 pub async fn init_table(
332 conn: &TardisRelDBlConnection,
333 tag: Option<&str>,
334 table_flag: &str,
335 table_create_content: &str,
336 indexes: Vec<(&str, &str)>,
338 primary_keys: Option<Vec<&str>>,
339 update_time_field: Option<&str>,
340 ctx: &TardisContext,
341 ) -> TardisResult<()> {
342 let tag = tag.map(|t| format!("_{t}")).unwrap_or_default();
343 let schema_name = get_schema_name_from_context(ctx);
344 do_init_table(&schema_name, conn, &tag, table_flag, table_create_content, None, indexes, primary_keys, update_time_field).await
345 }
346
347 async fn do_init_table(
348 schema_name: &str,
349 conn: &TardisRelDBlConnection,
350 tag: &str,
351 table_flag: &str,
352 table_create_content: &str,
353 table_inherits: Option<String>,
354 indexes: Vec<(&str, &str)>,
356 primary_keys: Option<Vec<&str>>,
357 update_time_field: Option<&str>,
358 ) -> TardisResult<()> {
359 conn.execute_one(
360 &format!(
361 r#"CREATE TABLE {schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag}
362(
363 {table_create_content}
364 ){}"#,
365 if let Some(inherits) = table_inherits {
366 format!(" INHERITS ({inherits})")
367 } else {
368 "".to_string()
369 }
370 ),
371 vec![],
372 )
373 .await?;
374 for (idx, (field_name_or_fun, index_type)) in indexes.into_iter().enumerate() {
375 #[inline]
379 fn truncate_str(s: &str, max_size: usize) -> &str {
380 &s[..max_size.min(s.len())]
381 }
382 let index_name = format!(
383 "idx_{schema_name}{tag}_{table_flag}_{idx}",
384 schema_name = truncate_str(schema_name, 18),
385 tag = truncate_str(tag, 11),
386 table_flag = truncate_str(table_flag, 25),
387 idx = truncate_str(idx.to_string().as_str(), 3),
388 );
389 conn.execute_one(
390 &format!("CREATE INDEX {index_name} ON {schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag} USING {index_type}({field_name_or_fun})"),
391 vec![],
392 )
393 .await?;
394 }
395 if let Some(primary_keys) = primary_keys {
396 let pks = primary_keys.join(", ");
397 conn.execute_one(
398 &format!(r#"ALTER TABLE {schema_name}.{GLOBAL_STORAGE_FLAG}_{table_flag}{tag} ADD PRIMARY KEY ({pks})"#),
399 vec![],
400 )
401 .await?;
402 }
403 if let Some(update_time_field) = update_time_field {
404 conn.execute_one(
405 &format!(
406 r###"CREATE OR REPLACE FUNCTION TARDIS_AUTO_UPDATE_TIME_{}()
407RETURNS TRIGGER AS $$
408BEGIN
409 NEW.{} = now();
410 RETURN NEW;
411END;
412$$ language 'plpgsql';"###,
413 update_time_field.replace('-', "_"),
414 update_time_field
415 ),
416 vec![],
417 )
418 .await?;
419 conn.execute_one(
420 &format!(
421 r###"CREATE OR REPLACE TRIGGER TARDIS_AUTO_UPDATE_TIME_ON
422 BEFORE UPDATE
423 ON
424 {}.{GLOBAL_STORAGE_FLAG}_{}{}
425 FOR EACH ROW
426EXECUTE PROCEDURE TARDIS_AUTO_UPDATE_TIME_{}();"###,
427 schema_name,
428 table_flag,
429 tag,
430 update_time_field.replace('-', "_")
431 ),
432 vec![],
433 )
434 .await?;
435 }
436 Ok(())
437 }
438}