sql-blocks 0.31.89__py3-none-any.whl → 1.25__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 +124 -46
- {sql_blocks-0.31.89.dist-info → sql_blocks-1.25.dist-info}/METADATA +56 -5
- sql_blocks-1.25.dist-info/RECORD +7 -0
- sql_blocks-0.31.89.dist-info/RECORD +0 -7
- {sql_blocks-0.31.89.dist-info → sql_blocks-1.25.dist-info}/LICENSE +0 -0
- {sql_blocks-0.31.89.dist-info → sql_blocks-1.25.dist-info}/WHEEL +0 -0
- {sql_blocks-0.31.89.dist-info → sql_blocks-1.25.dist-info}/top_level.txt +0 -0
sql_blocks/sql_blocks.py
CHANGED
@@ -22,6 +22,7 @@ KEYWORD = {
|
|
22
22
|
|
23
23
|
SELECT, FROM, WHERE, GROUP_BY, ORDER_BY, LIMIT = KEYWORD.keys()
|
24
24
|
USUAL_KEYS = [SELECT, WHERE, GROUP_BY, ORDER_BY, LIMIT]
|
25
|
+
TO_LIST = lambda x: x if isinstance(x, list) else [x]
|
25
26
|
|
26
27
|
|
27
28
|
class SQLObject:
|
@@ -110,9 +111,6 @@ class SQLObject:
|
|
110
111
|
self.values[key] = result
|
111
112
|
|
112
113
|
|
113
|
-
class Function:
|
114
|
-
...
|
115
|
-
|
116
114
|
class Field:
|
117
115
|
prefix = ''
|
118
116
|
|
@@ -134,17 +132,6 @@ class Field:
|
|
134
132
|
)
|
135
133
|
|
136
134
|
|
137
|
-
class Avg(Function, Field):
|
138
|
-
...
|
139
|
-
class Min(Function, Field):
|
140
|
-
...
|
141
|
-
class Max(Function, Field):
|
142
|
-
...
|
143
|
-
class Sum(Function, Field):
|
144
|
-
...
|
145
|
-
class Count(Function, Field):
|
146
|
-
...
|
147
|
-
|
148
135
|
class Distinct(Field):
|
149
136
|
prefix = 'DISTINCT '
|
150
137
|
|
@@ -163,6 +150,107 @@ class NamedField:
|
|
163
150
|
)
|
164
151
|
|
165
152
|
|
153
|
+
class Function:
|
154
|
+
instance: dict = {}
|
155
|
+
|
156
|
+
def __init__(self, *params: list):
|
157
|
+
func_name = self.__class__.__name__
|
158
|
+
Function.instance[func_name] = self
|
159
|
+
self.params = [str(p) for p in params]
|
160
|
+
self.class_type = Field
|
161
|
+
self.pattern = '{}({})'
|
162
|
+
self.extra = {}
|
163
|
+
|
164
|
+
def As(self, field_alias: str, modifiers=None):
|
165
|
+
if modifiers:
|
166
|
+
for mod in TO_LIST(modifiers):
|
167
|
+
self.extra[field_alias] = mod
|
168
|
+
self.class_type = NamedField(field_alias)
|
169
|
+
return self
|
170
|
+
|
171
|
+
@classmethod
|
172
|
+
def format(cls, name: str, main: SQLObject) -> str:
|
173
|
+
obj = cls.get_instance()
|
174
|
+
params = [
|
175
|
+
Field.format(name, main)
|
176
|
+
] + obj.params
|
177
|
+
return obj.pattern.format(
|
178
|
+
cls.__name__,
|
179
|
+
', '.join(params)
|
180
|
+
)
|
181
|
+
|
182
|
+
def __add(self, name: str, main: SQLObject):
|
183
|
+
name = self.format(name, main)
|
184
|
+
self.class_type.add(name, main)
|
185
|
+
if self.extra:
|
186
|
+
main.__call__(**self.extra)
|
187
|
+
|
188
|
+
@classmethod
|
189
|
+
def get_instance(cls):
|
190
|
+
obj = Function.instance.get(cls.__name__)
|
191
|
+
if not obj:
|
192
|
+
obj = cls()
|
193
|
+
return obj
|
194
|
+
|
195
|
+
@classmethod
|
196
|
+
def add(cls, name: str, main: SQLObject):
|
197
|
+
cls.get_instance().__add(name, main)
|
198
|
+
|
199
|
+
|
200
|
+
# ---- String Functions: ---------------------------------
|
201
|
+
class SubString(Function):
|
202
|
+
...
|
203
|
+
|
204
|
+
# ---- Numeric Functions: --------------------------------
|
205
|
+
class Round(Function):
|
206
|
+
...
|
207
|
+
|
208
|
+
# --- Date Functions: ------------------------------------
|
209
|
+
class DateDiff(Function):
|
210
|
+
...
|
211
|
+
class Extract(Function):
|
212
|
+
...
|
213
|
+
class DatePart(Function):
|
214
|
+
...
|
215
|
+
class Current_Date(Function):
|
216
|
+
...
|
217
|
+
|
218
|
+
class Aggregate:
|
219
|
+
break_lines: bool = True
|
220
|
+
|
221
|
+
def over(self, **args):
|
222
|
+
keywords = ' '.join(
|
223
|
+
'{}{} BY {}'.format(
|
224
|
+
'\n\t\t' if self.break_lines else '',
|
225
|
+
key.upper(), args[key]
|
226
|
+
) for key in ('partition', 'order')
|
227
|
+
if key in args
|
228
|
+
)
|
229
|
+
if keywords and self.break_lines:
|
230
|
+
keywords += '\n\t'
|
231
|
+
self.pattern = '{}({})' + f' OVER({keywords})'
|
232
|
+
return self
|
233
|
+
|
234
|
+
|
235
|
+
# ---- Aggregate Functions: -------------------------------
|
236
|
+
class Avg(Aggregate, Function):
|
237
|
+
...
|
238
|
+
class Min(Aggregate, Function):
|
239
|
+
...
|
240
|
+
class Max(Aggregate, Function):
|
241
|
+
...
|
242
|
+
class Sum(Aggregate, Function):
|
243
|
+
...
|
244
|
+
class Count(Aggregate, Function):
|
245
|
+
...
|
246
|
+
|
247
|
+
# ---- Conversions and other Functions: ---------------------
|
248
|
+
class Coalesce(Function):
|
249
|
+
...
|
250
|
+
class Cast(Function):
|
251
|
+
...
|
252
|
+
|
253
|
+
|
166
254
|
class ExpressionField:
|
167
255
|
def __init__(self, expr: str):
|
168
256
|
self.expr = expr
|
@@ -361,14 +449,9 @@ class Between:
|
|
361
449
|
Where.lte(self.end).add(name, main)
|
362
450
|
|
363
451
|
|
364
|
-
class
|
365
|
-
ASC = ''
|
366
|
-
DESC = ' DESC'
|
367
|
-
|
368
|
-
class OrderBy:
|
369
|
-
sort: SortType = SortType.ASC
|
452
|
+
class Clause:
|
370
453
|
@classmethod
|
371
|
-
def
|
454
|
+
def format(cls, name: str, main: SQLObject) -> str:
|
372
455
|
def is_function() -> bool:
|
373
456
|
diff = main.diff(SELECT, [name.lower()], True)
|
374
457
|
FUNCTION_CLASS = {f.__name__.lower(): f for f in Function.__subclasses__()}
|
@@ -378,13 +461,28 @@ class OrderBy:
|
|
378
461
|
name = found[0].replace('_', '')
|
379
462
|
elif main.alias and not is_function():
|
380
463
|
name = f'{main.alias}.{name}'
|
464
|
+
return name
|
465
|
+
|
466
|
+
|
467
|
+
class SortType(Enum):
|
468
|
+
ASC = ''
|
469
|
+
DESC = ' DESC'
|
470
|
+
|
471
|
+
|
472
|
+
class OrderBy(Clause):
|
473
|
+
sort: SortType = SortType.ASC
|
474
|
+
|
475
|
+
@classmethod
|
476
|
+
def add(cls, name: str, main: SQLObject):
|
477
|
+
name = cls.format(name, main)
|
381
478
|
main.values.setdefault(ORDER_BY, []).append(name+cls.sort.value)
|
382
479
|
|
383
480
|
|
384
|
-
class GroupBy:
|
385
|
-
@
|
386
|
-
def add(name: str, main: SQLObject):
|
387
|
-
|
481
|
+
class GroupBy(Clause):
|
482
|
+
@classmethod
|
483
|
+
def add(cls, name: str, main: SQLObject):
|
484
|
+
name = cls.format(name, main)
|
485
|
+
main.values.setdefault(GROUP_BY, []).append(name)
|
388
486
|
|
389
487
|
|
390
488
|
class Having:
|
@@ -1095,9 +1193,8 @@ class Select(SQLObject):
|
|
1095
1193
|
return self.translate_to(QueryLanguage)
|
1096
1194
|
|
1097
1195
|
def __call__(self, **values):
|
1098
|
-
to_list = lambda x: x if isinstance(x, list) else [x]
|
1099
1196
|
for name, params in values.items():
|
1100
|
-
for obj in
|
1197
|
+
for obj in TO_LIST(params):
|
1101
1198
|
obj.add(name, self)
|
1102
1199
|
return self
|
1103
1200
|
|
@@ -1260,22 +1357,3 @@ def detect(text: str) -> Select:
|
|
1260
1357
|
for query in query_list[1:]:
|
1261
1358
|
result += query
|
1262
1359
|
return result
|
1263
|
-
|
1264
|
-
|
1265
|
-
if __name__ == "__main__":
|
1266
|
-
print('░▒▓▒░'*20)
|
1267
|
-
p, c, a = Select.parse(
|
1268
|
-
'''
|
1269
|
-
Professor(?nome="Júlio Cascalles", id)
|
1270
|
-
<- Curso@disciplina(professor, aluno) ->
|
1271
|
-
Aluno(id ^count$qtd_alunos)
|
1272
|
-
''', CypherParser
|
1273
|
-
)
|
1274
|
-
print(p)
|
1275
|
-
print('-'*50)
|
1276
|
-
print(c)
|
1277
|
-
print('-'*50)
|
1278
|
-
print(a)
|
1279
|
-
print('='*50)
|
1280
|
-
print(a + c + p)
|
1281
|
-
print('░▒▓▒░'*20)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version:
|
3
|
+
Version: 1.25
|
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
|
@@ -427,7 +427,7 @@ ORDER BY
|
|
427
427
|
It is useful to write a query in a few lines, without specifying the script type (cypher, mongoDB, SQL, Neo4J...)
|
428
428
|
### Examples:
|
429
429
|
|
430
|
-
> **1 - Relationship**
|
430
|
+
> **13.1 - Relationship**
|
431
431
|
```
|
432
432
|
query = detect(
|
433
433
|
'MATCH(c:Customer)<-[:Order]->(p:Product)RETURN c, p'
|
@@ -439,7 +439,7 @@ print(query)
|
|
439
439
|
Order ord
|
440
440
|
LEFT JOIN Customer cus ON (ord.customer_id = cus.id)
|
441
441
|
RIGHT JOIN Product pro ON (ord.product_id = pro.id)
|
442
|
-
> **2 - Grouping**
|
442
|
+
> **13.2 - Grouping**
|
443
443
|
```
|
444
444
|
query = detect(
|
445
445
|
'People@gender(avg$age?region="SOUTH"^count$qtde)'
|
@@ -460,7 +460,7 @@ print(query)
|
|
460
460
|
ORDER BY
|
461
461
|
peo.qtde
|
462
462
|
|
463
|
-
> **3 - Many conditions...**
|
463
|
+
> **13.3 - Many conditions...**
|
464
464
|
```
|
465
465
|
print( detect('''
|
466
466
|
db.people.find({
|
@@ -491,7 +491,7 @@ print(query)
|
|
491
491
|
ORDER BY
|
492
492
|
peo.user_id DESC
|
493
493
|
|
494
|
-
> **4 - Relations with same table twice (or more)**
|
494
|
+
> **13.4 - Relations with same table twice (or more)**
|
495
495
|
|
496
496
|
Automatically assigns aliases to each side of the relationship (In this example, one user invites another to add to their contact list)
|
497
497
|
```
|
@@ -516,3 +516,54 @@ It consists of the inverse process of parsing: From a Select object, it returns
|
|
516
516
|
* QueryLanguage - default
|
517
517
|
* MongoDBLanguage
|
518
518
|
* Neo4JLanguage
|
519
|
+
|
520
|
+
---
|
521
|
+
### 14 - Window Function
|
522
|
+
|
523
|
+
Aggregation functions (Avg, Min, Max, Sum, Count) have the **over** method...
|
524
|
+
|
525
|
+
query=Select(
|
526
|
+
'Enrollment e',
|
527
|
+
payment=Sum().over(
|
528
|
+
partition='student_id', order='due_date'
|
529
|
+
).As('sum_per_student')
|
530
|
+
)
|
531
|
+
|
532
|
+
...that generates the following query:
|
533
|
+
|
534
|
+
```
|
535
|
+
SELECT
|
536
|
+
Sum(e.payment) OVER(
|
537
|
+
PARTITION BY student_id
|
538
|
+
ORDER BY due_date
|
539
|
+
) as sum_per_student
|
540
|
+
FROM
|
541
|
+
Enrollment e
|
542
|
+
```
|
543
|
+
---
|
544
|
+
### 15 - The `As` method:
|
545
|
+
query=Select(
|
546
|
+
'Customers c',
|
547
|
+
phone=[
|
548
|
+
Not.is_null(),
|
549
|
+
SubString(1, 4).As('area_code', GroupBy)
|
550
|
+
],
|
551
|
+
customer_id=[
|
552
|
+
Count().As('customer_count', OrderBy),
|
553
|
+
Having.count(gt(5))
|
554
|
+
]
|
555
|
+
)
|
556
|
+
You can use the result of a function as a new field -- and optionally use it in ORDER BY and/or GROUP BY clause(s):
|
557
|
+
```
|
558
|
+
SELECT
|
559
|
+
SubString(c.phone, 1, 4) as area_code,
|
560
|
+
Count(c.customer_id) as customer_count
|
561
|
+
FROM
|
562
|
+
Customers c
|
563
|
+
WHERE
|
564
|
+
NOT c.phone IS NULL
|
565
|
+
GROUP BY
|
566
|
+
area_code HAVING Count(c.customer_id) > 5
|
567
|
+
ORDER BY
|
568
|
+
customer_count
|
569
|
+
```
|
@@ -0,0 +1,7 @@
|
|
1
|
+
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
+
sql_blocks/sql_blocks.py,sha256=QxopGbzRtBgG_9oAs1ovBATFplDqrzEFSLwyKbOL-W8,45499
|
3
|
+
sql_blocks-1.25.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-1.25.dist-info/METADATA,sha256=cvHewfK05stZRHfWZeyIMMtKRjdOGxZN38o94JkVS-U,13421
|
5
|
+
sql_blocks-1.25.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-1.25.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-1.25.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=nsIT8JEeYHw3KIkzAUHUdzUAR_i2asJmdytsvW0hYKc,43342
|
3
|
-
sql_blocks-0.31.89.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-0.31.89.dist-info/METADATA,sha256=xKHVfM76nzvZBr-TYC5_PN4bAvYW9oWoi5ChPIPdbDE,12202
|
5
|
-
sql_blocks-0.31.89.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-0.31.89.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-0.31.89.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|