velocity-python 0.0.1__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.

@@ -0,0 +1,821 @@
1
+ import decimal
2
+ import hashlib
3
+ import datetime
4
+ import re
5
+ from velocity.db import exceptions
6
+
7
+
8
+ def initialize(config):
9
+ import pytds
10
+ from velocity.db.core.engine import Engine
11
+ return Engine(pytds, config, SQL)
12
+
13
+ def make_where(where, sql, vals, is_join=False):
14
+ if not where:
15
+ return
16
+ sql.append('WHERE')
17
+ if isinstance(where, str):
18
+ sql.append(where)
19
+ return
20
+ if isinstance(where,dict):
21
+ where = where.items()
22
+ if isinstance(where,list):
23
+ join = ''
24
+ for key,val in where:
25
+ if join: sql.append(join)
26
+ if is_join:
27
+ if '.' not in key:
28
+ key = 'A.' + key
29
+ if val == None:
30
+ if '!' in key:
31
+ key = key.replace('!','')
32
+ sql.append('{} is not NULL'.format(quote(key.lower())))
33
+ else:
34
+ sql.append('{} is NULL'.format(quote(key.lower())))
35
+ elif isinstance(val,(list,tuple)):
36
+ if '!' in key:
37
+ key = key.replace('!','')
38
+ sql.append('{} not in %s'.format(quote(key.lower())))
39
+ vals.append(tuple(val))
40
+ else:
41
+ sql.append('{} in %s'.format(quote(key.lower())))
42
+ vals.append(tuple(val))
43
+ else:
44
+ if '<>' in key:
45
+ key = key.replace('<>','')
46
+ op = '<>'
47
+ elif '!=' in key:
48
+ key = key.replace('!=','')
49
+ op = '<>'
50
+ elif '!%' in key:
51
+ key = key.replace('!%','')
52
+ op = 'not like'
53
+ elif '%%' in key:
54
+ key = key.replace('%%','')
55
+ op = '%'
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 = 'like'
80
+ elif '!' in key:
81
+ key = key.replace('!','')
82
+ op = '<>'
83
+ elif '=' in key:
84
+ key = key.replace('=','')
85
+ op = '='
86
+ else:
87
+ op = '='
88
+ if isinstance(val,str) and val[:2] == '@@':
89
+ sql.append('{} {} {}'.format(quote(key.lower()), op, val[2:]))
90
+ else:
91
+ if 'like' in op:
92
+ sql.append('lower({}) {} lower(%s)'.format(quote(key.lower()), op))
93
+ else:
94
+ sql.append('{} {} %s'.format(quote(key.lower()), op))
95
+ vals.append(val)
96
+ join = 'AND'
97
+ # for index, value in enumerate(vals):
98
+ # print "In loop..."
99
+ # if isinstance(value, (bytearray,buffer)):
100
+ # print "Converting bytearray to pytds.Binary..."
101
+ # print value
102
+ # vals[index] = pytds.Binary(str(value))
103
+
104
+ def quote(data):
105
+ if isinstance(data, list):
106
+ new = []
107
+ for item in data:
108
+ new.append(quote(item))
109
+ return new
110
+ else:
111
+ parts = data.split('.')
112
+ new = []
113
+ for part in parts:
114
+ if '[' in part:
115
+ new.append(part)
116
+ elif part.upper() in reserved_words:
117
+ new.append('['+part+']')
118
+ elif re.findall('[/]',part):
119
+ new.append('['+part+']')
120
+ else:
121
+ new.append(part)
122
+ return '.'.join(new)
123
+
124
+ class SQL:
125
+ server = "SQL Server"
126
+ type_column_identifier = 'data_type'
127
+ default_schema = 'dbo'
128
+
129
+ ApplicationErrorCodes = []
130
+
131
+ DatabaseMissingErrorCodes = []
132
+ TableMissingErrorCodes = [208,]
133
+ ColumnMissingErrorCodes = [207,1911]
134
+ ForeignKeyMissingErrorCodes =[]
135
+
136
+ ConnectionErrorCodes = []
137
+ DuplicateKeyErrorCodes = []
138
+ RetryTransactionCodes = []
139
+ TruncationErrorCodes = [8152,]
140
+ LockTimeoutErrorCodes = []
141
+ DatabaseObjectExistsErrorCodes = []
142
+
143
+ @classmethod
144
+ def version(cls):
145
+ return "select @@version", tuple()
146
+
147
+ @classmethod
148
+ def timestamp(cls):
149
+ return "select current_timestamp", tuple()
150
+
151
+ @classmethod
152
+ def user(cls):
153
+ return "select current_user", tuple()
154
+
155
+ @classmethod
156
+ def databases(cls):
157
+ return "select name from master.dbo.sysdatabases", tuple()
158
+
159
+ @classmethod
160
+ def schemas(cls):
161
+ return 'select schema_name from information_schema.schemata', tuple()
162
+
163
+ @classmethod
164
+ def current_schema(cls):
165
+ return 'select schema_name()', tuple()
166
+
167
+ @classmethod
168
+ def current_database(cls):
169
+ return 'select db_name() as current_database', tuple()
170
+
171
+ @classmethod
172
+ def tables(cls, system=False):
173
+ return """
174
+ select table_schema, table_name
175
+ from information_schema.tables
176
+ where table_type = 'BASE TABLE'
177
+ order by table_schema,table_name
178
+ """, tuple()
179
+
180
+ @classmethod
181
+ def views(cls, system=False):
182
+ return "SELECT s.name , v.name FROM sys.views v inner join sys.schemas s on s.schema_id = v.schema_id", tuple()
183
+
184
+ @classmethod
185
+ def __has_pointer(cls,columns):
186
+ if columns:
187
+ if isinstance(columns,list):
188
+ columns = ','.join(columns)
189
+ if '>' in columns:
190
+ return True
191
+ return False
192
+
193
+ @classmethod
194
+ def select(cls,columns=None,table=None,where=None,orderby=None,groupby=None,having=None,start=None,qty=None,tbl=None):
195
+ is_join = False
196
+
197
+ if isinstance(columns,str)\
198
+ and 'distinct' in columns.lower():
199
+ sql = [
200
+ 'SELECT',
201
+ columns,
202
+ 'FROM',
203
+ quote(table),
204
+ ]
205
+ elif cls.__has_pointer(columns):
206
+ is_join = True
207
+ if isinstance(columns,str):
208
+ columns = columns.split(',')
209
+ letter = 65
210
+ tables = {table: chr(letter)}
211
+ letter += 1
212
+ __select = []
213
+ __from = ['{} AS {}'.format(quote(table),tables.get(table))]
214
+ __left_join = []
215
+
216
+ for column in columns:
217
+ if '>' in column:
218
+ is_join = True
219
+ parts = column.split('>')
220
+ foreign = tbl.foreign_key_info(parts[0])
221
+ if not foreign:
222
+ raise exceptions.DbApplicationError("Foreign key not defined")
223
+ ref_table = foreign['referenced_table_name']
224
+ ref_schema = foreign['referenced_table_schema']
225
+ ref_column = foreign['referenced_column_name']
226
+ lookup = "{}:{}".format(ref_table,parts[0])
227
+ if tables.has_key(lookup):
228
+ __select.append('{}."{}" as "{}"'.format(tables.get(lookup),parts[1],'_'.join(parts)))
229
+ else:
230
+ tables[lookup] = chr(letter)
231
+ letter += 1
232
+ __select.append('{}."{}" as "{}"'.format(tables.get(lookup),parts[1],'_'.join(parts)))
233
+ __left_join.append('LEFT OUTER JOIN "{}"."{}" AS {}'.format(ref_schema,ref_table,tables.get(lookup)))
234
+ __left_join.append('ON {}."{}" = {}."{}"'.format(
235
+ tables.get(table),
236
+ parts[0],
237
+ tables.get(lookup),
238
+ ref_column
239
+ ))
240
+ if orderby and column in orderby:
241
+ orderby = orderby.replace(column,"{}.{}".format(tables.get(lookup),parts[1]))
242
+
243
+ else:
244
+ if '(' in column:
245
+ __select.append(column)
246
+ else:
247
+ __select.append("{}.{}".format(tables.get(table),column))
248
+ sql = ['SELECT']
249
+ sql.append(','.join(__select))
250
+ sql.append('FROM')
251
+ sql.extend(__from)
252
+ sql.extend(__left_join)
253
+ else:
254
+ if columns:
255
+ if isinstance(columns,str):
256
+ columns = columns.split(',')
257
+ if isinstance(columns,list):
258
+ columns = quote(columns)
259
+ columns = ','.join(columns)
260
+ else:
261
+ columns = '*'
262
+ sql = [
263
+ 'SELECT',
264
+ columns,
265
+ 'FROM',
266
+ quote(table),
267
+ ]
268
+ vals = []
269
+ make_where(where, sql, vals, is_join)
270
+ if groupby:
271
+ sql.append('GROUP BY')
272
+ if isinstance(groupby,(list,tuple)):
273
+ groupby = ','.join(groupby)
274
+ sql.append(groupby)
275
+ if having:
276
+ sql.append('HAVING')
277
+ if isinstance(having,(list,tuple)):
278
+ having = ','.join(having)
279
+ sql.append(having)
280
+ if orderby:
281
+ sql.append('ORDER BY')
282
+ if isinstance(orderby,(list,tuple)):
283
+ orderby = ','.join(orderby)
284
+ sql.append(orderby)
285
+ if start and qty:
286
+ sql.append('OFFSET {} ROWS FETCH NEXT {} ROWS ONLY'.format(start,qty))
287
+ elif start:
288
+ sql.append('OFFSET {} ROWS'.format(start))
289
+ elif qty:
290
+ sql.append('FETCH NEXT {} ROWS ONLY'.format(qty))
291
+ sql = ' '.join(sql)
292
+ return sql, tuple(vals)
293
+
294
+ @classmethod
295
+ def create_database(cls, name):
296
+ return 'create database ' + name, tuple()
297
+
298
+ @classmethod
299
+ def last_id(cls, table):
300
+ return "SELECT @@IDENTITY", tuple()
301
+
302
+ @classmethod
303
+ def drop_database(cls, name):
304
+ return 'drop database ' + name, tuple()
305
+
306
+ @classmethod
307
+ def foreign_key_info(cls,table=None,column=None,schema=None):
308
+ if '.' in table:
309
+ schema, table = table.split('.')
310
+
311
+ sql = ["""
312
+ SELECT
313
+ KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME
314
+ ,KCU1.TABLE_NAME AS FK_TABLE_NAME
315
+ ,KCU1.COLUMN_NAME AS FK_COLUMN_NAME
316
+ ,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION
317
+ ,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME
318
+ ,KCU2.TABLE_NAME AS referenced_table_name
319
+ ,KCU2.COLUMN_NAME AS referenced_column_name
320
+ ,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION
321
+ ,KCU2.CONSTRAINT_SCHEMA AS referenced_table_schema
322
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC
323
+
324
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1
325
+ ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
326
+ AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
327
+ AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
328
+
329
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU2
330
+ ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
331
+ AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
332
+ AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
333
+ AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
334
+ """]
335
+ vals = []
336
+ where = {}
337
+ if schema:
338
+ where['LOWER(KCU1.CONSTRAINT_SCHEMA)'] = schema.lower()
339
+ if table:
340
+ where['LOWER(KCU1.TABLE_NAME)'] = table.lower()
341
+ if column:
342
+ where['LOWER(KCU1.COLUMN_NAME)'] = column.lower()
343
+ make_where(where, sql, vals)
344
+ return ' '.join(sql), tuple(vals)
345
+
346
+ @classmethod
347
+ def create_foreign_key(cls, table, columns, key_to_table, key_to_columns, name=None, schema=None):
348
+ if '.' not in table and schema:
349
+ if schema == None:
350
+ schema = cls.default_schema
351
+ table = "{}.{}".format(schema,table)
352
+ if isinstance(key_to_columns, str):
353
+ key_to_columns = [key_to_columns]
354
+ if isinstance(columns, str):
355
+ columns = [columns]
356
+ if not name:
357
+ m = hashlib.md5()
358
+ m.update(table)
359
+ m.update(' '.join(columns))
360
+ m.update(key_to_table)
361
+ m.update(' ' .join(key_to_columns))
362
+ name = 'FK_' + m.hexdigest()
363
+ sql = "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE CASCADE ON UPDATE CASCADE;".format(table, name, ','.join(columns), key_to_table, ','.join(key_to_columns))
364
+
365
+ return sql, tuple()
366
+
367
+ @classmethod
368
+ def create_table(cls, name, columns={}, drop=False):
369
+ if '.' in name:
370
+ fqtn = name
371
+ else:
372
+ fqtn = cls.default_schema + '.' + name
373
+ schema,table = fqtn.split('.')
374
+ name = fqtn.replace('.','_')
375
+ trigger = 'on_update_row_{0}'.format(name)
376
+ sql = []
377
+ sql.append('DECLARE @script1 nVarChar(MAX);')
378
+ sql.append('DECLARE @script2 nVarChar(MAX);')
379
+ if drop:
380
+ sql.append(cls.drop_table(fqtn))
381
+ sql.append("""
382
+ SET @script1 = '
383
+ CREATE TABLE {0} (
384
+ sys_id int identity(1000,1) primary key,
385
+ sys_modified datetime not null default(getdate()),
386
+ sys_created datetime not null default(getdate())
387
+ )'
388
+ """.format(fqtn, table, trigger))
389
+ sql.append("""
390
+ SET @script2 = '
391
+ CREATE TRIGGER {2}
392
+ ON {0}
393
+ AFTER UPDATE
394
+ AS
395
+ BEGIN
396
+ UPDATE t
397
+ SET t.sys_modified = CURRENT_TIMESTAMP,
398
+ t.sys_created = d.sys_created
399
+ FROM {0} AS t
400
+ INNER JOIN deleted AS d on t.sys_id=i.sys_id
401
+ END'
402
+ """.format(fqtn, table, trigger))
403
+ sql.append('EXEC (@script1);')
404
+ sql.append('EXEC (@script2);')
405
+ for key,val in columns.items():
406
+ sql.append("ALTER TABLE {} ADD {} {};".format(fqtn,key,cls.get_type(val)))
407
+ return '\n\t'.join(sql), tuple()
408
+
409
+ @classmethod
410
+ def drop_table(cls, name):
411
+ return "IF OBJECT_ID('%s', 'U') IS NOT NULL DROP TABLE %s;" % (quote(cls.default_schema+'.'+name),quote(cls.default_schema+'.'+name)), tuple()
412
+
413
+ @classmethod
414
+ def columns(cls, name):
415
+ if '.' in name:
416
+ return """
417
+ select column_name
418
+ from information_schema.columns
419
+ where table_schema = %s
420
+ and table_name = %s
421
+ """, tuple(name.split('.'))
422
+ else:
423
+ return """
424
+ select column_name
425
+ from information_schema.columns
426
+ where table_name = %s
427
+ """, tuple([name])
428
+
429
+ @classmethod
430
+ def column_info(cls, table, name):
431
+ params = table.split('.')
432
+ params.append(name)
433
+ if '.' in table:
434
+ return """
435
+ select *
436
+ from information_schema.columns
437
+ where table_schema = %s
438
+ and table_name = %s
439
+ and column_name = %s
440
+ """, tuple(params)
441
+ else:
442
+ return """
443
+ select *
444
+ from information_schema.columns
445
+ where table_name = %s
446
+ and column_name = %s
447
+ """, tuple(params)
448
+
449
+
450
+ @classmethod
451
+ def primary_keys(cls, table):
452
+ params = table.split('.')
453
+ if '.' in table:
454
+ return """
455
+ SELECT COLUMN_NAME
456
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
457
+ WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
458
+ AND TABLE_SCHEMA = %s AND TABLE_NAME = %s
459
+ """, tuple(params)
460
+ else:
461
+ return """
462
+ SELECT COLUMN_NAME
463
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
464
+ WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
465
+ AND TABLE_NAME = %s
466
+ """, tuple(params)
467
+
468
+ @classmethod
469
+ def xforeign_keys(cls, table):
470
+ params = table.split('.')
471
+ if '.' in table:
472
+ return """
473
+ SELECT COLUMN_NAME
474
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
475
+ WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
476
+ AND TABLE_SCHEMA = %s AND TABLE_NAME = %s
477
+ """, tuple(params)
478
+ else:
479
+ return """
480
+ SELECT COLUMN_NAME
481
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
482
+ WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
483
+ AND TABLE_NAME = %s
484
+ """, tuple(params)
485
+
486
+ @classmethod
487
+ def insert(cls, table, data):
488
+ import pytds
489
+ keys = []
490
+ vals = []
491
+ args = []
492
+ for key,val in data.items():
493
+ keys.append(quote(key.lower()))
494
+ if isinstance(val,str) \
495
+ and len(val) > 2 \
496
+ and val[:2] == '@@':
497
+ vals.append(val[2:])
498
+ elif isinstance(val,(bytearray,bytes)):
499
+ vals.append('%s')
500
+ args.append(pytds.Binary(str(val)))
501
+ else:
502
+ vals.append('%s')
503
+ args.append(val)
504
+
505
+ sql = []
506
+ if "sys_id" in data:
507
+ sql.append('SET IDENTITY_INSERT {} ON;'.format(table))
508
+ sql.append('INSERT INTO')
509
+ sql.append(quote(table))
510
+ sql.append('(')
511
+ sql.append(','.join(keys))
512
+ sql.append(')')
513
+ sql.append('VALUES')
514
+ sql.append('(')
515
+ sql.append(','.join(vals))
516
+ sql.append(');')
517
+ if "sys_id" in data:
518
+ sql.append('SET IDENTITY_INSERT {} OFF;'.format(table))
519
+ sql = ' '.join(sql)
520
+ return sql, tuple(args)
521
+
522
+ @classmethod
523
+ def update(cls, table, data, pk):
524
+ import pytds
525
+ sql = ['UPDATE']
526
+ sql.append(quote(table))
527
+ sql.append('SET')
528
+ vals = []
529
+ join = ''
530
+ for key in sorted(data.keys()):
531
+ val = data[key]
532
+ if join:
533
+ sql.append(join)
534
+ sql.append('{} = %s'.format(quote(key.lower())))
535
+ if isinstance(val, (bytearray,bytes)):
536
+ vals.append(pytds.Binary(str(val)))
537
+ else:
538
+ vals.append(val)
539
+ join = ','
540
+ if pk:
541
+ sql.append('\nWHERE')
542
+ join = ''
543
+ for key in sorted(pk.keys()):
544
+ val = pk[key]
545
+ if join:
546
+ sql.append(join)
547
+ if val is None:
548
+ sql.append('{} is null'.format(quote(key.lower())))
549
+ else:
550
+ sql.append('{} = %s'.format(quote(key.lower())))
551
+ vals.append(val)
552
+ join = 'AND'
553
+ sql = ' '.join(sql)
554
+ return sql, tuple(vals)
555
+
556
+ @classmethod
557
+ def create_index(cls, table=None, columns=None, unique=False, direction=None, where=None, name=None, schema=None, trigram=None, tbl=None):
558
+ if '.' not in table and schema:
559
+ table = "{}.{}".format(schema,table)
560
+ if isinstance(columns,(list,set)):
561
+ columns = ','.join([quote(c.lower()) for c in sorted(columns)])
562
+ else:
563
+ columns = quote(columns)
564
+ sql = ['CREATE']
565
+ if unique:
566
+ sql.append('UNIQUE')
567
+ sql.append('INDEX')
568
+ tablename = quote(table)
569
+ if not name:
570
+ name = re.sub(r'\([^)]*\)', '', columns.replace(',','_'))
571
+ sql.append('IDX__{}__{}'.format(table.replace('.','_'),name))
572
+ sql.append('ON')
573
+ sql.append(tablename)
574
+ sql.append('(')
575
+ sql.append(columns)
576
+ sql.append(')')
577
+ return ' '.join(sql), tuple()
578
+
579
+ @classmethod
580
+ def drop_index(cls, table=None, columns=None, name=None, schema=None):
581
+ if '.' not in table and schema:
582
+ table = "{}.{}".format(schema,table)
583
+ if isinstance(columns,(list,set)):
584
+ columns = ','.join([quote(c.lower()) for c in sorted(columns)])
585
+ else:
586
+ columns = quote(columns)
587
+ sql = ['DROP']
588
+ sql.append('INDEX IF EXISTS')
589
+ tablename = quote(table)
590
+ if not name:
591
+ name = re.sub(r'\([^)]*\)', '', columns.replace(',','_'))
592
+ sql.append('IDX__{}__{}'.format(table.replace('.','_'),name))
593
+ print(' '.join(sql))
594
+ return ' '.join(sql), tuple()
595
+
596
+ @classmethod
597
+ def get_type(cls, v):
598
+ if isinstance(v, str):
599
+ if v[:2] == '@@':
600
+ return v[2:]
601
+ elif isinstance(v, (str, bytes)) \
602
+ or v is str \
603
+ or v is bytes:
604
+ return cls.TYPES.TEXT
605
+ elif isinstance(v, bool) \
606
+ or v is bool:
607
+ return cls.TYPES.BOOLEAN
608
+ elif isinstance(v, int) \
609
+ or v is int:
610
+ if v is int:
611
+ return cls.TYPES.INTEGER
612
+ if v > 2147483647 or v < -2147483648:
613
+ return cls.TYPES.BIGINT
614
+ else:
615
+ return cls.TYPES.INTEGER
616
+ elif isinstance(v, float) \
617
+ or v is float:
618
+ return cls.TYPES.NUMERIC + '(19, 6)'
619
+ elif isinstance(v, decimal.Decimal) \
620
+ or v is decimal.Decimal:
621
+ return cls.TYPES.NUMERIC + '(19, 6)'
622
+ elif isinstance (v, datetime.datetime) \
623
+ or v is datetime.datetime:
624
+ return cls.TYPES.DATETIME
625
+ elif isinstance (v, datetime.date) \
626
+ or v is datetime.date:
627
+ return cls.TYPES.DATE
628
+ elif isinstance(v, datetime.time) \
629
+ or v is datetime.time:
630
+ return cls.TYPES.TIME
631
+ elif isinstance (v, (bytearray, bytes)) \
632
+ or v is bytearray \
633
+ or v is bytes:
634
+ return cls.TYPES.BINARY
635
+ # Everything else defaults to TEXT, incl. None
636
+ return cls.TYPES.TEXT
637
+
638
+ @classmethod
639
+ def py_type(cls, v):
640
+ v = str(v).upper()
641
+ if v == cls.TYPES.INTEGER:
642
+ return int
643
+ elif v in cls.TYPES.TEXT:
644
+ return str
645
+ elif v == cls.TYPES.BOOLEAN:
646
+ return bool
647
+ elif v == cls.TYPES.DATE:
648
+ return datetime.date
649
+ elif v == cls.TYPES.TIME:
650
+ return datetime.time
651
+ elif v == cls.TYPES.DATETIME:
652
+ return datetime.datetime
653
+ else:
654
+ raise Exception("unmapped type %s" % v)
655
+
656
+ @classmethod
657
+ def massage_data(cls,data):
658
+ """
659
+
660
+ :param :
661
+ :param :
662
+ :param :
663
+ :returns:
664
+ """
665
+ data = {key.lower():val for key,val in data.items()}
666
+ primaryKey = set(cls.GetPrimaryKeyColumnNames())
667
+ if not primaryKey:
668
+ if not cls.Exists():
669
+ raise exceptions.DbTableMissingError
670
+ dataKeys = set(data.keys()).intersection( primaryKey )
671
+ dataColumns = set(data.keys()).difference( primaryKey )
672
+ pk = {}
673
+ pk.update([(k,data[k]) for k in dataKeys])
674
+ d = {}
675
+ d.update([(k,data[k]) for k in dataColumns])
676
+ return d, pk
677
+
678
+ @classmethod
679
+ def alter_add(cls, table, columns, null_allowed=True):
680
+ sql = []
681
+ null = 'NOT NULL' if not null_allowed else ''
682
+ if isinstance(columns,dict):
683
+ for key,val in columns.items():
684
+ sql.append("ALTER TABLE {} ADD {} {} {};".format(quote(table), quote(key), cls.get_type(val), null))
685
+ return '\n\t'.join(sql), tuple()
686
+
687
+ @classmethod
688
+ def alter_drop(cls, table, columns):
689
+ sql = ["ALTER TABLE {} DROP COLUMN".format(quote(table))]
690
+ if isinstance(columns,dict):
691
+ for key,val in columns.items():
692
+ sql.append("{},".format(key))
693
+ if sql[-1][-1] == ',':
694
+ sql[-1] = sql[-1][:-1]
695
+ return '\n\t'.join(sql), tuple()
696
+
697
+ @classmethod
698
+ def alter_column_by_type(cls, table, column, value, null_allowed=True):
699
+ sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
700
+ sql.append("{} {}".format(quote(column), cls.get_type(value)))
701
+ if not null_allowed:
702
+ sql.append('NOT NULL')
703
+ return '\n\t'.join(sql), tuple()
704
+
705
+ @classmethod
706
+ def alter_column_by_sql(cls, table, column, value):
707
+ sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
708
+ sql.append("{} {}".format(quote(column), value))
709
+ return ' '.join(sql), tuple()
710
+
711
+
712
+ @classmethod
713
+ def rename_column(cls, table, orig, new):
714
+ if '.' in table:
715
+ schema, table = table.split('.')
716
+ else:
717
+ schema = cls.default_schema
718
+ return "sp_rename '{}.{}.{}', '{}', 'COLUMN';".format(quote(schema), quote(table), quote(orig), new), tuple()
719
+
720
+ @classmethod
721
+ def rename_table(cls, table, name, new):
722
+ if '.' in table:
723
+ schema, table = table.split('.')
724
+ else:
725
+ schema = cls.default_schema
726
+ return "sp_rename '{}.{}', '{}';".format(quote(schema), quote(name), new), tuple()
727
+
728
+ @classmethod
729
+ def create_savepoint(cls, sp):
730
+ return None, tuple()
731
+
732
+ @classmethod
733
+ def release_savepoint(cls, sp):
734
+ return None, tuple()
735
+
736
+ @classmethod
737
+ def rollback_savepoint(cls, sp):
738
+ return None, tuple()
739
+
740
+ @classmethod
741
+ def find_duplicates(cls, table, columns, key):
742
+ if isinstance(columns, str):
743
+ columns = [columns]
744
+ return """
745
+ SELECT {2}
746
+ FROM (SELECT {2},
747
+ ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
748
+ FROM {0}) t
749
+ WHERE t.rnum > 1;
750
+ """.format(table, ','.join(quote(columns)), key), tuple()
751
+
752
+ @classmethod
753
+ def delete_duplicates(cls, table, columns, key):
754
+ if isinstance(columns, str):
755
+ columns = [columns]
756
+ return """
757
+ DELETE FROM {0}
758
+ WHERE {2} IN (SELECT {2}
759
+ FROM (SELECT {2},
760
+ ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
761
+ FROM {0}) t
762
+ WHERE t.rnum > 1);
763
+ """.format(table, ','.join(quote(columns)), key), tuple()
764
+
765
+ @classmethod
766
+ def delete(cls, table, where):
767
+ sql = ['DELETE FROM {}'.format(table)]
768
+ sql.append('WHERE')
769
+ vals = []
770
+ if isinstance(where,dict):
771
+ join = ''
772
+ for key in sorted(where.keys()):
773
+ if join: sql.append(join)
774
+ if where[key] == None:
775
+ sql.append('{} is NULL'.format(quote(key.lower())))
776
+ else:
777
+ sql.append('{} = %s'.format(quote(key.lower())))
778
+ vals.append(where[key])
779
+ join = 'AND'
780
+ else:
781
+ sql.append(where)
782
+ return ' '.join(sql), tuple(vals)
783
+
784
+ @classmethod
785
+ def truncate(cls, table):
786
+ return "truncate table {}".format(quote(table)), tuple()
787
+
788
+ @classmethod
789
+ def create_view(cls, name, query, temp=False, silent=True):
790
+ sql = ['CREATE']
791
+ if silent:
792
+ sql.append('OR REPLACE')
793
+ if temp:
794
+ sql.append('TEMPORARY')
795
+ sql.append('VIEW')
796
+ sql.append(cls.default_schema+'.'+name)
797
+ sql.append('AS')
798
+ sql.append(query)
799
+ return ' '.join(sql),tuple()
800
+
801
+ @classmethod
802
+ def drop_view(cls, name, silent=True):
803
+ sql = ['DROP VIEW']
804
+ if silent:
805
+ sql.append('IF EXISTS')
806
+ sql.append(cls.default_schema+'.'+name)
807
+ return ' '.join(sql),tuple()
808
+
809
+ class TYPES(object):
810
+ TEXT = 'VARCHAR(MAX)'
811
+ INTEGER = 'INT'
812
+ NUMERIC = 'NUMERIC'
813
+ DATETIME = 'DATETIME'
814
+ TIMESTAMP = 'DATETIME'
815
+ DATE = 'DATE'
816
+ TIME = 'TIME'
817
+ BIGINT = 'BIGINT'
818
+ BOOLEAN = 'BIT'
819
+ BINARY = 'VARBINARY(MAX)'
820
+
821
+ reserved_words = ['ABSOLUTE', 'ACTION', 'ADA', 'ADD', 'ALL', 'ALLOCATE', 'ALTER', 'AND', 'ANY', 'ARE', 'AS', 'ASC', 'ASSERTION', 'AT', 'AUTHORIZATION', 'AVG', 'BACKUP', 'BEGIN', 'BETWEEN', 'BIT', 'BIT_LENGTH', 'BOTH', 'BREAK', 'BROWSE', 'BULK', 'BY', 'CASCADE', 'CASCADED', 'CASE', 'CAST', 'CATALOG', 'CHAR', 'CHARACTER', 'CHARACTER_LENGTH', 'CHAR_LENGTH', 'CHECK', 'CHECKPOINT', 'CLOSE', 'CLUSTERED', 'COALESCE', 'COLLATE', 'COLLATION', 'COLUMN', 'COMMIT', 'COMPUTE', 'CONNECT', 'CONNECTION', 'CONSTRAINT', 'CONSTRAINTS', 'CONTAINS', 'CONTAINSTABLE', 'CONTINUE', 'CONVERT', 'CORRESPONDING', 'COUNT', 'CREATE', 'CROSS', 'CURRENT', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER', 'CURSOR', 'DATABASE', 'DATE', 'DAY', 'DBCC', 'DEALLOCATE', 'DEC', 'DECIMAL', 'DECLARE', 'DEFAULT', 'DEFERRABLE', 'DEFERRED', 'DELETE', 'DENY', 'DESC', 'DESCRIBE', 'DESCRIPTOR', 'DIAGNOSTICS', 'DISCONNECT', 'DISK', 'DISTINCT', 'DISTRIBUTED', 'DOMAIN', 'DOUBLE', 'DROP', 'DUMP', 'ELSE', 'END', 'END-EXEC', 'ERRLVL', 'ESCAPE', 'EXCEPT', 'EXCEPTION', 'EXEC', 'EXECUTE', 'EXISTS', 'EXIT', 'EXTERNAL', 'EXTRACT', 'FALSE', 'FETCH', 'FILE', 'FILLFACTOR', 'FIRST', 'FLOAT', 'FOR', 'FOREIGN', 'FORTRAN', 'FOUND', 'FREETEXT', 'FREETEXTTABLE', 'FROM', 'FULL', 'FUNCTION', 'GET', 'GLOBAL', 'GO', 'GOTO', 'GRANT', 'GROUP', 'HAVING', 'HOLDLOCK', 'HOUR', 'IDENTITY', 'IDENTITYCOL', 'IDENTITY_INSERT', 'IF', 'IMMEDIATE', 'IN', 'INCLUDE', 'INDEX', 'INDICATOR', 'INITIALLY', 'INNER', 'INPUT', 'INSENSITIVE', 'INSERT', 'INT', 'INTEGER', 'INTERSECT', 'INTERVAL', 'INTO', 'IS', 'ISOLATION', 'JOIN', 'KEY', 'KILL', 'LANGUAGE', 'LAST', 'LEADING', 'LEFT', 'LEVEL', 'LIKE', 'LINENO', 'LOAD', 'LOCAL', 'LOWER', 'MATCH', 'MAX', 'MERGE', 'MIN', 'MINUTE', 'MODULE', 'MONTH', 'NAMES', 'NATIONAL', 'NATURAL', 'NCHAR', 'NEXT', 'NO', 'NOCHECK', 'NONCLUSTERED', 'NONE', 'NOT', 'NULL', 'NULLIF', 'NUMERIC', 'OCTET_LENGTH', 'OF', 'OFF', 'OFFSETS', 'ON', 'ONLY', 'OPEN', 'OPENDATASOURCE', 'OPENQUERY', 'OPENROWSET', 'OPENXML', 'OPTION', 'OR', 'ORDER', 'OUTER', 'OUTPUT', 'OVER', 'OVERLAPS', 'PAD', 'PARTIAL', 'PASCAL', 'PERCENT', 'PIVOT', 'PLAN', 'POSITION', 'PRECISION', 'PREPARE', 'PRESERVE', 'PRIMARY', 'PRINT', 'PRIOR', 'PRIVILEGES', 'PROC', 'PROCEDURE', 'PUBLIC', 'RAISERROR', 'READ', 'READTEXT', 'REAL', 'RECONFIGURE', 'REFERENCES', 'RELATIVE', 'REPLICATION', 'RESTORE', 'RESTRICT', 'RETURN', 'REVERT', 'REVOKE', 'RIGHT', 'ROLLBACK', 'ROWCOUNT', 'ROWGUIDCOL', 'ROWS', 'RULE', 'SAVE', 'SCHEMA', 'SCROLL', 'SECOND', 'SECTION', 'SECURITYAUDIT', 'SELECT', 'SEMANTICKEYPHRASETABLE', 'SEMANTICSIMILARITYDETAILSTABLE', 'SEMANTICSIMILARITYTABLE', 'SESSION', 'SESSION_USER', 'SET', 'SETUSER', 'SHUTDOWN', 'SIZE', 'SMALLINT', 'SOME', 'SPACE', 'SQL', 'SQLCA', 'SQLCODE', 'SQLERROR', 'SQLSTATE', 'SQLWARNING', 'STATISTICS', 'SUBSTRING', 'SUM', 'SYSTEM_USER', 'TABLE', 'TABLESAMPLE', 'TEMPORARY', 'TEXTSIZE', 'THEN', 'TIME', 'TIMESTAMP', 'TIMEZONE_HOUR', 'TIMEZONE_MINUTE', 'TO', 'TOP', 'TRAILING', 'TRAN', 'TRANSACTION', 'TRANSLATE', 'TRANSLATION', 'TRIGGER', 'TRIM', 'TRUE', 'TRUNCATE', 'TRY_CONVERT', 'TSEQUAL', 'UNION', 'UNIQUE', 'UNKNOWN', 'UNPIVOT', 'UPDATE', 'UPDATETEXT', 'UPPER', 'USAGE', 'USE', 'USER', 'USING', 'VALUE', 'VALUES', 'VARCHAR', 'VARYING', 'VIEW', 'WAITFOR', 'WHEN', 'WHENEVER', 'WHERE', 'WHILE', 'WITH', 'WITHIN GROUP', 'WORK', 'WRITE', 'WRITETEXT', 'YEAR', 'ZONE',]