sql-blocks 1.25.19011849__py3-none-any.whl → 1.25.30011511__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 +88 -33
- {sql_blocks-1.25.19011849.dist-info → sql_blocks-1.25.30011511.dist-info}/METADATA +47 -1
- sql_blocks-1.25.30011511.dist-info/RECORD +7 -0
- sql_blocks-1.25.19011849.dist-info/RECORD +0 -7
- {sql_blocks-1.25.19011849.dist-info → sql_blocks-1.25.30011511.dist-info}/LICENSE +0 -0
- {sql_blocks-1.25.19011849.dist-info → sql_blocks-1.25.30011511.dist-info}/WHEEL +0 -0
- {sql_blocks-1.25.19011849.dist-info → sql_blocks-1.25.30011511.dist-info}/top_level.txt +0 -0
sql_blocks/sql_blocks.py
CHANGED
@@ -42,17 +42,23 @@ class SQLObject:
|
|
42
42
|
if not table_name:
|
43
43
|
return
|
44
44
|
cls = SQLObject
|
45
|
+
is_file_name = any([
|
46
|
+
'/' in table_name, '.' in table_name
|
47
|
+
])
|
48
|
+
ref = table_name
|
49
|
+
if is_file_name:
|
50
|
+
ref = table_name.split('/')[-1].split('.')[0]
|
45
51
|
if cls.ALIAS_FUNC:
|
46
|
-
self.__alias = cls.ALIAS_FUNC(
|
52
|
+
self.__alias = cls.ALIAS_FUNC(ref)
|
47
53
|
elif ' ' in table_name.strip():
|
48
54
|
table_name, self.__alias = table_name.split()
|
49
|
-
elif '_' in
|
55
|
+
elif '_' in ref:
|
50
56
|
self.__alias = ''.join(
|
51
57
|
word[0].lower()
|
52
|
-
for word in
|
58
|
+
for word in ref.split('_')
|
53
59
|
)
|
54
60
|
else:
|
55
|
-
self.__alias =
|
61
|
+
self.__alias = ref.lower()[:3]
|
56
62
|
self.values.setdefault(FROM, []).append(f'{table_name} {self.alias}')
|
57
63
|
|
58
64
|
@property
|
@@ -423,12 +429,12 @@ class Position(Enum):
|
|
423
429
|
class Where:
|
424
430
|
prefix = ''
|
425
431
|
|
426
|
-
def __init__(self,
|
427
|
-
self.
|
432
|
+
def __init__(self, content: str):
|
433
|
+
self.content = content
|
428
434
|
|
429
435
|
@classmethod
|
430
436
|
def __constructor(cls, operator: str, value):
|
431
|
-
return cls(
|
437
|
+
return cls(f'{operator} {quoted(value)}')
|
432
438
|
|
433
439
|
@classmethod
|
434
440
|
def eq(cls, value):
|
@@ -470,6 +476,34 @@ class Where:
|
|
470
476
|
values = ','.join(quoted(v) for v in values)
|
471
477
|
return cls(f'IN ({values})')
|
472
478
|
|
479
|
+
@classmethod
|
480
|
+
def formula(cls, formula: str):
|
481
|
+
where = cls( ExpressionField(formula) )
|
482
|
+
where.add = where.add_expression
|
483
|
+
return where
|
484
|
+
|
485
|
+
def add_expression(self, name: str, main: SQLObject):
|
486
|
+
self.content = self.content.format(name, main)
|
487
|
+
main.values.setdefault(WHERE, []).append('{} {}'.format(
|
488
|
+
self.prefix, self.content
|
489
|
+
))
|
490
|
+
|
491
|
+
@classmethod
|
492
|
+
def join(cls, query: SQLObject):
|
493
|
+
where = cls(query)
|
494
|
+
where.add = where.add_join
|
495
|
+
return where
|
496
|
+
|
497
|
+
def add_join(self, name: str, main: SQLObject):
|
498
|
+
query = self.content
|
499
|
+
main.values[FROM].append(f',{query.table_name} {query.alias}')
|
500
|
+
for key in USUAL_KEYS:
|
501
|
+
main.update_values(key, query.values.get(key, []))
|
502
|
+
main.values.setdefault(WHERE, []).append('({a1}.{f1} = {a2}.{f2})'.format(
|
503
|
+
a1=main.alias, f1=name,
|
504
|
+
a2=query.alias, f2=query.key_field
|
505
|
+
))
|
506
|
+
|
473
507
|
def add(self, name: str, main: SQLObject):
|
474
508
|
func_type = FUNCTION_CLASS.get(name.lower())
|
475
509
|
exists = any(
|
@@ -482,7 +516,7 @@ class Where:
|
|
482
516
|
elif not exists:
|
483
517
|
name = Field.format(name, main)
|
484
518
|
main.values.setdefault(WHERE, []).append('{}{} {}'.format(
|
485
|
-
self.prefix, name, self.
|
519
|
+
self.prefix, name, self.content
|
486
520
|
))
|
487
521
|
|
488
522
|
|
@@ -490,6 +524,7 @@ eq, contains, gt, gte, lt, lte, is_null, inside = (
|
|
490
524
|
getattr(Where, method) for method in
|
491
525
|
('eq', 'contains', 'gt', 'gte', 'lt', 'lte', 'is_null', 'inside')
|
492
526
|
)
|
527
|
+
startswith, endswith = [lambda x: contains(x, pos) for pos in Position if pos.value]
|
493
528
|
|
494
529
|
|
495
530
|
class Not(Where):
|
@@ -497,7 +532,7 @@ class Not(Where):
|
|
497
532
|
|
498
533
|
@classmethod
|
499
534
|
def eq(cls, value):
|
500
|
-
return Where(
|
535
|
+
return Where(f'<> {quoted(value)}')
|
501
536
|
|
502
537
|
|
503
538
|
class Case:
|
@@ -538,7 +573,7 @@ class Options:
|
|
538
573
|
child: Where
|
539
574
|
for field, child in self.__children.items():
|
540
575
|
conditions.append(' {} {} '.format(
|
541
|
-
Field.format(field, main), child.
|
576
|
+
Field.format(field, main), child.content
|
542
577
|
))
|
543
578
|
main.values.setdefault(WHERE, []).append(
|
544
579
|
'(' + logical_separator.join(conditions) + ')'
|
@@ -637,7 +672,7 @@ class Having:
|
|
637
672
|
|
638
673
|
def add(self, name: str, main:SQLObject):
|
639
674
|
main.values[GROUP_BY][-1] += ' HAVING {} {}'.format(
|
640
|
-
self.function.format(name, main), self.condition.
|
675
|
+
self.function.format(name, main), self.condition.content
|
641
676
|
)
|
642
677
|
|
643
678
|
@classmethod
|
@@ -1419,6 +1454,47 @@ class NotSelectIN(SelectIN):
|
|
1419
1454
|
condition_class = Not
|
1420
1455
|
|
1421
1456
|
|
1457
|
+
class CTE:
|
1458
|
+
prefix = ''
|
1459
|
+
|
1460
|
+
def __init__(self, name: str, query_list: list[Select]):
|
1461
|
+
self.name = name
|
1462
|
+
for query in query_list:
|
1463
|
+
query.break_lines = False
|
1464
|
+
self.query_list = query_list
|
1465
|
+
|
1466
|
+
def format(self, query: Select) -> str:
|
1467
|
+
LINE_MAX_SIZE = 50
|
1468
|
+
result, line = [], ''
|
1469
|
+
for word in str(query).split(' '):
|
1470
|
+
if len(line) >= LINE_MAX_SIZE and word in KEYWORD:
|
1471
|
+
result.append(line)
|
1472
|
+
line = ''
|
1473
|
+
line += word + ' '
|
1474
|
+
if line:
|
1475
|
+
result.append(line)
|
1476
|
+
return '\n\t'.join(result)
|
1477
|
+
|
1478
|
+
def __str__(self) -> str:
|
1479
|
+
return 'WITH {prefix}{name} AS (\n\t{queries}\n)SELECT * FROM {name}'.format(
|
1480
|
+
prefix=self.prefix, name=self.name,
|
1481
|
+
queries='\nUNION ALL\n\t'.join(
|
1482
|
+
self.format(q) for q in self.query_list
|
1483
|
+
)
|
1484
|
+
)
|
1485
|
+
|
1486
|
+
class Recursive(CTE):
|
1487
|
+
prefix = 'RECURSIVE '
|
1488
|
+
|
1489
|
+
def __str__(self) -> str:
|
1490
|
+
if len(self.query_list) > 1:
|
1491
|
+
alias = self.name[0].lower()
|
1492
|
+
self.query_list[-1].values[FROM].append(f', {self.name} {alias}')
|
1493
|
+
return super().__str__()
|
1494
|
+
|
1495
|
+
|
1496
|
+
# ----- Rules -----
|
1497
|
+
|
1422
1498
|
class RulePutLimit(Rule):
|
1423
1499
|
@classmethod
|
1424
1500
|
def apply(cls, target: Select):
|
@@ -1533,7 +1609,7 @@ def parser_class(text: str) -> Parser:
|
|
1533
1609
|
return None
|
1534
1610
|
|
1535
1611
|
|
1536
|
-
def detect(text: str, join_queries: bool = True) -> Select:
|
1612
|
+
def detect(text: str, join_queries: bool = True) -> Select | list[Select]:
|
1537
1613
|
from collections import Counter
|
1538
1614
|
parser = parser_class(text)
|
1539
1615
|
if not parser:
|
@@ -1556,24 +1632,3 @@ def detect(text: str, join_queries: bool = True) -> Select:
|
|
1556
1632
|
result += query
|
1557
1633
|
return result
|
1558
1634
|
|
1559
|
-
|
1560
|
-
if __name__ == '__main__':
|
1561
|
-
# query = Select(
|
1562
|
-
# 'Tips t',
|
1563
|
-
# tip=[Field, Lag().over(day=OrderBy).As('last')],
|
1564
|
-
# diff=[
|
1565
|
-
# ExpressionField('Round(tip-last, 2) as {f}'),
|
1566
|
-
# Not.is_null()
|
1567
|
-
# ]
|
1568
|
-
# )
|
1569
|
-
p, c, a = Select.parse('''
|
1570
|
-
Professor(?nome="Júlio Cascalles", id)
|
1571
|
-
<- Curso@disciplina(professor, aluno) ->
|
1572
|
-
Aluno(id ^count$qtd_alunos)
|
1573
|
-
''', CypherParser)
|
1574
|
-
query = p + c + a
|
1575
|
-
print(query)
|
1576
|
-
print('------------------')
|
1577
|
-
query.optimize([RuleReplaceJoinBySubselect])
|
1578
|
-
# ==============================================
|
1579
|
-
print(query)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 1.25.
|
3
|
+
Version: 1.25.30011511
|
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
|
@@ -115,6 +115,16 @@ based_on_book=Not.is_null()
|
|
115
115
|
hash_tag=inside(['space', 'monster', 'gore'])
|
116
116
|
```
|
117
117
|
|
118
|
+
3.6 -- Combining ExpressionField with Where condition:
|
119
|
+
* The **formula** method allows you to write an expression as a condition:
|
120
|
+
```
|
121
|
+
query=Select(
|
122
|
+
'Folks f2',
|
123
|
+
id=Where.formula('({af} = a.father OR {af} = a.mother)')
|
124
|
+
)
|
125
|
+
```
|
126
|
+
> Results: `WHERE...f2.id = a.father OR f2.id = a.mother`
|
127
|
+
|
118
128
|
---
|
119
129
|
### 4 - A field can be two things at the same time:
|
120
130
|
|
@@ -631,3 +641,39 @@ For example, if your query is going to run on Oracle, do the following:
|
|
631
641
|
|
632
642
|
`Function.dialect = Dialect.ORACLE`
|
633
643
|
|
644
|
+
---
|
645
|
+
|
646
|
+
### 17 - CTE and Recursive classes
|
647
|
+
|
648
|
+
* **_CTE class_**
|
649
|
+
```
|
650
|
+
query = Select(
|
651
|
+
'SocialMedia s', post=Count, reaction=Sum, user=GroupBy
|
652
|
+
)
|
653
|
+
print( CTE('Metrics', [query]) )
|
654
|
+
```
|
655
|
+
The result is...
|
656
|
+
```
|
657
|
+
WITH Metrics AS (
|
658
|
+
SELECT Count(s.post), Sum(s.reaction) FROM SocialMedia s GROUP BY user
|
659
|
+
)SELECT * FROM Metrics
|
660
|
+
```
|
661
|
+
|
662
|
+
* **_Recursive class_**
|
663
|
+
```
|
664
|
+
q1 = Select(
|
665
|
+
'SocialMedia me', name=[ eq(MY_NAME), Field ]
|
666
|
+
)
|
667
|
+
q2 = Select(
|
668
|
+
'SocialMedia you' name=Field, id=Where.formula('{af} = n.friend')
|
669
|
+
)
|
670
|
+
print( Recursive('Network', [q1, q2]) )
|
671
|
+
```
|
672
|
+
The result is...
|
673
|
+
```
|
674
|
+
WITH RECURSIVE Network AS (
|
675
|
+
SELECT me.name FROM SocialMedia me WHERE me.name = 'Júlio Cascalles'
|
676
|
+
UNION ALL
|
677
|
+
SELECT you.name FROM SocialMedia you , Network n WHERE you.id = n.friend
|
678
|
+
)SELECT * FROM Network
|
679
|
+
```
|
@@ -0,0 +1,7 @@
|
|
1
|
+
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
+
sql_blocks/sql_blocks.py,sha256=fTEX1TEkjr-uUg_aLVmkwVM5Ul3UQyvsW6Vw7z_TCQQ,54398
|
3
|
+
sql_blocks-1.25.30011511.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-1.25.30011511.dist-info/METADATA,sha256=Hqo-6uZ0zLVyhvw7H9E1Hc7vGT5Ny6Kjxy2nzTqIEn0,16187
|
5
|
+
sql_blocks-1.25.30011511.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-1.25.30011511.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-1.25.30011511.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=nMIOewFh6X7vC0UNKs4mdqz4F_gpCSvtF0oAkocEGHw,52542
|
3
|
-
sql_blocks-1.25.19011849.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-1.25.19011849.dist-info/METADATA,sha256=jp-OY-e66KmW-tXSklUT3l5-01CX0jfCCPJ6iANNpSY,15031
|
5
|
-
sql_blocks-1.25.19011849.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-1.25.19011849.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-1.25.19011849.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|