sql-blocks 0.1.3__py3-none-any.whl → 0.2.0__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.
- sql_blocks/sql_blocks.py +58 -26
- {sql_blocks-0.1.3.dist-info → sql_blocks-0.2.0.dist-info}/METADATA +32 -13
- sql_blocks-0.2.0.dist-info/RECORD +7 -0
- sql_blocks-0.1.3.dist-info/RECORD +0 -7
- {sql_blocks-0.1.3.dist-info → sql_blocks-0.2.0.dist-info}/LICENSE +0 -0
- {sql_blocks-0.1.3.dist-info → sql_blocks-0.2.0.dist-info}/WHEEL +0 -0
- {sql_blocks-0.1.3.dist-info → sql_blocks-0.2.0.dist-info}/top_level.txt +0 -0
sql_blocks/sql_blocks.py
CHANGED
@@ -74,14 +74,18 @@ class SQLObject:
|
|
74
74
|
if symmetrical:
|
75
75
|
fld = fld.lower()
|
76
76
|
return fld.strip()
|
77
|
-
|
77
|
+
def is_named_field(fld: str) -> bool:
|
78
|
+
return key == SELECT and re.search(' as | AS ', fld)
|
78
79
|
pattern = KEYWORD[key][2]
|
79
80
|
if key == WHERE and symmetrical:
|
80
81
|
pattern = f'{PATTERN_PREFIX}| '
|
81
82
|
separator = self.get_separator(key)
|
82
83
|
def field_set(source: list) -> set:
|
83
84
|
return set(
|
84
|
-
|
85
|
+
(
|
86
|
+
fld if is_named_field(fld) else
|
87
|
+
re.sub(pattern, '', cleanup(fld))
|
88
|
+
)
|
85
89
|
for string in source
|
86
90
|
for fld in re.split(separator, string)
|
87
91
|
)
|
@@ -172,17 +176,27 @@ class ExpressionField:
|
|
172
176
|
a=main.alias, f=name, t=main.table_name
|
173
177
|
)
|
174
178
|
|
179
|
+
class FieldList:
|
180
|
+
separator = ','
|
175
181
|
|
176
|
-
|
177
|
-
def __init__(self, fields: list=[]):
|
182
|
+
def __init__(self, fields: list=[], class_types = [Field]):
|
178
183
|
if isinstance(fields, str):
|
179
|
-
fields = [
|
184
|
+
fields = [
|
185
|
+
f.strip() for f in fields.split(self.separator)
|
186
|
+
]
|
180
187
|
self.fields = fields
|
188
|
+
self.class_types = class_types
|
181
189
|
|
182
190
|
def add(self, name: str, main: SQLObject):
|
183
|
-
main.set_table(name)
|
184
191
|
for field in self.fields:
|
185
|
-
|
192
|
+
for class_type in self.class_types:
|
193
|
+
class_type.add(field, main)
|
194
|
+
|
195
|
+
|
196
|
+
class Table(FieldList):
|
197
|
+
def add(self, name: str, main: SQLObject):
|
198
|
+
main.set_table(name)
|
199
|
+
super().add(name, main)
|
186
200
|
|
187
201
|
|
188
202
|
class PrimaryKey:
|
@@ -257,7 +271,7 @@ class Where:
|
|
257
271
|
return cls('IS NULL')
|
258
272
|
|
259
273
|
@classmethod
|
260
|
-
def
|
274
|
+
def contains(cls, values):
|
261
275
|
if isinstance(values, list):
|
262
276
|
values = ','.join(quoted(v) for v in values)
|
263
277
|
return cls(f'IN ({values})')
|
@@ -268,6 +282,12 @@ class Where:
|
|
268
282
|
))
|
269
283
|
|
270
284
|
|
285
|
+
eq, like, gt, gte, lt, lte, is_null, contains = (
|
286
|
+
getattr(Where, method) for method in
|
287
|
+
('eq', 'like', 'gt', 'gte', 'lt', 'lte', 'is_null', 'contains')
|
288
|
+
)
|
289
|
+
|
290
|
+
|
271
291
|
class Not(Where):
|
272
292
|
prefix = 'NOT '
|
273
293
|
|
@@ -342,9 +362,12 @@ class OrderBy:
|
|
342
362
|
sort: SortType = SortType.ASC
|
343
363
|
@classmethod
|
344
364
|
def add(cls, name: str, main: SQLObject):
|
345
|
-
|
365
|
+
found = re.findall(r'^_\d', name)
|
366
|
+
if found:
|
367
|
+
name = found[0].replace('_', '')
|
368
|
+
elif main.alias:
|
346
369
|
name = f'{main.alias}.{name}'
|
347
|
-
main.values.setdefault(ORDER_BY, []).append(name
|
370
|
+
main.values.setdefault(ORDER_BY, []).append(name+cls.sort.value)
|
348
371
|
|
349
372
|
|
350
373
|
class GroupBy:
|
@@ -410,17 +433,16 @@ class Select(SQLObject):
|
|
410
433
|
self.values.setdefault(key, []).append(value)
|
411
434
|
|
412
435
|
def add(self, name: str, main: SQLObject):
|
413
|
-
|
436
|
+
old_tables = main.values.get(FROM, [])
|
437
|
+
new_tables = set([
|
414
438
|
'{jt}JOIN {tb} {a2} ON ({a1}.{f1} = {a2}.{f2})'.format(
|
415
439
|
jt=self.join_type.value,
|
416
440
|
tb=self.table_name,
|
417
441
|
a1=main.alias, f1=name,
|
418
442
|
a2=self.alias, f2=self.key_field
|
419
443
|
)
|
420
|
-
]
|
421
|
-
|
422
|
-
new_tables += self.values[FROM][1:]
|
423
|
-
main.values.setdefault(FROM, []).extend(new_tables)
|
444
|
+
] + old_tables[1:])
|
445
|
+
main.values[FROM] = old_tables[:1] + list(new_tables)
|
424
446
|
for key in USUAL_KEYS:
|
425
447
|
main.update_values(key, self.values.get(key, []))
|
426
448
|
|
@@ -553,13 +575,22 @@ class Select(SQLObject):
|
|
553
575
|
for rule in rules:
|
554
576
|
rule.apply(self)
|
555
577
|
|
578
|
+
def add_fields(self, fields: list, order_by: bool=False, group_by:bool=False):
|
579
|
+
class_types = [Field]
|
580
|
+
if order_by:
|
581
|
+
class_types += [OrderBy]
|
582
|
+
if group_by:
|
583
|
+
class_types += [GroupBy]
|
584
|
+
FieldList(fields, class_types).add('', self)
|
585
|
+
|
586
|
+
|
556
587
|
|
557
588
|
class SelectIN(Select):
|
558
589
|
condition_class = Where
|
559
590
|
|
560
591
|
def add(self, name: str, main: SQLObject):
|
561
592
|
self.break_lines = False
|
562
|
-
self.condition_class.
|
593
|
+
self.condition_class.contains(self).add(name, main)
|
563
594
|
|
564
595
|
SubSelect = SelectIN
|
565
596
|
|
@@ -574,6 +605,7 @@ class RulePutLimit(Rule):
|
|
574
605
|
if need_limit:
|
575
606
|
target.limit()
|
576
607
|
|
608
|
+
|
577
609
|
class RuleSelectIN(Rule):
|
578
610
|
@classmethod
|
579
611
|
def apply(cls, target: Select):
|
@@ -588,6 +620,7 @@ class RuleSelectIN(Rule):
|
|
588
620
|
','.join(t.split('=')[-1].strip() for t in tokens)
|
589
621
|
)
|
590
622
|
|
623
|
+
|
591
624
|
class RuleAutoField(Rule):
|
592
625
|
@classmethod
|
593
626
|
def apply(cls, target: Select):
|
@@ -599,13 +632,11 @@ class RuleAutoField(Rule):
|
|
599
632
|
s2 = set(target.values[ORDER_BY])
|
600
633
|
target.values.setdefault(SELECT, []).extend( list(s2-s1) )
|
601
634
|
|
635
|
+
|
602
636
|
class RuleLogicalOp(Rule):
|
603
|
-
REVERSE = {
|
604
|
-
|
605
|
-
|
606
|
-
"<>": "=",
|
607
|
-
"=": "<>"
|
608
|
-
}
|
637
|
+
REVERSE = {">=": "<", "<=": ">", "=": "<>"}
|
638
|
+
REVERSE |= {v: k for k, v in REVERSE.items()}
|
639
|
+
|
609
640
|
@classmethod
|
610
641
|
def apply(cls, target: Select):
|
611
642
|
REGEX = re.compile('({})'.format(
|
@@ -613,13 +644,14 @@ class RuleLogicalOp(Rule):
|
|
613
644
|
))
|
614
645
|
for i, condition in enumerate(target.values.get(WHERE, [])):
|
615
646
|
expr = re.sub('\n|\t', ' ', condition)
|
616
|
-
|
617
|
-
if len(tokens) < 2 or not REGEX.findall(tokens[-1]):
|
647
|
+
if not re.search(r'\b(NOT|not)\b', expr):
|
618
648
|
continue
|
619
|
-
tokens =
|
620
|
-
|
649
|
+
tokens = [t.strip() for t in re.split(r'NOT\b|not\b|(<|>|=)', expr) if t]
|
650
|
+
op = ''.join(tokens[1: len(tokens)-1])
|
651
|
+
tokens = [tokens[0], cls.REVERSE[op], tokens[-1]]
|
621
652
|
target.values[WHERE][i] = ' '.join(tokens)
|
622
653
|
|
654
|
+
|
623
655
|
class RuleDateFuncReplace(Rule):
|
624
656
|
"""
|
625
657
|
SQL algorithm by Ralff Matias
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: Allows you to create objects for parts of SQL query commands. Also to combine these objects by joining them, adding or removing parts...
|
5
5
|
Home-page: https://github.com/julio-cascalles/sql_blocks
|
6
6
|
Author: Júlio Cascalles
|
@@ -55,12 +55,12 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
55
55
|
---
|
56
56
|
|
57
57
|
### 3 - To set conditions, use **Where**:
|
58
|
-
* For example, `a = Select(... age=
|
58
|
+
* For example, `a = Select(... age=gt(45) )`
|
59
59
|
|
60
60
|
Some possible conditions:
|
61
|
-
* field=
|
62
|
-
* field=
|
63
|
-
* field=
|
61
|
+
* field=eq(value) - ...the field is EQUAL to the value;
|
62
|
+
* field=gt(value) - ...the field is GREATER than the value;
|
63
|
+
* field=lt(value) - ...the field is LESS than the value;
|
64
64
|
|
65
65
|
3.1 -- If you want to filter the field on a range of values:
|
66
66
|
|
@@ -71,7 +71,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
71
71
|
query = Select('Movie m', title=Field,
|
72
72
|
id=SelectIN(
|
73
73
|
'Review r',
|
74
|
-
rate=
|
74
|
+
rate=gt(4.5),
|
75
75
|
movie_id=Distinct
|
76
76
|
)
|
77
77
|
)
|
@@ -92,8 +92,8 @@ query = Select('Movie m', title=Field,
|
|
92
92
|
3.3 -- Optional conditions:
|
93
93
|
```
|
94
94
|
OR=Options(
|
95
|
-
genre=
|
96
|
-
awards=
|
95
|
+
genre=eq("Sci-Fi"),
|
96
|
+
awards=like("Oscar")
|
97
97
|
)
|
98
98
|
```
|
99
99
|
> Could be AND=Options(...)
|
@@ -105,7 +105,7 @@ based_on_book=Not.is_null()
|
|
105
105
|
|
106
106
|
3.5 -- List of values
|
107
107
|
```
|
108
|
-
hash_tag=
|
108
|
+
hash_tag=contains(['space', 'monster', 'gore'])
|
109
109
|
```
|
110
110
|
|
111
111
|
---
|
@@ -117,7 +117,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
117
117
|
```
|
118
118
|
SelectIN(
|
119
119
|
'Review r', movie=[GroupBy, Distinct],
|
120
|
-
rate=Having.avg(
|
120
|
+
rate=Having.avg(gt(4.5))
|
121
121
|
)
|
122
122
|
```
|
123
123
|
---
|
@@ -299,7 +299,7 @@ m = Select...
|
|
299
299
|
```
|
300
300
|
best_movies = SelectIN(
|
301
301
|
Review=Table('role'),
|
302
|
-
rate=[GroupBy, Having.avg(
|
302
|
+
rate=[GroupBy, Having.avg(gt(4.5))]
|
303
303
|
)
|
304
304
|
m1 = Select(
|
305
305
|
Movie=Table('title,release_date'),
|
@@ -320,9 +320,9 @@ m2 = Select(
|
|
320
320
|
Select(
|
321
321
|
'Product',
|
322
322
|
label=Case('price').when(
|
323
|
-
|
323
|
+
lt(50), 'cheap'
|
324
324
|
).when(
|
325
|
-
|
325
|
+
gt(100), 'expensive'
|
326
326
|
).else_value(
|
327
327
|
'normal'
|
328
328
|
)
|
@@ -360,3 +360,22 @@ m2 = Select(
|
|
360
360
|
* Replace `YEAR` function with date range comparison.
|
361
361
|
|
362
362
|
> The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
|
363
|
+
|
364
|
+
---
|
365
|
+
|
366
|
+
### 12 - Adding multiple fields at once
|
367
|
+
```
|
368
|
+
query = Select('post p')
|
369
|
+
query.add_fields(
|
370
|
+
'user_id, created_at',
|
371
|
+
order_by=True, group_by=True
|
372
|
+
)
|
373
|
+
```
|
374
|
+
...is the same as...
|
375
|
+
```
|
376
|
+
query = Select(
|
377
|
+
'post p',
|
378
|
+
user_id=[Field, GroupBy, OrderBy],
|
379
|
+
created_at=[Field, GroupBy, OrderBy]
|
380
|
+
)
|
381
|
+
```
|
@@ -0,0 +1,7 @@
|
|
1
|
+
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
+
sql_blocks/sql_blocks.py,sha256=Fg7yPeASygx7c7Nct5yKurxRrnjVGsvp4RnR93Wwems,21519
|
3
|
+
sql_blocks-0.2.0.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-0.2.0.dist-info/METADATA,sha256=CU4oSLqoycnvhkPROx_SSbj9zJjCwwz4JoePIgk4wSE,8794
|
5
|
+
sql_blocks-0.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-0.2.0.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-0.2.0.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=RDOzn5zfqyYnzSI82hlglwOzTxzFIpmo3pEJHDGidAw,20506
|
3
|
-
sql_blocks-0.1.3.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-0.1.3.dist-info/METADATA,sha256=HmO5q0vmHuReqiAl0lO1__hvmq8vOtYH7HPJQWOqQxo,8503
|
5
|
-
sql_blocks-0.1.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-0.1.3.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-0.1.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|