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 CHANGED
@@ -266,23 +266,38 @@ class Current_Date(Function):
266
266
  return super().get_pattern()
267
267
  # --------------------------------------------------------
268
268
 
269
- class Aggregate:
269
+ class Frame:
270
270
  break_lines: bool = True
271
271
 
272
272
  def over(self, **args):
273
- keywords = ' '.join(
274
- '{}{} BY {}'.format(
275
- '\n\t\t' if self.break_lines else '',
276
- key.upper(), args[key]
277
- ) for key in ('partition', 'order')
278
- if key in args
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 = "LIKE '%{}%'"
383
- StartWith = "LIKE '{}%'"
384
- EndsWith = "LIKE '%{}'"
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(pos.value.format(content))
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
- `logical_separator` must be AND or OR
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.109
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,,