sql-blocks 0.0.1__tar.gz → 0.0.3__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.1/sql_blocks.egg-info → sql_blocks-0.0.3}/PKG-INFO +22 -4
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/README.md +21 -3
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/pyproject.toml +1 -1
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/setup.py +1 -1
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/sql_blocks/sql_blocks.py +80 -24
- {sql_blocks-0.0.1 → sql_blocks-0.0.3/sql_blocks.egg-info}/PKG-INFO +22 -4
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/tests/tests.py +7 -14
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/LICENSE +0 -0
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/setup.cfg +0 -0
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/sql_blocks/__init__.py +0 -0
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/sql_blocks.egg-info/SOURCES.txt +0 -0
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/sql_blocks.egg-info/dependency_links.txt +0 -0
- {sql_blocks-0.0.1 → sql_blocks-0.0.3}/sql_blocks.egg-info/top_level.txt +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.3
|
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
|
@@ -35,7 +35,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
35
35
|
|
36
36
|
- Select(
|
37
37
|
'Actor a',
|
38
|
-
name=NamedField('actors_name'
|
38
|
+
name=NamedField('actors_name', Distinct)
|
39
39
|
)
|
40
40
|
|
41
41
|
---
|
@@ -53,7 +53,6 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
53
53
|
`a = Select( 'Actor a', age=Between(45, 69) )`
|
54
54
|
|
55
55
|
3.2 -- Sub-queries:
|
56
|
-
|
57
56
|
```
|
58
57
|
query = Select('Movie m', title=Field,
|
59
58
|
id=SubSelect(
|
@@ -85,6 +84,15 @@ query = Select('Movie m', title=Field,
|
|
85
84
|
```
|
86
85
|
> Could be AND=Options(...)
|
87
86
|
|
87
|
+
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
88
|
+
```
|
89
|
+
franchise
|
90
|
+
based_on_book=Not.is_null()
|
91
|
+
```
|
92
|
+
|
93
|
+
3.5 -- List of values
|
94
|
+
hash_tag=Where.list(['space', 'monster', 'gore'])
|
95
|
+
|
88
96
|
---
|
89
97
|
### 4 - A field can be two things at the same time:
|
90
98
|
|
@@ -292,5 +300,15 @@ m2 = Select(
|
|
292
300
|
**m1 == m2 # --- True!**
|
293
301
|
|
294
302
|
---
|
295
|
-
---
|
296
303
|
|
304
|
+
### 10 - CASE...WHEN...THEN
|
305
|
+
Select(
|
306
|
+
'Product',
|
307
|
+
label=Case('price').when(
|
308
|
+
lt(50), 'cheap'
|
309
|
+
).when(
|
310
|
+
gt(100), 'expensive'
|
311
|
+
).else_value(
|
312
|
+
'normal'
|
313
|
+
)
|
314
|
+
)
|
@@ -20,7 +20,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
20
20
|
|
21
21
|
- Select(
|
22
22
|
'Actor a',
|
23
|
-
name=NamedField('actors_name'
|
23
|
+
name=NamedField('actors_name', Distinct)
|
24
24
|
)
|
25
25
|
|
26
26
|
---
|
@@ -38,7 +38,6 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
38
38
|
`a = Select( 'Actor a', age=Between(45, 69) )`
|
39
39
|
|
40
40
|
3.2 -- Sub-queries:
|
41
|
-
|
42
41
|
```
|
43
42
|
query = Select('Movie m', title=Field,
|
44
43
|
id=SubSelect(
|
@@ -70,6 +69,15 @@ query = Select('Movie m', title=Field,
|
|
70
69
|
```
|
71
70
|
> Could be AND=Options(...)
|
72
71
|
|
72
|
+
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
73
|
+
```
|
74
|
+
franchise
|
75
|
+
based_on_book=Not.is_null()
|
76
|
+
```
|
77
|
+
|
78
|
+
3.5 -- List of values
|
79
|
+
hash_tag=Where.list(['space', 'monster', 'gore'])
|
80
|
+
|
73
81
|
---
|
74
82
|
### 4 - A field can be two things at the same time:
|
75
83
|
|
@@ -277,5 +285,15 @@ m2 = Select(
|
|
277
285
|
**m1 == m2 # --- True!**
|
278
286
|
|
279
287
|
---
|
280
|
-
---
|
281
288
|
|
289
|
+
### 10 - CASE...WHEN...THEN
|
290
|
+
Select(
|
291
|
+
'Product',
|
292
|
+
label=Case('price').when(
|
293
|
+
lt(50), 'cheap'
|
294
|
+
).when(
|
295
|
+
gt(100), 'expensive'
|
296
|
+
).else_value(
|
297
|
+
'normal'
|
298
|
+
)
|
299
|
+
)
|
@@ -11,6 +11,7 @@ KEYWORD = {
|
|
11
11
|
SELECT, FROM, WHERE, GROUP_BY, ORDER_BY, LIMIT = KEYWORD.keys()
|
12
12
|
USUAL_KEYS = [SELECT, WHERE, GROUP_BY, ORDER_BY]
|
13
13
|
|
14
|
+
|
14
15
|
class SQLObject:
|
15
16
|
def __init__(self, table_name: str=''):
|
16
17
|
self.alias = ''
|
@@ -123,25 +124,36 @@ class ForeignKey:
|
|
123
124
|
def __init__(self, table_name: str):
|
124
125
|
self.table_name = table_name
|
125
126
|
|
127
|
+
@staticmethod
|
128
|
+
def get_key(obj1: SQLObject, obj2: SQLObject) -> tuple:
|
129
|
+
return obj1.table_name, obj2.table_name
|
130
|
+
|
126
131
|
def add(self, name: str, main: SQLObject):
|
127
|
-
key =
|
128
|
-
ForeignKey.references[key] = name
|
132
|
+
key = self.get_key(main, self)
|
133
|
+
ForeignKey.references[key] = (name, '')
|
129
134
|
|
130
135
|
@classmethod
|
131
|
-
def find(cls, obj1: SQLObject, obj2: SQLObject) ->
|
132
|
-
key =
|
133
|
-
|
136
|
+
def find(cls, obj1: SQLObject, obj2: SQLObject) -> tuple:
|
137
|
+
key = cls.get_key(obj1, obj2)
|
138
|
+
a, b = cls.references.get(key, ('', ''))
|
139
|
+
return a, (b or obj2.key_field)
|
140
|
+
|
141
|
+
|
142
|
+
def quoted(value) -> str:
|
143
|
+
if isinstance(value, str):
|
144
|
+
value = f"'{value}'"
|
145
|
+
return str(value)
|
134
146
|
|
135
147
|
|
136
148
|
class Where:
|
149
|
+
prefix = ''
|
150
|
+
|
137
151
|
def __init__(self, expr: str):
|
138
|
-
self.expr = expr
|
152
|
+
self.expr = f'{self.prefix}{expr}'
|
139
153
|
|
140
154
|
@classmethod
|
141
155
|
def __constructor(cls, operator: str, value):
|
142
|
-
|
143
|
-
return cls(expr=f"{operator} '{value}'")
|
144
|
-
return cls(expr=f'{operator} {value}')
|
156
|
+
return cls(expr=f'{operator} {quoted(value)}')
|
145
157
|
|
146
158
|
@classmethod
|
147
159
|
def eq(cls, value):
|
@@ -164,8 +176,18 @@ class Where:
|
|
164
176
|
return cls.__constructor('<', value)
|
165
177
|
|
166
178
|
@classmethod
|
167
|
-
def lte(cls, value)
|
179
|
+
def lte(cls, value):
|
168
180
|
return cls.__constructor('<=', value)
|
181
|
+
|
182
|
+
@classmethod
|
183
|
+
def is_null(cls):
|
184
|
+
return cls('IS NULL')
|
185
|
+
|
186
|
+
@classmethod
|
187
|
+
def list(cls, values):
|
188
|
+
if isinstance(values, list):
|
189
|
+
values = ','.join(quoted(v) for v in values)
|
190
|
+
return cls(f'IN ({values})')
|
169
191
|
|
170
192
|
def add(self, name: str, main: SQLObject):
|
171
193
|
main.values.setdefault(WHERE, []).append('{} {}'.format(
|
@@ -173,6 +195,41 @@ class Where:
|
|
173
195
|
))
|
174
196
|
|
175
197
|
|
198
|
+
class Not(Where):
|
199
|
+
prefix = 'NOT '
|
200
|
+
|
201
|
+
@classmethod
|
202
|
+
def eq(cls, value):
|
203
|
+
return Where.__constructor('<>', value)
|
204
|
+
|
205
|
+
|
206
|
+
class Case:
|
207
|
+
def __init__(self, field: str):
|
208
|
+
self.__conditions = {}
|
209
|
+
self.default = None
|
210
|
+
self.field = field
|
211
|
+
|
212
|
+
def when(self, condition: Where, result: str):
|
213
|
+
self.__conditions[result] = condition
|
214
|
+
return self
|
215
|
+
|
216
|
+
def else_value(self, default: str):
|
217
|
+
self.default = default
|
218
|
+
return self
|
219
|
+
|
220
|
+
def add(self, name: str, main: SQLObject):
|
221
|
+
field = Field.format(self.field, main)
|
222
|
+
default = quoted(self.default)
|
223
|
+
name = 'CASE \n{}\n\tEND AS {}'.format(
|
224
|
+
'\n'.join(
|
225
|
+
f'\t\tWHEN {field} {cond.expr} THEN {quoted(res)}'
|
226
|
+
for res, cond in self.__conditions.items()
|
227
|
+
) + f'\n\t\tELSE {default}' if default else '',
|
228
|
+
name
|
229
|
+
)
|
230
|
+
main.values.setdefault(SELECT, []).append(name)
|
231
|
+
|
232
|
+
|
176
233
|
class Options:
|
177
234
|
def __init__(self, **values):
|
178
235
|
self.__children: dict = values
|
@@ -266,10 +323,7 @@ class Select(SQLObject):
|
|
266
323
|
|
267
324
|
def __init__(self, table_name: str='', **values):
|
268
325
|
super().__init__(table_name)
|
269
|
-
|
270
|
-
for name, params in values.items():
|
271
|
-
for obj in to_list(params):
|
272
|
-
obj.add(name, self)
|
326
|
+
self.__call__(**values)
|
273
327
|
self.break_lines = True
|
274
328
|
|
275
329
|
def add(self, name: str, main: SQLObject):
|
@@ -292,17 +346,17 @@ class Select(SQLObject):
|
|
292
346
|
update_values(key, self.values.get(key, []))
|
293
347
|
|
294
348
|
def __add__(self, other: SQLObject):
|
295
|
-
foreign_field,
|
349
|
+
foreign_field, primary_key = ForeignKey.find(self, other)
|
296
350
|
if not foreign_field:
|
297
|
-
foreign_field,
|
351
|
+
foreign_field, primary_key = ForeignKey.find(other, self)
|
298
352
|
if foreign_field:
|
299
|
-
if primary_key:
|
300
|
-
PrimaryKey.add(primary_key
|
353
|
+
if primary_key and not self.key_field:
|
354
|
+
PrimaryKey.add(primary_key, self)
|
301
355
|
self.add(foreign_field, other)
|
302
356
|
return other
|
303
357
|
raise ValueError(f'No relationship found between {self.table_name} and {other.table_name}.')
|
304
358
|
elif primary_key:
|
305
|
-
PrimaryKey.add(primary_key
|
359
|
+
PrimaryKey.add(primary_key, other)
|
306
360
|
other.add(foreign_field, self)
|
307
361
|
return self
|
308
362
|
|
@@ -319,10 +373,12 @@ class Select(SQLObject):
|
|
319
373
|
return f'{select}{_from}{where}{groupBy}{orderBy}{limit}'.strip()
|
320
374
|
|
321
375
|
def __call__(self, **values):
|
322
|
-
|
323
|
-
|
376
|
+
to_list = lambda x: x if isinstance(x, list) else [x]
|
377
|
+
for name, params in values.items():
|
378
|
+
for obj in to_list(params):
|
379
|
+
obj.add(name, self)
|
324
380
|
return self
|
325
|
-
|
381
|
+
|
326
382
|
def __eq__(self, other: SQLObject) -> bool:
|
327
383
|
def sorted_values(obj: SQLObject, key: str) -> list:
|
328
384
|
return sorted(obj.values.get(key, []))
|
@@ -368,7 +424,7 @@ class Select(SQLObject):
|
|
368
424
|
fld.strip() for fld in re.split(
|
369
425
|
separator, values[key]
|
370
426
|
) if len(tables) == 1
|
371
|
-
or re.findall(f'^[( ]*{obj.alias}
|
427
|
+
or re.findall(f'^[( ]*{obj.alias}.', fld)
|
372
428
|
]
|
373
429
|
obj.values[key] = [
|
374
430
|
f'{obj.alias}.{f}' if not '.' in f else f for f in fields
|
@@ -380,4 +436,4 @@ class Select(SQLObject):
|
|
380
436
|
class SubSelect(Select):
|
381
437
|
def add(self, name: str, main: SQLObject):
|
382
438
|
self.break_lines = False
|
383
|
-
Where(
|
439
|
+
Where.list(self).add(name, main)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
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
|
@@ -35,7 +35,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
35
35
|
|
36
36
|
- Select(
|
37
37
|
'Actor a',
|
38
|
-
name=NamedField('actors_name'
|
38
|
+
name=NamedField('actors_name', Distinct)
|
39
39
|
)
|
40
40
|
|
41
41
|
---
|
@@ -53,7 +53,6 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
53
53
|
`a = Select( 'Actor a', age=Between(45, 69) )`
|
54
54
|
|
55
55
|
3.2 -- Sub-queries:
|
56
|
-
|
57
56
|
```
|
58
57
|
query = Select('Movie m', title=Field,
|
59
58
|
id=SubSelect(
|
@@ -85,6 +84,15 @@ query = Select('Movie m', title=Field,
|
|
85
84
|
```
|
86
85
|
> Could be AND=Options(...)
|
87
86
|
|
87
|
+
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
88
|
+
```
|
89
|
+
franchise
|
90
|
+
based_on_book=Not.is_null()
|
91
|
+
```
|
92
|
+
|
93
|
+
3.5 -- List of values
|
94
|
+
hash_tag=Where.list(['space', 'monster', 'gore'])
|
95
|
+
|
88
96
|
---
|
89
97
|
### 4 - A field can be two things at the same time:
|
90
98
|
|
@@ -292,5 +300,15 @@ m2 = Select(
|
|
292
300
|
**m1 == m2 # --- True!**
|
293
301
|
|
294
302
|
---
|
295
|
-
---
|
296
303
|
|
304
|
+
### 10 - CASE...WHEN...THEN
|
305
|
+
Select(
|
306
|
+
'Product',
|
307
|
+
label=Case('price').when(
|
308
|
+
lt(50), 'cheap'
|
309
|
+
).when(
|
310
|
+
gt(100), 'expensive'
|
311
|
+
).else_value(
|
312
|
+
'normal'
|
313
|
+
)
|
314
|
+
)
|
@@ -1,6 +1,9 @@
|
|
1
|
-
from sql_blocks import *
|
1
|
+
from sql_blocks.sql_blocks import *
|
2
2
|
|
3
3
|
|
4
|
+
Select.join_type = JoinType.LEFT
|
5
|
+
OrderBy.sort = SortType.DESC
|
6
|
+
|
4
7
|
def best_movies() -> SubSelect:
|
5
8
|
return SubSelect(
|
6
9
|
'Review r', movie=[GroupBy, Distinct], rate=Having.avg(Where.gt(4.5))
|
@@ -24,7 +27,7 @@ def detached_objects() -> tuple:
|
|
24
27
|
)
|
25
28
|
return select_actor(), select_cast(), select_movie()
|
26
29
|
|
27
|
-
def
|
30
|
+
def query_reference() -> Select:
|
28
31
|
return Select('Actor a', age=Between(45, 69),
|
29
32
|
cast=Select(
|
30
33
|
Cast=Table('role'), id=PrimaryKey,
|
@@ -65,8 +68,8 @@ def single_text_to_objects():
|
|
65
68
|
|
66
69
|
def many_texts_to_objects():
|
67
70
|
ForeignKey.references = {
|
68
|
-
'
|
69
|
-
'
|
71
|
+
('Actor', 'Cast'): ('cast', 'id'),
|
72
|
+
('Cast', 'Movie'): ('movie', 'id'),
|
70
73
|
}
|
71
74
|
actor = Select.parse('''
|
72
75
|
SELECT name as actors_name FROM Actor a
|
@@ -78,13 +81,3 @@ def many_texts_to_objects():
|
|
78
81
|
WHERE ( m.genre = 'Sci-Fi' OR m.awards LIKE '%Oscar%' ) GROUP BY director
|
79
82
|
""")[0]
|
80
83
|
return actor, cast, movie
|
81
|
-
|
82
|
-
Select.join_type = JoinType.LEFT
|
83
|
-
OrderBy.sort = SortType.DESC
|
84
|
-
b = best_movies()
|
85
|
-
for func in [single_text_to_objects, detached_objects, many_texts_to_objects]:
|
86
|
-
a, c, m = func()
|
87
|
-
m.delete('director')
|
88
|
-
m = m(id=b)
|
89
|
-
query = a + (m + c)
|
90
|
-
assert query == expected_result()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|