velocity-python 0.0.131__py3-none-any.whl → 0.0.132__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.

Potentially problematic release.


This version of velocity-python might be problematic. Click here for more details.

Files changed (35) hide show
  1. velocity/__init__.py +1 -1
  2. velocity/db/servers/base/__init__.py +9 -0
  3. velocity/db/servers/base/initializer.py +69 -0
  4. velocity/db/servers/base/operators.py +98 -0
  5. velocity/db/servers/base/sql.py +503 -0
  6. velocity/db/servers/base/types.py +135 -0
  7. velocity/db/servers/mysql/__init__.py +64 -0
  8. velocity/db/servers/mysql/operators.py +54 -0
  9. velocity/db/servers/{mysql_reserved.py → mysql/reserved.py} +2 -14
  10. velocity/db/servers/mysql/sql.py +569 -0
  11. velocity/db/servers/mysql/types.py +107 -0
  12. velocity/db/servers/postgres/__init__.py +40 -0
  13. velocity/db/servers/postgres/operators.py +34 -0
  14. velocity/db/servers/postgres/sql.py +4 -3
  15. velocity/db/servers/postgres/types.py +88 -2
  16. velocity/db/servers/sqlite/__init__.py +52 -0
  17. velocity/db/servers/sqlite/operators.py +52 -0
  18. velocity/db/servers/sqlite/reserved.py +20 -0
  19. velocity/db/servers/sqlite/sql.py +530 -0
  20. velocity/db/servers/sqlite/types.py +92 -0
  21. velocity/db/servers/sqlserver/__init__.py +64 -0
  22. velocity/db/servers/sqlserver/operators.py +47 -0
  23. velocity/db/servers/sqlserver/reserved.py +32 -0
  24. velocity/db/servers/sqlserver/sql.py +625 -0
  25. velocity/db/servers/sqlserver/types.py +114 -0
  26. {velocity_python-0.0.131.dist-info → velocity_python-0.0.132.dist-info}/METADATA +1 -1
  27. {velocity_python-0.0.131.dist-info → velocity_python-0.0.132.dist-info}/RECORD +30 -16
  28. velocity/db/servers/mysql.py +0 -640
  29. velocity/db/servers/sqlite.py +0 -968
  30. velocity/db/servers/sqlite_reserved.py +0 -208
  31. velocity/db/servers/sqlserver.py +0 -921
  32. velocity/db/servers/sqlserver_reserved.py +0 -314
  33. {velocity_python-0.0.131.dist-info → velocity_python-0.0.132.dist-info}/WHEEL +0 -0
  34. {velocity_python-0.0.131.dist-info → velocity_python-0.0.132.dist-info}/licenses/LICENSE +0 -0
  35. {velocity_python-0.0.131.dist-info → velocity_python-0.0.132.dist-info}/top_level.txt +0 -0
@@ -1,968 +0,0 @@
1
- import re
2
- import hashlib
3
- import decimal
4
- import datetime
5
-
6
- from velocity.db import exceptions
7
- from .sqlite_reserved import reserved_words
8
-
9
-
10
- def initialize(config):
11
- import sqlite3
12
- from velocity.db.core.engine import Engine
13
-
14
- return Engine(sqlite3, config, SQL)
15
-
16
-
17
- def quote(data):
18
- if isinstance(data, list):
19
- new = []
20
- for item in data:
21
- new.append(quote(item))
22
- return new
23
- else:
24
- parts = data.split(".")
25
- new = []
26
- for part in parts:
27
- if '"' in part:
28
- new.append(part)
29
- elif part.upper() in reserved_words:
30
- new.append('"' + part + '"')
31
- elif re.findall("[/]", part):
32
- new.append('"' + part + '"')
33
- else:
34
- new.append(part)
35
- return ".".join(new)
36
-
37
-
38
- class SQL(object):
39
- server = "SQLite3"
40
- type_column_identifier = "data_type"
41
- is_nullable = "is_nullable"
42
-
43
- default_schema = ""
44
-
45
- ApplicationErrorCodes = []
46
-
47
- DatabaseMissingErrorCodes = []
48
- TableMissingErrorCodes = []
49
- ColumnMissingErrorCodes = []
50
- ForeignKeyMissingErrorCodes = []
51
-
52
- ConnectionErrorCodes = []
53
- DuplicateKeyErrorCodes = []
54
- RetryTransactionCodes = []
55
- TruncationErrorCodes = []
56
- LockTimeoutErrorCodes = []
57
- DatabaseObjectExistsErrorCodes = []
58
-
59
- @classmethod
60
- def version(cls):
61
- return "select version()", tuple()
62
-
63
- @classmethod
64
- def timestamp(cls):
65
- return "select current_timestamp", tuple()
66
-
67
- @classmethod
68
- def user(cls):
69
- return "select current_user", tuple()
70
-
71
- @classmethod
72
- def databases(cls):
73
- return "select datname from pg_database where datistemplate = false", tuple()
74
-
75
- @classmethod
76
- def schemas(cls):
77
- return "select schema_name from information_schema.schemata", tuple()
78
-
79
- @classmethod
80
- def current_schema(cls):
81
- return "select current_schema", tuple()
82
-
83
- @classmethod
84
- def current_database(cls):
85
- return "select current_database()", tuple()
86
-
87
- @classmethod
88
- def tables(cls, system=False):
89
- return "SELECT name FROM sqlite_master WHERE type='table';", tuple()
90
-
91
- @classmethod
92
- def views(cls, system=False):
93
- if system:
94
- return 'SELECT name FROM sqlite_master WHERE type="view";', tuple()
95
- else:
96
- return 'SELECT name FROM sqlite_master WHERE type="view";', tuple()
97
-
98
- @classmethod
99
- def __has_pointer(cls, columns):
100
- if columns:
101
- if isinstance(columns, list):
102
- columns = ",".join(columns)
103
- if ">" in columns:
104
- return True
105
- return False
106
-
107
- @classmethod
108
- def select(
109
- cls,
110
- columns=None,
111
- table=None,
112
- where=None,
113
- orderby=None,
114
- groupby=None,
115
- having=None,
116
- start=None,
117
- qty=None,
118
- tbl=None,
119
- ):
120
- is_join = False
121
-
122
- if isinstance(columns, str) and "distinct" in columns.lower():
123
- sql = [
124
- "SELECT",
125
- columns,
126
- "FROM",
127
- quote(table),
128
- ]
129
- elif cls.__has_pointer(columns):
130
- if isinstance(columns, str):
131
- columns = columns.split(",")
132
- letter = 65
133
- tables = {table: chr(letter)}
134
- letter += 1
135
- __select = []
136
- __from = ["{} AS {}".format(quote(table), tables.get(table))]
137
- __left_join = []
138
-
139
- for column in columns:
140
- if ">" in column:
141
- is_join = True
142
- parts = column.split(">")
143
- foreign = tbl.foreign_key_info(parts[0])
144
- if not foreign:
145
- raise exceptions.DbApplicationError("Foreign key not defined")
146
- ref_table = foreign["referenced_table_name"]
147
- ref_schema = foreign["referenced_table_schema"]
148
- ref_column = foreign["referenced_column_name"]
149
- lookup = "{}:{}".format(ref_table, parts[0])
150
- if lookup in tables:
151
- __select.append(
152
- '{}."{}" as "{}"'.format(
153
- tables.get(lookup), parts[1], "_".join(parts)
154
- )
155
- )
156
- else:
157
- tables[lookup] = chr(letter)
158
- letter += 1
159
- __select.append(
160
- '{}."{}" as "{}"'.format(
161
- tables.get(lookup), parts[1], "_".join(parts)
162
- )
163
- )
164
- __left_join.append(
165
- 'LEFT OUTER JOIN "{}"."{}" AS {}'.format(
166
- ref_schema, ref_table, tables.get(lookup)
167
- )
168
- )
169
- __left_join.append(
170
- 'ON {}."{}" = {}."{}"'.format(
171
- tables.get(table),
172
- parts[0],
173
- tables.get(lookup),
174
- ref_column,
175
- )
176
- )
177
- if orderby and column in orderby:
178
- orderby = orderby.replace(
179
- column, "{}.{}".format(tables.get(lookup), parts[1])
180
- )
181
- else:
182
- if "(" in column:
183
- __select.append(column)
184
- else:
185
- __select.append("{}.{}".format(tables.get(table), column))
186
- sql = ["SELECT"]
187
- sql.append(",".join(__select))
188
- sql.append("FROM")
189
- sql.extend(__from)
190
- sql.extend(__left_join)
191
- else:
192
- if columns:
193
- if isinstance(columns, str):
194
- columns = columns.split(",")
195
- if isinstance(columns, list):
196
- columns = quote(columns)
197
- columns = ",".join(columns)
198
- else:
199
- columns = "*"
200
- sql = [
201
- "SELECT",
202
- columns,
203
- "FROM",
204
- quote(table),
205
- ]
206
- vals = []
207
- if where:
208
- sql.append("WHERE")
209
- if isinstance(where, dict):
210
- where = [x for x in where.items()]
211
- if isinstance(where, list):
212
- join = ""
213
- for key, val in where:
214
- if join:
215
- sql.append(join)
216
- if is_join:
217
- if "." not in key:
218
- key = "A." + key
219
- if val is None:
220
- if "!" in key:
221
- key = key.replace("!", "")
222
- sql.append("{} is not NULL".format(quote(key.lower())))
223
- else:
224
- sql.append("{} is NULL".format(quote(key.lower())))
225
- elif isinstance(val, (list, tuple)):
226
- if "!" in key:
227
- key = key.replace("!", "")
228
- sql.append("{} not in ?".format(quote(key.lower())))
229
- vals.append(tuple(val))
230
- else:
231
- sql.append("{} in ?".format(quote(key.lower())))
232
- vals.append(tuple(val))
233
- else:
234
- if "<>" in key:
235
- key = key.replace("<>", "")
236
- op = "<>"
237
- elif "!=" in key:
238
- key = key.replace("!=", "")
239
- op = "<>"
240
- elif "!%" in key:
241
- key = key.replace("!%", "")
242
- op = "not ilike"
243
- elif "%%" in key:
244
- key = key.replace("%%", "")
245
- op = "%"
246
- elif "%>" in key:
247
- key = key.replace("%>", "")
248
- op = "%>"
249
- elif "<%" in key:
250
- key = key.replace("<%", "")
251
- op = "<%"
252
- elif "==" in key:
253
- key = key.replace("==", "")
254
- op = "="
255
- elif "<=" in key:
256
- key = key.replace("<=", "")
257
- op = "<="
258
- elif ">=" in key:
259
- key = key.replace(">=", "")
260
- op = ">="
261
- elif "<" in key:
262
- key = key.replace("<", "")
263
- op = "<"
264
- elif ">" in key:
265
- key = key.replace(">", "")
266
- op = ">"
267
- elif "%" in key:
268
- key = key.replace("%", "")
269
- op = "ilike"
270
- elif "!" in key:
271
- key = key.replace("!", "")
272
- op = "<>"
273
- elif "=" in key:
274
- key = key.replace("=", "")
275
- op = "="
276
- else:
277
- op = "="
278
- if isinstance(val, str) and val[:2] == "@@":
279
- sql.append(
280
- "{} {} {}".format(quote(key.lower()), op, val[2:])
281
- )
282
- else:
283
- sql.append("{} {} ?".format(quote(key.lower()), op))
284
- vals.append(val)
285
- join = "AND"
286
- else:
287
- sql.append(where)
288
- if groupby:
289
- sql.append("GROUP BY")
290
- if isinstance(groupby, (list, tuple)):
291
- groupby = ",".join(groupby)
292
- sql.append(groupby)
293
- if having:
294
- sql.append("HAVING")
295
- if isinstance(having, (list, tuple)):
296
- having = ",".join(having)
297
- sql.append(having)
298
- if orderby:
299
- sql.append("ORDER BY")
300
- if isinstance(orderby, (list, tuple)):
301
- orderby = ",".join(orderby)
302
- sql.append(orderby)
303
- if start and qty:
304
- sql.append("OFFSET {} ROWS FETCH NEXT {} ROWS ONLY".format(start, qty))
305
- elif start:
306
- sql.append("OFFSET {} ROWS".format(start))
307
- elif qty:
308
- sql.append("FETCH NEXT {} ROWS ONLY".format(qty))
309
- sql = " ".join(sql)
310
- return sql, tuple(vals)
311
-
312
- @classmethod
313
- def create_database(cls, name):
314
- return "create database " + name, tuple()
315
-
316
- @classmethod
317
- def last_id(cls, table):
318
- return "SELECT last_insert_rowid()", tuple()
319
-
320
- @classmethod
321
- def drop_database(cls, name):
322
- return "drop database if exists " + name, tuple()
323
-
324
- @classmethod
325
- def create_table(cls, name, columns={}, drop=False):
326
- sql = []
327
- if drop:
328
- sql.append(cls.drop_table(name))
329
- sql.append(
330
- """
331
- CREATE TABLE {0} (
332
- sys_id INTEGER PRIMARY KEY,
333
- sys_modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
334
- sys_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
335
- """.format(
336
- name
337
- )
338
- )
339
-
340
- for key, val in columns.items():
341
- sql.append(",\n{} {}".format(quote(key), cls.get_type(val)))
342
-
343
- sql.append("\n);")
344
- return "\n\t".join(sql), tuple()
345
-
346
- @classmethod
347
- def drop_table(cls, name):
348
- return "drop table if exists {}".format(quote(name)), tuple()
349
-
350
- @classmethod
351
- def columns(cls, name):
352
- return "PRAGMA table_info({})".format(name), tuple()
353
-
354
- @classmethod
355
- def column_info(cls, table, name):
356
- params = table.split(".")
357
- params.append(name)
358
- if "." in table:
359
- return """
360
- select *
361
- from information_schema.columns
362
- where table_schema = ?
363
- and table_name = ?
364
- and column_name = ?
365
- """, tuple(
366
- params
367
- )
368
- else:
369
- return """
370
- select *
371
- from information_schema.columns
372
- where table_name = ?
373
- and column_name = ?
374
- """, tuple(
375
- params
376
- )
377
-
378
- @classmethod
379
- def primary_keys(cls, table):
380
- params = table.split(".")
381
- params.reverse()
382
- if "." in table:
383
- return """
384
- SELECT
385
- pg_attribute.attname
386
- FROM pg_index, pg_class, pg_attribute, pg_namespace
387
- WHERE
388
- pg_class.oid = %s::regclass AND
389
- indrelid = pg_class.oid AND
390
- nspname = %s AND
391
- pg_class.relnamespace = pg_namespace.oid AND
392
- pg_attribute.attrelid = pg_class.oid AND
393
- pg_attribute.attnum = any(pg_index.indkey)
394
- AND indisprimary
395
- """, tuple(
396
- params
397
- )
398
- else:
399
- return """
400
- SELECT
401
- pg_attribute.attname
402
- FROM pg_index, pg_class, pg_attribute, pg_namespace
403
- WHERE
404
- pg_class.oid = %s::regclass AND
405
- indrelid = pg_class.oid AND
406
- pg_class.relnamespace = pg_namespace.oid AND
407
- pg_attribute.attrelid = pg_class.oid AND
408
- pg_attribute.attnum = any(pg_index.indkey)
409
- AND indisprimary
410
- """, tuple(
411
- params
412
- )
413
-
414
- @classmethod
415
- def foreign_key_info(cls, table=None, column=None, schema=None):
416
- if "." in table:
417
- schema, table = table.split(".")
418
-
419
- sql = """
420
- SELECT sql
421
- FROM (
422
- SELECT
423
- sql sql,
424
- type type,
425
- tbl_name AS referenced_table_name,
426
- name AS referenced_column_name,
427
- NULL AS referenced_table_schema
428
- FROM sqlite_master
429
- UNION ALL
430
- SELECT
431
- sql,
432
- type,
433
- referenced_table_name,
434
- referenced_column_name,
435
- referenced_table_schema
436
- FROM sqlite_temp_master
437
- )
438
- WHERE type != 'meta'
439
- AND sql NOTNULL
440
- AND name NOT LIKE 'sqlite_%'
441
- ORDER BY substr(type, 2, 1), name
442
- """
443
-
444
- return sql, tuple()
445
-
446
- @classmethod
447
- def create_index(
448
- cls,
449
- table=None,
450
- columns=None,
451
- unique=False,
452
- direction=None,
453
- name=None,
454
- schema=None,
455
- tbl=None,
456
- ):
457
- if "." not in table and schema:
458
- table = "{}.{}".format(schema, table)
459
- if isinstance(columns, (list, set)):
460
- columns = ",".join([quote(c.lower()) for c in sorted(columns)])
461
- else:
462
- columns = quote(columns)
463
- sql = ["CREATE"]
464
- if unique:
465
- sql.append("UNIQUE")
466
- sql.append("INDEX")
467
- tablename = quote(table)
468
- if not name:
469
- name = re.sub(r"\([^)]*\)", "", columns.replace(",", "_"))
470
- sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
471
- sql.append("ON")
472
- sql.append(tablename)
473
- sql.append("(")
474
- sql.append(columns)
475
- sql.append(")")
476
- return " ".join(sql), tuple()
477
-
478
- # Copied from PostGreSQL
479
- @classmethod
480
- def create_foreign_key(
481
- cls, table, columns, key_to_table, key_to_columns, name=None, schema=None
482
- ):
483
- if not name:
484
- m = hashlib.md5()
485
- m.update(table.name)
486
- m.update(" ".join(columns))
487
- m.update(key_to_table)
488
- m.update(" ".join(key_to_columns))
489
- name = "FK_" + m.hexdigest()
490
-
491
- original_name = table.name
492
-
493
- # Get SQL query to generate table
494
- sql = (
495
- table.tx.table("sqlite_master")
496
- .select(columns="sql", where={"name": table.name, "type": "table"})
497
- .scalar()
498
- )
499
-
500
- # Rename original table
501
- table.rename(table.name + "_original_data")
502
-
503
- key_info = ""
504
- if isinstance(columns, list) and isinstance(key_to_columns, list):
505
- for c in columns:
506
- key_info += """,
507
- CONSTRAINT {}
508
- FOREIGN KEY ({})
509
- REFERENCES {}({})
510
- """.format(
511
- name, c, key_to_table, key_to_columns[columns.index(c)]
512
- )
513
- elif isinstance(columns, str) and isinstance(key_to_columns, str):
514
- key_info += """,
515
- CONSTRAINT {}
516
- FOREIGN KEY ({})
517
- REFERENCES {}({})
518
- """.format(
519
- name, columns, key_to_table, key_to_columns
520
- )
521
- else:
522
- print('Error parsing argument "columns" or "key_to_columns"')
523
-
524
- # Splits "CREATE TABLE" portion out to be readded to lines at the end
525
- sql_data = sql.split("(", 1)
526
-
527
- # Goes through the SQL code to generate table and adds foreign key info
528
- sql_list = sql_data[1].replace("\n", " ").split(",")
529
- new_sql_list = []
530
- for line in sql_list:
531
- line = line.strip().lower()
532
- for line in sql_list:
533
- if sql_list.index(line) == len(sql_list) - 1:
534
- if ")" in line:
535
- if line.index(")") == len(line) - 1:
536
- new_sql_list.append(line.replace(")", (key_info + ")")))
537
- else:
538
- new_sql_list.append(line)
539
-
540
- # Enable changes to be made to foreign keys
541
- table.tx.execute("PRAGMA foreign_keys=off;")
542
-
543
- # Add sql code to recreate original table with foreign keys
544
- create_db = ",".join(new_sql_list)
545
- # Adds "CREATE TABLE" portion back into sql code for execution
546
- create_db = "(".join([sql_data[0], create_db])
547
- table.tx.execute(create_db)
548
-
549
- # Create new table with original table name and copy all data from original table
550
- table.tx.execute(
551
- "INSERT INTO {} SELECT * FROM {};".format(original_name, table.name)
552
- )
553
- # Enable foreign keys
554
- create_db = "PRAGMA foreign_keys=on;"
555
-
556
- return create_db, tuple()
557
-
558
- @classmethod
559
- def drop_index(cls, table=None, columns=None, name=None, schema=None):
560
- if "." not in table and schema:
561
- table = "{}.{}".format(schema, table)
562
- if isinstance(columns, (list, set)):
563
- columns = ",".join([quote(c.lower()) for c in sorted(columns)])
564
- else:
565
- columns = quote(columns)
566
- sql = ["DROP"]
567
- sql.append("INDEX IF EXISTS")
568
- tablename = quote(table)
569
- if not name:
570
- name = re.sub(r"\([^)]*\)", "", columns.replace(",", "_"))
571
- sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
572
- return " ".join(sql), tuple()
573
-
574
- @classmethod
575
- def insert(cls, table, data):
576
- keys = []
577
- vals = []
578
- args = []
579
- for key, val in data.items():
580
- keys.append(quote(key.lower()))
581
- if isinstance(val, str) and len(val) > 2 and val[:2] == "@@":
582
- vals.append(val[2:])
583
- elif isinstance(val, bytearray):
584
- vals.append("?")
585
- args.append(bytes(val))
586
- else:
587
- vals.append("?")
588
- args.append(val)
589
-
590
- sql = ["INSERT INTO"]
591
- sql.append(quote(table))
592
- sql.append("(")
593
- sql.append(",".join(keys))
594
- sql.append(")")
595
- sql.append("VALUES")
596
- sql.append("(")
597
- sql.append(",".join(vals))
598
- sql.append(")")
599
- sql = " ".join(sql)
600
- return sql, tuple(args)
601
-
602
- @classmethod
603
- def update(cls, table, data, pk):
604
- sql = ["UPDATE"]
605
- sql.append(quote(table))
606
- sql.append("SET")
607
- vals = []
608
- join = ""
609
- for key in data.keys():
610
- val = data[key]
611
- if join:
612
- sql.append(join)
613
- if isinstance(val, str) and val[:2] == "@@":
614
- sql.append("{} = {}".format(quote(key.lower()), val[2:]))
615
- elif isinstance(val, bytearray):
616
- sql.append("{} = ?".format(quote(key.lower())))
617
- vals.append(bytes(val))
618
- else:
619
- sql.append("{} = ?".format(quote(key.lower())))
620
- vals.append(val)
621
- join = ","
622
- if pk:
623
- if isinstance(pk, list):
624
- items = pk
625
- elif isinstance(pk, dict):
626
- items = pk.items()
627
- sql.append("\nWHERE")
628
- join = ""
629
- for key, val in items:
630
- if join:
631
- sql.append(join)
632
- if val is None:
633
- if "!" in key:
634
- key = key.replace("!", "")
635
- sql.append("{} is not NULL".format(quote(key.lower())))
636
- else:
637
- sql.append("{} is NULL".format(quote(key.lower())))
638
- elif isinstance(val, (tuple, list)):
639
- if "!" in key:
640
- key = key.replace("!", "")
641
- sql.append("{} not in ?".format(quote(key.lower())))
642
- vals.append(tuple(val))
643
- else:
644
- sql.append("{} in ?".format(quote(key.lower())))
645
- vals.append(tuple(val))
646
- else:
647
- if "<>" in key:
648
- key = key.replace("<>", "")
649
- op = "<>"
650
- elif "!=" in key:
651
- key = key.replace("!=", "")
652
- op = "<>"
653
- elif "!%" in key:
654
- key = key.replace("!%", "")
655
- op = "not ilike"
656
- elif "%%" in key:
657
- key = key.replace("%%", "")
658
- op = "%"
659
- elif "%>" in key:
660
- key = key.replace("%>", "")
661
- op = "%>"
662
- elif "<%" in key:
663
- key = key.replace("<%", "")
664
- op = "<%"
665
- elif "==" in key:
666
- key = key.replace("==", "")
667
- op = "="
668
- elif "<=" in key:
669
- key = key.replace("<=", "")
670
- op = "<="
671
- elif ">=" in key:
672
- key = key.replace(">=", "")
673
- op = ">="
674
- elif "<" in key:
675
- key = key.replace("<", "")
676
- op = "<"
677
- elif ">" in key:
678
- key = key.replace(">", "")
679
- op = ">"
680
- elif "%" in key:
681
- key = key.replace("%", "")
682
- op = "ilike"
683
- elif "!" in key:
684
- key = key.replace("!", "")
685
- op = "<>"
686
- elif "=" in key:
687
- key = key.replace("=", "")
688
- op = "="
689
- else:
690
- op = "="
691
- if isinstance(val, str) and val[:2] == "@@":
692
- sql.append("{} {} {}".format(quote(key.lower()), op, val[2:]))
693
- else:
694
- sql.append("{} {} ?".format(quote(key.lower()), op))
695
- vals.append(val)
696
- join = "AND"
697
- sql = " ".join(sql)
698
- return sql, tuple(vals)
699
-
700
- @classmethod
701
- def get_type(cls, v):
702
- if isinstance(v, str):
703
- if v[:2] == "@@":
704
- return v[2:] or cls.TYPES.TEXT
705
- elif isinstance(v, (str, bytes)) or v is str or v is bytes:
706
- return cls.TYPES.TEXT
707
- elif isinstance(v, bool) or v is bool:
708
- return cls.TYPES.BOOLEAN
709
- elif isinstance(v, int) or v is int:
710
- if v is int:
711
- return cls.TYPES.INTEGER
712
- if v > 2147483647 or v < -2147483648:
713
- return cls.TYPES.BIGINT
714
- else:
715
- return cls.TYPES.INTEGER
716
- elif isinstance(v, float) or v is float:
717
- return cls.TYPES.NUMERIC + "(19, 6)"
718
- elif isinstance(v, decimal.Decimal) or v is decimal.Decimal:
719
- return cls.TYPES.NUMERIC + "(19, 6)"
720
- elif isinstance(v, datetime.datetime) or v is datetime.datetime:
721
- return cls.TYPES.DATETIME
722
- elif isinstance(v, datetime.date) or v is datetime.date:
723
- return cls.TYPES.DATE
724
- elif isinstance(v, datetime.time) or v is datetime.time:
725
- return cls.TYPES.TIME
726
- elif isinstance(v, datetime.timedelta) or v is datetime.timedelta:
727
- return cls.TYPES.INTERVAL
728
- elif isinstance(v, bytearray) or v is bytearray:
729
- return cls.TYPES.BINARY
730
- # Everything else defaults to TEXT, incl. None
731
- return cls.TYPES.TEXT
732
-
733
- @classmethod
734
- def py_type(cls, v):
735
- v = str(v).upper()
736
- if v == cls.TYPES.INTEGER:
737
- return int
738
- elif v == cls.TYPES.BIGINT:
739
- return int
740
- elif v == cls.TYPES.NUMERIC:
741
- return decimal.Decimal
742
- elif v == cls.TYPES.TEXT:
743
- return str
744
- elif v == cls.TYPES.BOOLEAN:
745
- return bool
746
- elif v == cls.TYPES.DATE:
747
- return datetime.date
748
- elif v == cls.TYPES.TIME:
749
- return datetime.time
750
- elif v == cls.TYPES.DATETIME:
751
- return datetime.datetime
752
- elif v == cls.TYPES.INTERVAL:
753
- return datetime.timedelta
754
- else:
755
- raise Exception("unmapped type %s" % v)
756
-
757
- @classmethod
758
- def massage_data(cls, data):
759
- """
760
-
761
- :param :
762
- :param :
763
- :param :
764
- :returns:
765
- """
766
- data = {key.lower(): val for key, val in data.items()}
767
- primaryKey = set(cls.GetPrimaryKeyColumnNames())
768
- if not primaryKey:
769
- if not cls.Exists():
770
- raise exceptions.DbTableMissingError
771
- dataKeys = set(data.keys()).intersection(primaryKey)
772
- dataColumns = set(data.keys()).difference(primaryKey)
773
- pk = {}
774
- pk.update([(k, data[k]) for k in dataKeys])
775
- d = {}
776
- d.update([(k, data[k]) for k in dataColumns])
777
- return d, pk
778
-
779
- @classmethod
780
- def alter_add(cls, table, columns, null_allowed=True):
781
- sql = []
782
- null = "NOT NULL" if not null_allowed else ""
783
- if isinstance(columns, dict):
784
- for key, val in columns.items():
785
- sql.append(
786
- "ALTER TABLE {} ADD {} {} {};".format(
787
- quote(table), quote(key), cls.get_type(val), null
788
- )
789
- )
790
- return "\n\t".join(sql), tuple()
791
-
792
- @classmethod
793
- def alter_drop(cls, table, columns):
794
- sql = ["ALTER TABLE {} DROP COLUMN".format(quote(table))]
795
- if isinstance(columns, dict):
796
- for key, val in columns.items():
797
- sql.append("{},".format(key))
798
- if sql[-1][-1] == ",":
799
- sql[-1] = sql[-1][:-1]
800
- return "\n\t".join(sql), tuple()
801
-
802
- @classmethod
803
- def alter_column_by_type(cls, table, column, value, null_allowed=True):
804
- sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
805
- sql.append("{} {}".format(quote(column), cls.get_type(value)))
806
- if not null_allowed:
807
- sql.append("NOT NULL")
808
- return "\n\t".join(sql), tuple()
809
-
810
- @classmethod
811
- def alter_column_by_sql(cls, table, column, value):
812
- sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
813
- sql.append("{} {}".format(quote(column), value))
814
- return " ".join(sql), tuple()
815
-
816
- # 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
817
- @classmethod
818
- def rename_column(cls, table, orig, new):
819
- # Solves case parity errors
820
- orig = orig.lower()
821
- new = new.lower()
822
- # Get SQL query to generate table
823
- sql = (
824
- table.tx.table("sqlite_master")
825
- .select(columns="sql", where={"name": table.name, "type": "table"})
826
- .scalar()
827
- )
828
- original_name = table.name
829
-
830
- # Splits "CREATE TABLE" portion out to be readded to lines at the end
831
- sql_data = sql.split("(", 1)
832
-
833
- sql_list = sql_data[1].replace("\n", " ").split(",")
834
- new_sql_list = []
835
- for line in sql_list:
836
- line = line.strip().lower()
837
- if orig in line:
838
- if line.index(orig) == 0:
839
- new_sql_list.append(line.replace(orig, new, 1))
840
- elif (line[0] == '"' or line[0] == "'") and line.index(orig) == 1:
841
- new_sql_list.append(line.replace(orig, new, 1))
842
- else:
843
- new_sql_list.append(line)
844
- else:
845
- new_sql_list.append(line)
846
-
847
- create_db = ",".join(new_sql_list)
848
-
849
- # Adds "CREATE TABLE" portion back into sql code for execution
850
- create_db = "(".join([sql_data[0], create_db])
851
- create_db += ";"
852
-
853
- # Rename original table
854
- table.rename(table.name + "_original_data")
855
-
856
- table.tx.execute(create_db)
857
- # Create new table with original table name and copy all data from original table
858
- create_db = "INSERT INTO {} SELECT * FROM {};".format(original_name, table.name)
859
- return create_db, tuple()
860
-
861
- @classmethod
862
- def rename_table(cls, table, new):
863
- return "ALTER TABLE {} RENAME TO {};".format(quote(table), quote(new)), tuple()
864
-
865
- @classmethod
866
- def create_savepoint(cls, sp):
867
- return "SAVEPOINT {};".format(sp), tuple()
868
-
869
- @classmethod
870
- def release_savepoint(cls, sp):
871
- return "RELEASE SAVEPOINT {};".format(sp), tuple()
872
-
873
- @classmethod
874
- def rollback_savepoint(cls, sp):
875
- return "ROLLBACK TO SAVEPOINT {};".format(sp), tuple()
876
-
877
- @classmethod
878
- def find_duplicates(cls, table, columns, key):
879
- if isinstance(columns, str):
880
- columns = [columns]
881
- return (
882
- """
883
- SELECT {2}
884
- FROM (SELECT {2},
885
- ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
886
- FROM {0}) t
887
- WHERE t.rnum > 1;
888
- """.format(
889
- table, ",".join(quote(columns)), key
890
- ),
891
- tuple(),
892
- )
893
-
894
- @classmethod
895
- def delete_duplicates(cls, table, columns, key):
896
- if isinstance(columns, str):
897
- columns = [columns]
898
- return (
899
- """
900
- DELETE FROM {0}
901
- WHERE {2} IN (SELECT {2}
902
- FROM (SELECT {2},
903
- ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
904
- FROM {0}) t
905
- WHERE t.rnum > 1);
906
- """.format(
907
- table, ",".join(quote(columns)), key
908
- ),
909
- tuple(),
910
- )
911
-
912
- @classmethod
913
- def delete(cls, table, where):
914
- sql = ["DELETE FROM {}".format(table)]
915
- sql.append("WHERE")
916
- vals = []
917
- if isinstance(where, dict):
918
- join = ""
919
- for key in sorted(where.keys()):
920
- if join:
921
- sql.append(join)
922
- if where[key] is None:
923
- sql.append("{} is NULL".format(quote(key.lower())))
924
- else:
925
- sql.append("{} = ?".format(quote(key.lower())))
926
- vals.append(where[key])
927
- join = "AND"
928
- else:
929
- sql.append(where)
930
- return " ".join(sql), tuple(vals)
931
-
932
- @classmethod
933
- def truncate(cls, table):
934
- return "truncate table {}".format(quote(table)), tuple()
935
-
936
- @classmethod
937
- def create_view(cls, name, query, temp=False, silent=True):
938
- sql = ["CREATE"]
939
- if silent:
940
- sql.append("OR REPLACE")
941
- if temp:
942
- sql.append("TEMPORARY")
943
- sql.append("VIEW")
944
- sql.append(name)
945
- sql.append("AS")
946
- sql.append(query)
947
- return " ".join(sql), tuple()
948
-
949
- @classmethod
950
- def drop_view(cls, name, silent=True):
951
- sql = ["DROP VIEW"]
952
- if silent:
953
- sql.append("IF EXISTS")
954
- sql.append(name)
955
- return " ".join(sql), tuple()
956
-
957
- class TYPES(object):
958
- TEXT = "TEXT"
959
- INTEGER = "INTEGER"
960
- NUMERIC = "NUMERIC"
961
- DATETIME = "TIMESTAMP WITHOUT TIME ZONE"
962
- TIMESTAMP = "TIMESTAMP WITHOUT TIME ZONE"
963
- DATE = "DATE"
964
- TIME = "TIME WITHOUT TIME ZONE"
965
- BIGINT = "BIGINT"
966
- BOOLEAN = "BOOLEAN"
967
- BINARY = "BLOB"
968
- INTERVAL = "INTERVAL"