velocity-python 0.0.30__py3-none-any.whl → 0.0.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- velocity/__init__.py +1 -1
- velocity/aws/__init__.py +4 -8
- velocity/db/core/column.py +31 -20
- velocity/db/core/database.py +9 -9
- velocity/db/core/engine.py +101 -99
- velocity/db/core/exceptions.py +23 -0
- velocity/db/core/sequence.py +11 -3
- velocity/db/servers/mysql.py +580 -282
- velocity/db/servers/sqlite.py +649 -371
- velocity/db/servers/sqlserver.py +746 -331
- velocity/misc/conv/__init__.py +1 -1
- velocity/misc/conv/oconv.py +74 -70
- velocity/misc/db.py +25 -19
- velocity/misc/export.py +50 -43
- velocity/misc/format.py +14 -13
- velocity/misc/mail.py +12 -5
- velocity/misc/merge.py +3 -2
- velocity/misc/timer.py +3 -3
- {velocity_python-0.0.30.dist-info → velocity_python-0.0.31.dist-info}/METADATA +1 -1
- velocity_python-0.0.31.dist-info/RECORD +39 -0
- {velocity_python-0.0.30.dist-info → velocity_python-0.0.31.dist-info}/WHEEL +1 -1
- velocity_python-0.0.30.dist-info/RECORD +0 -39
- {velocity_python-0.0.30.dist-info → velocity_python-0.0.31.dist-info}/LICENSE +0 -0
- {velocity_python-0.0.30.dist-info → velocity_python-0.0.31.dist-info}/top_level.txt +0 -0
velocity/db/servers/sqlite.py
CHANGED
|
@@ -5,11 +5,14 @@ import datetime
|
|
|
5
5
|
|
|
6
6
|
from velocity.db import exceptions
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
def initialize(config):
|
|
9
10
|
import sqlite3
|
|
10
11
|
from velocity.db.core.engine import Engine
|
|
12
|
+
|
|
11
13
|
return Engine(sqlite3, config, SQL)
|
|
12
14
|
|
|
15
|
+
|
|
13
16
|
def quote(data):
|
|
14
17
|
if isinstance(data, list):
|
|
15
18
|
new = []
|
|
@@ -17,32 +20,33 @@ def quote(data):
|
|
|
17
20
|
new.append(quote(item))
|
|
18
21
|
return new
|
|
19
22
|
else:
|
|
20
|
-
parts = data.split(
|
|
23
|
+
parts = data.split(".")
|
|
21
24
|
new = []
|
|
22
25
|
for part in parts:
|
|
23
26
|
if '"' in part:
|
|
24
27
|
new.append(part)
|
|
25
28
|
elif part.upper() in reserved_words:
|
|
26
|
-
new.append('"'+part+'"')
|
|
27
|
-
elif re.findall(
|
|
28
|
-
new.append('"'+part+'"')
|
|
29
|
+
new.append('"' + part + '"')
|
|
30
|
+
elif re.findall("[/]", part):
|
|
31
|
+
new.append('"' + part + '"')
|
|
29
32
|
else:
|
|
30
33
|
new.append(part)
|
|
31
|
-
return
|
|
34
|
+
return ".".join(new)
|
|
35
|
+
|
|
32
36
|
|
|
33
37
|
class SQL(object):
|
|
34
38
|
server = "SQLite3"
|
|
35
|
-
type_column_identifier =
|
|
36
|
-
is_nullable =
|
|
39
|
+
type_column_identifier = "data_type"
|
|
40
|
+
is_nullable = "is_nullable"
|
|
37
41
|
|
|
38
|
-
default_schema =
|
|
42
|
+
default_schema = ""
|
|
39
43
|
|
|
40
44
|
ApplicationErrorCodes = []
|
|
41
45
|
|
|
42
46
|
DatabaseMissingErrorCodes = []
|
|
43
47
|
TableMissingErrorCodes = []
|
|
44
48
|
ColumnMissingErrorCodes = []
|
|
45
|
-
ForeignKeyMissingErrorCodes =[]
|
|
49
|
+
ForeignKeyMissingErrorCodes = []
|
|
46
50
|
|
|
47
51
|
ConnectionErrorCodes = []
|
|
48
52
|
DuplicateKeyErrorCodes = []
|
|
@@ -69,15 +73,15 @@ class SQL(object):
|
|
|
69
73
|
|
|
70
74
|
@classmethod
|
|
71
75
|
def schemas(cls):
|
|
72
|
-
return
|
|
76
|
+
return "select schema_name from information_schema.schemata", tuple()
|
|
73
77
|
|
|
74
78
|
@classmethod
|
|
75
79
|
def current_schema(cls):
|
|
76
|
-
return
|
|
80
|
+
return "select current_schema", tuple()
|
|
77
81
|
|
|
78
82
|
@classmethod
|
|
79
83
|
def current_database(cls):
|
|
80
|
-
return
|
|
84
|
+
return "select current_database()", tuple()
|
|
81
85
|
|
|
82
86
|
@classmethod
|
|
83
87
|
def tables(cls, system=False):
|
|
@@ -91,219 +95,252 @@ class SQL(object):
|
|
|
91
95
|
return 'SELECT name FROM sqlite_master WHERE type="view";', tuple()
|
|
92
96
|
|
|
93
97
|
@classmethod
|
|
94
|
-
def __has_pointer(cls,columns):
|
|
98
|
+
def __has_pointer(cls, columns):
|
|
95
99
|
if columns:
|
|
96
|
-
if isinstance(columns,list):
|
|
97
|
-
columns =
|
|
98
|
-
if
|
|
100
|
+
if isinstance(columns, list):
|
|
101
|
+
columns = ",".join(columns)
|
|
102
|
+
if ">" in columns:
|
|
99
103
|
return True
|
|
100
104
|
return False
|
|
101
105
|
|
|
102
106
|
@classmethod
|
|
103
|
-
def select(
|
|
107
|
+
def select(
|
|
108
|
+
cls,
|
|
109
|
+
columns=None,
|
|
110
|
+
table=None,
|
|
111
|
+
where=None,
|
|
112
|
+
orderby=None,
|
|
113
|
+
groupby=None,
|
|
114
|
+
having=None,
|
|
115
|
+
start=None,
|
|
116
|
+
qty=None,
|
|
117
|
+
tbl=None,
|
|
118
|
+
):
|
|
104
119
|
is_join = False
|
|
105
120
|
|
|
106
|
-
if isinstance(columns,str)
|
|
107
|
-
and 'distinct' in columns.lower():
|
|
121
|
+
if isinstance(columns, str) and "distinct" in columns.lower():
|
|
108
122
|
sql = [
|
|
109
|
-
|
|
123
|
+
"SELECT",
|
|
110
124
|
columns,
|
|
111
|
-
|
|
125
|
+
"FROM",
|
|
112
126
|
quote(table),
|
|
113
|
-
|
|
127
|
+
]
|
|
114
128
|
elif cls.__has_pointer(columns):
|
|
115
|
-
if isinstance(columns,str):
|
|
116
|
-
columns = columns.split(
|
|
129
|
+
if isinstance(columns, str):
|
|
130
|
+
columns = columns.split(",")
|
|
117
131
|
letter = 65
|
|
118
132
|
tables = {table: chr(letter)}
|
|
119
133
|
letter += 1
|
|
120
134
|
__select = []
|
|
121
|
-
__from = [
|
|
135
|
+
__from = ["{} AS {}".format(quote(table), tables.get(table))]
|
|
122
136
|
__left_join = []
|
|
123
137
|
|
|
124
138
|
for column in columns:
|
|
125
|
-
if
|
|
139
|
+
if ">" in column:
|
|
126
140
|
is_join = True
|
|
127
|
-
parts = column.split(
|
|
141
|
+
parts = column.split(">")
|
|
128
142
|
foreign = tbl.foreign_key_info(parts[0])
|
|
129
143
|
if not foreign:
|
|
130
144
|
raise exceptions.DbApplicationError("Foreign key not defined")
|
|
131
|
-
ref_table = foreign[
|
|
132
|
-
ref_schema = foreign[
|
|
133
|
-
ref_column = foreign[
|
|
134
|
-
lookup = "{}:{}".format(ref_table,parts[0])
|
|
145
|
+
ref_table = foreign["referenced_table_name"]
|
|
146
|
+
ref_schema = foreign["referenced_table_schema"]
|
|
147
|
+
ref_column = foreign["referenced_column_name"]
|
|
148
|
+
lookup = "{}:{}".format(ref_table, parts[0])
|
|
135
149
|
if lookup in tables:
|
|
136
|
-
__select.append(
|
|
150
|
+
__select.append(
|
|
151
|
+
'{}."{}" as "{}"'.format(
|
|
152
|
+
tables.get(lookup), parts[1], "_".join(parts)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
137
155
|
else:
|
|
138
156
|
tables[lookup] = chr(letter)
|
|
139
157
|
letter += 1
|
|
140
|
-
__select.append(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
__select.append(
|
|
159
|
+
'{}."{}" as "{}"'.format(
|
|
160
|
+
tables.get(lookup), parts[1], "_".join(parts)
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
__left_join.append(
|
|
164
|
+
'LEFT OUTER JOIN "{}"."{}" AS {}'.format(
|
|
165
|
+
ref_schema, ref_table, tables.get(lookup)
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
__left_join.append(
|
|
169
|
+
'ON {}."{}" = {}."{}"'.format(
|
|
170
|
+
tables.get(table),
|
|
171
|
+
parts[0],
|
|
172
|
+
tables.get(lookup),
|
|
173
|
+
ref_column,
|
|
174
|
+
)
|
|
175
|
+
)
|
|
148
176
|
if orderby and column in orderby:
|
|
149
|
-
orderby = orderby.replace(
|
|
177
|
+
orderby = orderby.replace(
|
|
178
|
+
column, "{}.{}".format(tables.get(lookup), parts[1])
|
|
179
|
+
)
|
|
150
180
|
else:
|
|
151
|
-
if
|
|
181
|
+
if "(" in column:
|
|
152
182
|
__select.append(column)
|
|
153
183
|
else:
|
|
154
|
-
__select.append("{}.{}".format(tables.get(table),column))
|
|
155
|
-
sql = [
|
|
156
|
-
sql.append(
|
|
157
|
-
sql.append(
|
|
184
|
+
__select.append("{}.{}".format(tables.get(table), column))
|
|
185
|
+
sql = ["SELECT"]
|
|
186
|
+
sql.append(",".join(__select))
|
|
187
|
+
sql.append("FROM")
|
|
158
188
|
sql.extend(__from)
|
|
159
189
|
sql.extend(__left_join)
|
|
160
190
|
else:
|
|
161
191
|
if columns:
|
|
162
|
-
if isinstance(columns,str):
|
|
163
|
-
columns = columns.split(
|
|
164
|
-
if isinstance(columns,list):
|
|
192
|
+
if isinstance(columns, str):
|
|
193
|
+
columns = columns.split(",")
|
|
194
|
+
if isinstance(columns, list):
|
|
165
195
|
columns = quote(columns)
|
|
166
|
-
columns =
|
|
196
|
+
columns = ",".join(columns)
|
|
167
197
|
else:
|
|
168
|
-
columns =
|
|
198
|
+
columns = "*"
|
|
169
199
|
sql = [
|
|
170
|
-
|
|
200
|
+
"SELECT",
|
|
171
201
|
columns,
|
|
172
|
-
|
|
202
|
+
"FROM",
|
|
173
203
|
quote(table),
|
|
174
|
-
|
|
204
|
+
]
|
|
175
205
|
vals = []
|
|
176
206
|
if where:
|
|
177
|
-
sql.append(
|
|
178
|
-
if isinstance(where,dict):
|
|
207
|
+
sql.append("WHERE")
|
|
208
|
+
if isinstance(where, dict):
|
|
179
209
|
where = [x for x in where.items()]
|
|
180
|
-
if isinstance(where,list):
|
|
181
|
-
join =
|
|
182
|
-
for key,val in where:
|
|
183
|
-
if join:
|
|
210
|
+
if isinstance(where, list):
|
|
211
|
+
join = ""
|
|
212
|
+
for key, val in where:
|
|
213
|
+
if join:
|
|
214
|
+
sql.append(join)
|
|
184
215
|
if is_join:
|
|
185
|
-
if
|
|
186
|
-
key =
|
|
216
|
+
if "." not in key:
|
|
217
|
+
key = "A." + key
|
|
187
218
|
if val == None:
|
|
188
|
-
if
|
|
189
|
-
key = key.replace(
|
|
190
|
-
sql.append(
|
|
219
|
+
if "!" in key:
|
|
220
|
+
key = key.replace("!", "")
|
|
221
|
+
sql.append("{} is not NULL".format(quote(key.lower())))
|
|
191
222
|
else:
|
|
192
|
-
sql.append(
|
|
193
|
-
elif isinstance(val,(list,tuple)):
|
|
194
|
-
if
|
|
195
|
-
key = key.replace(
|
|
196
|
-
sql.append(
|
|
223
|
+
sql.append("{} is NULL".format(quote(key.lower())))
|
|
224
|
+
elif isinstance(val, (list, tuple)):
|
|
225
|
+
if "!" in key:
|
|
226
|
+
key = key.replace("!", "")
|
|
227
|
+
sql.append("{} not in ?".format(quote(key.lower())))
|
|
197
228
|
vals.append(tuple(val))
|
|
198
229
|
else:
|
|
199
|
-
sql.append(
|
|
230
|
+
sql.append("{} in ?".format(quote(key.lower())))
|
|
200
231
|
vals.append(tuple(val))
|
|
201
232
|
else:
|
|
202
|
-
if
|
|
203
|
-
key = key.replace(
|
|
204
|
-
op =
|
|
205
|
-
elif
|
|
206
|
-
key = key.replace(
|
|
207
|
-
op =
|
|
208
|
-
elif
|
|
209
|
-
key = key.replace(
|
|
210
|
-
op =
|
|
211
|
-
elif
|
|
212
|
-
key = key.replace(
|
|
213
|
-
op =
|
|
214
|
-
elif
|
|
215
|
-
key = key.replace(
|
|
216
|
-
op =
|
|
217
|
-
elif
|
|
218
|
-
key = key.replace(
|
|
219
|
-
op =
|
|
220
|
-
elif
|
|
221
|
-
key = key.replace(
|
|
222
|
-
op =
|
|
223
|
-
elif
|
|
224
|
-
key = key.replace(
|
|
225
|
-
op =
|
|
226
|
-
elif
|
|
227
|
-
key = key.replace(
|
|
228
|
-
op =
|
|
229
|
-
elif
|
|
230
|
-
key = key.replace(
|
|
231
|
-
op =
|
|
232
|
-
elif
|
|
233
|
-
key = key.replace(
|
|
234
|
-
op =
|
|
235
|
-
elif
|
|
236
|
-
key = key.replace(
|
|
237
|
-
op =
|
|
238
|
-
elif
|
|
239
|
-
key = key.replace(
|
|
240
|
-
op =
|
|
241
|
-
elif
|
|
242
|
-
key = key.replace(
|
|
243
|
-
op =
|
|
233
|
+
if "<>" in key:
|
|
234
|
+
key = key.replace("<>", "")
|
|
235
|
+
op = "<>"
|
|
236
|
+
elif "!=" in key:
|
|
237
|
+
key = key.replace("!=", "")
|
|
238
|
+
op = "<>"
|
|
239
|
+
elif "!%" in key:
|
|
240
|
+
key = key.replace("!%", "")
|
|
241
|
+
op = "not ilike"
|
|
242
|
+
elif "%%" in key:
|
|
243
|
+
key = key.replace("%%", "")
|
|
244
|
+
op = "%"
|
|
245
|
+
elif "%>" in key:
|
|
246
|
+
key = key.replace("%>", "")
|
|
247
|
+
op = "%>"
|
|
248
|
+
elif "<%" in key:
|
|
249
|
+
key = key.replace("<%", "")
|
|
250
|
+
op = "<%"
|
|
251
|
+
elif "==" in key:
|
|
252
|
+
key = key.replace("==", "")
|
|
253
|
+
op = "="
|
|
254
|
+
elif "<=" in key:
|
|
255
|
+
key = key.replace("<=", "")
|
|
256
|
+
op = "<="
|
|
257
|
+
elif ">=" in key:
|
|
258
|
+
key = key.replace(">=", "")
|
|
259
|
+
op = ">="
|
|
260
|
+
elif "<" in key:
|
|
261
|
+
key = key.replace("<", "")
|
|
262
|
+
op = "<"
|
|
263
|
+
elif ">" in key:
|
|
264
|
+
key = key.replace(">", "")
|
|
265
|
+
op = ">"
|
|
266
|
+
elif "%" in key:
|
|
267
|
+
key = key.replace("%", "")
|
|
268
|
+
op = "ilike"
|
|
269
|
+
elif "!" in key:
|
|
270
|
+
key = key.replace("!", "")
|
|
271
|
+
op = "<>"
|
|
272
|
+
elif "=" in key:
|
|
273
|
+
key = key.replace("=", "")
|
|
274
|
+
op = "="
|
|
244
275
|
else:
|
|
245
|
-
op =
|
|
246
|
-
if isinstance(val,str) and val[:2] ==
|
|
247
|
-
sql.append(
|
|
276
|
+
op = "="
|
|
277
|
+
if isinstance(val, str) and val[:2] == "@@":
|
|
278
|
+
sql.append(
|
|
279
|
+
"{} {} {}".format(quote(key.lower()), op, val[2:])
|
|
280
|
+
)
|
|
248
281
|
else:
|
|
249
|
-
sql.append(
|
|
282
|
+
sql.append("{} {} ?".format(quote(key.lower()), op))
|
|
250
283
|
vals.append(val)
|
|
251
|
-
join =
|
|
284
|
+
join = "AND"
|
|
252
285
|
else:
|
|
253
286
|
sql.append(where)
|
|
254
287
|
if groupby:
|
|
255
|
-
sql.append(
|
|
256
|
-
if isinstance(groupby,(list,tuple)):
|
|
257
|
-
groupby =
|
|
288
|
+
sql.append("GROUP BY")
|
|
289
|
+
if isinstance(groupby, (list, tuple)):
|
|
290
|
+
groupby = ",".join(groupby)
|
|
258
291
|
sql.append(groupby)
|
|
259
292
|
if having:
|
|
260
|
-
sql.append(
|
|
261
|
-
if isinstance(having,(list,tuple)):
|
|
262
|
-
having =
|
|
293
|
+
sql.append("HAVING")
|
|
294
|
+
if isinstance(having, (list, tuple)):
|
|
295
|
+
having = ",".join(having)
|
|
263
296
|
sql.append(having)
|
|
264
297
|
if orderby:
|
|
265
|
-
sql.append(
|
|
266
|
-
if isinstance(orderby,(list,tuple)):
|
|
267
|
-
orderby =
|
|
298
|
+
sql.append("ORDER BY")
|
|
299
|
+
if isinstance(orderby, (list, tuple)):
|
|
300
|
+
orderby = ",".join(orderby)
|
|
268
301
|
sql.append(orderby)
|
|
269
302
|
if start and qty:
|
|
270
|
-
sql.append(
|
|
303
|
+
sql.append("OFFSET {} ROWS FETCH NEXT {} ROWS ONLY".format(start, qty))
|
|
271
304
|
elif start:
|
|
272
|
-
sql.append(
|
|
305
|
+
sql.append("OFFSET {} ROWS".format(start))
|
|
273
306
|
elif qty:
|
|
274
|
-
sql.append(
|
|
275
|
-
sql =
|
|
307
|
+
sql.append("FETCH NEXT {} ROWS ONLY".format(qty))
|
|
308
|
+
sql = " ".join(sql)
|
|
276
309
|
return sql, tuple(vals)
|
|
277
310
|
|
|
278
311
|
@classmethod
|
|
279
312
|
def create_database(cls, name):
|
|
280
|
-
return
|
|
313
|
+
return "create database " + name, tuple()
|
|
281
314
|
|
|
282
315
|
@classmethod
|
|
283
316
|
def last_id(cls, table):
|
|
284
|
-
return
|
|
317
|
+
return "SELECT last_insert_rowid()", tuple()
|
|
285
318
|
|
|
286
319
|
@classmethod
|
|
287
320
|
def drop_database(cls, name):
|
|
288
|
-
return
|
|
321
|
+
return "drop database if exists " + name, tuple()
|
|
289
322
|
|
|
290
323
|
@classmethod
|
|
291
324
|
def create_table(cls, name, columns={}, drop=False):
|
|
292
325
|
sql = []
|
|
293
326
|
if drop:
|
|
294
327
|
sql.append(cls.drop_table(name))
|
|
295
|
-
sql.append(
|
|
328
|
+
sql.append(
|
|
329
|
+
"""
|
|
296
330
|
CREATE TABLE {0} (
|
|
297
331
|
sys_id INTEGER PRIMARY KEY,
|
|
298
332
|
sys_modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
299
333
|
sys_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
300
|
-
""".format(
|
|
334
|
+
""".format(
|
|
335
|
+
name
|
|
336
|
+
)
|
|
337
|
+
)
|
|
301
338
|
|
|
302
|
-
for key,val in columns.items():
|
|
303
|
-
sql.append(",\n{} {}".format(quote(key),cls.get_type(val)))
|
|
339
|
+
for key, val in columns.items():
|
|
340
|
+
sql.append(",\n{} {}".format(quote(key), cls.get_type(val)))
|
|
304
341
|
|
|
305
342
|
sql.append("\n);")
|
|
306
|
-
return
|
|
343
|
+
return "\n\t".join(sql), tuple()
|
|
307
344
|
|
|
308
345
|
@classmethod
|
|
309
346
|
def drop_table(cls, name):
|
|
@@ -315,30 +352,33 @@ class SQL(object):
|
|
|
315
352
|
|
|
316
353
|
@classmethod
|
|
317
354
|
def column_info(cls, table, name):
|
|
318
|
-
params = table.split(
|
|
355
|
+
params = table.split(".")
|
|
319
356
|
params.append(name)
|
|
320
|
-
if
|
|
357
|
+
if "." in table:
|
|
321
358
|
return """
|
|
322
359
|
select *
|
|
323
360
|
from information_schema.columns
|
|
324
361
|
where table_schema = ?
|
|
325
362
|
and table_name = ?
|
|
326
363
|
and column_name = ?
|
|
327
|
-
""", tuple(
|
|
364
|
+
""", tuple(
|
|
365
|
+
params
|
|
366
|
+
)
|
|
328
367
|
else:
|
|
329
368
|
return """
|
|
330
369
|
select *
|
|
331
370
|
from information_schema.columns
|
|
332
371
|
where table_name = ?
|
|
333
372
|
and column_name = ?
|
|
334
|
-
""", tuple(
|
|
335
|
-
|
|
373
|
+
""", tuple(
|
|
374
|
+
params
|
|
375
|
+
)
|
|
336
376
|
|
|
337
377
|
@classmethod
|
|
338
378
|
def primary_keys(cls, table):
|
|
339
|
-
params = table.split(
|
|
379
|
+
params = table.split(".")
|
|
340
380
|
params.reverse()
|
|
341
|
-
if
|
|
381
|
+
if "." in table:
|
|
342
382
|
return """
|
|
343
383
|
SELECT
|
|
344
384
|
pg_attribute.attname
|
|
@@ -351,7 +391,9 @@ class SQL(object):
|
|
|
351
391
|
pg_attribute.attrelid = pg_class.oid AND
|
|
352
392
|
pg_attribute.attnum = any(pg_index.indkey)
|
|
353
393
|
AND indisprimary
|
|
354
|
-
""", tuple(
|
|
394
|
+
""", tuple(
|
|
395
|
+
params
|
|
396
|
+
)
|
|
355
397
|
else:
|
|
356
398
|
return """
|
|
357
399
|
SELECT
|
|
@@ -364,12 +406,14 @@ class SQL(object):
|
|
|
364
406
|
pg_attribute.attrelid = pg_class.oid AND
|
|
365
407
|
pg_attribute.attnum = any(pg_index.indkey)
|
|
366
408
|
AND indisprimary
|
|
367
|
-
""", tuple(
|
|
409
|
+
""", tuple(
|
|
410
|
+
params
|
|
411
|
+
)
|
|
368
412
|
|
|
369
413
|
@classmethod
|
|
370
|
-
def foreign_key_info(cls,table=None,column=None,schema=None):
|
|
371
|
-
if
|
|
372
|
-
schema, table = table.split(
|
|
414
|
+
def foreign_key_info(cls, table=None, column=None, schema=None):
|
|
415
|
+
if "." in table:
|
|
416
|
+
schema, table = table.split(".")
|
|
373
417
|
|
|
374
418
|
sql = """
|
|
375
419
|
SELECT sql
|
|
@@ -399,77 +443,96 @@ class SQL(object):
|
|
|
399
443
|
return sql, tuple()
|
|
400
444
|
|
|
401
445
|
@classmethod
|
|
402
|
-
def create_index(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
446
|
+
def create_index(
|
|
447
|
+
cls,
|
|
448
|
+
table=None,
|
|
449
|
+
columns=None,
|
|
450
|
+
unique=False,
|
|
451
|
+
direction=None,
|
|
452
|
+
name=None,
|
|
453
|
+
schema=None,
|
|
454
|
+
tbl=None,
|
|
455
|
+
):
|
|
456
|
+
if "." not in table and schema:
|
|
457
|
+
table = "{}.{}".format(schema, table)
|
|
458
|
+
if isinstance(columns, (list, set)):
|
|
459
|
+
columns = ",".join([quote(c.lower()) for c in sorted(columns)])
|
|
407
460
|
else:
|
|
408
461
|
columns = quote(columns)
|
|
409
|
-
sql = [
|
|
462
|
+
sql = ["CREATE"]
|
|
410
463
|
if unique:
|
|
411
|
-
sql.append(
|
|
412
|
-
sql.append(
|
|
464
|
+
sql.append("UNIQUE")
|
|
465
|
+
sql.append("INDEX")
|
|
413
466
|
tablename = quote(table)
|
|
414
467
|
if not name:
|
|
415
|
-
name = re.sub(r
|
|
416
|
-
sql.append(
|
|
417
|
-
sql.append(
|
|
468
|
+
name = re.sub(r"\([^)]*\)", "", columns.replace(",", "_"))
|
|
469
|
+
sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
|
|
470
|
+
sql.append("ON")
|
|
418
471
|
sql.append(tablename)
|
|
419
|
-
sql.append(
|
|
472
|
+
sql.append("(")
|
|
420
473
|
sql.append(columns)
|
|
421
|
-
sql.append(
|
|
422
|
-
return
|
|
474
|
+
sql.append(")")
|
|
475
|
+
return " ".join(sql), tuple()
|
|
423
476
|
|
|
424
477
|
# Copied from PostGreSQL
|
|
425
478
|
@classmethod
|
|
426
|
-
def create_foreign_key(
|
|
479
|
+
def create_foreign_key(
|
|
480
|
+
cls, table, columns, key_to_table, key_to_columns, name=None, schema=None
|
|
481
|
+
):
|
|
427
482
|
if not name:
|
|
428
483
|
m = hashlib.md5()
|
|
429
484
|
m.update(table.name)
|
|
430
|
-
m.update(
|
|
485
|
+
m.update(" ".join(columns))
|
|
431
486
|
m.update(key_to_table)
|
|
432
|
-
m.update(
|
|
433
|
-
name =
|
|
487
|
+
m.update(" ".join(key_to_columns))
|
|
488
|
+
name = "FK_" + m.hexdigest()
|
|
434
489
|
|
|
435
490
|
original_name = table.name
|
|
436
491
|
|
|
437
492
|
# Get SQL query to generate table
|
|
438
|
-
sql =
|
|
493
|
+
sql = (
|
|
494
|
+
table.tx.table("sqlite_master")
|
|
495
|
+
.select(columns="sql", where={"name": table.name, "type": "table"})
|
|
496
|
+
.scalar()
|
|
497
|
+
)
|
|
439
498
|
|
|
440
499
|
# Rename original table
|
|
441
|
-
table.rename(table.name+"_original_data")
|
|
500
|
+
table.rename(table.name + "_original_data")
|
|
442
501
|
|
|
443
502
|
key_info = ""
|
|
444
|
-
if isinstance(columns,list) and isinstance(key_to_columns,list):
|
|
503
|
+
if isinstance(columns, list) and isinstance(key_to_columns, list):
|
|
445
504
|
for c in columns:
|
|
446
505
|
key_info += """,
|
|
447
506
|
CONSTRAINT {}
|
|
448
507
|
FOREIGN KEY ({})
|
|
449
508
|
REFERENCES {}({})
|
|
450
|
-
""".format(
|
|
451
|
-
|
|
509
|
+
""".format(
|
|
510
|
+
name, c, key_to_table, key_to_columns[columns.index(c)]
|
|
511
|
+
)
|
|
512
|
+
elif isinstance(columns, str) and isinstance(key_to_columns, str):
|
|
452
513
|
key_info += """,
|
|
453
514
|
CONSTRAINT {}
|
|
454
515
|
FOREIGN KEY ({})
|
|
455
516
|
REFERENCES {}({})
|
|
456
|
-
""".format(
|
|
517
|
+
""".format(
|
|
518
|
+
name, columns, key_to_table, key_to_columns
|
|
519
|
+
)
|
|
457
520
|
else:
|
|
458
|
-
print(
|
|
521
|
+
print('Error parsing argument "columns" or "key_to_columns"')
|
|
459
522
|
|
|
460
523
|
# Splits "CREATE TABLE" portion out to be readded to lines at the end
|
|
461
|
-
sql_data = sql.split("(",1)
|
|
524
|
+
sql_data = sql.split("(", 1)
|
|
462
525
|
|
|
463
526
|
# Goes through the SQL code to generate table and adds foreign key info
|
|
464
|
-
sql_list =
|
|
527
|
+
sql_list = sql_data[1].replace("\n", " ").split(",")
|
|
465
528
|
new_sql_list = []
|
|
466
529
|
for line in sql_list:
|
|
467
530
|
line = line.strip().lower()
|
|
468
531
|
for line in sql_list:
|
|
469
|
-
if sql_list.index(line) == len(sql_list)-1:
|
|
532
|
+
if sql_list.index(line) == len(sql_list) - 1:
|
|
470
533
|
if ")" in line:
|
|
471
|
-
if line.index(")") == len(line)-1:
|
|
472
|
-
new_sql_list.append(line.replace(")",(key_info+")")))
|
|
534
|
+
if line.index(")") == len(line) - 1:
|
|
535
|
+
new_sql_list.append(line.replace(")", (key_info + ")")))
|
|
473
536
|
else:
|
|
474
537
|
new_sql_list.append(line)
|
|
475
538
|
|
|
@@ -477,13 +540,15 @@ class SQL(object):
|
|
|
477
540
|
table.tx.execute("PRAGMA foreign_keys=off;")
|
|
478
541
|
|
|
479
542
|
# Add sql code to recreate original table with foreign keys
|
|
480
|
-
create_db =
|
|
543
|
+
create_db = ",".join(new_sql_list)
|
|
481
544
|
# Adds "CREATE TABLE" portion back into sql code for execution
|
|
482
|
-
create_db =
|
|
545
|
+
create_db = "(".join([sql_data[0], create_db])
|
|
483
546
|
table.tx.execute(create_db)
|
|
484
547
|
|
|
485
548
|
# Create new table with original table name and copy all data from original table
|
|
486
|
-
table.tx.execute(
|
|
549
|
+
table.tx.execute(
|
|
550
|
+
"INSERT INTO {} SELECT * FROM {};".format(original_name, table.name)
|
|
551
|
+
)
|
|
487
552
|
# Enable foreign keys
|
|
488
553
|
create_db = "PRAGMA foreign_keys=on;"
|
|
489
554
|
|
|
@@ -491,188 +556,175 @@ class SQL(object):
|
|
|
491
556
|
|
|
492
557
|
@classmethod
|
|
493
558
|
def drop_index(cls, table=None, columns=None, name=None, schema=None):
|
|
494
|
-
if
|
|
495
|
-
table = "{}.{}".format(schema,table)
|
|
496
|
-
if isinstance(columns,(list,set)):
|
|
497
|
-
columns =
|
|
559
|
+
if "." not in table and schema:
|
|
560
|
+
table = "{}.{}".format(schema, table)
|
|
561
|
+
if isinstance(columns, (list, set)):
|
|
562
|
+
columns = ",".join([quote(c.lower()) for c in sorted(columns)])
|
|
498
563
|
else:
|
|
499
564
|
columns = quote(columns)
|
|
500
|
-
sql = [
|
|
501
|
-
sql.append(
|
|
565
|
+
sql = ["DROP"]
|
|
566
|
+
sql.append("INDEX IF EXISTS")
|
|
502
567
|
tablename = quote(table)
|
|
503
568
|
if not name:
|
|
504
|
-
name = re.sub(r
|
|
505
|
-
sql.append(
|
|
506
|
-
return
|
|
569
|
+
name = re.sub(r"\([^)]*\)", "", columns.replace(",", "_"))
|
|
570
|
+
sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
|
|
571
|
+
return " ".join(sql), tuple()
|
|
507
572
|
|
|
508
573
|
@classmethod
|
|
509
574
|
def insert(cls, table, data):
|
|
510
575
|
keys = []
|
|
511
576
|
vals = []
|
|
512
577
|
args = []
|
|
513
|
-
for key,val in data.items():
|
|
578
|
+
for key, val in data.items():
|
|
514
579
|
keys.append(quote(key.lower()))
|
|
515
|
-
if isinstance(val,str)
|
|
516
|
-
and len(val) > 2 \
|
|
517
|
-
and val[:2] == '@@':
|
|
580
|
+
if isinstance(val, str) and len(val) > 2 and val[:2] == "@@":
|
|
518
581
|
vals.append(val[2:])
|
|
519
|
-
elif isinstance(val,bytearray):
|
|
520
|
-
vals.append(
|
|
582
|
+
elif isinstance(val, bytearray):
|
|
583
|
+
vals.append("?")
|
|
521
584
|
args.append(bytes(val))
|
|
522
585
|
else:
|
|
523
|
-
vals.append(
|
|
586
|
+
vals.append("?")
|
|
524
587
|
args.append(val)
|
|
525
588
|
|
|
526
|
-
sql = [
|
|
589
|
+
sql = ["INSERT INTO"]
|
|
527
590
|
sql.append(quote(table))
|
|
528
|
-
sql.append(
|
|
529
|
-
sql.append(
|
|
530
|
-
sql.append(
|
|
531
|
-
sql.append(
|
|
532
|
-
sql.append(
|
|
533
|
-
sql.append(
|
|
534
|
-
sql.append(
|
|
535
|
-
sql =
|
|
591
|
+
sql.append("(")
|
|
592
|
+
sql.append(",".join(keys))
|
|
593
|
+
sql.append(")")
|
|
594
|
+
sql.append("VALUES")
|
|
595
|
+
sql.append("(")
|
|
596
|
+
sql.append(",".join(vals))
|
|
597
|
+
sql.append(")")
|
|
598
|
+
sql = " ".join(sql)
|
|
536
599
|
return sql, tuple(args)
|
|
537
600
|
|
|
538
601
|
@classmethod
|
|
539
602
|
def update(cls, table, data, pk):
|
|
540
|
-
sql = [
|
|
603
|
+
sql = ["UPDATE"]
|
|
541
604
|
sql.append(quote(table))
|
|
542
|
-
sql.append(
|
|
605
|
+
sql.append("SET")
|
|
543
606
|
vals = []
|
|
544
|
-
join =
|
|
607
|
+
join = ""
|
|
545
608
|
for key in data.keys():
|
|
546
609
|
val = data[key]
|
|
547
610
|
if join:
|
|
548
611
|
sql.append(join)
|
|
549
|
-
if isinstance(val,str) and val[:2] ==
|
|
550
|
-
sql.append("{} = {}".format(quote(key.lower()),val[2:]))
|
|
612
|
+
if isinstance(val, str) and val[:2] == "@@":
|
|
613
|
+
sql.append("{} = {}".format(quote(key.lower()), val[2:]))
|
|
551
614
|
elif isinstance(val, bytearray):
|
|
552
|
-
sql.append(
|
|
615
|
+
sql.append("{} = ?".format(quote(key.lower())))
|
|
553
616
|
vals.append(bytes(val))
|
|
554
617
|
else:
|
|
555
|
-
sql.append(
|
|
618
|
+
sql.append("{} = ?".format(quote(key.lower())))
|
|
556
619
|
vals.append(val)
|
|
557
|
-
join =
|
|
620
|
+
join = ","
|
|
558
621
|
if pk:
|
|
559
622
|
if isinstance(pk, list):
|
|
560
623
|
items = pk
|
|
561
624
|
elif isinstance(pk, dict):
|
|
562
625
|
items = pk.items()
|
|
563
|
-
sql.append(
|
|
564
|
-
join =
|
|
565
|
-
for key,val in items:
|
|
626
|
+
sql.append("\nWHERE")
|
|
627
|
+
join = ""
|
|
628
|
+
for key, val in items:
|
|
566
629
|
if join:
|
|
567
630
|
sql.append(join)
|
|
568
631
|
if val is None:
|
|
569
|
-
if
|
|
570
|
-
key = key.replace(
|
|
571
|
-
sql.append(
|
|
632
|
+
if "!" in key:
|
|
633
|
+
key = key.replace("!", "")
|
|
634
|
+
sql.append("{} is not NULL".format(quote(key.lower())))
|
|
572
635
|
else:
|
|
573
|
-
sql.append(
|
|
574
|
-
elif isinstance(val,(tuple,list)):
|
|
575
|
-
if
|
|
576
|
-
key = key.replace(
|
|
577
|
-
sql.append(
|
|
636
|
+
sql.append("{} is NULL".format(quote(key.lower())))
|
|
637
|
+
elif isinstance(val, (tuple, list)):
|
|
638
|
+
if "!" in key:
|
|
639
|
+
key = key.replace("!", "")
|
|
640
|
+
sql.append("{} not in ?".format(quote(key.lower())))
|
|
578
641
|
vals.append(tuple(val))
|
|
579
642
|
else:
|
|
580
|
-
sql.append(
|
|
643
|
+
sql.append("{} in ?".format(quote(key.lower())))
|
|
581
644
|
vals.append(tuple(val))
|
|
582
645
|
else:
|
|
583
|
-
if
|
|
584
|
-
key = key.replace(
|
|
585
|
-
op =
|
|
586
|
-
elif
|
|
587
|
-
key = key.replace(
|
|
588
|
-
op =
|
|
589
|
-
elif
|
|
590
|
-
key = key.replace(
|
|
591
|
-
op =
|
|
592
|
-
elif
|
|
593
|
-
key = key.replace(
|
|
594
|
-
op =
|
|
595
|
-
elif
|
|
596
|
-
key = key.replace(
|
|
597
|
-
op =
|
|
598
|
-
elif
|
|
599
|
-
key = key.replace(
|
|
600
|
-
op =
|
|
601
|
-
elif
|
|
602
|
-
key = key.replace(
|
|
603
|
-
op =
|
|
604
|
-
elif
|
|
605
|
-
key = key.replace(
|
|
606
|
-
op =
|
|
607
|
-
elif
|
|
608
|
-
key = key.replace(
|
|
609
|
-
op =
|
|
610
|
-
elif
|
|
611
|
-
key = key.replace(
|
|
612
|
-
op =
|
|
613
|
-
elif
|
|
614
|
-
key = key.replace(
|
|
615
|
-
op =
|
|
616
|
-
elif
|
|
617
|
-
key = key.replace(
|
|
618
|
-
op =
|
|
619
|
-
elif
|
|
620
|
-
key = key.replace(
|
|
621
|
-
op =
|
|
622
|
-
elif
|
|
623
|
-
key = key.replace(
|
|
624
|
-
op =
|
|
646
|
+
if "<>" in key:
|
|
647
|
+
key = key.replace("<>", "")
|
|
648
|
+
op = "<>"
|
|
649
|
+
elif "!=" in key:
|
|
650
|
+
key = key.replace("!=", "")
|
|
651
|
+
op = "<>"
|
|
652
|
+
elif "!%" in key:
|
|
653
|
+
key = key.replace("!%", "")
|
|
654
|
+
op = "not ilike"
|
|
655
|
+
elif "%%" in key:
|
|
656
|
+
key = key.replace("%%", "")
|
|
657
|
+
op = "%"
|
|
658
|
+
elif "%>" in key:
|
|
659
|
+
key = key.replace("%>", "")
|
|
660
|
+
op = "%>"
|
|
661
|
+
elif "<%" in key:
|
|
662
|
+
key = key.replace("<%", "")
|
|
663
|
+
op = "<%"
|
|
664
|
+
elif "==" in key:
|
|
665
|
+
key = key.replace("==", "")
|
|
666
|
+
op = "="
|
|
667
|
+
elif "<=" in key:
|
|
668
|
+
key = key.replace("<=", "")
|
|
669
|
+
op = "<="
|
|
670
|
+
elif ">=" in key:
|
|
671
|
+
key = key.replace(">=", "")
|
|
672
|
+
op = ">="
|
|
673
|
+
elif "<" in key:
|
|
674
|
+
key = key.replace("<", "")
|
|
675
|
+
op = "<"
|
|
676
|
+
elif ">" in key:
|
|
677
|
+
key = key.replace(">", "")
|
|
678
|
+
op = ">"
|
|
679
|
+
elif "%" in key:
|
|
680
|
+
key = key.replace("%", "")
|
|
681
|
+
op = "ilike"
|
|
682
|
+
elif "!" in key:
|
|
683
|
+
key = key.replace("!", "")
|
|
684
|
+
op = "<>"
|
|
685
|
+
elif "=" in key:
|
|
686
|
+
key = key.replace("=", "")
|
|
687
|
+
op = "="
|
|
625
688
|
else:
|
|
626
|
-
op =
|
|
627
|
-
if isinstance(val,str) and val[:2] ==
|
|
628
|
-
sql.append(
|
|
689
|
+
op = "="
|
|
690
|
+
if isinstance(val, str) and val[:2] == "@@":
|
|
691
|
+
sql.append("{} {} {}".format(quote(key.lower()), op, val[2:]))
|
|
629
692
|
else:
|
|
630
|
-
sql.append(
|
|
693
|
+
sql.append("{} {} ?".format(quote(key.lower()), op))
|
|
631
694
|
vals.append(val)
|
|
632
|
-
join =
|
|
633
|
-
sql =
|
|
695
|
+
join = "AND"
|
|
696
|
+
sql = " ".join(sql)
|
|
634
697
|
return sql, tuple(vals)
|
|
635
698
|
|
|
636
699
|
@classmethod
|
|
637
700
|
def get_type(cls, v):
|
|
638
701
|
if isinstance(v, str):
|
|
639
|
-
if v[:2] ==
|
|
702
|
+
if v[:2] == "@@":
|
|
640
703
|
return v[2:] or cls.TYPES.TEXT
|
|
641
|
-
elif isinstance(v, (str, bytes))
|
|
642
|
-
|
|
643
|
-
or v is
|
|
644
|
-
return cls.TYPES.TEXT
|
|
645
|
-
elif isinstance(v, bool) \
|
|
646
|
-
or v is bool:
|
|
704
|
+
elif isinstance(v, (str, bytes)) or v is str or v is bytes:
|
|
705
|
+
return cls.TYPES.TEXT
|
|
706
|
+
elif isinstance(v, bool) or v is bool:
|
|
647
707
|
return cls.TYPES.BOOLEAN
|
|
648
|
-
elif isinstance(v, int)
|
|
649
|
-
or v is int:
|
|
708
|
+
elif isinstance(v, int) or v is int:
|
|
650
709
|
if v is int:
|
|
651
710
|
return cls.TYPES.INTEGER
|
|
652
711
|
if v > 2147483647 or v < -2147483648:
|
|
653
712
|
return cls.TYPES.BIGINT
|
|
654
713
|
else:
|
|
655
|
-
return
|
|
656
|
-
elif isinstance(v, float)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
or v is
|
|
661
|
-
return cls.TYPES.NUMERIC + '(19, 6)'
|
|
662
|
-
elif isinstance (v, datetime.datetime) \
|
|
663
|
-
or v is datetime.datetime:
|
|
714
|
+
return cls.TYPES.INTEGER
|
|
715
|
+
elif isinstance(v, float) or v is float:
|
|
716
|
+
return cls.TYPES.NUMERIC + "(19, 6)"
|
|
717
|
+
elif isinstance(v, decimal.Decimal) or v is decimal.Decimal:
|
|
718
|
+
return cls.TYPES.NUMERIC + "(19, 6)"
|
|
719
|
+
elif isinstance(v, datetime.datetime) or v is datetime.datetime:
|
|
664
720
|
return cls.TYPES.DATETIME
|
|
665
|
-
elif isinstance
|
|
666
|
-
or v is datetime.date:
|
|
721
|
+
elif isinstance(v, datetime.date) or v is datetime.date:
|
|
667
722
|
return cls.TYPES.DATE
|
|
668
|
-
elif isinstance(v, datetime.time)
|
|
669
|
-
or v is datetime.time:
|
|
723
|
+
elif isinstance(v, datetime.time) or v is datetime.time:
|
|
670
724
|
return cls.TYPES.TIME
|
|
671
|
-
elif isinstance(v, datetime.timedelta)
|
|
672
|
-
or v is datetime.timedelta:
|
|
725
|
+
elif isinstance(v, datetime.timedelta) or v is datetime.timedelta:
|
|
673
726
|
return cls.TYPES.INTERVAL
|
|
674
|
-
elif isinstance
|
|
675
|
-
or v is bytearray:
|
|
727
|
+
elif isinstance(v, bytearray) or v is bytearray:
|
|
676
728
|
return cls.TYPES.BINARY
|
|
677
729
|
# Everything else defaults to TEXT, incl. None
|
|
678
730
|
return cls.TYPES.TEXT
|
|
@@ -710,51 +762,55 @@ class SQL(object):
|
|
|
710
762
|
:param :
|
|
711
763
|
:returns:
|
|
712
764
|
"""
|
|
713
|
-
data = {key.lower():val for key,val in data.items()}
|
|
765
|
+
data = {key.lower(): val for key, val in data.items()}
|
|
714
766
|
primaryKey = set(cls.GetPrimaryKeyColumnNames())
|
|
715
767
|
if not primaryKey:
|
|
716
768
|
if not cls.Exists():
|
|
717
769
|
raise exceptions.DbTableMissingError
|
|
718
|
-
dataKeys = set(data.keys()).intersection(
|
|
719
|
-
dataColumns = set(data.keys()).difference(
|
|
770
|
+
dataKeys = set(data.keys()).intersection(primaryKey)
|
|
771
|
+
dataColumns = set(data.keys()).difference(primaryKey)
|
|
720
772
|
pk = {}
|
|
721
|
-
pk.update([(k,data[k]) for k in dataKeys])
|
|
773
|
+
pk.update([(k, data[k]) for k in dataKeys])
|
|
722
774
|
d = {}
|
|
723
|
-
d.update([(k,data[k]) for k in dataColumns])
|
|
724
|
-
return d,pk
|
|
775
|
+
d.update([(k, data[k]) for k in dataColumns])
|
|
776
|
+
return d, pk
|
|
725
777
|
|
|
726
778
|
@classmethod
|
|
727
779
|
def alter_add(cls, table, columns, null_allowed=True):
|
|
728
780
|
sql = []
|
|
729
|
-
null =
|
|
730
|
-
if isinstance(columns,dict):
|
|
731
|
-
for key,val in columns.items():
|
|
732
|
-
sql.append(
|
|
733
|
-
|
|
781
|
+
null = "NOT NULL" if not null_allowed else ""
|
|
782
|
+
if isinstance(columns, dict):
|
|
783
|
+
for key, val in columns.items():
|
|
784
|
+
sql.append(
|
|
785
|
+
"ALTER TABLE {} ADD {} {} {};".format(
|
|
786
|
+
quote(table), quote(key), cls.get_type(val), null
|
|
787
|
+
)
|
|
788
|
+
)
|
|
789
|
+
return "\n\t".join(sql), tuple()
|
|
734
790
|
|
|
735
791
|
@classmethod
|
|
736
792
|
def alter_drop(cls, table, columns):
|
|
737
793
|
sql = ["ALTER TABLE {} DROP COLUMN".format(quote(table))]
|
|
738
|
-
if isinstance(columns,dict):
|
|
739
|
-
for key,val in columns.items():
|
|
794
|
+
if isinstance(columns, dict):
|
|
795
|
+
for key, val in columns.items():
|
|
740
796
|
sql.append("{},".format(key))
|
|
741
|
-
if sql[-1][-1] ==
|
|
797
|
+
if sql[-1][-1] == ",":
|
|
742
798
|
sql[-1] = sql[-1][:-1]
|
|
743
|
-
return
|
|
799
|
+
return "\n\t".join(sql), tuple()
|
|
744
800
|
|
|
745
801
|
@classmethod
|
|
746
802
|
def alter_column_by_type(cls, table, column, value, null_allowed=True):
|
|
747
803
|
sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
|
|
748
804
|
sql.append("{} {}".format(quote(column), cls.get_type(value)))
|
|
749
805
|
if not null_allowed:
|
|
750
|
-
sql.append(
|
|
751
|
-
return
|
|
806
|
+
sql.append("NOT NULL")
|
|
807
|
+
return "\n\t".join(sql), tuple()
|
|
752
808
|
|
|
753
809
|
@classmethod
|
|
754
810
|
def alter_column_by_sql(cls, table, column, value):
|
|
755
811
|
sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
|
|
756
812
|
sql.append("{} {}".format(quote(column), value))
|
|
757
|
-
return
|
|
813
|
+
return " ".join(sql), tuple()
|
|
758
814
|
|
|
759
815
|
# SQLite3 does not support renaming columns, in order to do so the table must be copied to a version with the new column's name
|
|
760
816
|
@classmethod
|
|
@@ -763,100 +819,114 @@ class SQL(object):
|
|
|
763
819
|
orig = orig.lower()
|
|
764
820
|
new = new.lower()
|
|
765
821
|
# Get SQL query to generate table
|
|
766
|
-
sql =
|
|
822
|
+
sql = (
|
|
823
|
+
table.tx.table("sqlite_master")
|
|
824
|
+
.select(columns="sql", where={"name": table.name, "type": "table"})
|
|
825
|
+
.scalar()
|
|
826
|
+
)
|
|
767
827
|
original_name = table.name
|
|
768
828
|
|
|
769
829
|
# Splits "CREATE TABLE" portion out to be readded to lines at the end
|
|
770
|
-
sql_data = sql.split("(",1)
|
|
830
|
+
sql_data = sql.split("(", 1)
|
|
771
831
|
|
|
772
|
-
sql_list =
|
|
832
|
+
sql_list = sql_data[1].replace("\n", " ").split(",")
|
|
773
833
|
new_sql_list = []
|
|
774
834
|
for line in sql_list:
|
|
775
835
|
line = line.strip().lower()
|
|
776
836
|
if orig in line:
|
|
777
837
|
if line.index(orig) == 0:
|
|
778
|
-
new_sql_list.append(line.replace(orig,new,1))
|
|
779
|
-
elif (line[0] == "
|
|
780
|
-
new_sql_list.append(line.replace(orig,new,1))
|
|
838
|
+
new_sql_list.append(line.replace(orig, new, 1))
|
|
839
|
+
elif (line[0] == '"' or line[0] == "'") and line.index(orig) == 1:
|
|
840
|
+
new_sql_list.append(line.replace(orig, new, 1))
|
|
781
841
|
else:
|
|
782
842
|
new_sql_list.append(line)
|
|
783
843
|
else:
|
|
784
844
|
new_sql_list.append(line)
|
|
785
845
|
|
|
786
|
-
create_db =
|
|
846
|
+
create_db = ",".join(new_sql_list)
|
|
787
847
|
|
|
788
848
|
# Adds "CREATE TABLE" portion back into sql code for execution
|
|
789
|
-
create_db =
|
|
849
|
+
create_db = "(".join([sql_data[0], create_db])
|
|
790
850
|
create_db += ";"
|
|
791
851
|
|
|
792
852
|
# Rename original table
|
|
793
|
-
table.rename(table.name+"_original_data")
|
|
853
|
+
table.rename(table.name + "_original_data")
|
|
794
854
|
|
|
795
|
-
table.tx.execute(create_db)
|
|
855
|
+
table.tx.execute(create_db)
|
|
796
856
|
# Create new table with original table name and copy all data from original table
|
|
797
|
-
create_db = "INSERT INTO {} SELECT * FROM {};".format(original_name,table.name)
|
|
857
|
+
create_db = "INSERT INTO {} SELECT * FROM {};".format(original_name, table.name)
|
|
798
858
|
return create_db, tuple()
|
|
799
859
|
|
|
800
|
-
|
|
801
860
|
@classmethod
|
|
802
861
|
def rename_table(cls, table, new):
|
|
803
862
|
return "ALTER TABLE {} RENAME TO {};".format(quote(table), quote(new)), tuple()
|
|
804
863
|
|
|
805
864
|
@classmethod
|
|
806
865
|
def create_savepoint(cls, sp):
|
|
807
|
-
return
|
|
866
|
+
return "SAVEPOINT {};".format(sp), tuple()
|
|
808
867
|
|
|
809
868
|
@classmethod
|
|
810
869
|
def release_savepoint(cls, sp):
|
|
811
|
-
return
|
|
870
|
+
return "RELEASE SAVEPOINT {};".format(sp), tuple()
|
|
812
871
|
|
|
813
872
|
@classmethod
|
|
814
873
|
def rollback_savepoint(cls, sp):
|
|
815
|
-
return
|
|
874
|
+
return "ROLLBACK TO SAVEPOINT {};".format(sp), tuple()
|
|
816
875
|
|
|
817
876
|
@classmethod
|
|
818
877
|
def find_duplicates(cls, table, columns, key):
|
|
819
878
|
if isinstance(columns, str):
|
|
820
879
|
columns = [columns]
|
|
821
|
-
return
|
|
880
|
+
return (
|
|
881
|
+
"""
|
|
822
882
|
SELECT {2}
|
|
823
883
|
FROM (SELECT {2},
|
|
824
884
|
ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
|
|
825
885
|
FROM {0}) t
|
|
826
886
|
WHERE t.rnum > 1;
|
|
827
|
-
""".format(
|
|
887
|
+
""".format(
|
|
888
|
+
table, ",".join(quote(columns)), key
|
|
889
|
+
),
|
|
890
|
+
tuple(),
|
|
891
|
+
)
|
|
828
892
|
|
|
829
893
|
@classmethod
|
|
830
894
|
def delete_duplicates(cls, table, columns, key):
|
|
831
895
|
if isinstance(columns, str):
|
|
832
896
|
columns = [columns]
|
|
833
|
-
return
|
|
897
|
+
return (
|
|
898
|
+
"""
|
|
834
899
|
DELETE FROM {0}
|
|
835
900
|
WHERE {2} IN (SELECT {2}
|
|
836
901
|
FROM (SELECT {2},
|
|
837
902
|
ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
|
|
838
903
|
FROM {0}) t
|
|
839
904
|
WHERE t.rnum > 1);
|
|
840
|
-
""".format(
|
|
905
|
+
""".format(
|
|
906
|
+
table, ",".join(quote(columns)), key
|
|
907
|
+
),
|
|
908
|
+
tuple(),
|
|
909
|
+
)
|
|
841
910
|
|
|
842
911
|
@classmethod
|
|
843
912
|
def delete(cls, table, where):
|
|
844
|
-
sql = [
|
|
845
|
-
sql.append(
|
|
913
|
+
sql = ["DELETE FROM {}".format(table)]
|
|
914
|
+
sql.append("WHERE")
|
|
846
915
|
vals = []
|
|
847
|
-
if isinstance(where,dict):
|
|
848
|
-
join =
|
|
916
|
+
if isinstance(where, dict):
|
|
917
|
+
join = ""
|
|
849
918
|
for key in sorted(where.keys()):
|
|
850
|
-
if join:
|
|
919
|
+
if join:
|
|
920
|
+
sql.append(join)
|
|
851
921
|
if where[key] == None:
|
|
852
|
-
sql.append(
|
|
922
|
+
sql.append("{} is NULL".format(quote(key.lower())))
|
|
853
923
|
else:
|
|
854
|
-
sql.append(
|
|
924
|
+
sql.append("{} = ?".format(quote(key.lower())))
|
|
855
925
|
vals.append(where[key])
|
|
856
|
-
join =
|
|
926
|
+
join = "AND"
|
|
857
927
|
else:
|
|
858
928
|
sql.append(where)
|
|
859
|
-
return
|
|
929
|
+
return " ".join(sql), tuple(vals)
|
|
860
930
|
|
|
861
931
|
@classmethod
|
|
862
932
|
def truncate(cls, table):
|
|
@@ -864,36 +934,244 @@ class SQL(object):
|
|
|
864
934
|
|
|
865
935
|
@classmethod
|
|
866
936
|
def create_view(cls, name, query, temp=False, silent=True):
|
|
867
|
-
sql = [
|
|
937
|
+
sql = ["CREATE"]
|
|
868
938
|
if silent:
|
|
869
|
-
sql.append(
|
|
939
|
+
sql.append("OR REPLACE")
|
|
870
940
|
if temp:
|
|
871
|
-
sql.append(
|
|
872
|
-
sql.append(
|
|
941
|
+
sql.append("TEMPORARY")
|
|
942
|
+
sql.append("VIEW")
|
|
873
943
|
sql.append(name)
|
|
874
|
-
sql.append(
|
|
944
|
+
sql.append("AS")
|
|
875
945
|
sql.append(query)
|
|
876
|
-
return
|
|
946
|
+
return " ".join(sql), tuple()
|
|
877
947
|
|
|
878
948
|
@classmethod
|
|
879
949
|
def drop_view(cls, name, silent=True):
|
|
880
|
-
sql = [
|
|
950
|
+
sql = ["DROP VIEW"]
|
|
881
951
|
if silent:
|
|
882
|
-
sql.append(
|
|
952
|
+
sql.append("IF EXISTS")
|
|
883
953
|
sql.append(name)
|
|
884
|
-
return
|
|
954
|
+
return " ".join(sql), tuple()
|
|
885
955
|
|
|
886
956
|
class TYPES(object):
|
|
887
|
-
TEXT =
|
|
888
|
-
INTEGER =
|
|
889
|
-
NUMERIC =
|
|
890
|
-
DATETIME =
|
|
891
|
-
TIMESTAMP =
|
|
892
|
-
DATE =
|
|
893
|
-
TIME =
|
|
894
|
-
BIGINT =
|
|
895
|
-
BOOLEAN =
|
|
896
|
-
BINARY =
|
|
897
|
-
INTERVAL =
|
|
898
|
-
|
|
899
|
-
|
|
957
|
+
TEXT = "TEXT"
|
|
958
|
+
INTEGER = "INTEGER"
|
|
959
|
+
NUMERIC = "NUMERIC"
|
|
960
|
+
DATETIME = "TIMESTAMP WITHOUT TIME ZONE"
|
|
961
|
+
TIMESTAMP = "TIMESTAMP WITHOUT TIME ZONE"
|
|
962
|
+
DATE = "DATE"
|
|
963
|
+
TIME = "TIME WITHOUT TIME ZONE"
|
|
964
|
+
BIGINT = "BIGINT"
|
|
965
|
+
BOOLEAN = "BOOLEAN"
|
|
966
|
+
BINARY = "BLOB"
|
|
967
|
+
INTERVAL = "INTERVAL"
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
reserved_words = [
|
|
971
|
+
"ADMIN",
|
|
972
|
+
"ALIAS",
|
|
973
|
+
"ALL",
|
|
974
|
+
"ALLOCATE",
|
|
975
|
+
"ANALYSE",
|
|
976
|
+
"ANALYZE",
|
|
977
|
+
"AND",
|
|
978
|
+
"ANY",
|
|
979
|
+
"ARE",
|
|
980
|
+
"ARRAY",
|
|
981
|
+
"AS",
|
|
982
|
+
"ASC",
|
|
983
|
+
"AUTHORIZATION",
|
|
984
|
+
"BETWEEN",
|
|
985
|
+
"BINARY",
|
|
986
|
+
"BLOB",
|
|
987
|
+
"BOTH",
|
|
988
|
+
"BREADTH",
|
|
989
|
+
"CALL",
|
|
990
|
+
"CASCADED",
|
|
991
|
+
"CASE",
|
|
992
|
+
"CAST",
|
|
993
|
+
"CATALOG",
|
|
994
|
+
"CHECK",
|
|
995
|
+
"CLOB",
|
|
996
|
+
"COLLATE",
|
|
997
|
+
"COLLATION",
|
|
998
|
+
"COLUMN",
|
|
999
|
+
"COMPLETION",
|
|
1000
|
+
"CONNECT",
|
|
1001
|
+
"CONNECTION",
|
|
1002
|
+
"CONSTRAINT",
|
|
1003
|
+
"CONSTRUCTOR",
|
|
1004
|
+
"CONTINUE",
|
|
1005
|
+
"CORRESPONDING",
|
|
1006
|
+
"CREATE",
|
|
1007
|
+
"CROSS",
|
|
1008
|
+
"CUBE",
|
|
1009
|
+
"CURRENT",
|
|
1010
|
+
"CURRENT_DATE",
|
|
1011
|
+
"CURRENT_PATH",
|
|
1012
|
+
"CURRENT_ROLE",
|
|
1013
|
+
"CURRENT_TIME",
|
|
1014
|
+
"CURRENT_TIMESTAMP",
|
|
1015
|
+
"CURRENT_USER",
|
|
1016
|
+
"DATA",
|
|
1017
|
+
"DATE",
|
|
1018
|
+
"DEFAULT",
|
|
1019
|
+
"DEFERRABLE",
|
|
1020
|
+
"DEPTH",
|
|
1021
|
+
"DEREF",
|
|
1022
|
+
"DESC",
|
|
1023
|
+
"DESCRIBE",
|
|
1024
|
+
"DESCRIPTOR",
|
|
1025
|
+
"DESTROY",
|
|
1026
|
+
"DESTRUCTOR",
|
|
1027
|
+
"DETERMINISTIC",
|
|
1028
|
+
"DIAGNOSTICS",
|
|
1029
|
+
"DICTIONARY",
|
|
1030
|
+
"DISCONNECT",
|
|
1031
|
+
"DISTINCT",
|
|
1032
|
+
"DO",
|
|
1033
|
+
"DYNAMIC",
|
|
1034
|
+
"ELSE",
|
|
1035
|
+
"END",
|
|
1036
|
+
"END-EXEC",
|
|
1037
|
+
"EQUALS",
|
|
1038
|
+
"EVERY",
|
|
1039
|
+
"EXCEPT",
|
|
1040
|
+
"EXCEPTION",
|
|
1041
|
+
"EXEC",
|
|
1042
|
+
"FALSE",
|
|
1043
|
+
"FIRST",
|
|
1044
|
+
"FOR",
|
|
1045
|
+
"FOREIGN",
|
|
1046
|
+
"FOUND",
|
|
1047
|
+
"FREE",
|
|
1048
|
+
"FREEZE",
|
|
1049
|
+
"FROM",
|
|
1050
|
+
"FULL",
|
|
1051
|
+
"GENERAL",
|
|
1052
|
+
"GO",
|
|
1053
|
+
"GOTO",
|
|
1054
|
+
"GRANT",
|
|
1055
|
+
"GROUP",
|
|
1056
|
+
"GROUPING",
|
|
1057
|
+
"HAVING",
|
|
1058
|
+
"HOST",
|
|
1059
|
+
"IDENTITY",
|
|
1060
|
+
"IGNORE",
|
|
1061
|
+
"ILIKE",
|
|
1062
|
+
"IN",
|
|
1063
|
+
"INDICATOR",
|
|
1064
|
+
"INITIALIZE",
|
|
1065
|
+
"INITIALLY",
|
|
1066
|
+
"INNER",
|
|
1067
|
+
"INTERSECT",
|
|
1068
|
+
"INTO",
|
|
1069
|
+
"IS",
|
|
1070
|
+
"ISNULL",
|
|
1071
|
+
"ITERATE",
|
|
1072
|
+
"JOIN",
|
|
1073
|
+
"LARGE",
|
|
1074
|
+
"LAST",
|
|
1075
|
+
"LATERAL",
|
|
1076
|
+
"LEADING",
|
|
1077
|
+
"LEFT",
|
|
1078
|
+
"LESS",
|
|
1079
|
+
"LIKE",
|
|
1080
|
+
"LIMIT",
|
|
1081
|
+
"LOCALTIME",
|
|
1082
|
+
"LOCALTIMESTAMP",
|
|
1083
|
+
"LOCATOR",
|
|
1084
|
+
"MAP",
|
|
1085
|
+
"MODIFIES",
|
|
1086
|
+
"MODIFY",
|
|
1087
|
+
"MODULE",
|
|
1088
|
+
"NAME",
|
|
1089
|
+
"NATURAL",
|
|
1090
|
+
"NCLOB",
|
|
1091
|
+
"NEW",
|
|
1092
|
+
"NOT",
|
|
1093
|
+
"NOTNULL",
|
|
1094
|
+
"NULL",
|
|
1095
|
+
"OBJECT",
|
|
1096
|
+
"OFF",
|
|
1097
|
+
"OFFSET",
|
|
1098
|
+
"OLD",
|
|
1099
|
+
"ON",
|
|
1100
|
+
"ONLY",
|
|
1101
|
+
"OPEN",
|
|
1102
|
+
"OPERATION",
|
|
1103
|
+
"OR",
|
|
1104
|
+
"ORDER",
|
|
1105
|
+
"ORDINALITY",
|
|
1106
|
+
"OUTER",
|
|
1107
|
+
"OUTPUT",
|
|
1108
|
+
"OVERLAPS",
|
|
1109
|
+
"PAD",
|
|
1110
|
+
"PARAMETER",
|
|
1111
|
+
"PARAMETERS",
|
|
1112
|
+
"PLACING",
|
|
1113
|
+
"POSTFIX",
|
|
1114
|
+
"PREFIX",
|
|
1115
|
+
"PREORDER",
|
|
1116
|
+
"PRESERVE",
|
|
1117
|
+
"PRIMARY",
|
|
1118
|
+
"PUBLIC",
|
|
1119
|
+
"READS",
|
|
1120
|
+
"RECURSIVE",
|
|
1121
|
+
"REF",
|
|
1122
|
+
"REFERENCES",
|
|
1123
|
+
"REFERENCING",
|
|
1124
|
+
"RESULT",
|
|
1125
|
+
"RETURN",
|
|
1126
|
+
"RIGHT",
|
|
1127
|
+
"ROLE",
|
|
1128
|
+
"ROLLUP",
|
|
1129
|
+
"ROUTINE",
|
|
1130
|
+
"ROWS",
|
|
1131
|
+
"SAVEPOINT",
|
|
1132
|
+
"SCOPE",
|
|
1133
|
+
"SEARCH",
|
|
1134
|
+
"SECTION",
|
|
1135
|
+
"SELECT",
|
|
1136
|
+
"SESSION_USER",
|
|
1137
|
+
"SETS",
|
|
1138
|
+
"SIMILAR",
|
|
1139
|
+
"SIZE",
|
|
1140
|
+
"SOME",
|
|
1141
|
+
"SPACE",
|
|
1142
|
+
"SPECIFIC",
|
|
1143
|
+
"SPECIFICTYPE",
|
|
1144
|
+
"SQL",
|
|
1145
|
+
"SQLCODE",
|
|
1146
|
+
"SQLERROR",
|
|
1147
|
+
"SQLEXCEPTION",
|
|
1148
|
+
"SQLSTATE",
|
|
1149
|
+
"SQLWARNING",
|
|
1150
|
+
"STATE",
|
|
1151
|
+
"STATIC",
|
|
1152
|
+
"STRUCTURE",
|
|
1153
|
+
"SYSTEM_USER",
|
|
1154
|
+
"TABLE",
|
|
1155
|
+
"TERMINATE",
|
|
1156
|
+
"THAN",
|
|
1157
|
+
"THEN",
|
|
1158
|
+
"TIMESTAMP",
|
|
1159
|
+
"TIMEZONE_HOUR",
|
|
1160
|
+
"TIMEZONE_MINUTE",
|
|
1161
|
+
"TO",
|
|
1162
|
+
"TRAILING",
|
|
1163
|
+
"TRANSLATION",
|
|
1164
|
+
"TRUE",
|
|
1165
|
+
"UNDER",
|
|
1166
|
+
"UNION",
|
|
1167
|
+
"UNIQUE",
|
|
1168
|
+
"UNNEST",
|
|
1169
|
+
"USER",
|
|
1170
|
+
"USING",
|
|
1171
|
+
"VALUE",
|
|
1172
|
+
"VARIABLE",
|
|
1173
|
+
"VERBOSE",
|
|
1174
|
+
"WHEN",
|
|
1175
|
+
"WHENEVER",
|
|
1176
|
+
"WHERE",
|
|
1177
|
+
]
|