sql-blocks 1.25.109__py3-none-any.whl → 1.25.112__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 +83 -16
- {sql_blocks-1.25.109.dist-info → sql_blocks-1.25.112.dist-info}/METADATA +60 -2
- sql_blocks-1.25.112.dist-info/RECORD +7 -0
- sql_blocks-1.25.109.dist-info/RECORD +0 -7
- {sql_blocks-1.25.109.dist-info → sql_blocks-1.25.112.dist-info}/LICENSE +0 -0
- {sql_blocks-1.25.109.dist-info → sql_blocks-1.25.112.dist-info}/WHEEL +0 -0
- {sql_blocks-1.25.109.dist-info → sql_blocks-1.25.112.dist-info}/top_level.txt +0 -0
sql_blocks/sql_blocks.py
CHANGED
@@ -266,23 +266,38 @@ class Current_Date(Function):
|
|
266
266
|
return super().get_pattern()
|
267
267
|
# --------------------------------------------------------
|
268
268
|
|
269
|
-
class
|
269
|
+
class Frame:
|
270
270
|
break_lines: bool = True
|
271
271
|
|
272
272
|
def over(self, **args):
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
273
|
+
"""
|
274
|
+
How to use:
|
275
|
+
over(field1=OrderBy, field2=Partition)
|
276
|
+
"""
|
277
|
+
keywords = ''
|
278
|
+
for field, obj in args.items():
|
279
|
+
is_valid = any([
|
280
|
+
obj is class_type # or isinstance(obj, class_type)
|
281
|
+
for class_type in (OrderBy, Partition)
|
282
|
+
])
|
283
|
+
if not is_valid:
|
284
|
+
continue
|
285
|
+
keywords += '{}{} {}'.format(
|
286
|
+
'\n\t\t' if self.break_lines else ' ',
|
287
|
+
obj.cls_to_str(), field
|
288
|
+
)
|
280
289
|
if keywords and self.break_lines:
|
281
290
|
keywords += '\n\t'
|
282
291
|
self.pattern = self.get_pattern() + f' OVER({keywords})'
|
283
292
|
return self
|
284
293
|
|
285
294
|
|
295
|
+
class Aggregate(Frame):
|
296
|
+
...
|
297
|
+
|
298
|
+
class Window(Frame):
|
299
|
+
...
|
300
|
+
|
286
301
|
# ---- Aggregate Functions: -------------------------------
|
287
302
|
class Avg(Aggregate, Function):
|
288
303
|
...
|
@@ -295,6 +310,17 @@ class Sum(Aggregate, Function):
|
|
295
310
|
class Count(Aggregate, Function):
|
296
311
|
...
|
297
312
|
|
313
|
+
# ---- Window Functions: -----------------------------------
|
314
|
+
class Row_Number(Window, Function):
|
315
|
+
...
|
316
|
+
class Rank(Window, Function):
|
317
|
+
...
|
318
|
+
class Lag(Window, Function):
|
319
|
+
...
|
320
|
+
class Lead(Window, Function):
|
321
|
+
...
|
322
|
+
|
323
|
+
|
298
324
|
# ---- Conversions and other Functions: ---------------------
|
299
325
|
class Coalesce(Function):
|
300
326
|
...
|
@@ -379,10 +405,9 @@ def quoted(value) -> str:
|
|
379
405
|
|
380
406
|
|
381
407
|
class Position(Enum):
|
382
|
-
Middle =
|
383
|
-
|
384
|
-
EndsWith =
|
385
|
-
RegEx = "REGEXP_LIKE('{}')"
|
408
|
+
Middle = 0
|
409
|
+
StartsWith = 1
|
410
|
+
EndsWith = 2
|
386
411
|
|
387
412
|
|
388
413
|
class Where:
|
@@ -401,7 +426,13 @@ class Where:
|
|
401
426
|
|
402
427
|
@classmethod
|
403
428
|
def contains(cls, content: str, pos: Position = Position.Middle):
|
404
|
-
return cls(
|
429
|
+
return cls(
|
430
|
+
"LIKE '{}{}{}'".format(
|
431
|
+
'%' if pos != Position.StartsWith else '',
|
432
|
+
content,
|
433
|
+
'%' if pos != Position.EndsWith else ''
|
434
|
+
)
|
435
|
+
)
|
405
436
|
|
406
437
|
@classmethod
|
407
438
|
def gt(cls, value):
|
@@ -481,9 +512,8 @@ class Options:
|
|
481
512
|
self.__children: dict = values
|
482
513
|
|
483
514
|
def add(self, logical_separator: str, main: SQLObject):
|
484
|
-
|
485
|
-
|
486
|
-
"""
|
515
|
+
if logical_separator not in ('AND', 'OR'):
|
516
|
+
raise ValueError('`logical_separator` must be AND or OR')
|
487
517
|
conditions: list[str] = []
|
488
518
|
child: Where
|
489
519
|
for field, child in self.__children.items():
|
@@ -535,6 +565,16 @@ class OrderBy(Clause):
|
|
535
565
|
name = cls.format(name, main)
|
536
566
|
main.values.setdefault(ORDER_BY, []).append(name+cls.sort.value)
|
537
567
|
|
568
|
+
@classmethod
|
569
|
+
def cls_to_str(cls) -> str:
|
570
|
+
return ORDER_BY
|
571
|
+
|
572
|
+
PARTITION_BY = 'PARTITION BY'
|
573
|
+
class Partition:
|
574
|
+
@classmethod
|
575
|
+
def cls_to_str(cls) -> str:
|
576
|
+
return PARTITION_BY
|
577
|
+
|
538
578
|
|
539
579
|
class GroupBy(Clause):
|
540
580
|
@classmethod
|
@@ -1381,6 +1421,31 @@ class RuleDateFuncReplace(Rule):
|
|
1381
1421
|
target.values[WHERE][i] = ' AND '.join(temp.values[WHERE])
|
1382
1422
|
|
1383
1423
|
|
1424
|
+
class RuleReplaceJoinBySubselect(Rule):
|
1425
|
+
@classmethod
|
1426
|
+
def apply(cls, target: Select):
|
1427
|
+
main, *others = Select.parse( str(target) )
|
1428
|
+
modified = False
|
1429
|
+
for query in others:
|
1430
|
+
fk_field, primary_k = ForeignKey.find(main, query)
|
1431
|
+
more_relations = any([
|
1432
|
+
ref[0] == query.table_name for ref in ForeignKey.references
|
1433
|
+
])
|
1434
|
+
invalid = any([
|
1435
|
+
len( query.values.get(SELECT, []) ) > 0,
|
1436
|
+
len( query.values.get(WHERE, []) ) == 0,
|
1437
|
+
not fk_field, more_relations
|
1438
|
+
])
|
1439
|
+
if invalid:
|
1440
|
+
continue
|
1441
|
+
query.__class__ = SubSelect
|
1442
|
+
Field.add(primary_k, query)
|
1443
|
+
query.add(fk_field, main)
|
1444
|
+
modified = True
|
1445
|
+
if modified:
|
1446
|
+
target.values = main.values.copy()
|
1447
|
+
|
1448
|
+
|
1384
1449
|
def parser_class(text: str) -> Parser:
|
1385
1450
|
PARSER_REGEX = [
|
1386
1451
|
(r'select.*from', SQLParser),
|
@@ -1415,3 +1480,5 @@ def detect(text: str) -> Select:
|
|
1415
1480
|
for query in query_list[1:]:
|
1416
1481
|
result += query
|
1417
1482
|
return result
|
1483
|
+
|
1484
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 1.25.
|
3
|
+
Version: 1.25.112
|
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
|
@@ -97,8 +97,13 @@ query = Select('Movie m', title=Field,
|
|
97
97
|
genre=eq("Sci-Fi"),
|
98
98
|
awards=contains("Oscar")
|
99
99
|
)
|
100
|
+
AND=Options(
|
101
|
+
..., name=contains(
|
102
|
+
'Chris',
|
103
|
+
Position.StartsWith
|
104
|
+
)
|
105
|
+
)
|
100
106
|
```
|
101
|
-
> Could be AND=Options(...)
|
102
107
|
|
103
108
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
104
109
|
```
|
@@ -363,6 +368,35 @@ m2 = Select(
|
|
363
368
|
|
364
369
|
> The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
|
365
370
|
|
371
|
+
>> NOTE: When a joined table is used only as a filter, it is possible that it can be changed to a sub-query:
|
372
|
+
|
373
|
+
query = Select(
|
374
|
+
'Installments i', due_date=Field, customer=Select(
|
375
|
+
'Customer c', id=PrimaryKey,
|
376
|
+
name=contains('Smith', Position.EndsWith)
|
377
|
+
)
|
378
|
+
)
|
379
|
+
print(query)
|
380
|
+
print('-----')
|
381
|
+
query.optimize([RuleReplaceJoinBySubselect])
|
382
|
+
print(query)
|
383
|
+
```
|
384
|
+
SELECT
|
385
|
+
i.due_date
|
386
|
+
FROM
|
387
|
+
Installments i
|
388
|
+
JOIN Customer c ON (i.customer = c.id)
|
389
|
+
WHERE
|
390
|
+
c.name LIKE '%Smith'
|
391
|
+
-----
|
392
|
+
SELECT
|
393
|
+
i.due_date
|
394
|
+
FROM
|
395
|
+
Installments i
|
396
|
+
WHERE
|
397
|
+
i.customer IN (SELECT c.id FROM Customer c WHERE c.name LIKE '%Smith')
|
398
|
+
```
|
399
|
+
|
366
400
|
---
|
367
401
|
|
368
402
|
### 12 - Adding multiple fields at once
|
@@ -567,3 +601,27 @@ GROUP BY
|
|
567
601
|
ORDER BY
|
568
602
|
customer_count
|
569
603
|
```
|
604
|
+
---
|
605
|
+
### 16 - Function classes
|
606
|
+
You may use this functions:
|
607
|
+
* SubString
|
608
|
+
* Round
|
609
|
+
* DateDiff
|
610
|
+
* Year
|
611
|
+
* Current_Date
|
612
|
+
* Avg
|
613
|
+
* Min
|
614
|
+
* Max
|
615
|
+
* Sum
|
616
|
+
* Count
|
617
|
+
* Lag
|
618
|
+
* Lead
|
619
|
+
* Row_Number
|
620
|
+
* Rank
|
621
|
+
* Coalesce
|
622
|
+
* Cast
|
623
|
+
> Some of these functions may vary in syntax depending on the database.
|
624
|
+
For example, if your query is going to run on Oracle, do the following:
|
625
|
+
|
626
|
+
`Function.dialect = Dialect.ORACLE`
|
627
|
+
|
@@ -0,0 +1,7 @@
|
|
1
|
+
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
+
sql_blocks/sql_blocks.py,sha256=X13qSCvVhu-BVXdUmqXi48Z7H4THBvXHrP5zw2Hm4wM,49491
|
3
|
+
sql_blocks-1.25.112.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-1.25.112.dist-info/METADATA,sha256=npxMGB0xgAFt4J_IgoIaaWmVFcVoNu5lq3qnUAjhb-A,14638
|
5
|
+
sql_blocks-1.25.112.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-1.25.112.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-1.25.112.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=OnIlTb03DTgK_U8QBnXQtQ-rKrkSuKNj1hjJiHogmYE,47639
|
3
|
-
sql_blocks-1.25.109.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-1.25.109.dist-info/METADATA,sha256=TRybMQgmXhlz14FxEV6f4rg0-gTPpveyQi3xEYPdZ1w,13425
|
5
|
-
sql_blocks-1.25.109.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-1.25.109.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-1.25.109.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|