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/sqlserver.py
CHANGED
|
@@ -8,92 +8,97 @@ from velocity.db import exceptions
|
|
|
8
8
|
def initialize(config):
|
|
9
9
|
import pytds
|
|
10
10
|
from velocity.db.core.engine import Engine
|
|
11
|
+
|
|
11
12
|
return Engine(pytds, config, SQL)
|
|
12
13
|
|
|
14
|
+
|
|
13
15
|
def make_where(where, sql, vals, is_join=False):
|
|
14
16
|
if not where:
|
|
15
17
|
return
|
|
16
|
-
sql.append(
|
|
18
|
+
sql.append("WHERE")
|
|
17
19
|
if isinstance(where, str):
|
|
18
20
|
sql.append(where)
|
|
19
21
|
return
|
|
20
|
-
if isinstance(where,dict):
|
|
22
|
+
if isinstance(where, dict):
|
|
21
23
|
where = where.items()
|
|
22
|
-
if isinstance(where,list):
|
|
23
|
-
join =
|
|
24
|
-
for key,val in where:
|
|
25
|
-
if join:
|
|
24
|
+
if isinstance(where, list):
|
|
25
|
+
join = ""
|
|
26
|
+
for key, val in where:
|
|
27
|
+
if join:
|
|
28
|
+
sql.append(join)
|
|
26
29
|
if is_join:
|
|
27
|
-
if
|
|
28
|
-
key =
|
|
30
|
+
if "." not in key:
|
|
31
|
+
key = "A." + key
|
|
29
32
|
if val == None:
|
|
30
|
-
if
|
|
31
|
-
key = key.replace(
|
|
32
|
-
sql.append(
|
|
33
|
+
if "!" in key:
|
|
34
|
+
key = key.replace("!", "")
|
|
35
|
+
sql.append("{} is not NULL".format(quote(key.lower())))
|
|
33
36
|
else:
|
|
34
|
-
sql.append(
|
|
35
|
-
elif isinstance(val,(list,tuple)):
|
|
36
|
-
if
|
|
37
|
-
key = key.replace(
|
|
38
|
-
sql.append(
|
|
37
|
+
sql.append("{} is NULL".format(quote(key.lower())))
|
|
38
|
+
elif isinstance(val, (list, tuple)):
|
|
39
|
+
if "!" in key:
|
|
40
|
+
key = key.replace("!", "")
|
|
41
|
+
sql.append("{} not in %s".format(quote(key.lower())))
|
|
39
42
|
vals.append(tuple(val))
|
|
40
43
|
else:
|
|
41
|
-
sql.append(
|
|
44
|
+
sql.append("{} in %s".format(quote(key.lower())))
|
|
42
45
|
vals.append(tuple(val))
|
|
43
46
|
else:
|
|
44
|
-
if
|
|
45
|
-
key = key.replace(
|
|
46
|
-
op =
|
|
47
|
-
elif
|
|
48
|
-
key = key.replace(
|
|
49
|
-
op =
|
|
50
|
-
elif
|
|
51
|
-
key = key.replace(
|
|
52
|
-
op =
|
|
53
|
-
elif
|
|
54
|
-
key = key.replace(
|
|
55
|
-
op =
|
|
56
|
-
elif
|
|
57
|
-
key = key.replace(
|
|
58
|
-
op =
|
|
59
|
-
elif
|
|
60
|
-
key = key.replace(
|
|
61
|
-
op =
|
|
62
|
-
elif
|
|
63
|
-
key = key.replace(
|
|
64
|
-
op =
|
|
65
|
-
elif
|
|
66
|
-
key = key.replace(
|
|
67
|
-
op =
|
|
68
|
-
elif
|
|
69
|
-
key = key.replace(
|
|
70
|
-
op =
|
|
71
|
-
elif
|
|
72
|
-
key = key.replace(
|
|
73
|
-
op =
|
|
74
|
-
elif
|
|
75
|
-
key = key.replace(
|
|
76
|
-
op =
|
|
77
|
-
elif
|
|
78
|
-
key = key.replace(
|
|
79
|
-
op =
|
|
80
|
-
elif
|
|
81
|
-
key = key.replace(
|
|
82
|
-
op =
|
|
83
|
-
elif
|
|
84
|
-
key = key.replace(
|
|
85
|
-
op =
|
|
47
|
+
if "<>" in key:
|
|
48
|
+
key = key.replace("<>", "")
|
|
49
|
+
op = "<>"
|
|
50
|
+
elif "!=" in key:
|
|
51
|
+
key = key.replace("!=", "")
|
|
52
|
+
op = "<>"
|
|
53
|
+
elif "!%" in key:
|
|
54
|
+
key = key.replace("!%", "")
|
|
55
|
+
op = "not like"
|
|
56
|
+
elif "%%" in key:
|
|
57
|
+
key = key.replace("%%", "")
|
|
58
|
+
op = "%"
|
|
59
|
+
elif "%>" in key:
|
|
60
|
+
key = key.replace("%>", "")
|
|
61
|
+
op = "%>"
|
|
62
|
+
elif "<%" in key:
|
|
63
|
+
key = key.replace("<%", "")
|
|
64
|
+
op = "<%"
|
|
65
|
+
elif "==" in key:
|
|
66
|
+
key = key.replace("==", "")
|
|
67
|
+
op = "="
|
|
68
|
+
elif "<=" in key:
|
|
69
|
+
key = key.replace("<=", "")
|
|
70
|
+
op = "<="
|
|
71
|
+
elif ">=" in key:
|
|
72
|
+
key = key.replace(">=", "")
|
|
73
|
+
op = ">="
|
|
74
|
+
elif "<" in key:
|
|
75
|
+
key = key.replace("<", "")
|
|
76
|
+
op = "<"
|
|
77
|
+
elif ">" in key:
|
|
78
|
+
key = key.replace(">", "")
|
|
79
|
+
op = ">"
|
|
80
|
+
elif "%" in key:
|
|
81
|
+
key = key.replace("%", "")
|
|
82
|
+
op = "like"
|
|
83
|
+
elif "!" in key:
|
|
84
|
+
key = key.replace("!", "")
|
|
85
|
+
op = "<>"
|
|
86
|
+
elif "=" in key:
|
|
87
|
+
key = key.replace("=", "")
|
|
88
|
+
op = "="
|
|
86
89
|
else:
|
|
87
|
-
op =
|
|
88
|
-
if isinstance(val,str) and val[:2] ==
|
|
89
|
-
sql.append(
|
|
90
|
+
op = "="
|
|
91
|
+
if isinstance(val, str) and val[:2] == "@@":
|
|
92
|
+
sql.append("{} {} {}".format(quote(key.lower()), op, val[2:]))
|
|
90
93
|
else:
|
|
91
|
-
if
|
|
92
|
-
sql.append(
|
|
94
|
+
if "like" in op:
|
|
95
|
+
sql.append(
|
|
96
|
+
"lower({}) {} lower(%s)".format(quote(key.lower()), op)
|
|
97
|
+
)
|
|
93
98
|
else:
|
|
94
|
-
sql.append(
|
|
99
|
+
sql.append("{} {} %s".format(quote(key.lower()), op))
|
|
95
100
|
vals.append(val)
|
|
96
|
-
join =
|
|
101
|
+
join = "AND"
|
|
97
102
|
# for index, value in enumerate(vals):
|
|
98
103
|
# print "In loop..."
|
|
99
104
|
# if isinstance(value, (bytearray,buffer)):
|
|
@@ -101,6 +106,7 @@ def make_where(where, sql, vals, is_join=False):
|
|
|
101
106
|
# print value
|
|
102
107
|
# vals[index] = pytds.Binary(str(value))
|
|
103
108
|
|
|
109
|
+
|
|
104
110
|
def quote(data):
|
|
105
111
|
if isinstance(data, list):
|
|
106
112
|
new = []
|
|
@@ -108,35 +114,40 @@ def quote(data):
|
|
|
108
114
|
new.append(quote(item))
|
|
109
115
|
return new
|
|
110
116
|
else:
|
|
111
|
-
parts = data.split(
|
|
117
|
+
parts = data.split(".")
|
|
112
118
|
new = []
|
|
113
119
|
for part in parts:
|
|
114
|
-
if
|
|
120
|
+
if "[" in part:
|
|
115
121
|
new.append(part)
|
|
116
122
|
elif part.upper() in reserved_words:
|
|
117
|
-
new.append(
|
|
118
|
-
elif re.findall(
|
|
119
|
-
new.append(
|
|
123
|
+
new.append("[" + part + "]")
|
|
124
|
+
elif re.findall("[/]", part):
|
|
125
|
+
new.append("[" + part + "]")
|
|
120
126
|
else:
|
|
121
127
|
new.append(part)
|
|
122
|
-
return
|
|
128
|
+
return ".".join(new)
|
|
129
|
+
|
|
123
130
|
|
|
124
131
|
class SQL:
|
|
125
132
|
server = "SQL Server"
|
|
126
|
-
type_column_identifier =
|
|
127
|
-
default_schema =
|
|
133
|
+
type_column_identifier = "data_type"
|
|
134
|
+
default_schema = "dbo"
|
|
128
135
|
|
|
129
136
|
ApplicationErrorCodes = []
|
|
130
137
|
|
|
131
138
|
DatabaseMissingErrorCodes = []
|
|
132
|
-
TableMissingErrorCodes = [
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
TableMissingErrorCodes = [
|
|
140
|
+
208,
|
|
141
|
+
]
|
|
142
|
+
ColumnMissingErrorCodes = [207, 1911]
|
|
143
|
+
ForeignKeyMissingErrorCodes = []
|
|
135
144
|
|
|
136
145
|
ConnectionErrorCodes = []
|
|
137
146
|
DuplicateKeyErrorCodes = []
|
|
138
147
|
RetryTransactionCodes = []
|
|
139
|
-
TruncationErrorCodes = [
|
|
148
|
+
TruncationErrorCodes = [
|
|
149
|
+
8152,
|
|
150
|
+
]
|
|
140
151
|
LockTimeoutErrorCodes = []
|
|
141
152
|
DatabaseObjectExistsErrorCodes = []
|
|
142
153
|
|
|
@@ -158,157 +169,190 @@ class SQL:
|
|
|
158
169
|
|
|
159
170
|
@classmethod
|
|
160
171
|
def schemas(cls):
|
|
161
|
-
return
|
|
172
|
+
return "select schema_name from information_schema.schemata", tuple()
|
|
162
173
|
|
|
163
174
|
@classmethod
|
|
164
175
|
def current_schema(cls):
|
|
165
|
-
return
|
|
176
|
+
return "select schema_name()", tuple()
|
|
166
177
|
|
|
167
178
|
@classmethod
|
|
168
179
|
def current_database(cls):
|
|
169
|
-
return
|
|
180
|
+
return "select db_name() as current_database", tuple()
|
|
170
181
|
|
|
171
182
|
@classmethod
|
|
172
183
|
def tables(cls, system=False):
|
|
173
|
-
return
|
|
184
|
+
return (
|
|
185
|
+
"""
|
|
174
186
|
select table_schema, table_name
|
|
175
187
|
from information_schema.tables
|
|
176
188
|
where table_type = 'BASE TABLE'
|
|
177
189
|
order by table_schema,table_name
|
|
178
|
-
""",
|
|
190
|
+
""",
|
|
191
|
+
tuple(),
|
|
192
|
+
)
|
|
179
193
|
|
|
180
194
|
@classmethod
|
|
181
195
|
def views(cls, system=False):
|
|
182
|
-
return
|
|
196
|
+
return (
|
|
197
|
+
"SELECT s.name , v.name FROM sys.views v inner join sys.schemas s on s.schema_id = v.schema_id",
|
|
198
|
+
tuple(),
|
|
199
|
+
)
|
|
183
200
|
|
|
184
201
|
@classmethod
|
|
185
|
-
def __has_pointer(cls,columns):
|
|
202
|
+
def __has_pointer(cls, columns):
|
|
186
203
|
if columns:
|
|
187
|
-
if isinstance(columns,list):
|
|
188
|
-
columns =
|
|
189
|
-
if
|
|
204
|
+
if isinstance(columns, list):
|
|
205
|
+
columns = ",".join(columns)
|
|
206
|
+
if ">" in columns:
|
|
190
207
|
return True
|
|
191
208
|
return False
|
|
192
209
|
|
|
193
210
|
@classmethod
|
|
194
|
-
def select(
|
|
211
|
+
def select(
|
|
212
|
+
cls,
|
|
213
|
+
columns=None,
|
|
214
|
+
table=None,
|
|
215
|
+
where=None,
|
|
216
|
+
orderby=None,
|
|
217
|
+
groupby=None,
|
|
218
|
+
having=None,
|
|
219
|
+
start=None,
|
|
220
|
+
qty=None,
|
|
221
|
+
tbl=None,
|
|
222
|
+
):
|
|
195
223
|
is_join = False
|
|
196
224
|
|
|
197
|
-
if isinstance(columns,str)
|
|
198
|
-
and 'distinct' in columns.lower():
|
|
225
|
+
if isinstance(columns, str) and "distinct" in columns.lower():
|
|
199
226
|
sql = [
|
|
200
|
-
|
|
227
|
+
"SELECT",
|
|
201
228
|
columns,
|
|
202
|
-
|
|
229
|
+
"FROM",
|
|
203
230
|
quote(table),
|
|
204
|
-
|
|
231
|
+
]
|
|
205
232
|
elif cls.__has_pointer(columns):
|
|
206
233
|
is_join = True
|
|
207
|
-
if isinstance(columns,str):
|
|
208
|
-
columns = columns.split(
|
|
234
|
+
if isinstance(columns, str):
|
|
235
|
+
columns = columns.split(",")
|
|
209
236
|
letter = 65
|
|
210
237
|
tables = {table: chr(letter)}
|
|
211
238
|
letter += 1
|
|
212
239
|
__select = []
|
|
213
|
-
__from = [
|
|
240
|
+
__from = ["{} AS {}".format(quote(table), tables.get(table))]
|
|
214
241
|
__left_join = []
|
|
215
242
|
|
|
216
243
|
for column in columns:
|
|
217
|
-
if
|
|
244
|
+
if ">" in column:
|
|
218
245
|
is_join = True
|
|
219
|
-
parts = column.split(
|
|
246
|
+
parts = column.split(">")
|
|
220
247
|
foreign = tbl.foreign_key_info(parts[0])
|
|
221
248
|
if not foreign:
|
|
222
249
|
raise exceptions.DbApplicationError("Foreign key not defined")
|
|
223
|
-
ref_table = foreign[
|
|
224
|
-
ref_schema = foreign[
|
|
225
|
-
ref_column = foreign[
|
|
226
|
-
lookup = "{}:{}".format(ref_table,parts[0])
|
|
250
|
+
ref_table = foreign["referenced_table_name"]
|
|
251
|
+
ref_schema = foreign["referenced_table_schema"]
|
|
252
|
+
ref_column = foreign["referenced_column_name"]
|
|
253
|
+
lookup = "{}:{}".format(ref_table, parts[0])
|
|
227
254
|
if tables.has_key(lookup):
|
|
228
|
-
__select.append(
|
|
255
|
+
__select.append(
|
|
256
|
+
'{}."{}" as "{}"'.format(
|
|
257
|
+
tables.get(lookup), parts[1], "_".join(parts)
|
|
258
|
+
)
|
|
259
|
+
)
|
|
229
260
|
else:
|
|
230
261
|
tables[lookup] = chr(letter)
|
|
231
262
|
letter += 1
|
|
232
|
-
__select.append(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
263
|
+
__select.append(
|
|
264
|
+
'{}."{}" as "{}"'.format(
|
|
265
|
+
tables.get(lookup), parts[1], "_".join(parts)
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
__left_join.append(
|
|
269
|
+
'LEFT OUTER JOIN "{}"."{}" AS {}'.format(
|
|
270
|
+
ref_schema, ref_table, tables.get(lookup)
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
__left_join.append(
|
|
274
|
+
'ON {}."{}" = {}."{}"'.format(
|
|
275
|
+
tables.get(table),
|
|
276
|
+
parts[0],
|
|
277
|
+
tables.get(lookup),
|
|
278
|
+
ref_column,
|
|
279
|
+
)
|
|
280
|
+
)
|
|
240
281
|
if orderby and column in orderby:
|
|
241
|
-
orderby = orderby.replace(
|
|
282
|
+
orderby = orderby.replace(
|
|
283
|
+
column, "{}.{}".format(tables.get(lookup), parts[1])
|
|
284
|
+
)
|
|
242
285
|
|
|
243
286
|
else:
|
|
244
|
-
if
|
|
287
|
+
if "(" in column:
|
|
245
288
|
__select.append(column)
|
|
246
289
|
else:
|
|
247
|
-
__select.append("{}.{}".format(tables.get(table),column))
|
|
248
|
-
sql = [
|
|
249
|
-
sql.append(
|
|
250
|
-
sql.append(
|
|
290
|
+
__select.append("{}.{}".format(tables.get(table), column))
|
|
291
|
+
sql = ["SELECT"]
|
|
292
|
+
sql.append(",".join(__select))
|
|
293
|
+
sql.append("FROM")
|
|
251
294
|
sql.extend(__from)
|
|
252
295
|
sql.extend(__left_join)
|
|
253
296
|
else:
|
|
254
297
|
if columns:
|
|
255
|
-
if isinstance(columns,str):
|
|
256
|
-
columns = columns.split(
|
|
257
|
-
if isinstance(columns,list):
|
|
298
|
+
if isinstance(columns, str):
|
|
299
|
+
columns = columns.split(",")
|
|
300
|
+
if isinstance(columns, list):
|
|
258
301
|
columns = quote(columns)
|
|
259
|
-
columns =
|
|
302
|
+
columns = ",".join(columns)
|
|
260
303
|
else:
|
|
261
|
-
columns =
|
|
304
|
+
columns = "*"
|
|
262
305
|
sql = [
|
|
263
|
-
|
|
306
|
+
"SELECT",
|
|
264
307
|
columns,
|
|
265
|
-
|
|
308
|
+
"FROM",
|
|
266
309
|
quote(table),
|
|
267
|
-
|
|
310
|
+
]
|
|
268
311
|
vals = []
|
|
269
312
|
make_where(where, sql, vals, is_join)
|
|
270
313
|
if groupby:
|
|
271
|
-
sql.append(
|
|
272
|
-
if isinstance(groupby,(list,tuple)):
|
|
273
|
-
groupby =
|
|
314
|
+
sql.append("GROUP BY")
|
|
315
|
+
if isinstance(groupby, (list, tuple)):
|
|
316
|
+
groupby = ",".join(groupby)
|
|
274
317
|
sql.append(groupby)
|
|
275
318
|
if having:
|
|
276
|
-
sql.append(
|
|
277
|
-
if isinstance(having,(list,tuple)):
|
|
278
|
-
having =
|
|
319
|
+
sql.append("HAVING")
|
|
320
|
+
if isinstance(having, (list, tuple)):
|
|
321
|
+
having = ",".join(having)
|
|
279
322
|
sql.append(having)
|
|
280
323
|
if orderby:
|
|
281
|
-
sql.append(
|
|
282
|
-
if isinstance(orderby,(list,tuple)):
|
|
283
|
-
orderby =
|
|
324
|
+
sql.append("ORDER BY")
|
|
325
|
+
if isinstance(orderby, (list, tuple)):
|
|
326
|
+
orderby = ",".join(orderby)
|
|
284
327
|
sql.append(orderby)
|
|
285
328
|
if start and qty:
|
|
286
|
-
sql.append(
|
|
329
|
+
sql.append("OFFSET {} ROWS FETCH NEXT {} ROWS ONLY".format(start, qty))
|
|
287
330
|
elif start:
|
|
288
|
-
sql.append(
|
|
331
|
+
sql.append("OFFSET {} ROWS".format(start))
|
|
289
332
|
elif qty:
|
|
290
|
-
sql.append(
|
|
291
|
-
sql =
|
|
333
|
+
sql.append("FETCH NEXT {} ROWS ONLY".format(qty))
|
|
334
|
+
sql = " ".join(sql)
|
|
292
335
|
return sql, tuple(vals)
|
|
293
336
|
|
|
294
337
|
@classmethod
|
|
295
338
|
def create_database(cls, name):
|
|
296
|
-
return
|
|
339
|
+
return "create database " + name, tuple()
|
|
297
340
|
|
|
298
341
|
@classmethod
|
|
299
342
|
def last_id(cls, table):
|
|
300
|
-
return
|
|
343
|
+
return "SELECT @@IDENTITY", tuple()
|
|
301
344
|
|
|
302
345
|
@classmethod
|
|
303
346
|
def drop_database(cls, name):
|
|
304
|
-
return
|
|
347
|
+
return "drop database " + name, tuple()
|
|
305
348
|
|
|
306
349
|
@classmethod
|
|
307
|
-
def foreign_key_info(cls,table=None,column=None,schema=None):
|
|
308
|
-
if
|
|
309
|
-
schema, table = table.split(
|
|
350
|
+
def foreign_key_info(cls, table=None, column=None, schema=None):
|
|
351
|
+
if "." in table:
|
|
352
|
+
schema, table = table.split(".")
|
|
310
353
|
|
|
311
|
-
sql = [
|
|
354
|
+
sql = [
|
|
355
|
+
"""
|
|
312
356
|
SELECT
|
|
313
357
|
KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME
|
|
314
358
|
,KCU1.TABLE_NAME AS FK_TABLE_NAME
|
|
@@ -331,24 +375,27 @@ class SQL:
|
|
|
331
375
|
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
|
|
332
376
|
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
|
|
333
377
|
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
|
|
334
|
-
"""
|
|
378
|
+
"""
|
|
379
|
+
]
|
|
335
380
|
vals = []
|
|
336
381
|
where = {}
|
|
337
382
|
if schema:
|
|
338
|
-
where[
|
|
383
|
+
where["LOWER(KCU1.CONSTRAINT_SCHEMA)"] = schema.lower()
|
|
339
384
|
if table:
|
|
340
|
-
where[
|
|
385
|
+
where["LOWER(KCU1.TABLE_NAME)"] = table.lower()
|
|
341
386
|
if column:
|
|
342
|
-
where[
|
|
387
|
+
where["LOWER(KCU1.COLUMN_NAME)"] = column.lower()
|
|
343
388
|
make_where(where, sql, vals)
|
|
344
|
-
return
|
|
389
|
+
return " ".join(sql), tuple(vals)
|
|
345
390
|
|
|
346
391
|
@classmethod
|
|
347
|
-
def create_foreign_key(
|
|
348
|
-
|
|
392
|
+
def create_foreign_key(
|
|
393
|
+
cls, table, columns, key_to_table, key_to_columns, name=None, schema=None
|
|
394
|
+
):
|
|
395
|
+
if "." not in table and schema:
|
|
349
396
|
if schema == None:
|
|
350
397
|
schema = cls.default_schema
|
|
351
|
-
table = "{}.{}".format(schema,table)
|
|
398
|
+
table = "{}.{}".format(schema, table)
|
|
352
399
|
if isinstance(key_to_columns, str):
|
|
353
400
|
key_to_columns = [key_to_columns]
|
|
354
401
|
if isinstance(columns, str):
|
|
@@ -356,37 +403,44 @@ class SQL:
|
|
|
356
403
|
if not name:
|
|
357
404
|
m = hashlib.md5()
|
|
358
405
|
m.update(table)
|
|
359
|
-
m.update(
|
|
406
|
+
m.update(" ".join(columns))
|
|
360
407
|
m.update(key_to_table)
|
|
361
|
-
m.update(
|
|
362
|
-
name =
|
|
363
|
-
sql = "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE CASCADE ON UPDATE CASCADE;".format(
|
|
408
|
+
m.update(" ".join(key_to_columns))
|
|
409
|
+
name = "FK_" + m.hexdigest()
|
|
410
|
+
sql = "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE CASCADE ON UPDATE CASCADE;".format(
|
|
411
|
+
table, name, ",".join(columns), key_to_table, ",".join(key_to_columns)
|
|
412
|
+
)
|
|
364
413
|
|
|
365
414
|
return sql, tuple()
|
|
366
415
|
|
|
367
416
|
@classmethod
|
|
368
417
|
def create_table(cls, name, columns={}, drop=False):
|
|
369
|
-
if
|
|
418
|
+
if "." in name:
|
|
370
419
|
fqtn = name
|
|
371
420
|
else:
|
|
372
|
-
fqtn = cls.default_schema +
|
|
373
|
-
schema,table = fqtn.split(
|
|
374
|
-
name = fqtn.replace(
|
|
375
|
-
trigger =
|
|
421
|
+
fqtn = cls.default_schema + "." + name
|
|
422
|
+
schema, table = fqtn.split(".")
|
|
423
|
+
name = fqtn.replace(".", "_")
|
|
424
|
+
trigger = "on_update_row_{0}".format(name)
|
|
376
425
|
sql = []
|
|
377
|
-
sql.append(
|
|
378
|
-
sql.append(
|
|
426
|
+
sql.append("DECLARE @script1 nVarChar(MAX);")
|
|
427
|
+
sql.append("DECLARE @script2 nVarChar(MAX);")
|
|
379
428
|
if drop:
|
|
380
429
|
sql.append(cls.drop_table(fqtn))
|
|
381
|
-
sql.append(
|
|
430
|
+
sql.append(
|
|
431
|
+
"""
|
|
382
432
|
SET @script1 = '
|
|
383
433
|
CREATE TABLE {0} (
|
|
384
434
|
sys_id int identity(1000,1) primary key,
|
|
385
435
|
sys_modified datetime not null default(getdate()),
|
|
386
436
|
sys_created datetime not null default(getdate())
|
|
387
437
|
)'
|
|
388
|
-
""".format(
|
|
389
|
-
|
|
438
|
+
""".format(
|
|
439
|
+
fqtn, table, trigger
|
|
440
|
+
)
|
|
441
|
+
)
|
|
442
|
+
sql.append(
|
|
443
|
+
"""
|
|
390
444
|
SET @script2 = '
|
|
391
445
|
CREATE TRIGGER {2}
|
|
392
446
|
ON {0}
|
|
@@ -399,238 +453,263 @@ class SQL:
|
|
|
399
453
|
FROM {0} AS t
|
|
400
454
|
INNER JOIN deleted AS d on t.sys_id=i.sys_id
|
|
401
455
|
END'
|
|
402
|
-
""".format(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
456
|
+
""".format(
|
|
457
|
+
fqtn, table, trigger
|
|
458
|
+
)
|
|
459
|
+
)
|
|
460
|
+
sql.append("EXEC (@script1);")
|
|
461
|
+
sql.append("EXEC (@script2);")
|
|
462
|
+
for key, val in columns.items():
|
|
463
|
+
sql.append("ALTER TABLE {} ADD {} {};".format(fqtn, key, cls.get_type(val)))
|
|
464
|
+
return "\n\t".join(sql), tuple()
|
|
408
465
|
|
|
409
466
|
@classmethod
|
|
410
467
|
def drop_table(cls, name):
|
|
411
|
-
return
|
|
468
|
+
return (
|
|
469
|
+
"IF OBJECT_ID('%s', 'U') IS NOT NULL DROP TABLE %s;"
|
|
470
|
+
% (
|
|
471
|
+
quote(cls.default_schema + "." + name),
|
|
472
|
+
quote(cls.default_schema + "." + name),
|
|
473
|
+
),
|
|
474
|
+
tuple(),
|
|
475
|
+
)
|
|
412
476
|
|
|
413
477
|
@classmethod
|
|
414
478
|
def columns(cls, name):
|
|
415
|
-
if
|
|
479
|
+
if "." in name:
|
|
416
480
|
return """
|
|
417
481
|
select column_name
|
|
418
482
|
from information_schema.columns
|
|
419
483
|
where table_schema = %s
|
|
420
484
|
and table_name = %s
|
|
421
|
-
""", tuple(
|
|
485
|
+
""", tuple(
|
|
486
|
+
name.split(".")
|
|
487
|
+
)
|
|
422
488
|
else:
|
|
423
489
|
return """
|
|
424
490
|
select column_name
|
|
425
491
|
from information_schema.columns
|
|
426
492
|
where table_name = %s
|
|
427
|
-
""", tuple(
|
|
493
|
+
""", tuple(
|
|
494
|
+
[name]
|
|
495
|
+
)
|
|
428
496
|
|
|
429
497
|
@classmethod
|
|
430
498
|
def column_info(cls, table, name):
|
|
431
|
-
params = table.split(
|
|
499
|
+
params = table.split(".")
|
|
432
500
|
params.append(name)
|
|
433
|
-
if
|
|
501
|
+
if "." in table:
|
|
434
502
|
return """
|
|
435
503
|
select *
|
|
436
504
|
from information_schema.columns
|
|
437
505
|
where table_schema = %s
|
|
438
506
|
and table_name = %s
|
|
439
507
|
and column_name = %s
|
|
440
|
-
""", tuple(
|
|
508
|
+
""", tuple(
|
|
509
|
+
params
|
|
510
|
+
)
|
|
441
511
|
else:
|
|
442
512
|
return """
|
|
443
513
|
select *
|
|
444
514
|
from information_schema.columns
|
|
445
515
|
where table_name = %s
|
|
446
516
|
and column_name = %s
|
|
447
|
-
""", tuple(
|
|
448
|
-
|
|
517
|
+
""", tuple(
|
|
518
|
+
params
|
|
519
|
+
)
|
|
449
520
|
|
|
450
521
|
@classmethod
|
|
451
522
|
def primary_keys(cls, table):
|
|
452
|
-
params = table.split(
|
|
453
|
-
if
|
|
523
|
+
params = table.split(".")
|
|
524
|
+
if "." in table:
|
|
454
525
|
return """
|
|
455
526
|
SELECT COLUMN_NAME
|
|
456
527
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
457
528
|
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
|
|
458
529
|
AND TABLE_SCHEMA = %s AND TABLE_NAME = %s
|
|
459
|
-
""", tuple(
|
|
530
|
+
""", tuple(
|
|
531
|
+
params
|
|
532
|
+
)
|
|
460
533
|
else:
|
|
461
534
|
return """
|
|
462
535
|
SELECT COLUMN_NAME
|
|
463
536
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
464
537
|
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
|
|
465
538
|
AND TABLE_NAME = %s
|
|
466
|
-
""", tuple(
|
|
539
|
+
""", tuple(
|
|
540
|
+
params
|
|
541
|
+
)
|
|
467
542
|
|
|
468
543
|
@classmethod
|
|
469
544
|
def xforeign_keys(cls, table):
|
|
470
|
-
params = table.split(
|
|
471
|
-
if
|
|
545
|
+
params = table.split(".")
|
|
546
|
+
if "." in table:
|
|
472
547
|
return """
|
|
473
548
|
SELECT COLUMN_NAME
|
|
474
549
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
475
550
|
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
|
|
476
551
|
AND TABLE_SCHEMA = %s AND TABLE_NAME = %s
|
|
477
|
-
""", tuple(
|
|
552
|
+
""", tuple(
|
|
553
|
+
params
|
|
554
|
+
)
|
|
478
555
|
else:
|
|
479
556
|
return """
|
|
480
557
|
SELECT COLUMN_NAME
|
|
481
558
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
482
559
|
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
|
|
483
560
|
AND TABLE_NAME = %s
|
|
484
|
-
""", tuple(
|
|
561
|
+
""", tuple(
|
|
562
|
+
params
|
|
563
|
+
)
|
|
485
564
|
|
|
486
565
|
@classmethod
|
|
487
566
|
def insert(cls, table, data):
|
|
488
567
|
import pytds
|
|
568
|
+
|
|
489
569
|
keys = []
|
|
490
570
|
vals = []
|
|
491
571
|
args = []
|
|
492
|
-
for key,val in data.items():
|
|
572
|
+
for key, val in data.items():
|
|
493
573
|
keys.append(quote(key.lower()))
|
|
494
|
-
if isinstance(val,str)
|
|
495
|
-
and len(val) > 2 \
|
|
496
|
-
and val[:2] == '@@':
|
|
574
|
+
if isinstance(val, str) and len(val) > 2 and val[:2] == "@@":
|
|
497
575
|
vals.append(val[2:])
|
|
498
|
-
elif isinstance(val,(bytearray,bytes)):
|
|
499
|
-
vals.append(
|
|
576
|
+
elif isinstance(val, (bytearray, bytes)):
|
|
577
|
+
vals.append("%s")
|
|
500
578
|
args.append(pytds.Binary(str(val)))
|
|
501
579
|
else:
|
|
502
|
-
vals.append(
|
|
580
|
+
vals.append("%s")
|
|
503
581
|
args.append(val)
|
|
504
582
|
|
|
505
583
|
sql = []
|
|
506
584
|
if "sys_id" in data:
|
|
507
|
-
sql.append(
|
|
508
|
-
sql.append(
|
|
585
|
+
sql.append("SET IDENTITY_INSERT {} ON;".format(table))
|
|
586
|
+
sql.append("INSERT INTO")
|
|
509
587
|
sql.append(quote(table))
|
|
510
|
-
sql.append(
|
|
511
|
-
sql.append(
|
|
512
|
-
sql.append(
|
|
513
|
-
sql.append(
|
|
514
|
-
sql.append(
|
|
515
|
-
sql.append(
|
|
516
|
-
sql.append(
|
|
588
|
+
sql.append("(")
|
|
589
|
+
sql.append(",".join(keys))
|
|
590
|
+
sql.append(")")
|
|
591
|
+
sql.append("VALUES")
|
|
592
|
+
sql.append("(")
|
|
593
|
+
sql.append(",".join(vals))
|
|
594
|
+
sql.append(");")
|
|
517
595
|
if "sys_id" in data:
|
|
518
|
-
sql.append(
|
|
519
|
-
sql =
|
|
596
|
+
sql.append("SET IDENTITY_INSERT {} OFF;".format(table))
|
|
597
|
+
sql = " ".join(sql)
|
|
520
598
|
return sql, tuple(args)
|
|
521
599
|
|
|
522
600
|
@classmethod
|
|
523
601
|
def update(cls, table, data, pk):
|
|
524
602
|
import pytds
|
|
525
|
-
|
|
603
|
+
|
|
604
|
+
sql = ["UPDATE"]
|
|
526
605
|
sql.append(quote(table))
|
|
527
|
-
sql.append(
|
|
606
|
+
sql.append("SET")
|
|
528
607
|
vals = []
|
|
529
|
-
join =
|
|
608
|
+
join = ""
|
|
530
609
|
for key in sorted(data.keys()):
|
|
531
610
|
val = data[key]
|
|
532
611
|
if join:
|
|
533
612
|
sql.append(join)
|
|
534
|
-
sql.append(
|
|
535
|
-
if isinstance(val, (bytearray,bytes)):
|
|
613
|
+
sql.append("{} = %s".format(quote(key.lower())))
|
|
614
|
+
if isinstance(val, (bytearray, bytes)):
|
|
536
615
|
vals.append(pytds.Binary(str(val)))
|
|
537
616
|
else:
|
|
538
617
|
vals.append(val)
|
|
539
|
-
join =
|
|
618
|
+
join = ","
|
|
540
619
|
if pk:
|
|
541
|
-
sql.append(
|
|
542
|
-
join =
|
|
620
|
+
sql.append("\nWHERE")
|
|
621
|
+
join = ""
|
|
543
622
|
for key in sorted(pk.keys()):
|
|
544
623
|
val = pk[key]
|
|
545
624
|
if join:
|
|
546
625
|
sql.append(join)
|
|
547
626
|
if val is None:
|
|
548
|
-
sql.append(
|
|
627
|
+
sql.append("{} is null".format(quote(key.lower())))
|
|
549
628
|
else:
|
|
550
|
-
sql.append(
|
|
629
|
+
sql.append("{} = %s".format(quote(key.lower())))
|
|
551
630
|
vals.append(val)
|
|
552
|
-
join =
|
|
553
|
-
sql =
|
|
631
|
+
join = "AND"
|
|
632
|
+
sql = " ".join(sql)
|
|
554
633
|
return sql, tuple(vals)
|
|
555
634
|
|
|
556
635
|
@classmethod
|
|
557
|
-
def create_index(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
636
|
+
def create_index(
|
|
637
|
+
cls,
|
|
638
|
+
table=None,
|
|
639
|
+
columns=None,
|
|
640
|
+
unique=False,
|
|
641
|
+
direction=None,
|
|
642
|
+
where=None,
|
|
643
|
+
name=None,
|
|
644
|
+
schema=None,
|
|
645
|
+
trigram=None,
|
|
646
|
+
tbl=None,
|
|
647
|
+
):
|
|
648
|
+
if "." not in table and schema:
|
|
649
|
+
table = "{}.{}".format(schema, table)
|
|
650
|
+
if isinstance(columns, (list, set)):
|
|
651
|
+
columns = ",".join([quote(c.lower()) for c in sorted(columns)])
|
|
562
652
|
else:
|
|
563
653
|
columns = quote(columns)
|
|
564
|
-
sql = [
|
|
654
|
+
sql = ["CREATE"]
|
|
565
655
|
if unique:
|
|
566
|
-
sql.append(
|
|
567
|
-
sql.append(
|
|
656
|
+
sql.append("UNIQUE")
|
|
657
|
+
sql.append("INDEX")
|
|
568
658
|
tablename = quote(table)
|
|
569
659
|
if not name:
|
|
570
|
-
name = re.sub(r
|
|
571
|
-
sql.append(
|
|
572
|
-
sql.append(
|
|
660
|
+
name = re.sub(r"\([^)]*\)", "", columns.replace(",", "_"))
|
|
661
|
+
sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
|
|
662
|
+
sql.append("ON")
|
|
573
663
|
sql.append(tablename)
|
|
574
|
-
sql.append(
|
|
664
|
+
sql.append("(")
|
|
575
665
|
sql.append(columns)
|
|
576
|
-
sql.append(
|
|
577
|
-
return
|
|
666
|
+
sql.append(")")
|
|
667
|
+
return " ".join(sql), tuple()
|
|
578
668
|
|
|
579
669
|
@classmethod
|
|
580
670
|
def drop_index(cls, table=None, columns=None, name=None, schema=None):
|
|
581
|
-
if
|
|
582
|
-
table = "{}.{}".format(schema,table)
|
|
583
|
-
if isinstance(columns,(list,set)):
|
|
584
|
-
columns =
|
|
671
|
+
if "." not in table and schema:
|
|
672
|
+
table = "{}.{}".format(schema, table)
|
|
673
|
+
if isinstance(columns, (list, set)):
|
|
674
|
+
columns = ",".join([quote(c.lower()) for c in sorted(columns)])
|
|
585
675
|
else:
|
|
586
676
|
columns = quote(columns)
|
|
587
|
-
sql = [
|
|
588
|
-
sql.append(
|
|
677
|
+
sql = ["DROP"]
|
|
678
|
+
sql.append("INDEX IF EXISTS")
|
|
589
679
|
tablename = quote(table)
|
|
590
680
|
if not name:
|
|
591
|
-
name = re.sub(r
|
|
592
|
-
sql.append(
|
|
593
|
-
print(
|
|
594
|
-
return
|
|
681
|
+
name = re.sub(r"\([^)]*\)", "", columns.replace(",", "_"))
|
|
682
|
+
sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
|
|
683
|
+
print(" ".join(sql))
|
|
684
|
+
return " ".join(sql), tuple()
|
|
595
685
|
|
|
596
686
|
@classmethod
|
|
597
687
|
def get_type(cls, v):
|
|
598
688
|
if isinstance(v, str):
|
|
599
|
-
if v[:2] ==
|
|
689
|
+
if v[:2] == "@@":
|
|
600
690
|
return v[2:]
|
|
601
|
-
elif isinstance(v, (str, bytes))
|
|
602
|
-
|
|
603
|
-
or v is
|
|
604
|
-
return cls.TYPES.TEXT
|
|
605
|
-
elif isinstance(v, bool) \
|
|
606
|
-
or v is bool:
|
|
691
|
+
elif isinstance(v, (str, bytes)) or v is str or v is bytes:
|
|
692
|
+
return cls.TYPES.TEXT
|
|
693
|
+
elif isinstance(v, bool) or v is bool:
|
|
607
694
|
return cls.TYPES.BOOLEAN
|
|
608
|
-
elif isinstance(v, int)
|
|
609
|
-
or v is int:
|
|
695
|
+
elif isinstance(v, int) or v is int:
|
|
610
696
|
if v is int:
|
|
611
697
|
return cls.TYPES.INTEGER
|
|
612
698
|
if v > 2147483647 or v < -2147483648:
|
|
613
699
|
return cls.TYPES.BIGINT
|
|
614
700
|
else:
|
|
615
|
-
return
|
|
616
|
-
elif isinstance(v, float)
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
or v is
|
|
621
|
-
return cls.TYPES.NUMERIC + '(19, 6)'
|
|
622
|
-
elif isinstance (v, datetime.datetime) \
|
|
623
|
-
or v is datetime.datetime:
|
|
701
|
+
return cls.TYPES.INTEGER
|
|
702
|
+
elif isinstance(v, float) or v is float:
|
|
703
|
+
return cls.TYPES.NUMERIC + "(19, 6)"
|
|
704
|
+
elif isinstance(v, decimal.Decimal) or v is decimal.Decimal:
|
|
705
|
+
return cls.TYPES.NUMERIC + "(19, 6)"
|
|
706
|
+
elif isinstance(v, datetime.datetime) or v is datetime.datetime:
|
|
624
707
|
return cls.TYPES.DATETIME
|
|
625
|
-
elif isinstance
|
|
626
|
-
or v is datetime.date:
|
|
708
|
+
elif isinstance(v, datetime.date) or v is datetime.date:
|
|
627
709
|
return cls.TYPES.DATE
|
|
628
|
-
elif isinstance(v, datetime.time)
|
|
629
|
-
or v is datetime.time:
|
|
710
|
+
elif isinstance(v, datetime.time) or v is datetime.time:
|
|
630
711
|
return cls.TYPES.TIME
|
|
631
|
-
elif isinstance
|
|
632
|
-
or v is bytearray \
|
|
633
|
-
or v is bytes:
|
|
712
|
+
elif isinstance(v, (bytearray, bytes)) or v is bytearray or v is bytes:
|
|
634
713
|
return cls.TYPES.BINARY
|
|
635
714
|
# Everything else defaults to TEXT, incl. None
|
|
636
715
|
return cls.TYPES.TEXT
|
|
@@ -654,7 +733,7 @@ class SQL:
|
|
|
654
733
|
raise Exception("unmapped type %s" % v)
|
|
655
734
|
|
|
656
735
|
@classmethod
|
|
657
|
-
def massage_data(cls,data):
|
|
736
|
+
def massage_data(cls, data):
|
|
658
737
|
"""
|
|
659
738
|
|
|
660
739
|
:param :
|
|
@@ -662,68 +741,79 @@ class SQL:
|
|
|
662
741
|
:param :
|
|
663
742
|
:returns:
|
|
664
743
|
"""
|
|
665
|
-
data = {key.lower():val for key,val in data.items()}
|
|
744
|
+
data = {key.lower(): val for key, val in data.items()}
|
|
666
745
|
primaryKey = set(cls.GetPrimaryKeyColumnNames())
|
|
667
746
|
if not primaryKey:
|
|
668
747
|
if not cls.Exists():
|
|
669
748
|
raise exceptions.DbTableMissingError
|
|
670
|
-
dataKeys = set(data.keys()).intersection(
|
|
671
|
-
dataColumns = set(data.keys()).difference(
|
|
749
|
+
dataKeys = set(data.keys()).intersection(primaryKey)
|
|
750
|
+
dataColumns = set(data.keys()).difference(primaryKey)
|
|
672
751
|
pk = {}
|
|
673
|
-
pk.update([(k,data[k]) for k in dataKeys])
|
|
752
|
+
pk.update([(k, data[k]) for k in dataKeys])
|
|
674
753
|
d = {}
|
|
675
|
-
d.update([(k,data[k]) for k in dataColumns])
|
|
754
|
+
d.update([(k, data[k]) for k in dataColumns])
|
|
676
755
|
return d, pk
|
|
677
756
|
|
|
678
757
|
@classmethod
|
|
679
758
|
def alter_add(cls, table, columns, null_allowed=True):
|
|
680
759
|
sql = []
|
|
681
|
-
null =
|
|
682
|
-
if isinstance(columns,dict):
|
|
683
|
-
for key,val in columns.items():
|
|
684
|
-
sql.append(
|
|
685
|
-
|
|
760
|
+
null = "NOT NULL" if not null_allowed else ""
|
|
761
|
+
if isinstance(columns, dict):
|
|
762
|
+
for key, val in columns.items():
|
|
763
|
+
sql.append(
|
|
764
|
+
"ALTER TABLE {} ADD {} {} {};".format(
|
|
765
|
+
quote(table), quote(key), cls.get_type(val), null
|
|
766
|
+
)
|
|
767
|
+
)
|
|
768
|
+
return "\n\t".join(sql), tuple()
|
|
686
769
|
|
|
687
770
|
@classmethod
|
|
688
771
|
def alter_drop(cls, table, columns):
|
|
689
772
|
sql = ["ALTER TABLE {} DROP COLUMN".format(quote(table))]
|
|
690
|
-
if isinstance(columns,dict):
|
|
691
|
-
for key,val in columns.items():
|
|
773
|
+
if isinstance(columns, dict):
|
|
774
|
+
for key, val in columns.items():
|
|
692
775
|
sql.append("{},".format(key))
|
|
693
|
-
if sql[-1][-1] ==
|
|
776
|
+
if sql[-1][-1] == ",":
|
|
694
777
|
sql[-1] = sql[-1][:-1]
|
|
695
|
-
return
|
|
778
|
+
return "\n\t".join(sql), tuple()
|
|
696
779
|
|
|
697
780
|
@classmethod
|
|
698
781
|
def alter_column_by_type(cls, table, column, value, null_allowed=True):
|
|
699
782
|
sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
|
|
700
783
|
sql.append("{} {}".format(quote(column), cls.get_type(value)))
|
|
701
784
|
if not null_allowed:
|
|
702
|
-
sql.append(
|
|
703
|
-
return
|
|
785
|
+
sql.append("NOT NULL")
|
|
786
|
+
return "\n\t".join(sql), tuple()
|
|
704
787
|
|
|
705
788
|
@classmethod
|
|
706
789
|
def alter_column_by_sql(cls, table, column, value):
|
|
707
790
|
sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
|
|
708
791
|
sql.append("{} {}".format(quote(column), value))
|
|
709
|
-
return
|
|
710
|
-
|
|
792
|
+
return " ".join(sql), tuple()
|
|
711
793
|
|
|
712
794
|
@classmethod
|
|
713
795
|
def rename_column(cls, table, orig, new):
|
|
714
|
-
if
|
|
715
|
-
schema, table = table.split(
|
|
796
|
+
if "." in table:
|
|
797
|
+
schema, table = table.split(".")
|
|
716
798
|
else:
|
|
717
799
|
schema = cls.default_schema
|
|
718
|
-
return
|
|
800
|
+
return (
|
|
801
|
+
"sp_rename '{}.{}.{}', '{}', 'COLUMN';".format(
|
|
802
|
+
quote(schema), quote(table), quote(orig), new
|
|
803
|
+
),
|
|
804
|
+
tuple(),
|
|
805
|
+
)
|
|
719
806
|
|
|
720
807
|
@classmethod
|
|
721
808
|
def rename_table(cls, table, name, new):
|
|
722
|
-
if
|
|
723
|
-
schema, table = table.split(
|
|
809
|
+
if "." in table:
|
|
810
|
+
schema, table = table.split(".")
|
|
724
811
|
else:
|
|
725
812
|
schema = cls.default_schema
|
|
726
|
-
return
|
|
813
|
+
return (
|
|
814
|
+
"sp_rename '{}.{}', '{}';".format(quote(schema), quote(name), new),
|
|
815
|
+
tuple(),
|
|
816
|
+
)
|
|
727
817
|
|
|
728
818
|
@classmethod
|
|
729
819
|
def create_savepoint(cls, sp):
|
|
@@ -741,45 +831,56 @@ class SQL:
|
|
|
741
831
|
def find_duplicates(cls, table, columns, key):
|
|
742
832
|
if isinstance(columns, str):
|
|
743
833
|
columns = [columns]
|
|
744
|
-
return
|
|
834
|
+
return (
|
|
835
|
+
"""
|
|
745
836
|
SELECT {2}
|
|
746
837
|
FROM (SELECT {2},
|
|
747
838
|
ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
|
|
748
839
|
FROM {0}) t
|
|
749
840
|
WHERE t.rnum > 1;
|
|
750
|
-
""".format(
|
|
841
|
+
""".format(
|
|
842
|
+
table, ",".join(quote(columns)), key
|
|
843
|
+
),
|
|
844
|
+
tuple(),
|
|
845
|
+
)
|
|
751
846
|
|
|
752
847
|
@classmethod
|
|
753
848
|
def delete_duplicates(cls, table, columns, key):
|
|
754
849
|
if isinstance(columns, str):
|
|
755
850
|
columns = [columns]
|
|
756
|
-
return
|
|
851
|
+
return (
|
|
852
|
+
"""
|
|
757
853
|
DELETE FROM {0}
|
|
758
854
|
WHERE {2} IN (SELECT {2}
|
|
759
855
|
FROM (SELECT {2},
|
|
760
856
|
ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
|
|
761
857
|
FROM {0}) t
|
|
762
858
|
WHERE t.rnum > 1);
|
|
763
|
-
""".format(
|
|
859
|
+
""".format(
|
|
860
|
+
table, ",".join(quote(columns)), key
|
|
861
|
+
),
|
|
862
|
+
tuple(),
|
|
863
|
+
)
|
|
764
864
|
|
|
765
865
|
@classmethod
|
|
766
866
|
def delete(cls, table, where):
|
|
767
|
-
sql = [
|
|
768
|
-
sql.append(
|
|
867
|
+
sql = ["DELETE FROM {}".format(table)]
|
|
868
|
+
sql.append("WHERE")
|
|
769
869
|
vals = []
|
|
770
|
-
if isinstance(where,dict):
|
|
771
|
-
join =
|
|
870
|
+
if isinstance(where, dict):
|
|
871
|
+
join = ""
|
|
772
872
|
for key in sorted(where.keys()):
|
|
773
|
-
if join:
|
|
873
|
+
if join:
|
|
874
|
+
sql.append(join)
|
|
774
875
|
if where[key] == None:
|
|
775
|
-
sql.append(
|
|
876
|
+
sql.append("{} is NULL".format(quote(key.lower())))
|
|
776
877
|
else:
|
|
777
|
-
sql.append(
|
|
878
|
+
sql.append("{} = %s".format(quote(key.lower())))
|
|
778
879
|
vals.append(where[key])
|
|
779
|
-
join =
|
|
880
|
+
join = "AND"
|
|
780
881
|
else:
|
|
781
882
|
sql.append(where)
|
|
782
|
-
return
|
|
883
|
+
return " ".join(sql), tuple(vals)
|
|
783
884
|
|
|
784
885
|
@classmethod
|
|
785
886
|
def truncate(cls, table):
|
|
@@ -787,35 +888,349 @@ class SQL:
|
|
|
787
888
|
|
|
788
889
|
@classmethod
|
|
789
890
|
def create_view(cls, name, query, temp=False, silent=True):
|
|
790
|
-
sql = [
|
|
891
|
+
sql = ["CREATE"]
|
|
791
892
|
if silent:
|
|
792
|
-
sql.append(
|
|
893
|
+
sql.append("OR REPLACE")
|
|
793
894
|
if temp:
|
|
794
|
-
sql.append(
|
|
795
|
-
sql.append(
|
|
796
|
-
sql.append(cls.default_schema+
|
|
797
|
-
sql.append(
|
|
895
|
+
sql.append("TEMPORARY")
|
|
896
|
+
sql.append("VIEW")
|
|
897
|
+
sql.append(cls.default_schema + "." + name)
|
|
898
|
+
sql.append("AS")
|
|
798
899
|
sql.append(query)
|
|
799
|
-
return
|
|
900
|
+
return " ".join(sql), tuple()
|
|
800
901
|
|
|
801
902
|
@classmethod
|
|
802
903
|
def drop_view(cls, name, silent=True):
|
|
803
|
-
sql = [
|
|
904
|
+
sql = ["DROP VIEW"]
|
|
804
905
|
if silent:
|
|
805
|
-
sql.append(
|
|
806
|
-
sql.append(cls.default_schema+
|
|
807
|
-
return
|
|
906
|
+
sql.append("IF EXISTS")
|
|
907
|
+
sql.append(cls.default_schema + "." + name)
|
|
908
|
+
return " ".join(sql), tuple()
|
|
808
909
|
|
|
809
910
|
class TYPES(object):
|
|
810
|
-
TEXT =
|
|
811
|
-
INTEGER =
|
|
812
|
-
NUMERIC =
|
|
813
|
-
DATETIME =
|
|
814
|
-
TIMESTAMP =
|
|
815
|
-
DATE =
|
|
816
|
-
TIME =
|
|
817
|
-
BIGINT =
|
|
818
|
-
BOOLEAN =
|
|
819
|
-
BINARY =
|
|
820
|
-
|
|
821
|
-
|
|
911
|
+
TEXT = "VARCHAR(MAX)"
|
|
912
|
+
INTEGER = "INT"
|
|
913
|
+
NUMERIC = "NUMERIC"
|
|
914
|
+
DATETIME = "DATETIME"
|
|
915
|
+
TIMESTAMP = "DATETIME"
|
|
916
|
+
DATE = "DATE"
|
|
917
|
+
TIME = "TIME"
|
|
918
|
+
BIGINT = "BIGINT"
|
|
919
|
+
BOOLEAN = "BIT"
|
|
920
|
+
BINARY = "VARBINARY(MAX)"
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
reserved_words = [
|
|
924
|
+
"ABSOLUTE",
|
|
925
|
+
"ACTION",
|
|
926
|
+
"ADA",
|
|
927
|
+
"ADD",
|
|
928
|
+
"ALL",
|
|
929
|
+
"ALLOCATE",
|
|
930
|
+
"ALTER",
|
|
931
|
+
"AND",
|
|
932
|
+
"ANY",
|
|
933
|
+
"ARE",
|
|
934
|
+
"AS",
|
|
935
|
+
"ASC",
|
|
936
|
+
"ASSERTION",
|
|
937
|
+
"AT",
|
|
938
|
+
"AUTHORIZATION",
|
|
939
|
+
"AVG",
|
|
940
|
+
"BACKUP",
|
|
941
|
+
"BEGIN",
|
|
942
|
+
"BETWEEN",
|
|
943
|
+
"BIT",
|
|
944
|
+
"BIT_LENGTH",
|
|
945
|
+
"BOTH",
|
|
946
|
+
"BREAK",
|
|
947
|
+
"BROWSE",
|
|
948
|
+
"BULK",
|
|
949
|
+
"BY",
|
|
950
|
+
"CASCADE",
|
|
951
|
+
"CASCADED",
|
|
952
|
+
"CASE",
|
|
953
|
+
"CAST",
|
|
954
|
+
"CATALOG",
|
|
955
|
+
"CHAR",
|
|
956
|
+
"CHARACTER",
|
|
957
|
+
"CHARACTER_LENGTH",
|
|
958
|
+
"CHAR_LENGTH",
|
|
959
|
+
"CHECK",
|
|
960
|
+
"CHECKPOINT",
|
|
961
|
+
"CLOSE",
|
|
962
|
+
"CLUSTERED",
|
|
963
|
+
"COALESCE",
|
|
964
|
+
"COLLATE",
|
|
965
|
+
"COLLATION",
|
|
966
|
+
"COLUMN",
|
|
967
|
+
"COMMIT",
|
|
968
|
+
"COMPUTE",
|
|
969
|
+
"CONNECT",
|
|
970
|
+
"CONNECTION",
|
|
971
|
+
"CONSTRAINT",
|
|
972
|
+
"CONSTRAINTS",
|
|
973
|
+
"CONTAINS",
|
|
974
|
+
"CONTAINSTABLE",
|
|
975
|
+
"CONTINUE",
|
|
976
|
+
"CONVERT",
|
|
977
|
+
"CORRESPONDING",
|
|
978
|
+
"COUNT",
|
|
979
|
+
"CREATE",
|
|
980
|
+
"CROSS",
|
|
981
|
+
"CURRENT",
|
|
982
|
+
"CURRENT_DATE",
|
|
983
|
+
"CURRENT_TIME",
|
|
984
|
+
"CURRENT_TIMESTAMP",
|
|
985
|
+
"CURRENT_USER",
|
|
986
|
+
"CURSOR",
|
|
987
|
+
"DATABASE",
|
|
988
|
+
"DATE",
|
|
989
|
+
"DAY",
|
|
990
|
+
"DBCC",
|
|
991
|
+
"DEALLOCATE",
|
|
992
|
+
"DEC",
|
|
993
|
+
"DECIMAL",
|
|
994
|
+
"DECLARE",
|
|
995
|
+
"DEFAULT",
|
|
996
|
+
"DEFERRABLE",
|
|
997
|
+
"DEFERRED",
|
|
998
|
+
"DELETE",
|
|
999
|
+
"DENY",
|
|
1000
|
+
"DESC",
|
|
1001
|
+
"DESCRIBE",
|
|
1002
|
+
"DESCRIPTOR",
|
|
1003
|
+
"DIAGNOSTICS",
|
|
1004
|
+
"DISCONNECT",
|
|
1005
|
+
"DISK",
|
|
1006
|
+
"DISTINCT",
|
|
1007
|
+
"DISTRIBUTED",
|
|
1008
|
+
"DOMAIN",
|
|
1009
|
+
"DOUBLE",
|
|
1010
|
+
"DROP",
|
|
1011
|
+
"DUMP",
|
|
1012
|
+
"ELSE",
|
|
1013
|
+
"END",
|
|
1014
|
+
"END-EXEC",
|
|
1015
|
+
"ERRLVL",
|
|
1016
|
+
"ESCAPE",
|
|
1017
|
+
"EXCEPT",
|
|
1018
|
+
"EXCEPTION",
|
|
1019
|
+
"EXEC",
|
|
1020
|
+
"EXECUTE",
|
|
1021
|
+
"EXISTS",
|
|
1022
|
+
"EXIT",
|
|
1023
|
+
"EXTERNAL",
|
|
1024
|
+
"EXTRACT",
|
|
1025
|
+
"FALSE",
|
|
1026
|
+
"FETCH",
|
|
1027
|
+
"FILE",
|
|
1028
|
+
"FILLFACTOR",
|
|
1029
|
+
"FIRST",
|
|
1030
|
+
"FLOAT",
|
|
1031
|
+
"FOR",
|
|
1032
|
+
"FOREIGN",
|
|
1033
|
+
"FORTRAN",
|
|
1034
|
+
"FOUND",
|
|
1035
|
+
"FREETEXT",
|
|
1036
|
+
"FREETEXTTABLE",
|
|
1037
|
+
"FROM",
|
|
1038
|
+
"FULL",
|
|
1039
|
+
"FUNCTION",
|
|
1040
|
+
"GET",
|
|
1041
|
+
"GLOBAL",
|
|
1042
|
+
"GO",
|
|
1043
|
+
"GOTO",
|
|
1044
|
+
"GRANT",
|
|
1045
|
+
"GROUP",
|
|
1046
|
+
"HAVING",
|
|
1047
|
+
"HOLDLOCK",
|
|
1048
|
+
"HOUR",
|
|
1049
|
+
"IDENTITY",
|
|
1050
|
+
"IDENTITYCOL",
|
|
1051
|
+
"IDENTITY_INSERT",
|
|
1052
|
+
"IF",
|
|
1053
|
+
"IMMEDIATE",
|
|
1054
|
+
"IN",
|
|
1055
|
+
"INCLUDE",
|
|
1056
|
+
"INDEX",
|
|
1057
|
+
"INDICATOR",
|
|
1058
|
+
"INITIALLY",
|
|
1059
|
+
"INNER",
|
|
1060
|
+
"INPUT",
|
|
1061
|
+
"INSENSITIVE",
|
|
1062
|
+
"INSERT",
|
|
1063
|
+
"INT",
|
|
1064
|
+
"INTEGER",
|
|
1065
|
+
"INTERSECT",
|
|
1066
|
+
"INTERVAL",
|
|
1067
|
+
"INTO",
|
|
1068
|
+
"IS",
|
|
1069
|
+
"ISOLATION",
|
|
1070
|
+
"JOIN",
|
|
1071
|
+
"KEY",
|
|
1072
|
+
"KILL",
|
|
1073
|
+
"LANGUAGE",
|
|
1074
|
+
"LAST",
|
|
1075
|
+
"LEADING",
|
|
1076
|
+
"LEFT",
|
|
1077
|
+
"LEVEL",
|
|
1078
|
+
"LIKE",
|
|
1079
|
+
"LINENO",
|
|
1080
|
+
"LOAD",
|
|
1081
|
+
"LOCAL",
|
|
1082
|
+
"LOWER",
|
|
1083
|
+
"MATCH",
|
|
1084
|
+
"MAX",
|
|
1085
|
+
"MERGE",
|
|
1086
|
+
"MIN",
|
|
1087
|
+
"MINUTE",
|
|
1088
|
+
"MODULE",
|
|
1089
|
+
"MONTH",
|
|
1090
|
+
"NAMES",
|
|
1091
|
+
"NATIONAL",
|
|
1092
|
+
"NATURAL",
|
|
1093
|
+
"NCHAR",
|
|
1094
|
+
"NEXT",
|
|
1095
|
+
"NO",
|
|
1096
|
+
"NOCHECK",
|
|
1097
|
+
"NONCLUSTERED",
|
|
1098
|
+
"NONE",
|
|
1099
|
+
"NOT",
|
|
1100
|
+
"NULL",
|
|
1101
|
+
"NULLIF",
|
|
1102
|
+
"NUMERIC",
|
|
1103
|
+
"OCTET_LENGTH",
|
|
1104
|
+
"OF",
|
|
1105
|
+
"OFF",
|
|
1106
|
+
"OFFSETS",
|
|
1107
|
+
"ON",
|
|
1108
|
+
"ONLY",
|
|
1109
|
+
"OPEN",
|
|
1110
|
+
"OPENDATASOURCE",
|
|
1111
|
+
"OPENQUERY",
|
|
1112
|
+
"OPENROWSET",
|
|
1113
|
+
"OPENXML",
|
|
1114
|
+
"OPTION",
|
|
1115
|
+
"OR",
|
|
1116
|
+
"ORDER",
|
|
1117
|
+
"OUTER",
|
|
1118
|
+
"OUTPUT",
|
|
1119
|
+
"OVER",
|
|
1120
|
+
"OVERLAPS",
|
|
1121
|
+
"PAD",
|
|
1122
|
+
"PARTIAL",
|
|
1123
|
+
"PASCAL",
|
|
1124
|
+
"PERCENT",
|
|
1125
|
+
"PIVOT",
|
|
1126
|
+
"PLAN",
|
|
1127
|
+
"POSITION",
|
|
1128
|
+
"PRECISION",
|
|
1129
|
+
"PREPARE",
|
|
1130
|
+
"PRESERVE",
|
|
1131
|
+
"PRIMARY",
|
|
1132
|
+
"PRINT",
|
|
1133
|
+
"PRIOR",
|
|
1134
|
+
"PRIVILEGES",
|
|
1135
|
+
"PROC",
|
|
1136
|
+
"PROCEDURE",
|
|
1137
|
+
"PUBLIC",
|
|
1138
|
+
"RAISERROR",
|
|
1139
|
+
"READ",
|
|
1140
|
+
"READTEXT",
|
|
1141
|
+
"REAL",
|
|
1142
|
+
"RECONFIGURE",
|
|
1143
|
+
"REFERENCES",
|
|
1144
|
+
"RELATIVE",
|
|
1145
|
+
"REPLICATION",
|
|
1146
|
+
"RESTORE",
|
|
1147
|
+
"RESTRICT",
|
|
1148
|
+
"RETURN",
|
|
1149
|
+
"REVERT",
|
|
1150
|
+
"REVOKE",
|
|
1151
|
+
"RIGHT",
|
|
1152
|
+
"ROLLBACK",
|
|
1153
|
+
"ROWCOUNT",
|
|
1154
|
+
"ROWGUIDCOL",
|
|
1155
|
+
"ROWS",
|
|
1156
|
+
"RULE",
|
|
1157
|
+
"SAVE",
|
|
1158
|
+
"SCHEMA",
|
|
1159
|
+
"SCROLL",
|
|
1160
|
+
"SECOND",
|
|
1161
|
+
"SECTION",
|
|
1162
|
+
"SECURITYAUDIT",
|
|
1163
|
+
"SELECT",
|
|
1164
|
+
"SEMANTICKEYPHRASETABLE",
|
|
1165
|
+
"SEMANTICSIMILARITYDETAILSTABLE",
|
|
1166
|
+
"SEMANTICSIMILARITYTABLE",
|
|
1167
|
+
"SESSION",
|
|
1168
|
+
"SESSION_USER",
|
|
1169
|
+
"SET",
|
|
1170
|
+
"SETUSER",
|
|
1171
|
+
"SHUTDOWN",
|
|
1172
|
+
"SIZE",
|
|
1173
|
+
"SMALLINT",
|
|
1174
|
+
"SOME",
|
|
1175
|
+
"SPACE",
|
|
1176
|
+
"SQL",
|
|
1177
|
+
"SQLCA",
|
|
1178
|
+
"SQLCODE",
|
|
1179
|
+
"SQLERROR",
|
|
1180
|
+
"SQLSTATE",
|
|
1181
|
+
"SQLWARNING",
|
|
1182
|
+
"STATISTICS",
|
|
1183
|
+
"SUBSTRING",
|
|
1184
|
+
"SUM",
|
|
1185
|
+
"SYSTEM_USER",
|
|
1186
|
+
"TABLE",
|
|
1187
|
+
"TABLESAMPLE",
|
|
1188
|
+
"TEMPORARY",
|
|
1189
|
+
"TEXTSIZE",
|
|
1190
|
+
"THEN",
|
|
1191
|
+
"TIME",
|
|
1192
|
+
"TIMESTAMP",
|
|
1193
|
+
"TIMEZONE_HOUR",
|
|
1194
|
+
"TIMEZONE_MINUTE",
|
|
1195
|
+
"TO",
|
|
1196
|
+
"TOP",
|
|
1197
|
+
"TRAILING",
|
|
1198
|
+
"TRAN",
|
|
1199
|
+
"TRANSACTION",
|
|
1200
|
+
"TRANSLATE",
|
|
1201
|
+
"TRANSLATION",
|
|
1202
|
+
"TRIGGER",
|
|
1203
|
+
"TRIM",
|
|
1204
|
+
"TRUE",
|
|
1205
|
+
"TRUNCATE",
|
|
1206
|
+
"TRY_CONVERT",
|
|
1207
|
+
"TSEQUAL",
|
|
1208
|
+
"UNION",
|
|
1209
|
+
"UNIQUE",
|
|
1210
|
+
"UNKNOWN",
|
|
1211
|
+
"UNPIVOT",
|
|
1212
|
+
"UPDATE",
|
|
1213
|
+
"UPDATETEXT",
|
|
1214
|
+
"UPPER",
|
|
1215
|
+
"USAGE",
|
|
1216
|
+
"USE",
|
|
1217
|
+
"USER",
|
|
1218
|
+
"USING",
|
|
1219
|
+
"VALUE",
|
|
1220
|
+
"VALUES",
|
|
1221
|
+
"VARCHAR",
|
|
1222
|
+
"VARYING",
|
|
1223
|
+
"VIEW",
|
|
1224
|
+
"WAITFOR",
|
|
1225
|
+
"WHEN",
|
|
1226
|
+
"WHENEVER",
|
|
1227
|
+
"WHERE",
|
|
1228
|
+
"WHILE",
|
|
1229
|
+
"WITH",
|
|
1230
|
+
"WITHIN GROUP",
|
|
1231
|
+
"WORK",
|
|
1232
|
+
"WRITE",
|
|
1233
|
+
"WRITETEXT",
|
|
1234
|
+
"YEAR",
|
|
1235
|
+
"ZONE",
|
|
1236
|
+
]
|