teo_sql_connector/exts/
index.rs

1use std::borrow::Cow;
2use teo_runtime::model::index::Type;
3use teo_runtime::model::index::Item;
4use crate::exts::sort::SortExt;
5use crate::schema::dialect::SQLDialect;
6
7pub trait IndexExt {
8
9    fn psql_primary_to_unique(&self, table_name: &str) -> Self;
10
11    fn sql_name(&self, table_name: &str, dialect: SQLDialect) -> Cow<str>;
12
13    fn joined_names(&self) -> String;
14
15    fn psql_suffix(&self) -> &str;
16
17    fn normalize_name_psql(&self, table_name: &str) -> String;
18
19    fn normalize_name_normal(&self, table_name: &str) -> String;
20
21    fn normalize_name(&self, table_name: &str, dialect: SQLDialect) -> String;
22
23    fn to_sql_drop(&self, dialect: SQLDialect, table_name: &str) -> String;
24
25    fn to_sql_create(&self, dialect: SQLDialect, table_name: &str) -> String;
26
27    fn sql_format_item(dialect: SQLDialect, item: &Item, table_create_mode: bool) -> String;
28}
29
30impl IndexExt for teo_runtime::model::Index {
31
32    fn psql_primary_to_unique(&self, table_name: &str) -> Self {
33        Self::new(Type::Unique, format!("{table_name}_{}_pkey", self.joined_names()), self.items().clone())
34    }
35
36    fn sql_name(&self, table_name: &str, dialect: SQLDialect) -> Cow<str> {
37        if self.r#type().is_primary() {
38            Cow::Owned(self.normalize_name(table_name, dialect))
39        } else {
40            if dialect.is_sqlite() || (dialect.is_postgres() && !self.name().ends_with("pkey")) {
41                Cow::Owned(format!("{}_{}", table_name, self.name()))
42            } else {
43                Cow::Borrowed(self.name())
44            }
45        }
46    }
47
48    fn joined_names(&self) -> String {
49        self.keys().join("_")
50    }
51
52    fn psql_suffix(&self) -> &str {
53        if self.r#type().is_primary() {
54            "pkey"
55        } else {
56            "idx"
57        }
58    }
59
60    fn normalize_name_psql(&self, table_name: &str) -> String {
61        if self.r#type().is_primary() {
62            format!("{table_name}_{}", self.psql_suffix())
63        } else {
64            format!("{table_name}_{}_{}", self.joined_names(), self.psql_suffix())
65        }
66    }
67
68    fn normalize_name_normal(&self, table_name: &str) -> String {
69        format!("{table_name}_{}", self.joined_names())
70    }
71
72    fn normalize_name(&self, table_name: &str, dialect: SQLDialect) -> String {
73        match self.r#type() {
74            Type::Primary => match dialect {
75                SQLDialect::MySQL => "PRIMARY".to_owned(),
76                SQLDialect::SQLite => format!("teo_primary_sqlite_index_{}", table_name),
77                //SQLDialect::SQLite => format!("sqlite_autoindex_{}_1", table_name),
78                SQLDialect::PostgreSQL => self.normalize_name_psql(table_name),
79                _ => unreachable!()
80            },
81            _ => match dialect {
82                SQLDialect::PostgreSQL => self.normalize_name_psql(table_name),
83                _ => self.normalize_name_normal(table_name),
84            }
85        }
86    }
87
88    fn to_sql_drop(&self, dialect: SQLDialect, table_name: &str) -> String {
89        let escape = dialect.escape();
90        let index_name_cow = self.sql_name(table_name, dialect);
91        let index_name = index_name_cow.as_ref();
92        if dialect == SQLDialect::PostgreSQL {
93            format!("DROP INDEX {escape}{index_name}{escape}")
94        } else if dialect == SQLDialect::SQLite {
95            format!("DROP INDEX IF EXISTS {escape}{index_name}{escape}")
96        } else {
97            format!("DROP INDEX {escape}{index_name}{escape} ON {escape}{table_name}{escape}")
98        }
99    }
100
101    fn to_sql_create(&self, dialect: SQLDialect, table_name: &str) -> String {
102        let escape = dialect.escape();
103        let index_name_cow = self.sql_name(table_name, dialect);
104        let index_name = index_name_cow.as_ref();
105        let unique = if self.r#type().is_unique() { "UNIQUE " } else { "" };
106        let fields: Vec<String> = self.items().iter().map(|item| {
107            Self::sql_format_item(dialect, item, false)
108        }).collect();
109        format!("CREATE {unique}INDEX {escape}{index_name}{escape} ON {escape}{table_name}{escape}({})", fields.join(","))
110    }
111
112    fn sql_format_item(dialect: SQLDialect, item: &Item, table_create_mode: bool) -> String {
113        let escape = dialect.escape();
114        let name = &item.field;
115        let sort = item.sort.to_str();
116        let len = if let Some(len) = item.len {
117            if dialect == SQLDialect::MySQL {
118                Cow::Owned(format!("({})", len))
119            } else {
120                Cow::Borrowed("")
121            }
122        } else {
123            Cow::Borrowed("")
124        };
125        if table_create_mode && dialect == SQLDialect::PostgreSQL {
126            format!("{escape}{name}{escape}")
127        } else {
128            format!("{escape}{name}{escape}{len} {sort}")
129        }
130    }
131}