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

velocity/db/core/table.py CHANGED
@@ -1,19 +1,26 @@
1
- from velocity.db import exceptions
1
+ from velocity.db import exceptions
2
2
  from velocity.db.core.row import Row
3
- from velocity.db.servers.sql import Query
4
3
  from velocity.db.core.result import Result
5
4
  from velocity.db.core.column import Column
6
- from velocity.db.core.decorators import return_default, create_missing, retry_on_dup_key
7
- from functools import wraps
5
+ from velocity.db.core.decorators import (
6
+ return_default,
7
+ create_missing,
8
+ reset_id_on_dup_key,
9
+ )
10
+
11
+
12
+ class Query(str):
13
+ pass
14
+
8
15
 
9
16
  class Table(object):
10
17
  def __init__(self, tx, name):
11
18
  self.tx = tx
12
- assert(self.tx)
19
+ assert self.tx
13
20
  self.name = name.lower()
14
- assert(self.name)
21
+ assert self.name
15
22
  self.sql = tx.engine.sql
16
- assert(self.sql)
23
+ assert self.sql
17
24
 
18
25
  def __str__(self):
19
26
  return """
@@ -22,10 +29,10 @@ class Table(object):
22
29
  Columns: %s
23
30
  Rows: %s
24
31
  """ % (
25
- self.name,
26
- self.exists(),
27
- len(self.columns),
28
- len(self)
32
+ self.name,
33
+ self.exists(),
34
+ len(self.columns),
35
+ len(self),
29
36
  )
30
37
 
31
38
  def __enter__(self):
@@ -50,13 +57,13 @@ class Table(object):
50
57
  return self._cursor
51
58
 
52
59
  def __call__(self, where=None):
53
- sql,val = self.sql.select(table=self.name,where=where)
54
- for data in self.tx.execute(sql,val):
60
+ sql, val = self.sql.select(table=self.name, where=where)
61
+ for data in self.tx.execute(sql, val):
55
62
  yield self.row(data)
56
63
 
57
64
  def __iter__(self):
58
- sql,val = self.sql.select(table=self.name, orderby="sys_id")
59
- for data in self.tx.execute(sql,val):
65
+ sql, val = self.sql.select(table=self.name, orderby="sys_id")
66
+ for data in self.tx.execute(sql, val):
60
67
  yield self.row(data)
61
68
 
62
69
  @property
@@ -69,13 +76,15 @@ class Table(object):
69
76
  def columns(self):
70
77
  columns = []
71
78
  for column in self.sys_columns:
72
- if 'sys_' not in column:
73
- columns.append(column)
79
+ if "sys_" not in column:
80
+ columns.append(column)
74
81
  return columns
75
82
 
76
- @return_default(None)
83
+ @return_default(None, (exceptions.DbObjectExistsError,))
77
84
  def create_index(self, columns, unique=False, direction=None, where=None, **kwds):
78
- sql, vals = self.sql.create_index(self.name, columns, unique, direction, where, tbl=self, **kwds)
85
+ sql, vals = self.sql.create_index(
86
+ self.name, columns, unique, direction, where, tbl=self, **kwds
87
+ )
79
88
  self.tx.execute(sql, vals, cursor=self.cursor)
80
89
 
81
90
  @return_default(None)
@@ -99,7 +108,7 @@ class Table(object):
99
108
  def exists(self):
100
109
  sql, vals = self.sql.tables()
101
110
  result = self.tx.execute(sql, vals, cursor=self.cursor)
102
- if '.' in self.name:
111
+ if "." in self.name:
103
112
  return bool(self.name in ["%s.%s" % x for x in result.as_tuple()])
104
113
  else:
105
114
  return bool(self.name in ["%s" % x[1] for x in result.as_tuple()])
@@ -107,95 +116,143 @@ class Table(object):
107
116
  def column(self, name):
108
117
  return Column(self, name)
109
118
 
110
- def row(self, key=None):
119
+ def row(self, key=None, lock=None):
111
120
  if key == None:
112
- return self.new()
121
+ return self.new(lock=lock)
113
122
  return Row(self, key)
114
123
 
115
124
  def dict(self, key):
116
125
  row = self.find(key)
117
126
  return row.to_dict() if row else {}
118
127
 
119
- def rows(self, where=None, orderby=None, qty=None):
120
- for key in self.ids(where=where, orderby=orderby, qty=qty):
121
- yield Row(self, key)
122
-
123
- def ids(self, where=None, orderby=None, groupby=None, having=None, start=None, qty=None):
124
- for key in self.select('sys_id', where=where,orderby=orderby,groupby=groupby,having=having,start=start,qty=qty):
125
- yield key['sys_id']
128
+ def rows(self, where=None, orderby=None, qty=None, lock=None, skip_locked=None):
129
+ for key in self.ids(
130
+ where=where, orderby=orderby, qty=qty, lock=lock, skip_locked=skip_locked
131
+ ):
132
+ yield Row(self, key, lock=lock)
133
+
134
+ def ids(
135
+ self,
136
+ where=None,
137
+ orderby=None,
138
+ groupby=None,
139
+ having=None,
140
+ start=None,
141
+ qty=None,
142
+ lock=None,
143
+ skip_locked=None,
144
+ ):
145
+ for key in self.select(
146
+ "sys_id",
147
+ where=where,
148
+ orderby=orderby,
149
+ groupby=groupby,
150
+ having=having,
151
+ start=start,
152
+ qty=qty,
153
+ lock=lock,
154
+ skip_locked=skip_locked,
155
+ ):
156
+ yield key["sys_id"]
126
157
 
127
158
  def set_id(self, start):
128
159
  sql, vals = self.sql.set_id(self.name, start)
129
160
  result = self.tx.execute(sql, vals, cursor=self.cursor)
130
161
 
131
- def new(self, data={'sys_modified':'@@CURRENT_TIMESTAMP'}):
132
- if len(data) == 1 and 'sys_id' in data:
133
- return self.row(data).touch()
162
+ def new(self, data={"sys_modified": "@@CURRENT_TIMESTAMP"}, lock=None):
163
+ if len(data) == 1 and "sys_id" in data:
164
+ return self.row(data, lock=lock).touch()
134
165
  val = self.insert(data)
135
166
  sql, vals = self.sql.last_id(self.name)
136
167
  sys_id = self.tx.execute(sql, vals).scalar()
137
- return self.row(sys_id)
168
+ return self.row(sys_id, lock=lock)
138
169
 
139
- def get(self, where):
170
+ def get(self, where, lock=None, use_where=False):
140
171
  if where is None:
141
172
  raise Exception("None is not allowed as a primary key")
142
173
  if isinstance(where, int):
143
- where = {'sys_id': where}
144
- result = self.select('sys_id', where=where).all()
174
+ where = {"sys_id": where}
175
+ result = self.select("sys_id", where=where, lock=lock).all()
145
176
  if len(result) > 1:
146
- sql = self.selectSQL('sys_id', where=where)
147
- raise exceptions.DuplicateRowsFoundError("More than one entry found. {}".format(sql))
177
+ sql = self.selectSQL("sys_id", where=where, lock=lock)
178
+ raise exceptions.DuplicateRowsFoundError(
179
+ "More than one entry found. {}".format(sql)
180
+ )
148
181
  elif len(result) < 1:
149
182
  where = where.copy()
150
183
  keys = list(where.keys())
151
184
  for key in keys:
152
- chars = set('<>!=%')
185
+ chars = set("<>!=%")
153
186
  if any((c in chars) for c in key):
154
187
  where.pop(key)
155
- return self.new(where)
156
- return Row(self, result[0]['sys_id'])
188
+ return self.new(where, lock=lock)
189
+ if use_where:
190
+ return Row(self, where, lock=lock)
191
+ else:
192
+ return Row(self, result[0]["sys_id"], lock=lock)
157
193
 
158
194
  @return_default(None)
159
- def find(self, where):
195
+ def find(self, where, lock=None, use_where=False):
160
196
  if where is None:
161
197
  raise Exception("None is not allowed as a primary key")
162
198
  if isinstance(where, int):
163
- where = {'sys_id': where}
164
- result = self.select('sys_id', where=where).all()
199
+ where = {"sys_id": where}
200
+ result = self.select("sys_id", where=where, lock=lock).all()
165
201
  if len(result) > 1:
166
- sql = self.selectSQL('sys_id', where=where)
167
- raise exceptions.DuplicateRowsFoundError("More than one entry found. {}".format(sql))
202
+ sql = self.selectSQL("sys_id", where=where, lock=lock)
203
+ raise exceptions.DuplicateRowsFoundError(
204
+ "More than one entry found. {}".format(sql)
205
+ )
168
206
  elif len(result) < 1:
169
207
  return None
170
- return Row(self, result[0]['sys_id'])
208
+ if use_where:
209
+ return Row(self, where, lock=lock)
210
+ else:
211
+ return Row(self, result[0]["sys_id"], lock=lock)
171
212
 
172
213
  @return_default(None)
173
- def first(self, where, orderby=None, create_new=False):
214
+ def first(
215
+ self,
216
+ where,
217
+ orderby=None,
218
+ create_new=False,
219
+ lock=None,
220
+ skip_locked=None,
221
+ use_where=False,
222
+ ):
174
223
  if where is None:
175
- raise Exception("None is not allowed as a primary key")
224
+ raise Exception("None is not allowed as a where clause")
176
225
  if isinstance(where, int):
177
- where = {'sys_id': where}
178
- result = self.select('sys_id', where=where, orderby=orderby).all()
226
+ where = {"sys_id": where}
227
+ result = self.select(
228
+ "sys_id", where=where, orderby=orderby, skip_locked=skip_locked
229
+ ).all()
179
230
  if len(result) < 1:
180
231
  if create_new:
181
232
  where = where.copy()
182
233
  keys = list(where.keys())
183
234
  for key in keys:
184
- chars = set('<>!=%')
235
+ chars = set("<>!=%")
185
236
  if any((c in chars) for c in key):
186
237
  where.pop(key)
187
- return self.new(where)
238
+ return self.new(where, lock=lock)
188
239
  return None
189
- return Row(self, result[0]['sys_id'])
240
+ if use_where:
241
+ return Row(self, where, lock=lock)
242
+ else:
243
+ return Row(self, result[0]["sys_id"], lock=lock)
190
244
 
191
245
  @return_default(None)
192
- def one(self, where=None, orderby=None):
246
+ def one(self, where=None, orderby=None, lock=None, use_where=False):
193
247
  if isinstance(where, int):
194
- where = {'sys_id': where}
195
- result = self.select('sys_id', where=where, orderby=orderby).all()
248
+ where = {"sys_id": where}
249
+ result = self.select("sys_id", where=where, orderby=orderby).all()
196
250
  if len(result) < 1:
197
251
  return None
198
- return Row(self, result[0]['sys_id'])
252
+ if use_where:
253
+ return Row(self, where, lock=lock)
254
+ else:
255
+ return Row(self, result[0]["sys_id"], lock=lock)
199
256
 
200
257
  @property
201
258
  def primary_keys(self):
@@ -214,12 +271,16 @@ class Table(object):
214
271
  return self.tx.execute(sql, vals, cursor=self.cursor).one()
215
272
 
216
273
  @return_default()
217
- def create_foreign_key(self, columns, key_to_table, key_to_columns='sys_id'):
218
- sql, vals = self.sql.create_foreign_key(self.name, columns, key_to_table, key_to_columns)
274
+ def create_foreign_key(self, columns, key_to_table, key_to_columns="sys_id"):
275
+ sql, vals = self.sql.create_foreign_key(
276
+ self.name, columns, key_to_table, key_to_columns
277
+ )
219
278
  return self.tx.execute(sql, vals, cursor=self.cursor)
220
279
 
221
- def drop_foreign_key(self, columns, key_to_table, key_to_columns='sys_id'):
222
- sql, vals = self.sql.create_foreign_key(self.name, columns, key_to_table, key_to_columns)
280
+ def drop_foreign_key(self, columns, key_to_table, key_to_columns="sys_id"):
281
+ sql, vals = self.sql.create_foreign_key(
282
+ self.name, columns, key_to_table, key_to_columns
283
+ )
223
284
  return self.tx.execute(sql, vals, cursor=self.cursor)
224
285
 
225
286
  def rename(self, name):
@@ -229,7 +290,7 @@ class Table(object):
229
290
 
230
291
  def lower_keys(self, arg):
231
292
  new = {}
232
- if isinstance(arg,dict):
293
+ if isinstance(arg, dict):
233
294
  for key in list(arg.keys()):
234
295
  new[key.lower()] = arg[key]
235
296
  return new
@@ -241,146 +302,226 @@ class Table(object):
241
302
  columns = self.lower_keys(columns)
242
303
  # Need to maintain order of keys
243
304
  for k in list(columns.keys()):
244
- diff.append(k) if k not in self.columns else None
305
+ diff.append(k) if k not in self.sys_columns else None
245
306
  else:
246
- raise Exception("I don't know how to handle columns data type in this context")
307
+ raise Exception(
308
+ "I don't know how to handle columns data type in this context"
309
+ )
247
310
  if diff:
248
- new = dict([(key,columns[key]) for key in diff if 'sys_' not in key])
311
+ new = dict([(key, columns[key]) for key in diff])
249
312
  sql, vals = self.sql.alter_add(self.name, new)
250
313
  self.tx.execute(sql, vals, cursor=self.cursor)
251
314
 
252
315
  @create_missing
253
316
  def alter_type(self, column, type_or_value, nullable=True):
254
- sql, vals = self.sql.alter_column_by_type(self.name, column, type_or_value, nullable)
317
+ sql, vals = self.sql.alter_column_by_type(
318
+ self.name, column, type_or_value, nullable
319
+ )
255
320
  self.tx.execute(sql, vals, cursor=self.cursor)
256
321
 
257
322
  @create_missing
258
- def update(self, data, pk, left_join=None, inner_join =None, outer_join=None):
259
- sql, vals = self.sql.update(self.name, data, pk, left_join, inner_join, outer_join)
323
+ def update(self, data, pk, left_join=None, inner_join=None, outer_join=None):
324
+ sql, vals = self.sql.update(
325
+ self.name, data, pk, left_join, inner_join, outer_join
326
+ )
260
327
  result = self.tx.execute(sql, vals, cursor=self.cursor)
261
328
  return result.cursor.rowcount
262
329
 
263
- @retry_on_dup_key
330
+ @reset_id_on_dup_key
264
331
  @create_missing
265
332
  def insert(self, data):
266
333
  sql, vals = self.sql.insert(self.name, data)
267
334
  result = self.tx.execute(sql, vals, cursor=self.cursor)
268
335
  return result.cursor.rowcount
269
336
 
270
- def upsert(self, data, pk):
271
- if not self.update(data, pk):
272
- new = {}
273
- new.update(pk)
274
- new.update(data)
275
- self.insert(new)
337
+ @reset_id_on_dup_key
338
+ @create_missing
339
+ def merge(self, data, pk):
340
+ sql, vals = self.sql.merge(
341
+ self.name, data, pk, on_conflict_do_nothing=False, on_conflict_update=True
342
+ )
343
+ result = self.tx.execute(sql, vals, cursor=self.cursor)
344
+ return result.cursor.rowcount
276
345
 
277
- def indate(self, data, pk):
278
- new = {}
279
- new.update(pk)
280
- new.update(data)
281
- try:
282
- sp = self.tx.create_savepoint(cursor=self.cursor)
283
- self.insert(new)
284
- except exceptions.DbDuplicateKeyError:
285
- self.tx.rollback_savepoint(sp, cursor=self.cursor)
286
- self.update(data, pk)
346
+ upsert = merge
347
+ indate = merge
348
+
349
+ def mergeSQL(self, data, pk):
350
+ return self.sql.merge(
351
+ self.name, data, pk, on_conflict_do_nothing=False, on_conflict_update=True
352
+ )
287
353
 
288
- def updateSQL(self, data, pk, left_join=None, inner_join =None, outer_join=None):
354
+ def insertSQL(self, data):
355
+ return self.sql.insert(self.name, data)
356
+
357
+ def updateSQL(self, data, pk, left_join=None, inner_join=None, outer_join=None):
289
358
  return self.sql.update(self.name, data, pk, left_join, inner_join, outer_join)
290
359
 
291
360
  @return_default(0)
292
361
  def count(self, where=None):
293
- sql, vals = self.sql.select(columns='count(*)', table=self.name, where=where)
362
+ sql, vals = self.sql.select(columns="count(*)", table=self.name, where=where)
294
363
  return self.tx.execute(sql, vals, cursor=self.cursor).scalar()
295
364
 
296
365
  @return_default(0)
297
366
  def sum(self, column, where=None):
298
- sql, vals = self.sql.select(columns='sum({})'.format(column), table=self.name, where=where)
367
+ sql, vals = self.sql.select(
368
+ columns="coalesce(sum(coalesce({},0)),0)".format(column),
369
+ table=self.name,
370
+ where=where,
371
+ )
299
372
  return self.tx.execute(sql, vals, cursor=self.cursor).scalar()
300
373
 
301
374
  @return_default(0)
302
375
  def __len__(self):
303
- sql, vals = self.sql.select(columns='count(*)', table=self.name)
376
+ sql, vals = self.sql.select(columns="count(*)", table=self.name)
304
377
  return self.tx.execute(sql, vals, cursor=self.cursor).scalar()
305
378
 
306
379
  @return_default(None)
307
- def oldest(self, where={}, field='sys_modified', columns='sys_id'):
308
- sql,vals = self.sql.select(columns=columns,
309
- table=self.name,
310
- where=where,
311
- orderby=field + ' asc',
312
- qty=1)
380
+ def oldest(self, where={}, field="sys_modified", columns="sys_id", lock=None):
381
+ sql, vals = self.sql.select(
382
+ columns=columns,
383
+ table=self.name,
384
+ where=where,
385
+ orderby=field + " asc",
386
+ qty=1,
387
+ lock=lock,
388
+ )
313
389
  return self.tx.execute(sql, vals, cursor=self.cursor).scalar()
314
390
 
315
391
  @return_default(None)
316
- def newest(self, where={}, field='sys_modified', columns='sys_id'):
317
- sql,vals = self.sql.select(columns=columns,
318
- table=self.name,
319
- where=where,
320
- orderby=field + ' desc',
321
- qty=1)
392
+ def newest(self, where={}, field="sys_modified", columns="sys_id", lock=None):
393
+ sql, vals = self.sql.select(
394
+ columns=columns,
395
+ table=self.name,
396
+ where=where,
397
+ orderby=field + " desc",
398
+ qty=1,
399
+ lock=lock,
400
+ )
322
401
  return self.tx.execute(sql, vals, cursor=self.cursor).scalar()
323
402
 
324
403
  @return_default(Result())
325
- def select(self,columns=None,where=None,orderby=None,groupby=None,having=None,start=None,qty=None):
326
- sql,vals = self.sql.select(columns=columns,
327
- table=self.name,
328
- where=where,
329
- orderby=orderby,
330
- groupby=groupby,
331
- having=having,
332
- start=start,
333
- qty=qty,
334
- tbl=self)
404
+ def select(
405
+ self,
406
+ columns=None,
407
+ where=None,
408
+ orderby=None,
409
+ groupby=None,
410
+ having=None,
411
+ start=None,
412
+ qty=None,
413
+ lock=None,
414
+ skip_locked=None,
415
+ ):
416
+ sql, vals = self.sql.select(
417
+ columns=columns,
418
+ table=self.name,
419
+ where=where,
420
+ orderby=orderby,
421
+ groupby=groupby,
422
+ having=having,
423
+ start=start,
424
+ qty=qty,
425
+ tbl=self,
426
+ lock=lock,
427
+ skip_locked=skip_locked,
428
+ )
335
429
  return self.tx.execute(sql, vals)
336
430
 
337
431
  def list(self, *args, **kwds):
338
432
  return self.select(*args, **kwds).all()
339
433
 
340
- def selectSQL(self,columns=None,where=None,orderby=None,groupby=None,having=None,start=None,qty=None):
341
- return self.sql.select(columns=columns,
342
- table=self.name,
343
- where=where,
344
- orderby=orderby,
345
- groupby=groupby,
346
- having=having,
347
- start=start,
348
- qty=qty,
349
- tbl=self)
350
-
351
- def query(self,columns=None,where=None,orderby=None,groupby=None,having=None,start=None,qty=None):
352
- sql,vals = self.sql.select(columns=columns,
353
- table=self.name,
354
- where=where,
355
- orderby=orderby,
356
- groupby=groupby,
357
- having=having,
358
- start=start,
359
- qty=qty,
360
- tbl=self)
434
+ def selectSQL(
435
+ self,
436
+ columns=None,
437
+ where=None,
438
+ orderby=None,
439
+ groupby=None,
440
+ having=None,
441
+ start=None,
442
+ qty=None,
443
+ lock=None,
444
+ skip_locked=None,
445
+ ):
446
+ return self.sql.select(
447
+ columns=columns,
448
+ table=self.name,
449
+ where=where,
450
+ orderby=orderby,
451
+ groupby=groupby,
452
+ having=having,
453
+ start=start,
454
+ qty=qty,
455
+ tbl=self,
456
+ lock=lock,
457
+ skip_locked=skip_locked,
458
+ )
459
+
460
+ def query(
461
+ self,
462
+ columns=None,
463
+ where=None,
464
+ orderby=None,
465
+ groupby=None,
466
+ having=None,
467
+ start=None,
468
+ qty=None,
469
+ lock=None,
470
+ skip_locked=None,
471
+ ):
472
+ sql, vals = self.sql.select(
473
+ columns=columns,
474
+ table=self.name,
475
+ where=where,
476
+ orderby=orderby,
477
+ groupby=groupby,
478
+ having=having,
479
+ start=start,
480
+ qty=qty,
481
+ tbl=self,
482
+ lock=lock,
483
+ skip_locked=skip_locked,
484
+ )
361
485
  if vals:
362
- raise Exception("a query generator does not support dictionary type as where clause")
486
+ raise Exception(
487
+ "a query generator does not support dictionary type as where clause"
488
+ )
363
489
  return Query(sql)
364
490
 
365
491
  @return_default(Result())
366
- def server_select(self,columns=None,where=None,orderby=None,groupby=None,having=None,start=None,qty=None):
367
- sql,vals = self.sql.select(columns=columns,
368
- table=self.name,
369
- where=where,
370
- orderby=orderby,
371
- groupby=groupby,
372
- having=having,
373
- start=start,
374
- qty=qty,
375
- tbl=self)
492
+ def server_select(
493
+ self,
494
+ columns=None,
495
+ where=None,
496
+ orderby=None,
497
+ groupby=None,
498
+ having=None,
499
+ start=None,
500
+ qty=None,
501
+ lock=None,
502
+ skip_locked=None,
503
+ ):
504
+ sql, vals = self.sql.select(
505
+ columns=columns,
506
+ table=self.name,
507
+ where=where,
508
+ orderby=orderby,
509
+ groupby=groupby,
510
+ having=having,
511
+ start=start,
512
+ qty=qty,
513
+ tbl=self,
514
+ lock=lock,
515
+ skip_locked=skip_locked,
516
+ )
376
517
  return self.tx.server_execute(sql, vals)
377
518
 
378
519
  @return_default(Result())
379
520
  def batch(self, size=100, *args, **kwds):
380
521
  current = 0
381
522
  while True:
382
- kwds['start'] = current
383
- kwds['qty'] = size
523
+ kwds["start"] = current
524
+ kwds["qty"] = size
384
525
  results = self.select(*args, **kwds).all()
385
526
  if results:
386
527
  yield results
@@ -388,21 +529,23 @@ class Table(object):
388
529
  else:
389
530
  raise StopIteration
390
531
 
391
-
392
-
393
532
  def get_value(self, key, pk):
394
533
  return self.select(columns=key, where=pk).scalar()
395
534
 
396
535
  @return_default({})
397
- def get_row(self, where):
536
+ def get_row(self, where, lock=None):
398
537
  if not where:
399
538
  raise Exception("Unique key for the row to be retrieved is required.")
400
- sql, vals = self.sql.select(columns='*', table=self.name, where=where)
539
+ sql, vals = self.sql.select(
540
+ columns="*", table=self.name, where=where, lock=lock
541
+ )
401
542
  return self.tx.execute(sql, vals, cursor=self.cursor).one()
402
543
 
403
544
  def delete(self, where):
404
545
  if not where:
405
- raise Exception("You just tried to delete an entire table. Use truncate instead.")
546
+ raise Exception(
547
+ "You just tried to delete an entire table. Use truncate instead."
548
+ )
406
549
  sql, vals = self.sql.delete(table=self.name, where=where)
407
550
  result = self.tx.execute(sql, vals)
408
551
  return result.cursor.rowcount
@@ -411,27 +554,31 @@ class Table(object):
411
554
  sql, vals = self.sql.truncate(table=self.name)
412
555
  self.tx.execute(sql, vals)
413
556
 
414
- def has_duplicates(self, columns=['sys_id'], key='sys_id'):
415
- sql, vals = self.sql.find_duplicates(self.name, columns, key)
416
- return bool([x for x in self.tx.execute(sql, vals)])
417
-
418
- def find_duplicates(self, columns=['sys_id'], key='sys_id'):
419
- sql, vals = self.sql.find_duplicates(self.name, columns, key)
420
- return [x for x in self.tx.execute(sql, vals)]
557
+ def duplicate_rows(self, columns=["sys_id"], where={}, group=False):
558
+ sql, vals = self.sql.duplicate_rows(self.name, columns, where)
559
+ for result in self.tx.execute(sql, vals):
560
+ result.update(where)
561
+ if group:
562
+ yield self.tx.table(self.name).select(where=result).all()
563
+ else:
564
+ for row in self.tx.table(self.name).select(where=result):
565
+ yield row
421
566
 
422
- def delete_duplicates(self, columns=['sys_id'], key='sys_id'):
423
- sql, vals = self.sql.delete_duplicates(self.name, columns, key='sys_id')
424
- self.tx.execute(sql, vals)
567
+ def has_duplicates(self, columns=["sys_id"]):
568
+ sql, vals = self.sql.duplicate_rows(self.name, columns)
569
+ return bool([x for x in self.tx.execute(sql, vals)])
425
570
 
426
571
  def create_view(self, name, query, temp=False, silent=True):
427
- sql, vals = self.sql.create_view(name=name,query=query,temp=temp,silent=silent)
572
+ sql, vals = self.sql.create_view(
573
+ name=name, query=query, temp=temp, silent=silent
574
+ )
428
575
  return self.tx.execute(sql, vals)
429
576
 
430
577
  def drop_view(self, name, silent=True):
431
- sql, vals = self.sql.drop_view(name=name,silent=silent)
578
+ sql, vals = self.sql.drop_view(name=name, silent=silent)
432
579
  return self.tx.execute(sql, vals)
433
580
 
434
- def alter_trigger(self, name='USER', state='ENABLE'):
581
+ def alter_trigger(self, name="USER", state="ENABLE"):
435
582
  sql, vals = self.sql.alter_trigger(table=self.name, state=state, name=name)
436
583
  return self.tx.execute(sql, vals)
437
584
 
@@ -442,13 +589,15 @@ class Table(object):
442
589
  def set_sequence(self, next_value=1000):
443
590
  sql, vals = self.sql.set_sequence(table=self.name, next_value=next_value)
444
591
  return self.tx.execute(sql, vals).scalar()
445
-
592
+
446
593
  def get_sequence(self):
447
594
  sql, vals = self.sql.current_id(table=self.name)
448
595
  return self.tx.execute(sql, vals).scalar()
449
-
450
- def missing(self, list, column='sys_id', where=None):
451
- sql, vals = self.sql.missing(table=self.name, list=list, column=column, where=where)
596
+
597
+ def missing(self, list, column="sys_id", where=None):
598
+ sql, vals = self.sql.missing(
599
+ table=self.name, list=list, column=column, where=where
600
+ )
452
601
  return self.tx.execute(sql, vals).as_simple_list().all()
453
602
 
454
603
  def lock(self, mode="ACCESS EXCLUSIVE", wait_for_lock=None):
@@ -470,4 +619,4 @@ class Table(object):
470
619
  sql, vals = self.sql.select(
471
620
  columns="min({})".format(column), table=self.name, where=where
472
621
  )
473
- return self.tx.execute(sql, vals, cursor=self.cursor).scalar()
622
+ return self.tx.execute(sql, vals, cursor=self.cursor).scalar()