sql-blocks 0.0.3__tar.gz → 0.0.4__tar.gz
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-0.0.3/sql_blocks.egg-info → sql_blocks-0.0.4}/PKG-INFO +15 -14
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/README.md +14 -13
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/pyproject.toml +1 -1
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/setup.py +1 -1
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/sql_blocks/sql_blocks.py +92 -20
- {sql_blocks-0.0.3 → sql_blocks-0.0.4/sql_blocks.egg-info}/PKG-INFO +15 -14
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/LICENSE +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/setup.cfg +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/sql_blocks/__init__.py +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/sql_blocks.egg-info/SOURCES.txt +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/sql_blocks.egg-info/dependency_links.txt +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/sql_blocks.egg-info/top_level.txt +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.4}/tests/tests.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
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,7 +55,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
55
55
|
3.2 -- Sub-queries:
|
56
56
|
```
|
57
57
|
query = Select('Movie m', title=Field,
|
58
|
-
id=
|
58
|
+
id=SelectIN(
|
59
59
|
'Review r',
|
60
60
|
rate=Where.gt(4.5),
|
61
61
|
movie_id=Distinct
|
@@ -86,12 +86,13 @@ query = Select('Movie m', title=Field,
|
|
86
86
|
|
87
87
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
88
88
|
```
|
89
|
-
franchise
|
90
89
|
based_on_book=Not.is_null()
|
91
90
|
```
|
92
91
|
|
93
92
|
3.5 -- List of values
|
93
|
+
```
|
94
94
|
hash_tag=Where.list(['space', 'monster', 'gore'])
|
95
|
+
```
|
95
96
|
|
96
97
|
---
|
97
98
|
### 4 - A field can be two things at the same time:
|
@@ -100,7 +101,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
100
101
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
101
102
|
* Applying **GROUP BY** to item 3.2, it would look like this:
|
102
103
|
```
|
103
|
-
|
104
|
+
SelectIN(
|
104
105
|
'Review r', movie=[GroupBy, Distinct],
|
105
106
|
rate=Having.avg(Where.gt(4.5))
|
106
107
|
)
|
@@ -282,7 +283,7 @@ m = Select...
|
|
282
283
|
|
283
284
|
9.3
|
284
285
|
```
|
285
|
-
best_movies =
|
286
|
+
best_movies = SelectIN(
|
286
287
|
Review=Table('role'),
|
287
288
|
rate=[GroupBy, Having.avg(Where.gt(4.5))]
|
288
289
|
)
|
@@ -302,13 +303,13 @@ m2 = Select(
|
|
302
303
|
---
|
303
304
|
|
304
305
|
### 10 - CASE...WHEN...THEN
|
305
|
-
Select(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
306
|
+
Select(
|
307
|
+
'Product',
|
308
|
+
label=Case('price').when(
|
309
|
+
lt(50), 'cheap'
|
310
|
+
).when(
|
311
|
+
gt(100), 'expensive'
|
312
|
+
).else_value(
|
313
|
+
'normal'
|
314
|
+
)
|
313
315
|
)
|
314
|
-
)
|
@@ -40,7 +40,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
40
40
|
3.2 -- Sub-queries:
|
41
41
|
```
|
42
42
|
query = Select('Movie m', title=Field,
|
43
|
-
id=
|
43
|
+
id=SelectIN(
|
44
44
|
'Review r',
|
45
45
|
rate=Where.gt(4.5),
|
46
46
|
movie_id=Distinct
|
@@ -71,12 +71,13 @@ query = Select('Movie m', title=Field,
|
|
71
71
|
|
72
72
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
73
73
|
```
|
74
|
-
franchise
|
75
74
|
based_on_book=Not.is_null()
|
76
75
|
```
|
77
76
|
|
78
77
|
3.5 -- List of values
|
78
|
+
```
|
79
79
|
hash_tag=Where.list(['space', 'monster', 'gore'])
|
80
|
+
```
|
80
81
|
|
81
82
|
---
|
82
83
|
### 4 - A field can be two things at the same time:
|
@@ -85,7 +86,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
85
86
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
86
87
|
* Applying **GROUP BY** to item 3.2, it would look like this:
|
87
88
|
```
|
88
|
-
|
89
|
+
SelectIN(
|
89
90
|
'Review r', movie=[GroupBy, Distinct],
|
90
91
|
rate=Having.avg(Where.gt(4.5))
|
91
92
|
)
|
@@ -267,7 +268,7 @@ m = Select...
|
|
267
268
|
|
268
269
|
9.3
|
269
270
|
```
|
270
|
-
best_movies =
|
271
|
+
best_movies = SelectIN(
|
271
272
|
Review=Table('role'),
|
272
273
|
rate=[GroupBy, Having.avg(Where.gt(4.5))]
|
273
274
|
)
|
@@ -287,13 +288,13 @@ m2 = Select(
|
|
287
288
|
---
|
288
289
|
|
289
290
|
### 10 - CASE...WHEN...THEN
|
290
|
-
Select(
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
291
|
+
Select(
|
292
|
+
'Product',
|
293
|
+
label=Case('price').when(
|
294
|
+
lt(50), 'cheap'
|
295
|
+
).when(
|
296
|
+
gt(100), 'expensive'
|
297
|
+
).else_value(
|
298
|
+
'normal'
|
299
|
+
)
|
298
300
|
)
|
299
|
-
)
|
@@ -1,4 +1,6 @@
|
|
1
1
|
from enum import Enum
|
2
|
+
import re
|
3
|
+
|
2
4
|
|
3
5
|
KEYWORD = {
|
4
6
|
'SELECT': (',{}', 'SELECT *'),
|
@@ -54,9 +56,10 @@ class Field:
|
|
54
56
|
|
55
57
|
@classmethod
|
56
58
|
def format(cls, name: str, main: SQLObject) -> str:
|
59
|
+
name = name.strip()
|
57
60
|
if name == '_':
|
58
61
|
name = '*'
|
59
|
-
|
62
|
+
elif '.' not in name:
|
60
63
|
name = f'{main.alias}.{name}'
|
61
64
|
if Function in cls.__bases__:
|
62
65
|
name = f'{cls.__name__}({name})'
|
@@ -319,7 +322,7 @@ class JoinType(Enum):
|
|
319
322
|
|
320
323
|
class Select(SQLObject):
|
321
324
|
join_type: JoinType = JoinType.INNER
|
322
|
-
REGEX =
|
325
|
+
REGEX = {}
|
323
326
|
|
324
327
|
def __init__(self, table_name: str='', **values):
|
325
328
|
super().__init__(table_name)
|
@@ -350,7 +353,7 @@ class Select(SQLObject):
|
|
350
353
|
if not foreign_field:
|
351
354
|
foreign_field, primary_key = ForeignKey.find(other, self)
|
352
355
|
if foreign_field:
|
353
|
-
if primary_key
|
356
|
+
if primary_key:
|
354
357
|
PrimaryKey.add(primary_key, self)
|
355
358
|
self.add(foreign_field, other)
|
356
359
|
return other
|
@@ -396,15 +399,52 @@ class Select(SQLObject):
|
|
396
399
|
|
397
400
|
@classmethod
|
398
401
|
def parse(cls, txt: str) -> list[SQLObject]:
|
399
|
-
|
400
|
-
|
402
|
+
def find_last_word(pos: int) -> int:
|
403
|
+
SPACE, WORD = 1, 2
|
404
|
+
found = set()
|
405
|
+
for i in range(pos, 0, -1):
|
406
|
+
if txt[i] in [' ', '\t', '\n']:
|
407
|
+
if sum(found) == 3:
|
408
|
+
return i
|
409
|
+
found.add(SPACE)
|
410
|
+
if txt[i].isalpha():
|
411
|
+
found.add(WORD)
|
412
|
+
elif txt[i] == '.':
|
413
|
+
found.remove(WORD)
|
414
|
+
def find_parenthesis(pos: int) -> int:
|
415
|
+
for i in range(pos, len(txt)-1):
|
416
|
+
if txt[i] == ')':
|
417
|
+
return i+1
|
401
418
|
if not cls.REGEX:
|
402
|
-
keywords = '|'.join(KEYWORD)
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
tables = [t.strip() for t in re.split('JOIN|LEFT|RIGHT|ON', values[FROM]) if t.strip()]
|
419
|
+
keywords = '|'.join(k + r'\b' for k in KEYWORD)
|
420
|
+
flags = re.IGNORECASE + re.MULTILINE
|
421
|
+
cls.REGEX['keywords'] = re.compile(f'({keywords})', flags)
|
422
|
+
cls.REGEX['subquery'] = re.compile(r'(\w\.)*\w+ +in +\(SELECT.*?\)', flags)
|
407
423
|
result = {}
|
424
|
+
found = cls.REGEX['subquery'].search(txt)
|
425
|
+
while found:
|
426
|
+
start, end = found.span()
|
427
|
+
inner = txt[start: end]
|
428
|
+
if inner.count('(') > inner.count(')'):
|
429
|
+
end = find_parenthesis(end)
|
430
|
+
inner = txt[start: end]
|
431
|
+
fld, *inner = re.split(r' IN | in', inner, maxsplit=1)
|
432
|
+
if fld.upper() == 'NOT':
|
433
|
+
pos = find_last_word(start)
|
434
|
+
fld = txt[pos: start].strip() # [To-Do] Use the value of `fld`
|
435
|
+
start = pos
|
436
|
+
class_type = NotSelectIN
|
437
|
+
else:
|
438
|
+
class_type = SelectIN
|
439
|
+
obj = class_type.parse(
|
440
|
+
' '.join(re.sub(r'^\(', '', s.strip()) for s in inner)
|
441
|
+
)[0]
|
442
|
+
result[obj.alias] = obj
|
443
|
+
txt = txt[:start-1] + txt[end+1:]
|
444
|
+
found = cls.REGEX['subquery'].search(txt)
|
445
|
+
tokens = [t.strip() for t in cls.REGEX['keywords'].split(txt) if re.findall(r'\w+', t)]
|
446
|
+
values = {k.upper(): v for k, v in zip(tokens[::2], tokens[1::2])}
|
447
|
+
tables = [t.strip() for t in re.split('JOIN|LEFT|RIGHT|ON', values[FROM]) if t.strip()]
|
408
448
|
for item in tables:
|
409
449
|
if '=' in item:
|
410
450
|
a1, f1, a2, f2 = [r.strip() for r in re.split('[().=]', item) if r]
|
@@ -417,23 +457,55 @@ class Select(SQLObject):
|
|
417
457
|
for key in USUAL_KEYS:
|
418
458
|
if not key in values:
|
419
459
|
continue
|
420
|
-
separator = KEYWORD[key][0].format(
|
421
|
-
' ' if key == WHERE else ''
|
422
|
-
)
|
460
|
+
separator = KEYWORD[key][0].format('')
|
423
461
|
fields = [
|
424
|
-
|
462
|
+
Field.format(fld, obj)
|
463
|
+
for fld in re.split(
|
425
464
|
separator, values[key]
|
426
465
|
) if len(tables) == 1
|
427
|
-
or re.findall(f'
|
428
|
-
]
|
429
|
-
obj.values[key] = [
|
430
|
-
f'{obj.alias}.{f}' if not '.' in f else f for f in fields
|
466
|
+
or re.findall(f'\b*{obj.alias}[.]', fld)
|
431
467
|
]
|
468
|
+
obj.values[key] = [ f for f in fields if f.strip() ]
|
432
469
|
result[obj.alias] = obj
|
433
470
|
return list( result.values() )
|
434
471
|
|
472
|
+
class SelectIN(Select):
|
473
|
+
condition_class = Where
|
435
474
|
|
436
|
-
class SubSelect(Select):
|
437
475
|
def add(self, name: str, main: SQLObject):
|
438
476
|
self.break_lines = False
|
439
|
-
|
477
|
+
self.condition_class.list(self).add(name, main)
|
478
|
+
|
479
|
+
SubSelect = SelectIN
|
480
|
+
|
481
|
+
class NotSelectIN(SelectIN):
|
482
|
+
condition_class = Not
|
483
|
+
|
484
|
+
|
485
|
+
if __name__ == "__main__":
|
486
|
+
query_list = Select.parse("""
|
487
|
+
SELECT
|
488
|
+
cas.role,
|
489
|
+
m.title,
|
490
|
+
m.release_date,
|
491
|
+
a.name as actors_name
|
492
|
+
FROM
|
493
|
+
Actor a
|
494
|
+
LEFT JOIN Cast cas ON (a.cast = cas.id)
|
495
|
+
LEFT JOIN Movie m ON (cas.movie = m.id)
|
496
|
+
WHERE
|
497
|
+
m.genre NOT in (SELECT g.id from Genres g where g.name in ('sci-fi', 'horror', 'distopia'))
|
498
|
+
AND (m.hashtag = '#cult' OR m.awards LIKE '%Oscar%')
|
499
|
+
AND m.id IN (select DISTINCT r.movie FROM Review r GROUP BY r.movie HAVING Avg(r.rate) > 4.5)
|
500
|
+
AND a.age <= 69 AND a.age >= 45
|
501
|
+
ORDER BY
|
502
|
+
m.release_date
|
503
|
+
""")
|
504
|
+
for query in query_list:
|
505
|
+
descr = ' {} ({}) '.format(
|
506
|
+
query.table_name,
|
507
|
+
query.__class__.__name__
|
508
|
+
)
|
509
|
+
print(descr.center(50, '-'))
|
510
|
+
print(query)
|
511
|
+
print('='*50)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
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,7 +55,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
55
55
|
3.2 -- Sub-queries:
|
56
56
|
```
|
57
57
|
query = Select('Movie m', title=Field,
|
58
|
-
id=
|
58
|
+
id=SelectIN(
|
59
59
|
'Review r',
|
60
60
|
rate=Where.gt(4.5),
|
61
61
|
movie_id=Distinct
|
@@ -86,12 +86,13 @@ query = Select('Movie m', title=Field,
|
|
86
86
|
|
87
87
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
88
88
|
```
|
89
|
-
franchise
|
90
89
|
based_on_book=Not.is_null()
|
91
90
|
```
|
92
91
|
|
93
92
|
3.5 -- List of values
|
93
|
+
```
|
94
94
|
hash_tag=Where.list(['space', 'monster', 'gore'])
|
95
|
+
```
|
95
96
|
|
96
97
|
---
|
97
98
|
### 4 - A field can be two things at the same time:
|
@@ -100,7 +101,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
100
101
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
101
102
|
* Applying **GROUP BY** to item 3.2, it would look like this:
|
102
103
|
```
|
103
|
-
|
104
|
+
SelectIN(
|
104
105
|
'Review r', movie=[GroupBy, Distinct],
|
105
106
|
rate=Having.avg(Where.gt(4.5))
|
106
107
|
)
|
@@ -282,7 +283,7 @@ m = Select...
|
|
282
283
|
|
283
284
|
9.3
|
284
285
|
```
|
285
|
-
best_movies =
|
286
|
+
best_movies = SelectIN(
|
286
287
|
Review=Table('role'),
|
287
288
|
rate=[GroupBy, Having.avg(Where.gt(4.5))]
|
288
289
|
)
|
@@ -302,13 +303,13 @@ m2 = Select(
|
|
302
303
|
---
|
303
304
|
|
304
305
|
### 10 - CASE...WHEN...THEN
|
305
|
-
Select(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
306
|
+
Select(
|
307
|
+
'Product',
|
308
|
+
label=Case('price').when(
|
309
|
+
lt(50), 'cheap'
|
310
|
+
).when(
|
311
|
+
gt(100), 'expensive'
|
312
|
+
).else_value(
|
313
|
+
'normal'
|
314
|
+
)
|
313
315
|
)
|
314
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|