1
- from os .path import isfile
2
- from sqlite3 import Connection , Cursor , connect
1
+ import re
2
+ import sys
3
+ from sqlite3 import Connection , Cursor , connect , Error
3
4
4
5
5
6
class DB :
6
7
DB_PATH = "./data/database.db"
7
8
cxn : Connection
8
9
cur : Cursor
9
10
10
- tables : dict [str , dict [str , str ]] = {
11
- 'bans' : {
12
- 'guild_id' : 'TEXT' ,
13
- 'user_id' : 'TEXT' ,
14
- 'mod_id' : 'TEXT' ,
15
- 'reason' : 'TEXT' ,
16
- 'ts' : 'INTEGER'
11
+ table_info : list [dict [str , dict [str , dict [str , str | list [str ]]]]] = [
12
+ {
13
+ 'table' : {
14
+ 'guilds' : {
15
+ 'id' : 'TEXT' ,
16
+ 'level_id' : 'TEXT' ,
17
+ 'announce_id' : 'TEXT'
18
+ }
19
+ },
20
+ 'constraints' : {'not_null' : ['id' ], 'primary_key' : 'id' }
17
21
},
18
- 'guilds' : {
19
- 'id' : 'TEXT PRIMARY KEY NOT NULL' ,
20
- 'level_id' : 'TEXT' ,
21
- 'announce_id' : 'TEXT'
22
+ {
23
+ 'table' : {
24
+ 'bans' : {
25
+ 'guild_id' : 'TEXT' ,
26
+ 'user_id' : 'TEXT' ,
27
+ 'mod_id' : 'TEXT' ,
28
+ 'reason' : 'TEXT' ,
29
+ 'ts' : 'INTEGER'
30
+ }
31
+ },
32
+ 'constraints' : {'foreign_key' : {
33
+ 'guild_id' : ['guilds' , 'id' ]
34
+ }}
22
35
},
23
- 'reports' : {
24
- 'guild_id' : 'TEXT' ,
25
- 'user_id' : 'TEXT' ,
26
- 'target_id' : 'TEXT' ,
27
- 'report' : 'TEXT'
36
+ {
37
+ 'table' : {
38
+ 'reports' : {
39
+ 'guild_id' : 'TEXT' ,
40
+ 'user_id' : 'TEXT' ,
41
+ 'target_id' : 'TEXT' ,
42
+ 'report' : 'TEXT'
43
+ }
44
+ },
45
+ 'constraints' : {}
28
46
},
29
- 'commands' : {
30
- 'command_name' : 'TEXT' ,
31
- 'guild_id' : 'TEXT' ,
32
- 'user_id' : 'TEXT' ,
33
- 'params' : 'JSON' ,
34
- 'ts' : 'INTEGER'
47
+ {
48
+ 'table' : {
49
+ 'commands' : {
50
+ 'command_name' : 'TEXT' ,
51
+ 'guild_id' : 'TEXT' ,
52
+ 'user_id' : 'TEXT' ,
53
+ 'params' : 'JSON' ,
54
+ 'ts' : 'INTEGER'
55
+ }
56
+ },
57
+ 'constraints' : {}
35
58
},
36
- 'levels' : {
37
- 'guild_id' : 'TEXT' ,
38
- 'user_id' : 'TEXT' ,
39
- 'xp' : 'INTEGER'
59
+ {
60
+ 'table' : {
61
+ 'levels' : {
62
+ 'guild_id' : 'TEXT' ,
63
+ 'user_id' : 'TEXT' ,
64
+ 'xp' : 'INTEGER'
65
+ }
66
+ },
67
+ 'constraints' : {}
40
68
},
41
- 'youtube' : {
42
- 'handle' : 'TEXT' ,
43
- 'guild_id' : 'TEXT' ,
44
- 'message' : 'TEXT' ,
45
- 'latest_url' : 'TEXT'
69
+ {
70
+ 'table' : {
71
+ 'youtube' : {
72
+ 'handle' : 'TEXT' ,
73
+ 'guild_id' : 'TEXT' ,
74
+ 'message' : 'TEXT' ,
75
+ 'latest_url' : 'TEXT'
76
+ }
77
+ },
78
+ 'constraints' : {}
46
79
}
47
- }
80
+ ]
48
81
49
82
def __init__ (self ):
50
83
self .cxn = connect (self .DB_PATH , check_same_thread = False )
51
84
self .cur = self .cxn .cursor ()
52
85
53
- def update_table (self , table : str , columns_str_list : list [str ], columns_list : dict [str , str ]):
54
- self .cur .execute (f"CREATE TABLE { table } _new ({ ' ' .join (columns_str_list )} );" )
86
+ def update_table (self , table : str , data : str ):
87
+ try :
88
+ self .cur .execute (f"SELECT * FROM { table } _new" )
89
+ self .cur .execute (f"INSERT INTO { table } SELECT * FROM { table } _new" )
90
+ self .cur .execute (f"DROP TABLE { table } _new" )
91
+ except Error :
92
+ pass
93
+ self .cur .execute (f"CREATE TABLE { table } _new { data } ;" )
55
94
self .cur .execute (f"INSERT INTO { table } _new SELECT * FROM { table } ;" )
56
95
self .cur .execute (f"DROP TABLE { table } ;" )
57
96
self .cur .execute (f"ALTER TABLE { table } _new RENAME TO { table } ;" )
58
97
59
98
def build (self ) -> None :
60
- for table , columns in self .tables .items ():
61
- # Create tables
62
- columns_str_list = []
63
- for index , (name , data_type ) in enumerate (columns .items ()):
64
- columns_str_list .append (f"{ name } { data_type } ," if index < len (columns ) - 1 else f"{ name } { data_type } " )
65
-
66
- self .cur .execute (f"CREATE TABLE IF NOT EXISTS { table } ({ ' ' .join (columns_str_list )} );" )
67
-
68
- # Update tables in case the types need to be updated
69
- self .update_table (table , columns_str_list , columns )
99
+ print ('Building Database...' )
100
+ for table in self .table_info :
101
+ table_data = table ['table' ]
102
+ for table_name , columns in table_data .items ():
103
+ print (f' - { table_name } ' )
104
+ create_table_query = f"CREATE TABLE IF NOT EXISTS { table_name } ("
105
+ constraints = table .get ('constraints' )
106
+ for column_name , data_type in columns .items ():
107
+ print (f' - { column_name } ' )
108
+ if constraints :
109
+ not_null = constraints .get ('not_null' )
110
+ if not_null :
111
+ if column_name in not_null :
112
+ data_type += f" NOT NULL"
113
+ create_table_query += f"{ column_name } { data_type } , "
114
+ if constraints :
115
+ primary_key = constraints .get ('primary_key' )
116
+ default = constraints .get ('default' )
117
+ if default :
118
+ for column , value in default :
119
+ create_table_query += f"{ column } DEFAULT { value } , "
120
+ if primary_key :
121
+ create_table_query += f"PRIMARY KEY({ primary_key } ), "
122
+ unique = constraints .get ('unique' )
123
+ if unique :
124
+ for column in unique :
125
+ create_table_query += f"UNIQUE({ column } ), "
126
+ foreign_key = constraints .get ('foreign_key' )
127
+ if foreign_key :
128
+ for column , [ref_table , ref_column ] in foreign_key .items ():
129
+ create_table_query += f"FOREIGN KEY({ column } ) REFERENCES { ref_table } ({ ref_column } ), "
130
+ check = constraints .get ('check' )
131
+ if check :
132
+ for column , condition in check :
133
+ create_table_query += f"CHECK({ column } { condition } ), "
134
+ create_table_query = create_table_query .rstrip (', ' )
135
+ create_table_query += ")"
136
+ self .cur .execute (create_table_query )
137
+ data = re .search (r"\((?<=\()(?:.)+" , create_table_query ).group ()
138
+ self .update_table (table_name , data )
70
139
self .commit ()
71
140
72
141
def commit (self ) -> None :
@@ -91,11 +160,6 @@ def records(self, command, *values) -> list:
91
160
92
161
return self .cur .fetchall ()
93
162
94
- def column (self , command , * values ) -> list :
95
- self .cur .execute (command , tuple (values ))
96
-
97
- return [item [0 ] for item in self .cur .fetchall ()]
98
-
99
163
def execute (self , command , * values ) -> None :
100
164
self .cur .execute (command , tuple (values ))
101
165
0 commit comments