sql-blocks 0.1.4__tar.gz → 0.2.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 0.1.4
3
+ Version: 0.2.1
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
@@ -24,7 +24,7 @@ _Note that an alias "act" has been added._
24
24
  You can specify your own alias: `a = Select('Actor a')`
25
25
 
26
26
  ---
27
- ### 2 - You can also add a field, like this...
27
+ ### 2 - You can also add a field, contains this...
28
28
 
29
29
  * a = Select('Actor a', **name=Field**)
30
30
 
@@ -55,12 +55,12 @@ You can specify your own alias: `a = Select('Actor a')`
55
55
  ---
56
56
 
57
57
  ### 3 - To set conditions, use **Where**:
58
- * For example, `a = Select(... age=Where.gt(45) )`
58
+ * For example, `a = Select(... age=gt(45) )`
59
59
 
60
60
  Some possible conditions:
61
- * field=Where.eq(value) - ...the field is EQUAL to the value;
62
- * field=Where.gt(value) - ...the field is GREATER than the value;
63
- * field=Where.lt(value) - ...the field is LESS than the value;
61
+ * field=eq(value) - ...the field is EQUAL to the value;
62
+ * field=gt(value) - ...the field is GREATER than the value;
63
+ * field=lt(value) - ...the field is LESS than the value;
64
64
 
65
65
  3.1 -- If you want to filter the field on a range of values:
66
66
 
@@ -71,7 +71,7 @@ You can specify your own alias: `a = Select('Actor a')`
71
71
  query = Select('Movie m', title=Field,
72
72
  id=SelectIN(
73
73
  'Review r',
74
- rate=Where.gt(4.5),
74
+ rate=gt(4.5),
75
75
  movie_id=Distinct
76
76
  )
77
77
  )
@@ -92,8 +92,8 @@ query = Select('Movie m', title=Field,
92
92
  3.3 -- Optional conditions:
93
93
  ```
94
94
  OR=Options(
95
- genre=Where.eq("Sci-Fi"),
96
- awards=Where.like("Oscar")
95
+ genre=eq("Sci-Fi"),
96
+ awards=contains("Oscar")
97
97
  )
98
98
  ```
99
99
  > Could be AND=Options(...)
@@ -105,7 +105,7 @@ based_on_book=Not.is_null()
105
105
 
106
106
  3.5 -- List of values
107
107
  ```
108
- hash_tag=Where.list(['space', 'monster', 'gore'])
108
+ hash_tag=inside(['space', 'monster', 'gore'])
109
109
  ```
110
110
 
111
111
  ---
@@ -113,11 +113,11 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
113
113
 
114
114
  * m = Select('Movie m' release_date=[Field, OrderBy])
115
115
  - This means that the field will appear in the results and also that the query will be ordered by that field.
116
- * Applying **GROUP BY** to item 3.2, it would look like this:
116
+ * Applying **GROUP BY** to item 3.2, it would look contains this:
117
117
  ```
118
118
  SelectIN(
119
119
  'Review r', movie=[GroupBy, Distinct],
120
- rate=Having.avg(Where.gt(4.5))
120
+ rate=Having.avg(gt(4.5))
121
121
  )
122
122
  ```
123
123
  ---
@@ -299,7 +299,7 @@ m = Select...
299
299
  ```
300
300
  best_movies = SelectIN(
301
301
  Review=Table('role'),
302
- rate=[GroupBy, Having.avg(Where.gt(4.5))]
302
+ rate=[GroupBy, Having.avg(gt(4.5))]
303
303
  )
304
304
  m1 = Select(
305
305
  Movie=Table('title,release_date'),
@@ -320,9 +320,9 @@ m2 = Select(
320
320
  Select(
321
321
  'Product',
322
322
  label=Case('price').when(
323
- Where.lt(50), 'cheap'
323
+ lt(50), 'cheap'
324
324
  ).when(
325
- Where.gt(100), 'expensive'
325
+ gt(100), 'expensive'
326
326
  ).else_value(
327
327
  'normal'
328
328
  )
@@ -360,3 +360,22 @@ m2 = Select(
360
360
  * Replace `YEAR` function with date range comparison.
361
361
 
362
362
  > The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
363
+
364
+ ---
365
+
366
+ ### 12 - Adding multiple fields at once
367
+ ```
368
+ query = Select('post p')
369
+ query.add_fields(
370
+ 'user_id, created_at',
371
+ order_by=True, group_by=True
372
+ )
373
+ ```
374
+ ...is the same as...
375
+ ```
376
+ query = Select(
377
+ 'post p',
378
+ user_id=[Field, GroupBy, OrderBy],
379
+ created_at=[Field, GroupBy, OrderBy]
380
+ )
381
+ ```
@@ -9,7 +9,7 @@ _Note that an alias "act" has been added._
9
9
  You can specify your own alias: `a = Select('Actor a')`
10
10
 
11
11
  ---
12
- ### 2 - You can also add a field, like this...
12
+ ### 2 - You can also add a field, contains this...
13
13
 
14
14
  * a = Select('Actor a', **name=Field**)
15
15
 
@@ -40,12 +40,12 @@ You can specify your own alias: `a = Select('Actor a')`
40
40
  ---
41
41
 
42
42
  ### 3 - To set conditions, use **Where**:
43
- * For example, `a = Select(... age=Where.gt(45) )`
43
+ * For example, `a = Select(... age=gt(45) )`
44
44
 
45
45
  Some possible conditions:
46
- * field=Where.eq(value) - ...the field is EQUAL to the value;
47
- * field=Where.gt(value) - ...the field is GREATER than the value;
48
- * field=Where.lt(value) - ...the field is LESS than the value;
46
+ * field=eq(value) - ...the field is EQUAL to the value;
47
+ * field=gt(value) - ...the field is GREATER than the value;
48
+ * field=lt(value) - ...the field is LESS than the value;
49
49
 
50
50
  3.1 -- If you want to filter the field on a range of values:
51
51
 
@@ -56,7 +56,7 @@ You can specify your own alias: `a = Select('Actor a')`
56
56
  query = Select('Movie m', title=Field,
57
57
  id=SelectIN(
58
58
  'Review r',
59
- rate=Where.gt(4.5),
59
+ rate=gt(4.5),
60
60
  movie_id=Distinct
61
61
  )
62
62
  )
@@ -77,8 +77,8 @@ query = Select('Movie m', title=Field,
77
77
  3.3 -- Optional conditions:
78
78
  ```
79
79
  OR=Options(
80
- genre=Where.eq("Sci-Fi"),
81
- awards=Where.like("Oscar")
80
+ genre=eq("Sci-Fi"),
81
+ awards=contains("Oscar")
82
82
  )
83
83
  ```
84
84
  > Could be AND=Options(...)
@@ -90,7 +90,7 @@ based_on_book=Not.is_null()
90
90
 
91
91
  3.5 -- List of values
92
92
  ```
93
- hash_tag=Where.list(['space', 'monster', 'gore'])
93
+ hash_tag=inside(['space', 'monster', 'gore'])
94
94
  ```
95
95
 
96
96
  ---
@@ -98,11 +98,11 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
98
98
 
99
99
  * m = Select('Movie m' release_date=[Field, OrderBy])
100
100
  - This means that the field will appear in the results and also that the query will be ordered by that field.
101
- * Applying **GROUP BY** to item 3.2, it would look like this:
101
+ * Applying **GROUP BY** to item 3.2, it would look contains this:
102
102
  ```
103
103
  SelectIN(
104
104
  'Review r', movie=[GroupBy, Distinct],
105
- rate=Having.avg(Where.gt(4.5))
105
+ rate=Having.avg(gt(4.5))
106
106
  )
107
107
  ```
108
108
  ---
@@ -284,7 +284,7 @@ m = Select...
284
284
  ```
285
285
  best_movies = SelectIN(
286
286
  Review=Table('role'),
287
- rate=[GroupBy, Having.avg(Where.gt(4.5))]
287
+ rate=[GroupBy, Having.avg(gt(4.5))]
288
288
  )
289
289
  m1 = Select(
290
290
  Movie=Table('title,release_date'),
@@ -305,9 +305,9 @@ m2 = Select(
305
305
  Select(
306
306
  'Product',
307
307
  label=Case('price').when(
308
- Where.lt(50), 'cheap'
308
+ lt(50), 'cheap'
309
309
  ).when(
310
- Where.gt(100), 'expensive'
310
+ gt(100), 'expensive'
311
311
  ).else_value(
312
312
  'normal'
313
313
  )
@@ -344,4 +344,23 @@ m2 = Select(
344
344
  * Auto includes fields present in `ORDER/GROUP BY`;
345
345
  * Replace `YEAR` function with date range comparison.
346
346
 
347
- > The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
347
+ > The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
348
+
349
+ ---
350
+
351
+ ### 12 - Adding multiple fields at once
352
+ ```
353
+ query = Select('post p')
354
+ query.add_fields(
355
+ 'user_id, created_at',
356
+ order_by=True, group_by=True
357
+ )
358
+ ```
359
+ ...is the same as...
360
+ ```
361
+ query = Select(
362
+ 'post p',
363
+ user_id=[Field, GroupBy, OrderBy],
364
+ created_at=[Field, GroupBy, OrderBy]
365
+ )
366
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sql_blocks"
3
- version = "0.1.4"
3
+ version = "0.2.1"
4
4
  authors = [
5
5
  { name="Julio Cascalles", email="julio.cascalles@outlook.com" },
6
6
  ]
@@ -3,7 +3,7 @@ from setuptools import setup
3
3
 
4
4
  setup(
5
5
  name = 'sql_blocks',
6
- version = '0.1.4',
6
+ version = '0.2.1',
7
7
  author = 'Júlio Cascalles',
8
8
  author_email = 'julio.cascalles@outlook.com',
9
9
  packages = ['sql_blocks'],
@@ -176,17 +176,27 @@ class ExpressionField:
176
176
  a=main.alias, f=name, t=main.table_name
177
177
  )
178
178
 
179
+ class FieldList:
180
+ separator = ','
179
181
 
180
- class Table:
181
- def __init__(self, fields: list=[]):
182
+ def __init__(self, fields: list=[], class_types = [Field]):
182
183
  if isinstance(fields, str):
183
- fields = [f.strip() for f in fields.split(',')]
184
+ fields = [
185
+ f.strip() for f in fields.split(self.separator)
186
+ ]
184
187
  self.fields = fields
188
+ self.class_types = class_types
185
189
 
186
190
  def add(self, name: str, main: SQLObject):
187
- main.set_table(name)
188
191
  for field in self.fields:
189
- Field.add(field, main)
192
+ for class_type in self.class_types:
193
+ class_type.add(field, main)
194
+
195
+
196
+ class Table(FieldList):
197
+ def add(self, name: str, main: SQLObject):
198
+ main.set_table(name)
199
+ super().add(name, main)
190
200
 
191
201
 
192
202
  class PrimaryKey:
@@ -237,7 +247,7 @@ class Where:
237
247
  return cls.__constructor('=', value)
238
248
 
239
249
  @classmethod
240
- def like(cls, value: str):
250
+ def contains(cls, value: str):
241
251
  return cls(f"LIKE '%{value}%'")
242
252
 
243
253
  @classmethod
@@ -261,7 +271,7 @@ class Where:
261
271
  return cls('IS NULL')
262
272
 
263
273
  @classmethod
264
- def list(cls, values):
274
+ def inside(cls, values):
265
275
  if isinstance(values, list):
266
276
  values = ','.join(quoted(v) for v in values)
267
277
  return cls(f'IN ({values})')
@@ -272,6 +282,12 @@ class Where:
272
282
  ))
273
283
 
274
284
 
285
+ eq, contains, gt, gte, lt, lte, is_null, inside = (
286
+ getattr(Where, method) for method in
287
+ ('eq', 'contains', 'gt', 'gte', 'lt', 'lte', 'is_null', 'inside')
288
+ )
289
+
290
+
275
291
  class Not(Where):
276
292
  prefix = 'NOT '
277
293
 
@@ -346,9 +362,12 @@ class OrderBy:
346
362
  sort: SortType = SortType.ASC
347
363
  @classmethod
348
364
  def add(cls, name: str, main: SQLObject):
349
- if main.alias:
365
+ found = re.findall(r'^_\d', name)
366
+ if found:
367
+ name = found[0].replace('_', '')
368
+ elif main.alias:
350
369
  name = f'{main.alias}.{name}'
351
- main.values.setdefault(ORDER_BY, []).append(name + cls.sort.value)
370
+ main.values.setdefault(ORDER_BY, []).append(name+cls.sort.value)
352
371
 
353
372
 
354
373
  class GroupBy:
@@ -414,38 +433,39 @@ class Select(SQLObject):
414
433
  self.values.setdefault(key, []).append(value)
415
434
 
416
435
  def add(self, name: str, main: SQLObject):
417
- new_tables = [
436
+ old_tables = main.values.get(FROM, [])
437
+ new_tables = set([
418
438
  '{jt}JOIN {tb} {a2} ON ({a1}.{f1} = {a2}.{f2})'.format(
419
439
  jt=self.join_type.value,
420
440
  tb=self.table_name,
421
441
  a1=main.alias, f1=name,
422
442
  a2=self.alias, f2=self.key_field
423
443
  )
424
- ]
425
- if new_tables not in main.values.get(FROM, []):
426
- new_tables += self.values[FROM][1:]
427
- main.values.setdefault(FROM, []).extend(new_tables)
444
+ ] + old_tables[1:])
445
+ main.values[FROM] = old_tables[:1] + list(new_tables)
428
446
  for key in USUAL_KEYS:
429
447
  main.update_values(key, self.values.get(key, []))
430
448
 
431
449
  def __add__(self, other: SQLObject):
432
- if self.table_name.lower() == other.table_name.lower():
450
+ from copy import deepcopy
451
+ query = deepcopy(self)
452
+ if query.table_name.lower() == other.table_name.lower():
433
453
  for key in USUAL_KEYS:
434
- self.update_values(key, other.values.get(key, []))
435
- return self
436
- foreign_field, primary_key = ForeignKey.find(self, other)
454
+ query.update_values(key, other.values.get(key, []))
455
+ return query
456
+ foreign_field, primary_key = ForeignKey.find(query, other)
437
457
  if not foreign_field:
438
- foreign_field, primary_key = ForeignKey.find(other, self)
458
+ foreign_field, primary_key = ForeignKey.find(other, query)
439
459
  if foreign_field:
440
460
  if primary_key:
441
- PrimaryKey.add(primary_key, self)
442
- self.add(foreign_field, other)
461
+ PrimaryKey.add(primary_key, query)
462
+ query.add(foreign_field, other)
443
463
  return other
444
- raise ValueError(f'No relationship found between {self.table_name} and {other.table_name}.')
464
+ raise ValueError(f'No relationship found between {query.table_name} and {other.table_name}.')
445
465
  elif primary_key:
446
466
  PrimaryKey.add(primary_key, other)
447
- other.add(foreign_field, self)
448
- return self
467
+ other.add(foreign_field, query)
468
+ return query
449
469
 
450
470
  def __str__(self) -> str:
451
471
  TABULATION = '\n\t' if self.break_lines else ' '
@@ -557,13 +577,22 @@ class Select(SQLObject):
557
577
  for rule in rules:
558
578
  rule.apply(self)
559
579
 
580
+ def add_fields(self, fields: list, order_by: bool=False, group_by:bool=False):
581
+ class_types = [Field]
582
+ if order_by:
583
+ class_types += [OrderBy]
584
+ if group_by:
585
+ class_types += [GroupBy]
586
+ FieldList(fields, class_types).add('', self)
587
+
588
+
560
589
 
561
590
  class SelectIN(Select):
562
591
  condition_class = Where
563
592
 
564
593
  def add(self, name: str, main: SQLObject):
565
594
  self.break_lines = False
566
- self.condition_class.list(self).add(name, main)
595
+ self.condition_class.inside(self).add(name, main)
567
596
 
568
597
  SubSelect = SelectIN
569
598
 
@@ -578,6 +607,7 @@ class RulePutLimit(Rule):
578
607
  if need_limit:
579
608
  target.limit()
580
609
 
610
+
581
611
  class RuleSelectIN(Rule):
582
612
  @classmethod
583
613
  def apply(cls, target: Select):
@@ -592,6 +622,7 @@ class RuleSelectIN(Rule):
592
622
  ','.join(t.split('=')[-1].strip() for t in tokens)
593
623
  )
594
624
 
625
+
595
626
  class RuleAutoField(Rule):
596
627
  @classmethod
597
628
  def apply(cls, target: Select):
@@ -603,13 +634,11 @@ class RuleAutoField(Rule):
603
634
  s2 = set(target.values[ORDER_BY])
604
635
  target.values.setdefault(SELECT, []).extend( list(s2-s1) )
605
636
 
637
+
606
638
  class RuleLogicalOp(Rule):
607
- REVERSE = {
608
- ">=": "<",
609
- "<=": ">",
610
- "<>": "=",
611
- "=": "<>"
612
- }
639
+ REVERSE = {">=": "<", "<=": ">", "=": "<>"}
640
+ REVERSE |= {v: k for k, v in REVERSE.items()}
641
+
613
642
  @classmethod
614
643
  def apply(cls, target: Select):
615
644
  REGEX = re.compile('({})'.format(
@@ -617,13 +646,14 @@ class RuleLogicalOp(Rule):
617
646
  ))
618
647
  for i, condition in enumerate(target.values.get(WHERE, [])):
619
648
  expr = re.sub('\n|\t', ' ', condition)
620
- tokens = [t for t in re.split(r'(NOT\b|not\b)',expr) if t.strip()]
621
- if len(tokens) < 2 or not REGEX.findall(tokens[-1]):
649
+ if not re.search(r'\b(NOT|not)\b', expr):
622
650
  continue
623
- tokens = REGEX.split(tokens[-1])
624
- tokens[1] = cls.REVERSE[tokens[1]]
651
+ tokens = [t.strip() for t in re.split(r'NOT\b|not\b|(<|>|=)', expr) if t]
652
+ op = ''.join(tokens[1: len(tokens)-1])
653
+ tokens = [tokens[0], cls.REVERSE[op], tokens[-1]]
625
654
  target.values[WHERE][i] = ' '.join(tokens)
626
655
 
656
+
627
657
  class RuleDateFuncReplace(Rule):
628
658
  """
629
659
  SQL algorithm by Ralff Matias
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 0.1.4
3
+ Version: 0.2.1
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
@@ -24,7 +24,7 @@ _Note that an alias "act" has been added._
24
24
  You can specify your own alias: `a = Select('Actor a')`
25
25
 
26
26
  ---
27
- ### 2 - You can also add a field, like this...
27
+ ### 2 - You can also add a field, contains this...
28
28
 
29
29
  * a = Select('Actor a', **name=Field**)
30
30
 
@@ -55,12 +55,12 @@ You can specify your own alias: `a = Select('Actor a')`
55
55
  ---
56
56
 
57
57
  ### 3 - To set conditions, use **Where**:
58
- * For example, `a = Select(... age=Where.gt(45) )`
58
+ * For example, `a = Select(... age=gt(45) )`
59
59
 
60
60
  Some possible conditions:
61
- * field=Where.eq(value) - ...the field is EQUAL to the value;
62
- * field=Where.gt(value) - ...the field is GREATER than the value;
63
- * field=Where.lt(value) - ...the field is LESS than the value;
61
+ * field=eq(value) - ...the field is EQUAL to the value;
62
+ * field=gt(value) - ...the field is GREATER than the value;
63
+ * field=lt(value) - ...the field is LESS than the value;
64
64
 
65
65
  3.1 -- If you want to filter the field on a range of values:
66
66
 
@@ -71,7 +71,7 @@ You can specify your own alias: `a = Select('Actor a')`
71
71
  query = Select('Movie m', title=Field,
72
72
  id=SelectIN(
73
73
  'Review r',
74
- rate=Where.gt(4.5),
74
+ rate=gt(4.5),
75
75
  movie_id=Distinct
76
76
  )
77
77
  )
@@ -92,8 +92,8 @@ query = Select('Movie m', title=Field,
92
92
  3.3 -- Optional conditions:
93
93
  ```
94
94
  OR=Options(
95
- genre=Where.eq("Sci-Fi"),
96
- awards=Where.like("Oscar")
95
+ genre=eq("Sci-Fi"),
96
+ awards=contains("Oscar")
97
97
  )
98
98
  ```
99
99
  > Could be AND=Options(...)
@@ -105,7 +105,7 @@ based_on_book=Not.is_null()
105
105
 
106
106
  3.5 -- List of values
107
107
  ```
108
- hash_tag=Where.list(['space', 'monster', 'gore'])
108
+ hash_tag=inside(['space', 'monster', 'gore'])
109
109
  ```
110
110
 
111
111
  ---
@@ -113,11 +113,11 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
113
113
 
114
114
  * m = Select('Movie m' release_date=[Field, OrderBy])
115
115
  - This means that the field will appear in the results and also that the query will be ordered by that field.
116
- * Applying **GROUP BY** to item 3.2, it would look like this:
116
+ * Applying **GROUP BY** to item 3.2, it would look contains this:
117
117
  ```
118
118
  SelectIN(
119
119
  'Review r', movie=[GroupBy, Distinct],
120
- rate=Having.avg(Where.gt(4.5))
120
+ rate=Having.avg(gt(4.5))
121
121
  )
122
122
  ```
123
123
  ---
@@ -299,7 +299,7 @@ m = Select...
299
299
  ```
300
300
  best_movies = SelectIN(
301
301
  Review=Table('role'),
302
- rate=[GroupBy, Having.avg(Where.gt(4.5))]
302
+ rate=[GroupBy, Having.avg(gt(4.5))]
303
303
  )
304
304
  m1 = Select(
305
305
  Movie=Table('title,release_date'),
@@ -320,9 +320,9 @@ m2 = Select(
320
320
  Select(
321
321
  'Product',
322
322
  label=Case('price').when(
323
- Where.lt(50), 'cheap'
323
+ lt(50), 'cheap'
324
324
  ).when(
325
- Where.gt(100), 'expensive'
325
+ gt(100), 'expensive'
326
326
  ).else_value(
327
327
  'normal'
328
328
  )
@@ -360,3 +360,22 @@ m2 = Select(
360
360
  * Replace `YEAR` function with date range comparison.
361
361
 
362
362
  > The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
363
+
364
+ ---
365
+
366
+ ### 12 - Adding multiple fields at once
367
+ ```
368
+ query = Select('post p')
369
+ query.add_fields(
370
+ 'user_id, created_at',
371
+ order_by=True, group_by=True
372
+ )
373
+ ```
374
+ ...is the same as...
375
+ ```
376
+ query = Select(
377
+ 'post p',
378
+ user_id=[Field, GroupBy, OrderBy],
379
+ created_at=[Field, GroupBy, OrderBy]
380
+ )
381
+ ```
File without changes
File without changes