sql-blocks 0.1.4__py3-none-any.whl → 0.2.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.
- sql_blocks/sql_blocks.py +65 -35
- {sql_blocks-0.1.4.dist-info → sql_blocks-0.2.1.dist-info}/METADATA +34 -15
- sql_blocks-0.2.1.dist-info/RECORD +7 -0
- sql_blocks-0.1.4.dist-info/RECORD +0 -7
- {sql_blocks-0.1.4.dist-info → sql_blocks-0.2.1.dist-info}/LICENSE +0 -0
- {sql_blocks-0.1.4.dist-info → sql_blocks-0.2.1.dist-info}/WHEEL +0 -0
- {sql_blocks-0.1.4.dist-info → sql_blocks-0.2.1.dist-info}/top_level.txt +0 -0
sql_blocks/sql_blocks.py
CHANGED
@@ -176,17 +176,27 @@ class ExpressionField:
|
|
176
176
|
a=main.alias, f=name, t=main.table_name
|
177
177
|
)
|
178
178
|
|
179
|
+
class FieldList:
|
180
|
+
separator = ','
|
179
181
|
|
180
|
-
|
181
|
-
def __init__(self, fields: list=[]):
|
182
|
+
def __init__(self, fields: list=[], class_types = [Field]):
|
182
183
|
if isinstance(fields, str):
|
183
|
-
fields = [
|
184
|
+
fields = [
|
185
|
+
f.strip() for f in fields.split(self.separator)
|
186
|
+
]
|
184
187
|
self.fields = fields
|
188
|
+
self.class_types = class_types
|
185
189
|
|
186
190
|
def add(self, name: str, main: SQLObject):
|
187
|
-
main.set_table(name)
|
188
191
|
for field in self.fields:
|
189
|
-
|
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)
|
190
200
|
|
191
201
|
|
192
202
|
class PrimaryKey:
|
@@ -237,7 +247,7 @@ class Where:
|
|
237
247
|
return cls.__constructor('=', value)
|
238
248
|
|
239
249
|
@classmethod
|
240
|
-
def
|
250
|
+
def contains(cls, value: str):
|
241
251
|
return cls(f"LIKE '%{value}%'")
|
242
252
|
|
243
253
|
@classmethod
|
@@ -261,7 +271,7 @@ class Where:
|
|
261
271
|
return cls('IS NULL')
|
262
272
|
|
263
273
|
@classmethod
|
264
|
-
def
|
274
|
+
def inside(cls, values):
|
265
275
|
if isinstance(values, list):
|
266
276
|
values = ','.join(quoted(v) for v in values)
|
267
277
|
return cls(f'IN ({values})')
|
@@ -272,6 +282,12 @@ class Where:
|
|
272
282
|
))
|
273
283
|
|
274
284
|
|
285
|
+
eq, contains, gt, gte, lt, lte, is_null, inside = (
|
286
|
+
getattr(Where, method) for method in
|
287
|
+
('eq', 'contains', 'gt', 'gte', 'lt', 'lte', 'is_null', 'inside')
|
288
|
+
)
|
289
|
+
|
290
|
+
|
275
291
|
class Not(Where):
|
276
292
|
prefix = 'NOT '
|
277
293
|
|
@@ -346,9 +362,12 @@ class OrderBy:
|
|
346
362
|
sort: SortType = SortType.ASC
|
347
363
|
@classmethod
|
348
364
|
def add(cls, name: str, main: SQLObject):
|
349
|
-
|
365
|
+
found = re.findall(r'^_\d', name)
|
366
|
+
if found:
|
367
|
+
name = found[0].replace('_', '')
|
368
|
+
elif main.alias:
|
350
369
|
name = f'{main.alias}.{name}'
|
351
|
-
main.values.setdefault(ORDER_BY, []).append(name
|
370
|
+
main.values.setdefault(ORDER_BY, []).append(name+cls.sort.value)
|
352
371
|
|
353
372
|
|
354
373
|
class GroupBy:
|
@@ -414,38 +433,39 @@ class Select(SQLObject):
|
|
414
433
|
self.values.setdefault(key, []).append(value)
|
415
434
|
|
416
435
|
def add(self, name: str, main: SQLObject):
|
417
|
-
|
436
|
+
old_tables = main.values.get(FROM, [])
|
437
|
+
new_tables = set([
|
418
438
|
'{jt}JOIN {tb} {a2} ON ({a1}.{f1} = {a2}.{f2})'.format(
|
419
439
|
jt=self.join_type.value,
|
420
440
|
tb=self.table_name,
|
421
441
|
a1=main.alias, f1=name,
|
422
442
|
a2=self.alias, f2=self.key_field
|
423
443
|
)
|
424
|
-
]
|
425
|
-
|
426
|
-
new_tables += self.values[FROM][1:]
|
427
|
-
main.values.setdefault(FROM, []).extend(new_tables)
|
444
|
+
] + old_tables[1:])
|
445
|
+
main.values[FROM] = old_tables[:1] + list(new_tables)
|
428
446
|
for key in USUAL_KEYS:
|
429
447
|
main.update_values(key, self.values.get(key, []))
|
430
448
|
|
431
449
|
def __add__(self, other: SQLObject):
|
432
|
-
|
450
|
+
from copy import deepcopy
|
451
|
+
query = deepcopy(self)
|
452
|
+
if query.table_name.lower() == other.table_name.lower():
|
433
453
|
for key in USUAL_KEYS:
|
434
|
-
|
435
|
-
return
|
436
|
-
foreign_field, primary_key = ForeignKey.find(
|
454
|
+
query.update_values(key, other.values.get(key, []))
|
455
|
+
return query
|
456
|
+
foreign_field, primary_key = ForeignKey.find(query, other)
|
437
457
|
if not foreign_field:
|
438
|
-
foreign_field, primary_key = ForeignKey.find(other,
|
458
|
+
foreign_field, primary_key = ForeignKey.find(other, query)
|
439
459
|
if foreign_field:
|
440
460
|
if primary_key:
|
441
|
-
PrimaryKey.add(primary_key,
|
442
|
-
|
461
|
+
PrimaryKey.add(primary_key, query)
|
462
|
+
query.add(foreign_field, other)
|
443
463
|
return other
|
444
|
-
raise ValueError(f'No relationship found between {
|
464
|
+
raise ValueError(f'No relationship found between {query.table_name} and {other.table_name}.')
|
445
465
|
elif primary_key:
|
446
466
|
PrimaryKey.add(primary_key, other)
|
447
|
-
other.add(foreign_field,
|
448
|
-
return
|
467
|
+
other.add(foreign_field, query)
|
468
|
+
return query
|
449
469
|
|
450
470
|
def __str__(self) -> str:
|
451
471
|
TABULATION = '\n\t' if self.break_lines else ' '
|
@@ -557,13 +577,22 @@ class Select(SQLObject):
|
|
557
577
|
for rule in rules:
|
558
578
|
rule.apply(self)
|
559
579
|
|
580
|
+
def add_fields(self, fields: list, order_by: bool=False, group_by:bool=False):
|
581
|
+
class_types = [Field]
|
582
|
+
if order_by:
|
583
|
+
class_types += [OrderBy]
|
584
|
+
if group_by:
|
585
|
+
class_types += [GroupBy]
|
586
|
+
FieldList(fields, class_types).add('', self)
|
587
|
+
|
588
|
+
|
560
589
|
|
561
590
|
class SelectIN(Select):
|
562
591
|
condition_class = Where
|
563
592
|
|
564
593
|
def add(self, name: str, main: SQLObject):
|
565
594
|
self.break_lines = False
|
566
|
-
self.condition_class.
|
595
|
+
self.condition_class.inside(self).add(name, main)
|
567
596
|
|
568
597
|
SubSelect = SelectIN
|
569
598
|
|
@@ -578,6 +607,7 @@ class RulePutLimit(Rule):
|
|
578
607
|
if need_limit:
|
579
608
|
target.limit()
|
580
609
|
|
610
|
+
|
581
611
|
class RuleSelectIN(Rule):
|
582
612
|
@classmethod
|
583
613
|
def apply(cls, target: Select):
|
@@ -592,6 +622,7 @@ class RuleSelectIN(Rule):
|
|
592
622
|
','.join(t.split('=')[-1].strip() for t in tokens)
|
593
623
|
)
|
594
624
|
|
625
|
+
|
595
626
|
class RuleAutoField(Rule):
|
596
627
|
@classmethod
|
597
628
|
def apply(cls, target: Select):
|
@@ -603,13 +634,11 @@ class RuleAutoField(Rule):
|
|
603
634
|
s2 = set(target.values[ORDER_BY])
|
604
635
|
target.values.setdefault(SELECT, []).extend( list(s2-s1) )
|
605
636
|
|
637
|
+
|
606
638
|
class RuleLogicalOp(Rule):
|
607
|
-
REVERSE = {
|
608
|
-
|
609
|
-
|
610
|
-
"<>": "=",
|
611
|
-
"=": "<>"
|
612
|
-
}
|
639
|
+
REVERSE = {">=": "<", "<=": ">", "=": "<>"}
|
640
|
+
REVERSE |= {v: k for k, v in REVERSE.items()}
|
641
|
+
|
613
642
|
@classmethod
|
614
643
|
def apply(cls, target: Select):
|
615
644
|
REGEX = re.compile('({})'.format(
|
@@ -617,13 +646,14 @@ class RuleLogicalOp(Rule):
|
|
617
646
|
))
|
618
647
|
for i, condition in enumerate(target.values.get(WHERE, [])):
|
619
648
|
expr = re.sub('\n|\t', ' ', condition)
|
620
|
-
|
621
|
-
if len(tokens) < 2 or not REGEX.findall(tokens[-1]):
|
649
|
+
if not re.search(r'\b(NOT|not)\b', expr):
|
622
650
|
continue
|
623
|
-
tokens =
|
624
|
-
|
651
|
+
tokens = [t.strip() for t in re.split(r'NOT\b|not\b|(<|>|=)', expr) if t]
|
652
|
+
op = ''.join(tokens[1: len(tokens)-1])
|
653
|
+
tokens = [tokens[0], cls.REVERSE[op], tokens[-1]]
|
625
654
|
target.values[WHERE][i] = ' '.join(tokens)
|
626
655
|
|
656
|
+
|
627
657
|
class RuleDateFuncReplace(Rule):
|
628
658
|
"""
|
629
659
|
SQL algorithm by Ralff Matias
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.1
|
3
|
+
Version: 0.2.1
|
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
|
@@ -24,7 +24,7 @@ _Note that an alias "act" has been added._
|
|
24
24
|
You can specify your own alias: `a = Select('Actor a')`
|
25
25
|
|
26
26
|
---
|
27
|
-
### 2 - You can also add a field,
|
27
|
+
### 2 - You can also add a field, contains this...
|
28
28
|
|
29
29
|
* a = Select('Actor a', **name=Field**)
|
30
30
|
|
@@ -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=contains("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=inside(['space', 'monster', 'gore'])
|
109
109
|
```
|
110
110
|
|
111
111
|
---
|
@@ -113,11 +113,11 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
113
113
|
|
114
114
|
* m = Select('Movie m' release_date=[Field, OrderBy])
|
115
115
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
116
|
-
* Applying **GROUP BY** to item 3.2, it would look
|
116
|
+
* Applying **GROUP BY** to item 3.2, it would look contains this:
|
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=5WBEFqik86gcV4WwPHx4R2e2vMeA1D_et1C9oEMnoQw,21600
|
3
|
+
sql_blocks-0.2.1.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-0.2.1.dist-info/METADATA,sha256=g5YLLFr736iWjO8dD0pO7nph5xEbyG6fynoAKo9-XtI,8804
|
5
|
+
sql_blocks-0.2.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-0.2.1.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-0.2.1.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=wj54KGT9Bnbo9Jtyn_jA6lbHhgkmGirRUp6ZHq9ilZ8,20654
|
3
|
-
sql_blocks-0.1.4.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-0.1.4.dist-info/METADATA,sha256=DWqKIgeFG_wjr8u4qusK-bcb_cgbxHXZ3DNFUXmMwKo,8503
|
5
|
-
sql_blocks-0.1.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-0.1.4.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-0.1.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|