sql-blocks 1.25.33022258__py3-none-any.whl → 1.25.37022003__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 +56 -37
- {sql_blocks-1.25.33022258.dist-info → sql_blocks-1.25.37022003.dist-info}/METADATA +26 -1
- sql_blocks-1.25.37022003.dist-info/RECORD +7 -0
- sql_blocks-1.25.33022258.dist-info/RECORD +0 -7
- {sql_blocks-1.25.33022258.dist-info → sql_blocks-1.25.37022003.dist-info}/LICENSE +0 -0
- {sql_blocks-1.25.33022258.dist-info → sql_blocks-1.25.37022003.dist-info}/WHEEL +0 -0
- {sql_blocks-1.25.33022258.dist-info → sql_blocks-1.25.37022003.dist-info}/top_level.txt +0 -0
sql_blocks/sql_blocks.py
CHANGED
@@ -82,8 +82,14 @@ class SQLObject:
|
|
82
82
|
return KEYWORD[key][0].format(appendix.get(key, ''))
|
83
83
|
|
84
84
|
@staticmethod
|
85
|
-
def is_named_field(fld: str,
|
86
|
-
return
|
85
|
+
def is_named_field(fld: str, name: str='') -> bool:
|
86
|
+
return re.search(fr'(\s+as\s+|\s+AS\s+){name}', fld)
|
87
|
+
|
88
|
+
def has_named_field(self, name: str) -> bool:
|
89
|
+
return any(
|
90
|
+
self.is_named_field(fld, name)
|
91
|
+
for fld in self.values.get(SELECT, [])
|
92
|
+
)
|
87
93
|
|
88
94
|
def diff(self, key: str, search_list: list, exact: bool=False) -> set:
|
89
95
|
def disassemble(source: list) -> list:
|
@@ -100,7 +106,9 @@ class SQLObject:
|
|
100
106
|
def field_set(source: list) -> set:
|
101
107
|
return set(
|
102
108
|
(
|
103
|
-
fld
|
109
|
+
fld
|
110
|
+
if key == SELECT and self.is_named_field(fld, key)
|
111
|
+
else
|
104
112
|
re.sub(pattern, '', cleanup(fld))
|
105
113
|
)
|
106
114
|
for string in disassemble(source)
|
@@ -150,7 +158,7 @@ class Field:
|
|
150
158
|
name = name.strip()
|
151
159
|
if name in ('_', '*'):
|
152
160
|
name = '*'
|
153
|
-
elif not is_const():
|
161
|
+
elif not is_const() and not main.has_named_field(name):
|
154
162
|
name = f'{main.alias}.{name}'
|
155
163
|
if Function in cls.__bases__:
|
156
164
|
name = f'{cls.__name__}({name})'
|
@@ -190,13 +198,15 @@ class Dialect(Enum):
|
|
190
198
|
|
191
199
|
class Function:
|
192
200
|
dialect = Dialect.ANSI
|
201
|
+
need_params = True
|
202
|
+
separator = ', '
|
193
203
|
|
194
204
|
def __init__(self, *params: list):
|
195
205
|
# --- Replace class methods by instance methods: ------
|
196
206
|
self.add = self.__add
|
197
207
|
self.format = self.__format
|
198
208
|
# -----------------------------------------------------
|
199
|
-
self.params =
|
209
|
+
self.params = list(params)
|
200
210
|
self.field_class = Field
|
201
211
|
self.pattern = self.get_pattern()
|
202
212
|
self.extra = {}
|
@@ -213,14 +223,24 @@ class Function:
|
|
213
223
|
def __str__(self) -> str:
|
214
224
|
return self.pattern.format(
|
215
225
|
func_name=self.__class__.__name__,
|
216
|
-
params=
|
226
|
+
params=self.separator.join(str(p) for p in self.params)
|
217
227
|
)
|
218
228
|
|
229
|
+
def set_main_param(self, name: str, main: SQLObject) -> bool:
|
230
|
+
nested_functions = [
|
231
|
+
param for param in self.params if isinstance(param, Function)
|
232
|
+
]
|
233
|
+
for func in nested_functions:
|
234
|
+
if func.need_params:
|
235
|
+
func.set_main_param(name, main)
|
236
|
+
return
|
237
|
+
self.params = [
|
238
|
+
Field.format(name, main)
|
239
|
+
] + self.params
|
240
|
+
|
219
241
|
def __format(self, name: str, main: SQLObject) -> str:
|
220
242
|
if name not in '*_':
|
221
|
-
self.
|
222
|
-
Field.format(name, main)
|
223
|
-
] + self.params
|
243
|
+
self.set_main_param(name, main)
|
224
244
|
return str(self)
|
225
245
|
|
226
246
|
@classmethod
|
@@ -254,10 +274,11 @@ class DateDiff(Function):
|
|
254
274
|
def get_pattern(self) -> str:
|
255
275
|
def is_field_or_func(name: str) -> bool:
|
256
276
|
return re.sub('[()]', '', name).isidentifier()
|
277
|
+
params = [str(p) for p in self.params]
|
257
278
|
if self.dialect != Dialect.SQL_SERVER:
|
258
279
|
return ' - '.join(
|
259
280
|
p if is_field_or_func(p) else f"'{p}'"
|
260
|
-
for p in
|
281
|
+
for p in params
|
261
282
|
) # <==== Date subtract
|
262
283
|
return super().get_pattern()
|
263
284
|
|
@@ -272,6 +293,8 @@ class Year(Function):
|
|
272
293
|
return super().get_pattern()
|
273
294
|
|
274
295
|
class Current_Date(Function):
|
296
|
+
need_params = False
|
297
|
+
|
275
298
|
def get_pattern(self) -> str:
|
276
299
|
database_type = {
|
277
300
|
Dialect.ORACLE: SQL_CONST_SYSDATE,
|
@@ -343,7 +366,7 @@ class Lead(Window, Function):
|
|
343
366
|
class Coalesce(Function):
|
344
367
|
...
|
345
368
|
class Cast(Function):
|
346
|
-
|
369
|
+
separator = ' As '
|
347
370
|
|
348
371
|
|
349
372
|
FUNCTION_CLASS = {f.__name__.lower(): f for f in Function.__subclasses__()}
|
@@ -516,14 +539,9 @@ class Where:
|
|
516
539
|
|
517
540
|
def add(self, name: str, main: SQLObject):
|
518
541
|
func_type = FUNCTION_CLASS.get(name.lower())
|
519
|
-
exists = any(
|
520
|
-
main.is_named_field(fld, SELECT)
|
521
|
-
for fld in main.values.get(SELECT, [])
|
522
|
-
if name in fld
|
523
|
-
)
|
524
542
|
if func_type:
|
525
543
|
name = func_type.format('*', main)
|
526
|
-
elif not
|
544
|
+
elif not main.has_named_field(name):
|
527
545
|
name = Field.format(name, main)
|
528
546
|
main.values.setdefault(WHERE, []).append('{}{} {}'.format(
|
529
547
|
self.prefix, name, self.content
|
@@ -604,6 +622,14 @@ class Between:
|
|
604
622
|
Where.gte(self.start).add(name, main),
|
605
623
|
Where.lte(self.end).add(name, main)
|
606
624
|
|
625
|
+
class SameDay(Between):
|
626
|
+
def __init__(self, date: str):
|
627
|
+
super().__init__(
|
628
|
+
f'{date} 00:00:00',
|
629
|
+
f'{date} 23:59:59',
|
630
|
+
)
|
631
|
+
|
632
|
+
|
607
633
|
|
608
634
|
class Clause:
|
609
635
|
@classmethod
|
@@ -1438,11 +1464,7 @@ class Select(SQLObject):
|
|
1438
1464
|
Recognizes if the field is from the current table
|
1439
1465
|
'''
|
1440
1466
|
if key in (ORDER_BY, GROUP_BY) and '.' not in field:
|
1441
|
-
return
|
1442
|
-
self.is_named_field(fld, SELECT)
|
1443
|
-
for fld in self.values[SELECT]
|
1444
|
-
if field in fld
|
1445
|
-
)
|
1467
|
+
return main.has_named_field(field)
|
1446
1468
|
return re.findall(f'\b*{self.alias}[.]', field) != []
|
1447
1469
|
|
1448
1470
|
@classmethod
|
@@ -1455,12 +1477,10 @@ class Select(SQLObject):
|
|
1455
1477
|
for rule in rules:
|
1456
1478
|
rule.apply(self)
|
1457
1479
|
|
1458
|
-
def add_fields(self, fields: list,
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
if group_by:
|
1463
|
-
class_types += [GroupBy]
|
1480
|
+
def add_fields(self, fields: list, class_types=None):
|
1481
|
+
if not class_types:
|
1482
|
+
class_types = []
|
1483
|
+
class_types += [Field]
|
1464
1484
|
FieldList(fields, class_types).add('', self)
|
1465
1485
|
|
1466
1486
|
def translate_to(self, language: QueryLanguage) -> str:
|
@@ -1491,12 +1511,17 @@ class CTE(Select):
|
|
1491
1511
|
self.break_lines = False
|
1492
1512
|
|
1493
1513
|
def __str__(self) -> str:
|
1514
|
+
size = 0
|
1515
|
+
for key in USUAL_KEYS:
|
1516
|
+
size += sum(len(v) for v in self.values.get(key, []) if '\n' not in v)
|
1517
|
+
if size > 70:
|
1518
|
+
self.break_lines = True
|
1494
1519
|
# ---------------------------------------------------------
|
1495
1520
|
def justify(query: Select) -> str:
|
1496
1521
|
result, line = [], ''
|
1497
1522
|
keywords = '|'.join(KEYWORD)
|
1498
1523
|
for word in re.split(fr'({keywords}|AND|OR|,)', str(query)):
|
1499
|
-
if len(line) >=
|
1524
|
+
if len(line) >= 60:
|
1500
1525
|
result.append(line)
|
1501
1526
|
line = ''
|
1502
1527
|
line += word
|
@@ -1510,6 +1535,7 @@ class CTE(Select):
|
|
1510
1535
|
justify(q) for q in self.query_list
|
1511
1536
|
), super().__str__()
|
1512
1537
|
)
|
1538
|
+
|
1513
1539
|
def join(self, pattern: str, fields: list | str, format: str=''):
|
1514
1540
|
if isinstance(fields, str):
|
1515
1541
|
count = len( fields.split(',') )
|
@@ -1702,11 +1728,4 @@ def detect(text: str, join_queries: bool = True, format: str='') -> Select | lis
|
|
1702
1728
|
for query in query_list[1:]:
|
1703
1729
|
result += query
|
1704
1730
|
return result
|
1705
|
-
|
1706
|
-
|
1707
|
-
if __name__ == "__main__":
|
1708
|
-
CAMPO_MEDIA = 'MEDIA_SALARIAL_DEPTO'
|
1709
|
-
employees = detect(
|
1710
|
-
f'Employees@department_id(avg$salary:{CAMPO_MEDIA})'
|
1711
|
-
)
|
1712
|
-
print(employees)
|
1731
|
+
# ===========================================================================================//
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 1.25.
|
3
|
+
Version: 1.25.37022003
|
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
|
@@ -67,6 +67,16 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
67
67
|
3.1 -- If you want to filter the field on a range of values:
|
68
68
|
|
69
69
|
`a = Select( 'Actor a', age=Between(45, 69) )`
|
70
|
+
...but if it is a time slot within the same day, you can do it like this:
|
71
|
+
`Select(..., event_date=SameDay("2024-10-03"))`
|
72
|
+
This results in
|
73
|
+
```
|
74
|
+
SELECT ...
|
75
|
+
WHERE
|
76
|
+
event_date >= '2024-10-03 00:00:00' AND
|
77
|
+
event_date <= '2024-10-03 23:59:59'
|
78
|
+
```
|
79
|
+
---
|
70
80
|
|
71
81
|
3.2 -- Sub-queries:
|
72
82
|
```
|
@@ -668,6 +678,21 @@ For example, if your query is going to run on Oracle, do the following:
|
|
668
678
|
|
669
679
|
`Function.dialect = Dialect.ORACLE`
|
670
680
|
|
681
|
+
|
682
|
+
> Most of this functions you can use nested inside each other.
|
683
|
+
*Example:*
|
684
|
+
```
|
685
|
+
Select(...
|
686
|
+
event_date=Substring(
|
687
|
+
Cast("CHAR"), 12, 19
|
688
|
+
).As('time')
|
689
|
+
)
|
690
|
+
```
|
691
|
+
Results...
|
692
|
+
```
|
693
|
+
SELECT ...
|
694
|
+
SubString(Cast(event_date As char), 12, 19) as time
|
695
|
+
```
|
671
696
|
---
|
672
697
|
|
673
698
|
### 17 - CTE and Recursive classes
|
@@ -0,0 +1,7 @@
|
|
1
|
+
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
+
sql_blocks/sql_blocks.py,sha256=kaI6nCcq1X3CenGFP4HkKkk3rsDt_jts8xErg-ml7dk,58217
|
3
|
+
sql_blocks-1.25.37022003.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-1.25.37022003.dist-info/METADATA,sha256=AUa01HeqYtgwVObYWMTkqixy0RNoxwNQXurTMEWjjQ8,20146
|
5
|
+
sql_blocks-1.25.37022003.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-1.25.37022003.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-1.25.37022003.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=dGmMtLWiyluRp1ZwPplmBqPs1nDOL-vemRR0gi2htWA,57513
|
3
|
-
sql_blocks-1.25.33022258.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-1.25.33022258.dist-info/METADATA,sha256=GsTRa0fLkerPn-D-ourudj9Pve0oP69IzQ-YGHMtRnI,19574
|
5
|
-
sql_blocks-1.25.33022258.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-1.25.33022258.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-1.25.33022258.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|