python-sql 1.5.0__py3-none-any.whl → 1.5.2__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.
- {python_sql-1.5.0.dist-info → python_sql-1.5.2.dist-info}/METADATA +1 -1
- python_sql-1.5.2.dist-info/RECORD +41 -0
- {python_sql-1.5.0.dist-info → python_sql-1.5.2.dist-info}/WHEEL +1 -1
- sql/__init__.py +199 -104
- sql/aggregate.py +21 -6
- sql/functions.py +9 -8
- sql/operators.py +6 -3
- sql/tests/test_aggregate.py +25 -1
- sql/tests/test_alias.py +15 -0
- sql/tests/test_collate.py +0 -5
- sql/tests/test_combining_query.py +5 -1
- sql/tests/test_delete.py +21 -1
- sql/tests/test_excluded.py +15 -0
- sql/tests/test_expression.py +17 -0
- sql/tests/test_flavor.py +46 -0
- sql/tests/test_for.py +4 -0
- sql/tests/test_from.py +25 -0
- sql/tests/test_from_item.py +46 -0
- sql/tests/test_functions.py +17 -1
- sql/tests/test_grouping.py +13 -0
- sql/tests/test_insert.py +57 -1
- sql/tests/test_join.py +23 -0
- sql/tests/test_lateral.py +1 -1
- sql/tests/test_merge.py +43 -5
- sql/tests/test_operators.py +20 -1
- sql/tests/test_order.py +16 -7
- sql/tests/test_rollup.py +13 -0
- sql/tests/test_select.py +66 -10
- sql/tests/test_update.py +8 -0
- sql/tests/test_window.py +30 -6
- sql/tests/test_with.py +4 -0
- python_sql-1.5.0.dist-info/RECORD +0 -34
- {python_sql-1.5.0.dist-info → python_sql-1.5.2.dist-info}/top_level.txt +0 -0
sql/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ from collections import defaultdict
|
|
|
7
7
|
from itertools import chain
|
|
8
8
|
from threading import current_thread, local
|
|
9
9
|
|
|
10
|
-
__version__ = '1.5.
|
|
10
|
+
__version__ = '1.5.2'
|
|
11
11
|
__all__ = [
|
|
12
12
|
'Flavor', 'Table', 'Values', 'Literal', 'Column', 'Grouping', 'Conflict',
|
|
13
13
|
'Matched', 'MatchedUpdate', 'MatchedDelete',
|
|
@@ -62,17 +62,23 @@ class Flavor(object):
|
|
|
62
62
|
def __init__(self, limitstyle='limit', max_limit=None, paramstyle='format',
|
|
63
63
|
ilike=False, no_as=False, no_boolean=False, null_ordering=True,
|
|
64
64
|
function_mapping=None, filter_=False, escape_empty=False):
|
|
65
|
-
|
|
65
|
+
if limitstyle not in {'fetch', 'limit', 'rownum'}:
|
|
66
|
+
raise ValueError("unsupported limitstyle: %r" % limitstyle)
|
|
66
67
|
self.limitstyle = limitstyle
|
|
68
|
+
if (max_limit is not None
|
|
69
|
+
and not isinstance(max_limit, numbers.Integral)):
|
|
70
|
+
raise ValueError("unsupported max_limit: %r" % max_limit)
|
|
67
71
|
self.max_limit = max_limit
|
|
72
|
+
if paramstyle not in {'format', 'qmark'}:
|
|
73
|
+
raise ValueError("unsupported paramstyle: %r" % paramstyle)
|
|
68
74
|
self.paramstyle = paramstyle
|
|
69
|
-
self.ilike = ilike
|
|
70
|
-
self.no_as = no_as
|
|
71
|
-
self.no_boolean = no_boolean
|
|
72
|
-
self.null_ordering = null_ordering
|
|
73
|
-
self.function_mapping = function_mapping or {}
|
|
74
|
-
self.filter_ = filter_
|
|
75
|
-
self.escape_empty = escape_empty
|
|
75
|
+
self.ilike = bool(ilike)
|
|
76
|
+
self.no_as = bool(no_as)
|
|
77
|
+
self.no_boolean = bool(no_boolean)
|
|
78
|
+
self.null_ordering = bool(null_ordering)
|
|
79
|
+
self.function_mapping = dict(function_mapping or {})
|
|
80
|
+
self.filter_ = bool(filter_)
|
|
81
|
+
self.escape_empty = bool(escape_empty)
|
|
76
82
|
|
|
77
83
|
@property
|
|
78
84
|
def param(self):
|
|
@@ -213,7 +219,8 @@ class WithQuery(Query):
|
|
|
213
219
|
if value is not None:
|
|
214
220
|
if isinstance(value, With):
|
|
215
221
|
value = [value]
|
|
216
|
-
|
|
222
|
+
if any(not isinstance(w, With) for w in value):
|
|
223
|
+
raise ValueError("invalid with: %r" % value)
|
|
217
224
|
self._with = value
|
|
218
225
|
|
|
219
226
|
def _with_str(self):
|
|
@@ -252,7 +259,8 @@ class FromItem(object):
|
|
|
252
259
|
return Column(self, name)
|
|
253
260
|
|
|
254
261
|
def __add__(self, other):
|
|
255
|
-
|
|
262
|
+
if not isinstance(other, FromItem):
|
|
263
|
+
return NotImplemented
|
|
256
264
|
return From((self, other))
|
|
257
265
|
|
|
258
266
|
def select(self, *args, **kwargs):
|
|
@@ -348,7 +356,8 @@ class SelectQuery(WithQuery):
|
|
|
348
356
|
if value is not None:
|
|
349
357
|
if isinstance(value, Expression):
|
|
350
358
|
value = [value]
|
|
351
|
-
|
|
359
|
+
if any(not isinstance(col, Expression) for col in value):
|
|
360
|
+
raise ValueError("invalid order by: %r" % value)
|
|
352
361
|
self._order_by = value
|
|
353
362
|
|
|
354
363
|
@property
|
|
@@ -365,7 +374,8 @@ class SelectQuery(WithQuery):
|
|
|
365
374
|
@limit.setter
|
|
366
375
|
def limit(self, value):
|
|
367
376
|
if value is not None:
|
|
368
|
-
|
|
377
|
+
if not isinstance(value, numbers.Integral):
|
|
378
|
+
raise ValueError("invalid limit: %r" % value)
|
|
369
379
|
self._limit = value
|
|
370
380
|
|
|
371
381
|
@property
|
|
@@ -375,18 +385,20 @@ class SelectQuery(WithQuery):
|
|
|
375
385
|
@offset.setter
|
|
376
386
|
def offset(self, value):
|
|
377
387
|
if value is not None:
|
|
378
|
-
|
|
388
|
+
if not isinstance(value, numbers.Integral):
|
|
389
|
+
raise ValueError("invalid offset: %r" % value)
|
|
379
390
|
self._offset = value
|
|
380
391
|
|
|
381
392
|
@property
|
|
382
393
|
def _limit_offset_str(self):
|
|
394
|
+
param = Flavor.get().param
|
|
383
395
|
if Flavor.get().limitstyle == 'limit':
|
|
384
396
|
offset = ''
|
|
385
397
|
if self.offset:
|
|
386
|
-
offset = ' OFFSET %s' %
|
|
398
|
+
offset = ' OFFSET %s' % param
|
|
387
399
|
limit = ''
|
|
388
400
|
if self.limit is not None:
|
|
389
|
-
limit = ' LIMIT %s' %
|
|
401
|
+
limit = ' LIMIT %s' % param
|
|
390
402
|
elif self.offset:
|
|
391
403
|
max_limit = Flavor.get().max_limit
|
|
392
404
|
if max_limit:
|
|
@@ -395,12 +407,27 @@ class SelectQuery(WithQuery):
|
|
|
395
407
|
else:
|
|
396
408
|
offset = ''
|
|
397
409
|
if self.offset:
|
|
398
|
-
offset = ' OFFSET (%s) ROWS' %
|
|
410
|
+
offset = ' OFFSET (%s) ROWS' % param
|
|
399
411
|
fetch = ''
|
|
400
412
|
if self.limit is not None:
|
|
401
|
-
fetch = ' FETCH FIRST (%s) ROWS ONLY' %
|
|
413
|
+
fetch = ' FETCH FIRST (%s) ROWS ONLY' % param
|
|
402
414
|
return offset + fetch
|
|
403
415
|
|
|
416
|
+
@property
|
|
417
|
+
def _limit_offset_params(self):
|
|
418
|
+
p = []
|
|
419
|
+
if Flavor.get().limitstyle == 'limit':
|
|
420
|
+
if self.limit is not None:
|
|
421
|
+
p.append(self.limit)
|
|
422
|
+
if self.offset:
|
|
423
|
+
p.append(self.offset)
|
|
424
|
+
else:
|
|
425
|
+
if self.offset:
|
|
426
|
+
p.append(self.offset)
|
|
427
|
+
if self.limit is not None:
|
|
428
|
+
p.append(self.limit)
|
|
429
|
+
return tuple(p)
|
|
430
|
+
|
|
404
431
|
def as_(self, output_name):
|
|
405
432
|
return As(self, output_name)
|
|
406
433
|
|
|
@@ -448,7 +475,8 @@ class Select(FromItem, SelectQuery):
|
|
|
448
475
|
if value is not None:
|
|
449
476
|
if isinstance(value, Expression):
|
|
450
477
|
value = [value]
|
|
451
|
-
|
|
478
|
+
if any(not isinstance(col, Expression) for col in value):
|
|
479
|
+
raise ValueError("invalid distinct on: %r" % value)
|
|
452
480
|
self._distinct_on = value
|
|
453
481
|
|
|
454
482
|
@property
|
|
@@ -457,7 +485,10 @@ class Select(FromItem, SelectQuery):
|
|
|
457
485
|
|
|
458
486
|
@columns.setter
|
|
459
487
|
def columns(self, value):
|
|
460
|
-
|
|
488
|
+
if any(
|
|
489
|
+
not isinstance(col, (Expression, SelectQuery))
|
|
490
|
+
for col in value):
|
|
491
|
+
raise ValueError("invalid columns: %r" % value)
|
|
461
492
|
self._columns = tuple(value)
|
|
462
493
|
|
|
463
494
|
@property
|
|
@@ -468,7 +499,8 @@ class Select(FromItem, SelectQuery):
|
|
|
468
499
|
def where(self, value):
|
|
469
500
|
from sql.operators import And, Or
|
|
470
501
|
if value is not None:
|
|
471
|
-
|
|
502
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
503
|
+
raise ValueError("invalid where: %r" % value)
|
|
472
504
|
self._where = value
|
|
473
505
|
|
|
474
506
|
@property
|
|
@@ -480,7 +512,8 @@ class Select(FromItem, SelectQuery):
|
|
|
480
512
|
if value is not None:
|
|
481
513
|
if isinstance(value, Expression):
|
|
482
514
|
value = [value]
|
|
483
|
-
|
|
515
|
+
if any(not isinstance(col, Expression) for col in value):
|
|
516
|
+
raise ValueError("invalid group by: %r" % value)
|
|
484
517
|
self._group_by = value
|
|
485
518
|
|
|
486
519
|
@property
|
|
@@ -491,7 +524,8 @@ class Select(FromItem, SelectQuery):
|
|
|
491
524
|
def having(self, value):
|
|
492
525
|
from sql.operators import And, Or
|
|
493
526
|
if value is not None:
|
|
494
|
-
|
|
527
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
528
|
+
raise ValueError("invalid having: %r" % value)
|
|
495
529
|
self._having = value
|
|
496
530
|
|
|
497
531
|
@property
|
|
@@ -503,7 +537,8 @@ class Select(FromItem, SelectQuery):
|
|
|
503
537
|
if value is not None:
|
|
504
538
|
if isinstance(value, For):
|
|
505
539
|
value = [value]
|
|
506
|
-
|
|
540
|
+
if any(not isinstance(f, For) for f in value):
|
|
541
|
+
raise ValueError("invalid for: %r" % value)
|
|
507
542
|
self._for_ = value
|
|
508
543
|
|
|
509
544
|
@property
|
|
@@ -531,7 +566,8 @@ class Select(FromItem, SelectQuery):
|
|
|
531
566
|
@windows.setter
|
|
532
567
|
def windows(self, value):
|
|
533
568
|
if value is not None:
|
|
534
|
-
|
|
569
|
+
if any(not isinstance(w, Window) for w in value):
|
|
570
|
+
raise ValueError("invalid windows: %r" % value)
|
|
535
571
|
self._windows = value
|
|
536
572
|
|
|
537
573
|
@staticmethod
|
|
@@ -663,6 +699,7 @@ class Select(FromItem, SelectQuery):
|
|
|
663
699
|
p.extend(self.having.params)
|
|
664
700
|
for window in self.windows:
|
|
665
701
|
p.extend(window.params)
|
|
702
|
+
p.extend(self._limit_offset_params)
|
|
666
703
|
return tuple(p)
|
|
667
704
|
|
|
668
705
|
|
|
@@ -690,7 +727,8 @@ class Insert(WithQuery):
|
|
|
690
727
|
|
|
691
728
|
@table.setter
|
|
692
729
|
def table(self, value):
|
|
693
|
-
|
|
730
|
+
if not isinstance(value, Table):
|
|
731
|
+
raise ValueError("invalid table: %r" % value)
|
|
694
732
|
self._table = value
|
|
695
733
|
|
|
696
734
|
@property
|
|
@@ -700,8 +738,10 @@ class Insert(WithQuery):
|
|
|
700
738
|
@columns.setter
|
|
701
739
|
def columns(self, value):
|
|
702
740
|
if value is not None:
|
|
703
|
-
|
|
704
|
-
|
|
741
|
+
if any(
|
|
742
|
+
not isinstance(col, Column) or col.table != self.table
|
|
743
|
+
for col in value):
|
|
744
|
+
raise ValueError("invalid columns: %r" % value)
|
|
705
745
|
self._columns = value
|
|
706
746
|
|
|
707
747
|
@property
|
|
@@ -711,7 +751,8 @@ class Insert(WithQuery):
|
|
|
711
751
|
@values.setter
|
|
712
752
|
def values(self, value):
|
|
713
753
|
if value is not None:
|
|
714
|
-
|
|
754
|
+
if not isinstance(value, (list, Select)):
|
|
755
|
+
raise ValueError("invalid values: %r" % value)
|
|
715
756
|
if isinstance(value, list):
|
|
716
757
|
value = Values(value)
|
|
717
758
|
self._values = value
|
|
@@ -723,8 +764,8 @@ class Insert(WithQuery):
|
|
|
723
764
|
@on_conflict.setter
|
|
724
765
|
def on_conflict(self, value):
|
|
725
766
|
if value is not None:
|
|
726
|
-
|
|
727
|
-
|
|
767
|
+
if not isinstance(value, Conflict) or value.table != self.table:
|
|
768
|
+
raise ValueError("invalid on conflict: %r" % value)
|
|
728
769
|
self._on_conflict = value
|
|
729
770
|
|
|
730
771
|
@property
|
|
@@ -734,7 +775,8 @@ class Insert(WithQuery):
|
|
|
734
775
|
@returning.setter
|
|
735
776
|
def returning(self, value):
|
|
736
777
|
if value is not None:
|
|
737
|
-
|
|
778
|
+
if not isinstance(value, list):
|
|
779
|
+
raise ValueError("invalid returning: %r" % value)
|
|
738
780
|
self._returning = value
|
|
739
781
|
|
|
740
782
|
@staticmethod
|
|
@@ -817,7 +859,8 @@ class Conflict(object):
|
|
|
817
859
|
|
|
818
860
|
@table.setter
|
|
819
861
|
def table(self, value):
|
|
820
|
-
|
|
862
|
+
if not isinstance(value, Table):
|
|
863
|
+
raise ValueError("invalid table: %r" % value)
|
|
821
864
|
self._table = value
|
|
822
865
|
|
|
823
866
|
@property
|
|
@@ -827,8 +870,10 @@ class Conflict(object):
|
|
|
827
870
|
@indexed_columns.setter
|
|
828
871
|
def indexed_columns(self, value):
|
|
829
872
|
if value is not None:
|
|
830
|
-
|
|
831
|
-
|
|
873
|
+
if any(
|
|
874
|
+
not isinstance(col, Column) or col.table != self.table
|
|
875
|
+
for col in value):
|
|
876
|
+
raise ValueError("invalid indexed columns: %r" % value)
|
|
832
877
|
self._indexed_columns = value
|
|
833
878
|
|
|
834
879
|
@property
|
|
@@ -839,7 +884,8 @@ class Conflict(object):
|
|
|
839
884
|
def index_where(self, value):
|
|
840
885
|
from sql.operators import And, Or
|
|
841
886
|
if value is not None:
|
|
842
|
-
|
|
887
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
888
|
+
raise ValueError("invalid index where: %r" % value)
|
|
843
889
|
self._index_where = value
|
|
844
890
|
|
|
845
891
|
@property
|
|
@@ -849,8 +895,10 @@ class Conflict(object):
|
|
|
849
895
|
@columns.setter
|
|
850
896
|
def columns(self, value):
|
|
851
897
|
if value is not None:
|
|
852
|
-
|
|
853
|
-
|
|
898
|
+
if any(
|
|
899
|
+
not isinstance(col, Column) or col.table != self.table
|
|
900
|
+
for col in value):
|
|
901
|
+
raise ValueError("invalid columns: %r" % value)
|
|
854
902
|
self._columns = value
|
|
855
903
|
|
|
856
904
|
@property
|
|
@@ -860,7 +908,8 @@ class Conflict(object):
|
|
|
860
908
|
@values.setter
|
|
861
909
|
def values(self, value):
|
|
862
910
|
if value is not None:
|
|
863
|
-
|
|
911
|
+
if not isinstance(value, (list, Select)):
|
|
912
|
+
raise ValueError("invalid values: %r" % value)
|
|
864
913
|
if isinstance(value, list):
|
|
865
914
|
value = Values([value])
|
|
866
915
|
self._values = value
|
|
@@ -873,7 +922,8 @@ class Conflict(object):
|
|
|
873
922
|
def where(self, value):
|
|
874
923
|
from sql.operators import And, Or
|
|
875
924
|
if value is not None:
|
|
876
|
-
|
|
925
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
926
|
+
raise ValueError("invalid where: %r" % value)
|
|
877
927
|
self._where = value
|
|
878
928
|
|
|
879
929
|
def __str__(self):
|
|
@@ -944,7 +994,8 @@ class Update(Insert):
|
|
|
944
994
|
def values(self, value):
|
|
945
995
|
if isinstance(value, Select):
|
|
946
996
|
value = [value]
|
|
947
|
-
|
|
997
|
+
if not isinstance(value, list):
|
|
998
|
+
raise ValueError("invalid values: %r" % value)
|
|
948
999
|
self._values = value
|
|
949
1000
|
|
|
950
1001
|
@property
|
|
@@ -955,9 +1006,14 @@ class Update(Insert):
|
|
|
955
1006
|
def where(self, value):
|
|
956
1007
|
from sql.operators import And, Or
|
|
957
1008
|
if value is not None:
|
|
958
|
-
|
|
1009
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
1010
|
+
raise ValueError("invalid where: %r" % value)
|
|
959
1011
|
self._where = value
|
|
960
1012
|
|
|
1013
|
+
@staticmethod
|
|
1014
|
+
def _format_column(value):
|
|
1015
|
+
return Select._format_column(value)
|
|
1016
|
+
|
|
961
1017
|
def __str__(self):
|
|
962
1018
|
assert all(col.table == self.table for col in self.columns)
|
|
963
1019
|
# Get columns without alias
|
|
@@ -975,7 +1031,7 @@ class Update(Insert):
|
|
|
975
1031
|
returning = ''
|
|
976
1032
|
if self.returning:
|
|
977
1033
|
returning = ' RETURNING ' + ', '.join(
|
|
978
|
-
map(self.
|
|
1034
|
+
map(self._format_column, self.returning))
|
|
979
1035
|
return (self._with_str()
|
|
980
1036
|
+ 'UPDATE %s AS "%s" SET ' % (self.table, self.table.alias)
|
|
981
1037
|
+ values + from_ + where + returning)
|
|
@@ -1020,7 +1076,8 @@ class Delete(WithQuery):
|
|
|
1020
1076
|
|
|
1021
1077
|
@table.setter
|
|
1022
1078
|
def table(self, value):
|
|
1023
|
-
|
|
1079
|
+
if not isinstance(value, Table):
|
|
1080
|
+
raise ValueError("invalid table: %r" % value)
|
|
1024
1081
|
self._table = value
|
|
1025
1082
|
|
|
1026
1083
|
@property
|
|
@@ -1031,7 +1088,8 @@ class Delete(WithQuery):
|
|
|
1031
1088
|
def where(self, value):
|
|
1032
1089
|
from sql.operators import And, Or
|
|
1033
1090
|
if value is not None:
|
|
1034
|
-
|
|
1091
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
1092
|
+
raise ValueError("invalid where: %r" % value)
|
|
1035
1093
|
self._where = value
|
|
1036
1094
|
|
|
1037
1095
|
@property
|
|
@@ -1041,19 +1099,15 @@ class Delete(WithQuery):
|
|
|
1041
1099
|
@returning.setter
|
|
1042
1100
|
def returning(self, value):
|
|
1043
1101
|
if value is not None:
|
|
1044
|
-
|
|
1102
|
+
if any(
|
|
1103
|
+
not isinstance(col, (Expression, SelectQuery))
|
|
1104
|
+
for col in value):
|
|
1105
|
+
raise ValueError("invalid returning: %r" % value)
|
|
1045
1106
|
self._returning = value
|
|
1046
1107
|
|
|
1047
1108
|
@staticmethod
|
|
1048
|
-
def _format(value
|
|
1049
|
-
|
|
1050
|
-
param = Flavor.get().param
|
|
1051
|
-
if isinstance(value, Expression):
|
|
1052
|
-
return str(value)
|
|
1053
|
-
elif isinstance(value, Select):
|
|
1054
|
-
return '(%s)' % value
|
|
1055
|
-
else:
|
|
1056
|
-
return param
|
|
1109
|
+
def _format(value):
|
|
1110
|
+
return Select._format_column(value)
|
|
1057
1111
|
|
|
1058
1112
|
def __str__(self):
|
|
1059
1113
|
with AliasManager(exclude=[self.table]):
|
|
@@ -1101,7 +1155,8 @@ class Merge(WithQuery):
|
|
|
1101
1155
|
|
|
1102
1156
|
@target.setter
|
|
1103
1157
|
def target(self, value):
|
|
1104
|
-
|
|
1158
|
+
if not isinstance(value, Table):
|
|
1159
|
+
raise ValueError("invalid target: %r" % value)
|
|
1105
1160
|
self._target = value
|
|
1106
1161
|
|
|
1107
1162
|
@property
|
|
@@ -1110,7 +1165,8 @@ class Merge(WithQuery):
|
|
|
1110
1165
|
|
|
1111
1166
|
@source.setter
|
|
1112
1167
|
def source(self, value):
|
|
1113
|
-
|
|
1168
|
+
if not isinstance(value, (Table, SelectQuery, Values)):
|
|
1169
|
+
raise ValueError("invalid source: %r" % value)
|
|
1114
1170
|
self._source = value
|
|
1115
1171
|
|
|
1116
1172
|
@property
|
|
@@ -1119,7 +1175,8 @@ class Merge(WithQuery):
|
|
|
1119
1175
|
|
|
1120
1176
|
@condition.setter
|
|
1121
1177
|
def condition(self, value):
|
|
1122
|
-
|
|
1178
|
+
if not isinstance(value, Expression):
|
|
1179
|
+
raise ValueError("invalid condition: %r" % value)
|
|
1123
1180
|
self._condition = value
|
|
1124
1181
|
|
|
1125
1182
|
@property
|
|
@@ -1128,7 +1185,8 @@ class Merge(WithQuery):
|
|
|
1128
1185
|
|
|
1129
1186
|
@whens.setter
|
|
1130
1187
|
def whens(self, value):
|
|
1131
|
-
|
|
1188
|
+
if any(not isinstance(w, Matched) for w in value):
|
|
1189
|
+
raise ValueError("invalid whens: %r" % value)
|
|
1132
1190
|
self._whens = tuple(value)
|
|
1133
1191
|
|
|
1134
1192
|
def __str__(self):
|
|
@@ -1137,10 +1195,7 @@ class Merge(WithQuery):
|
|
|
1137
1195
|
source = '(%s)' % self.source
|
|
1138
1196
|
else:
|
|
1139
1197
|
source = self.source
|
|
1140
|
-
|
|
1141
|
-
condition = 'ON %s' % self.condition
|
|
1142
|
-
else:
|
|
1143
|
-
condition = ''
|
|
1198
|
+
condition = 'ON %s' % self.condition
|
|
1144
1199
|
return (self._with_str()
|
|
1145
1200
|
+ 'MERGE INTO %s AS "%s" ' % (self.target, self.target.alias)
|
|
1146
1201
|
+ 'USING %s AS "%s" ' % (source, self.source.alias)
|
|
@@ -1174,7 +1229,8 @@ class Matched(object):
|
|
|
1174
1229
|
@condition.setter
|
|
1175
1230
|
def condition(self, value):
|
|
1176
1231
|
if value is not None:
|
|
1177
|
-
|
|
1232
|
+
if not isinstance(value, Expression):
|
|
1233
|
+
raise ValueError("invalid condition: %r" % value)
|
|
1178
1234
|
self._condition = value
|
|
1179
1235
|
|
|
1180
1236
|
def _then_str(self):
|
|
@@ -1211,7 +1267,8 @@ class _MatchedValues(Matched):
|
|
|
1211
1267
|
|
|
1212
1268
|
@columns.setter
|
|
1213
1269
|
def columns(self, value):
|
|
1214
|
-
|
|
1270
|
+
if any(not isinstance(col, Column) for col in value):
|
|
1271
|
+
raise ValueError("invalid columns: %r" % value)
|
|
1215
1272
|
self._columns = value
|
|
1216
1273
|
|
|
1217
1274
|
|
|
@@ -1264,13 +1321,15 @@ class NotMatchedInsert(_MatchedValues, NotMatched):
|
|
|
1264
1321
|
|
|
1265
1322
|
@values.setter
|
|
1266
1323
|
def values(self, value):
|
|
1267
|
-
|
|
1324
|
+
if value is not None:
|
|
1325
|
+
value = Values([value])
|
|
1326
|
+
self._values = value
|
|
1268
1327
|
|
|
1269
1328
|
def _then_str(self):
|
|
1270
1329
|
columns = ', '.join(c.column_name for c in self.columns)
|
|
1271
1330
|
columns = '(' + columns + ')'
|
|
1272
1331
|
if self.values is None:
|
|
1273
|
-
values = ' DEFAULT VALUES
|
|
1332
|
+
values = ' DEFAULT VALUES'
|
|
1274
1333
|
else:
|
|
1275
1334
|
values = ' ' + str(self.values)
|
|
1276
1335
|
return 'INSERT ' + columns + values
|
|
@@ -1278,7 +1337,8 @@ class NotMatchedInsert(_MatchedValues, NotMatched):
|
|
|
1278
1337
|
@property
|
|
1279
1338
|
def params(self):
|
|
1280
1339
|
p = list(super().params)
|
|
1281
|
-
|
|
1340
|
+
if self.values:
|
|
1341
|
+
p.extend(self.values.params)
|
|
1282
1342
|
return tuple(p)
|
|
1283
1343
|
|
|
1284
1344
|
|
|
@@ -1287,7 +1347,8 @@ class CombiningQuery(FromItem, SelectQuery):
|
|
|
1287
1347
|
_operator = ''
|
|
1288
1348
|
|
|
1289
1349
|
def __init__(self, *queries, **kwargs):
|
|
1290
|
-
|
|
1350
|
+
if any(not isinstance(q, Query) for q in queries):
|
|
1351
|
+
raise ValueError("invalid queries: %r" % (queries,))
|
|
1291
1352
|
self.queries = queries
|
|
1292
1353
|
self.all_ = kwargs.pop('all_', False)
|
|
1293
1354
|
super(CombiningQuery, self).__init__(**kwargs)
|
|
@@ -1407,7 +1468,8 @@ class Join(FromItem):
|
|
|
1407
1468
|
|
|
1408
1469
|
@left.setter
|
|
1409
1470
|
def left(self, value):
|
|
1410
|
-
|
|
1471
|
+
if not isinstance(value, FromItem):
|
|
1472
|
+
raise ValueError("invalid left: %r" % value)
|
|
1411
1473
|
self._left = value
|
|
1412
1474
|
|
|
1413
1475
|
@property
|
|
@@ -1416,7 +1478,8 @@ class Join(FromItem):
|
|
|
1416
1478
|
|
|
1417
1479
|
@right.setter
|
|
1418
1480
|
def right(self, value):
|
|
1419
|
-
|
|
1481
|
+
if not isinstance(value, FromItem):
|
|
1482
|
+
raise ValueError("invalid right: %r" % value)
|
|
1420
1483
|
self._right = value
|
|
1421
1484
|
|
|
1422
1485
|
@property
|
|
@@ -1427,7 +1490,8 @@ class Join(FromItem):
|
|
|
1427
1490
|
def condition(self, value):
|
|
1428
1491
|
from sql.operators import And, Or
|
|
1429
1492
|
if value is not None:
|
|
1430
|
-
|
|
1493
|
+
if not isinstance(value, (Expression, And, Or)):
|
|
1494
|
+
raise ValueError("invalid condition: %r" % value)
|
|
1431
1495
|
self._condition = value
|
|
1432
1496
|
|
|
1433
1497
|
@property
|
|
@@ -1437,8 +1501,10 @@ class Join(FromItem):
|
|
|
1437
1501
|
@type_.setter
|
|
1438
1502
|
def type_(self, value):
|
|
1439
1503
|
value = value.upper()
|
|
1440
|
-
|
|
1441
|
-
|
|
1504
|
+
if value not in {
|
|
1505
|
+
'INNER', 'LEFT', 'LEFT OUTER', 'RIGHT', 'RIGHT OUTER', 'FULL',
|
|
1506
|
+
'FULL OUTER', 'CROSS'}:
|
|
1507
|
+
raise ValueError("invalid type: %r" % value)
|
|
1442
1508
|
self._type_ = value
|
|
1443
1509
|
|
|
1444
1510
|
def __str__(self):
|
|
@@ -1454,14 +1520,9 @@ class Join(FromItem):
|
|
|
1454
1520
|
def params(self):
|
|
1455
1521
|
p = []
|
|
1456
1522
|
for item in (self.left, self.right):
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
except AttributeError:
|
|
1460
|
-
pass
|
|
1461
|
-
try:
|
|
1523
|
+
p.extend(item.params)
|
|
1524
|
+
if self.condition:
|
|
1462
1525
|
p.extend(self.condition.params)
|
|
1463
|
-
except AttributeError:
|
|
1464
|
-
pass
|
|
1465
1526
|
return tuple(p)
|
|
1466
1527
|
|
|
1467
1528
|
@property
|
|
@@ -1517,8 +1578,10 @@ class From(list):
|
|
|
1517
1578
|
return tuple(p)
|
|
1518
1579
|
|
|
1519
1580
|
def __add__(self, other):
|
|
1520
|
-
|
|
1521
|
-
|
|
1581
|
+
if not isinstance(other, FromItem):
|
|
1582
|
+
return NotImplemented
|
|
1583
|
+
elif isinstance(other, CombiningQuery):
|
|
1584
|
+
return NotImplemented
|
|
1522
1585
|
return From(super(From, self).__add__([other]))
|
|
1523
1586
|
|
|
1524
1587
|
|
|
@@ -1541,7 +1604,7 @@ class Values(list, Query, FromItem):
|
|
|
1541
1604
|
|
|
1542
1605
|
@property
|
|
1543
1606
|
def params(self):
|
|
1544
|
-
p =
|
|
1607
|
+
p = list(super().params)
|
|
1545
1608
|
for values in self:
|
|
1546
1609
|
for value in values:
|
|
1547
1610
|
if isinstance(value, Expression):
|
|
@@ -1806,21 +1869,35 @@ class Cast(Expression):
|
|
|
1806
1869
|
|
|
1807
1870
|
|
|
1808
1871
|
class Collate(Expression):
|
|
1809
|
-
__slots__ = ('
|
|
1872
|
+
__slots__ = ('_expression', '_collation')
|
|
1810
1873
|
|
|
1811
1874
|
def __init__(self, expression, collation):
|
|
1812
1875
|
super(Collate, self).__init__()
|
|
1813
1876
|
self.expression = expression
|
|
1814
1877
|
self.collation = collation
|
|
1815
1878
|
|
|
1879
|
+
@property
|
|
1880
|
+
def expression(self):
|
|
1881
|
+
return self._expression
|
|
1882
|
+
|
|
1883
|
+
@expression.setter
|
|
1884
|
+
def expression(self, value):
|
|
1885
|
+
self._expression = value
|
|
1886
|
+
|
|
1887
|
+
@property
|
|
1888
|
+
def collation(self):
|
|
1889
|
+
return self._collation
|
|
1890
|
+
|
|
1891
|
+
@collation.setter
|
|
1892
|
+
def collation(self, value):
|
|
1893
|
+
self._collation = value
|
|
1894
|
+
|
|
1816
1895
|
def __str__(self):
|
|
1817
1896
|
if isinstance(self.expression, Expression):
|
|
1818
1897
|
value = self.expression
|
|
1819
1898
|
else:
|
|
1820
1899
|
value = Flavor.get().param
|
|
1821
|
-
|
|
1822
|
-
raise ValueError("Wrong collation %s" % self.collation)
|
|
1823
|
-
return '%s COLLATE "%s"' % (value, self.collation)
|
|
1900
|
+
return '%s COLLATE %s' % (value, _escape_identifier(self.collation))
|
|
1824
1901
|
|
|
1825
1902
|
@property
|
|
1826
1903
|
def params(self):
|
|
@@ -1843,8 +1920,11 @@ class Grouping(Expression):
|
|
|
1843
1920
|
|
|
1844
1921
|
@sets.setter
|
|
1845
1922
|
def sets(self, value):
|
|
1846
|
-
|
|
1847
|
-
|
|
1923
|
+
if any(
|
|
1924
|
+
not isinstance(col, Expression)
|
|
1925
|
+
for cols in value
|
|
1926
|
+
for col in cols):
|
|
1927
|
+
raise ValueError("invalid sets: %r" % value)
|
|
1848
1928
|
self._sets = tuple(tuple(cols) for cols in value)
|
|
1849
1929
|
|
|
1850
1930
|
def __str__(self):
|
|
@@ -1871,10 +1951,11 @@ class Rollup(Expression):
|
|
|
1871
1951
|
|
|
1872
1952
|
@expressions.setter
|
|
1873
1953
|
def expressions(self, value):
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1954
|
+
if not all(
|
|
1955
|
+
isinstance(col, Expression)
|
|
1956
|
+
or all(isinstance(c, Expression) for c in col)
|
|
1957
|
+
for col in value):
|
|
1958
|
+
raise ValueError("invalid expressions: %r" % value)
|
|
1878
1959
|
self._expressions = tuple(value)
|
|
1879
1960
|
|
|
1880
1961
|
def __str__(self):
|
|
@@ -1928,7 +2009,8 @@ class Window(object):
|
|
|
1928
2009
|
|
|
1929
2010
|
@partition.setter
|
|
1930
2011
|
def partition(self, value):
|
|
1931
|
-
|
|
2012
|
+
if any(not isinstance(e, Expression) for e in value):
|
|
2013
|
+
raise ValueError("invalid partition: %r" % value)
|
|
1932
2014
|
self._partition = value
|
|
1933
2015
|
|
|
1934
2016
|
@property
|
|
@@ -1940,7 +2022,8 @@ class Window(object):
|
|
|
1940
2022
|
if value is not None:
|
|
1941
2023
|
if isinstance(value, Expression):
|
|
1942
2024
|
value = [value]
|
|
1943
|
-
|
|
2025
|
+
if any(not isinstance(col, Expression) for col in value):
|
|
2026
|
+
raise ValueError("invalid order by: %r" % value)
|
|
1944
2027
|
self._order_by = value
|
|
1945
2028
|
|
|
1946
2029
|
@property
|
|
@@ -1950,7 +2033,8 @@ class Window(object):
|
|
|
1950
2033
|
@frame.setter
|
|
1951
2034
|
def frame(self, value):
|
|
1952
2035
|
if value:
|
|
1953
|
-
|
|
2036
|
+
if value not in {'RANGE', 'ROWS', 'GROUPS'}:
|
|
2037
|
+
raise ValueError("invalid frame: %r" % value)
|
|
1954
2038
|
self._frame = value
|
|
1955
2039
|
|
|
1956
2040
|
@property
|
|
@@ -1960,7 +2044,8 @@ class Window(object):
|
|
|
1960
2044
|
@start.setter
|
|
1961
2045
|
def start(self, value):
|
|
1962
2046
|
if value:
|
|
1963
|
-
|
|
2047
|
+
if not isinstance(value, numbers.Integral):
|
|
2048
|
+
raise ValueError("invalid start: %r" % value)
|
|
1964
2049
|
self._start = value
|
|
1965
2050
|
|
|
1966
2051
|
@property
|
|
@@ -1970,7 +2055,8 @@ class Window(object):
|
|
|
1970
2055
|
@end.setter
|
|
1971
2056
|
def end(self, value):
|
|
1972
2057
|
if value:
|
|
1973
|
-
|
|
2058
|
+
if not isinstance(value, numbers.Integral):
|
|
2059
|
+
raise ValueError("invalid end: %r" % value)
|
|
1974
2060
|
self._end = value
|
|
1975
2061
|
|
|
1976
2062
|
@property
|
|
@@ -1980,7 +2066,8 @@ class Window(object):
|
|
|
1980
2066
|
@exclude.setter
|
|
1981
2067
|
def exclude(self, value):
|
|
1982
2068
|
if value:
|
|
1983
|
-
|
|
2069
|
+
if value not in {'CURRENT ROW', 'GROUP', 'TIES'}:
|
|
2070
|
+
raise ValueError("invalid exclude: %r" % value)
|
|
1984
2071
|
self._exclude = value
|
|
1985
2072
|
|
|
1986
2073
|
@property
|
|
@@ -1992,6 +2079,7 @@ class Window(object):
|
|
|
1992
2079
|
return AliasManager.contains(self)
|
|
1993
2080
|
|
|
1994
2081
|
def __str__(self):
|
|
2082
|
+
param = Flavor.get().param
|
|
1995
2083
|
partition = ''
|
|
1996
2084
|
if self.partition:
|
|
1997
2085
|
partition = 'PARTITION BY ' + ', '.join(map(str, self.partition))
|
|
@@ -2005,9 +2093,9 @@ class Window(object):
|
|
|
2005
2093
|
elif not frame:
|
|
2006
2094
|
return 'CURRENT ROW'
|
|
2007
2095
|
elif frame < 0:
|
|
2008
|
-
return '%s PRECEDING' %
|
|
2096
|
+
return '%s PRECEDING' % param
|
|
2009
2097
|
elif frame > 0:
|
|
2010
|
-
return '%s FOLLOWING' %
|
|
2098
|
+
return '%s FOLLOWING' % param
|
|
2011
2099
|
|
|
2012
2100
|
frame = ''
|
|
2013
2101
|
if self.frame:
|
|
@@ -2028,6 +2116,11 @@ class Window(object):
|
|
|
2028
2116
|
if self.order_by:
|
|
2029
2117
|
for expression in self.order_by:
|
|
2030
2118
|
p.extend(expression.params)
|
|
2119
|
+
if self.frame:
|
|
2120
|
+
if self.start:
|
|
2121
|
+
p.append(abs(self.start))
|
|
2122
|
+
if self.end:
|
|
2123
|
+
p.append(abs(self.end))
|
|
2031
2124
|
return tuple(p)
|
|
2032
2125
|
|
|
2033
2126
|
|
|
@@ -2047,7 +2140,8 @@ class Order(Expression):
|
|
|
2047
2140
|
|
|
2048
2141
|
@expression.setter
|
|
2049
2142
|
def expression(self, value):
|
|
2050
|
-
|
|
2143
|
+
if not isinstance(value, (Expression, SelectQuery)):
|
|
2144
|
+
raise ValueError("invalid expression: %r" % value)
|
|
2051
2145
|
self._expression = value
|
|
2052
2146
|
|
|
2053
2147
|
def __str__(self):
|
|
@@ -2150,7 +2244,8 @@ class For(object):
|
|
|
2150
2244
|
@type_.setter
|
|
2151
2245
|
def type_(self, value):
|
|
2152
2246
|
value = value.upper()
|
|
2153
|
-
|
|
2247
|
+
if value not in {'UPDATE', 'SHARE'}:
|
|
2248
|
+
raise ValueError("invalid type: %r" % value)
|
|
2154
2249
|
self._type_ = value
|
|
2155
2250
|
|
|
2156
2251
|
def __str__(self):
|