sql-blocks 1.25.19011849__py3-none-any.whl → 1.25.30011511__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
@@ -42,17 +42,23 @@ class SQLObject:
42
42
  if not table_name:
43
43
  return
44
44
  cls = SQLObject
45
+ is_file_name = any([
46
+ '/' in table_name, '.' in table_name
47
+ ])
48
+ ref = table_name
49
+ if is_file_name:
50
+ ref = table_name.split('/')[-1].split('.')[0]
45
51
  if cls.ALIAS_FUNC:
46
- self.__alias = cls.ALIAS_FUNC(table_name)
52
+ self.__alias = cls.ALIAS_FUNC(ref)
47
53
  elif ' ' in table_name.strip():
48
54
  table_name, self.__alias = table_name.split()
49
- elif '_' in table_name:
55
+ elif '_' in ref:
50
56
  self.__alias = ''.join(
51
57
  word[0].lower()
52
- for word in table_name.split('_')
58
+ for word in ref.split('_')
53
59
  )
54
60
  else:
55
- self.__alias = table_name.lower()[:3]
61
+ self.__alias = ref.lower()[:3]
56
62
  self.values.setdefault(FROM, []).append(f'{table_name} {self.alias}')
57
63
 
58
64
  @property
@@ -423,12 +429,12 @@ class Position(Enum):
423
429
  class Where:
424
430
  prefix = ''
425
431
 
426
- def __init__(self, expr: str):
427
- self.expr = expr
432
+ def __init__(self, content: str):
433
+ self.content = content
428
434
 
429
435
  @classmethod
430
436
  def __constructor(cls, operator: str, value):
431
- return cls(expr=f'{operator} {quoted(value)}')
437
+ return cls(f'{operator} {quoted(value)}')
432
438
 
433
439
  @classmethod
434
440
  def eq(cls, value):
@@ -470,6 +476,34 @@ class Where:
470
476
  values = ','.join(quoted(v) for v in values)
471
477
  return cls(f'IN ({values})')
472
478
 
479
+ @classmethod
480
+ def formula(cls, formula: str):
481
+ where = cls( ExpressionField(formula) )
482
+ where.add = where.add_expression
483
+ return where
484
+
485
+ def add_expression(self, name: str, main: SQLObject):
486
+ self.content = self.content.format(name, main)
487
+ main.values.setdefault(WHERE, []).append('{} {}'.format(
488
+ self.prefix, self.content
489
+ ))
490
+
491
+ @classmethod
492
+ def join(cls, query: SQLObject):
493
+ where = cls(query)
494
+ where.add = where.add_join
495
+ return where
496
+
497
+ def add_join(self, name: str, main: SQLObject):
498
+ query = self.content
499
+ main.values[FROM].append(f',{query.table_name} {query.alias}')
500
+ for key in USUAL_KEYS:
501
+ main.update_values(key, query.values.get(key, []))
502
+ main.values.setdefault(WHERE, []).append('({a1}.{f1} = {a2}.{f2})'.format(
503
+ a1=main.alias, f1=name,
504
+ a2=query.alias, f2=query.key_field
505
+ ))
506
+
473
507
  def add(self, name: str, main: SQLObject):
474
508
  func_type = FUNCTION_CLASS.get(name.lower())
475
509
  exists = any(
@@ -482,7 +516,7 @@ class Where:
482
516
  elif not exists:
483
517
  name = Field.format(name, main)
484
518
  main.values.setdefault(WHERE, []).append('{}{} {}'.format(
485
- self.prefix, name, self.expr
519
+ self.prefix, name, self.content
486
520
  ))
487
521
 
488
522
 
@@ -490,6 +524,7 @@ eq, contains, gt, gte, lt, lte, is_null, inside = (
490
524
  getattr(Where, method) for method in
491
525
  ('eq', 'contains', 'gt', 'gte', 'lt', 'lte', 'is_null', 'inside')
492
526
  )
527
+ startswith, endswith = [lambda x: contains(x, pos) for pos in Position if pos.value]
493
528
 
494
529
 
495
530
  class Not(Where):
@@ -497,7 +532,7 @@ class Not(Where):
497
532
 
498
533
  @classmethod
499
534
  def eq(cls, value):
500
- return Where(expr=f'<> {quoted(value)}')
535
+ return Where(f'<> {quoted(value)}')
501
536
 
502
537
 
503
538
  class Case:
@@ -538,7 +573,7 @@ class Options:
538
573
  child: Where
539
574
  for field, child in self.__children.items():
540
575
  conditions.append(' {} {} '.format(
541
- Field.format(field, main), child.expr
576
+ Field.format(field, main), child.content
542
577
  ))
543
578
  main.values.setdefault(WHERE, []).append(
544
579
  '(' + logical_separator.join(conditions) + ')'
@@ -637,7 +672,7 @@ class Having:
637
672
 
638
673
  def add(self, name: str, main:SQLObject):
639
674
  main.values[GROUP_BY][-1] += ' HAVING {} {}'.format(
640
- self.function.format(name, main), self.condition.expr
675
+ self.function.format(name, main), self.condition.content
641
676
  )
642
677
 
643
678
  @classmethod
@@ -1419,6 +1454,47 @@ class NotSelectIN(SelectIN):
1419
1454
  condition_class = Not
1420
1455
 
1421
1456
 
1457
+ class CTE:
1458
+ prefix = ''
1459
+
1460
+ def __init__(self, name: str, query_list: list[Select]):
1461
+ self.name = name
1462
+ for query in query_list:
1463
+ query.break_lines = False
1464
+ self.query_list = query_list
1465
+
1466
+ def format(self, query: Select) -> str:
1467
+ LINE_MAX_SIZE = 50
1468
+ result, line = [], ''
1469
+ for word in str(query).split(' '):
1470
+ if len(line) >= LINE_MAX_SIZE and word in KEYWORD:
1471
+ result.append(line)
1472
+ line = ''
1473
+ line += word + ' '
1474
+ if line:
1475
+ result.append(line)
1476
+ return '\n\t'.join(result)
1477
+
1478
+ def __str__(self) -> str:
1479
+ return 'WITH {prefix}{name} AS (\n\t{queries}\n)SELECT * FROM {name}'.format(
1480
+ prefix=self.prefix, name=self.name,
1481
+ queries='\nUNION ALL\n\t'.join(
1482
+ self.format(q) for q in self.query_list
1483
+ )
1484
+ )
1485
+
1486
+ class Recursive(CTE):
1487
+ prefix = 'RECURSIVE '
1488
+
1489
+ def __str__(self) -> str:
1490
+ if len(self.query_list) > 1:
1491
+ alias = self.name[0].lower()
1492
+ self.query_list[-1].values[FROM].append(f', {self.name} {alias}')
1493
+ return super().__str__()
1494
+
1495
+
1496
+ # ----- Rules -----
1497
+
1422
1498
  class RulePutLimit(Rule):
1423
1499
  @classmethod
1424
1500
  def apply(cls, target: Select):
@@ -1533,7 +1609,7 @@ def parser_class(text: str) -> Parser:
1533
1609
  return None
1534
1610
 
1535
1611
 
1536
- def detect(text: str, join_queries: bool = True) -> Select:
1612
+ def detect(text: str, join_queries: bool = True) -> Select | list[Select]:
1537
1613
  from collections import Counter
1538
1614
  parser = parser_class(text)
1539
1615
  if not parser:
@@ -1556,24 +1632,3 @@ def detect(text: str, join_queries: bool = True) -> Select:
1556
1632
  result += query
1557
1633
  return result
1558
1634
 
1559
-
1560
- if __name__ == '__main__':
1561
- # query = Select(
1562
- # 'Tips t',
1563
- # tip=[Field, Lag().over(day=OrderBy).As('last')],
1564
- # diff=[
1565
- # ExpressionField('Round(tip-last, 2) as {f}'),
1566
- # Not.is_null()
1567
- # ]
1568
- # )
1569
- p, c, a = Select.parse('''
1570
- Professor(?nome="Júlio Cascalles", id)
1571
- <- Curso@disciplina(professor, aluno) ->
1572
- Aluno(id ^count$qtd_alunos)
1573
- ''', CypherParser)
1574
- query = p + c + a
1575
- print(query)
1576
- print('------------------')
1577
- query.optimize([RuleReplaceJoinBySubselect])
1578
- # ==============================================
1579
- print(query)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.25.19011849
3
+ Version: 1.25.30011511
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
@@ -115,6 +115,16 @@ based_on_book=Not.is_null()
115
115
  hash_tag=inside(['space', 'monster', 'gore'])
116
116
  ```
117
117
 
118
+ 3.6 -- Combining ExpressionField with Where condition:
119
+ * The **formula** method allows you to write an expression as a condition:
120
+ ```
121
+ query=Select(
122
+ 'Folks f2',
123
+ id=Where.formula('({af} = a.father OR {af} = a.mother)')
124
+ )
125
+ ```
126
+ > Results: `WHERE...f2.id = a.father OR f2.id = a.mother`
127
+
118
128
  ---
119
129
  ### 4 - A field can be two things at the same time:
120
130
 
@@ -631,3 +641,39 @@ For example, if your query is going to run on Oracle, do the following:
631
641
 
632
642
  `Function.dialect = Dialect.ORACLE`
633
643
 
644
+ ---
645
+
646
+ ### 17 - CTE and Recursive classes
647
+
648
+ * **_CTE class_**
649
+ ```
650
+ query = Select(
651
+ 'SocialMedia s', post=Count, reaction=Sum, user=GroupBy
652
+ )
653
+ print( CTE('Metrics', [query]) )
654
+ ```
655
+ The result is...
656
+ ```
657
+ WITH Metrics AS (
658
+ SELECT Count(s.post), Sum(s.reaction) FROM SocialMedia s GROUP BY user
659
+ )SELECT * FROM Metrics
660
+ ```
661
+
662
+ * **_Recursive class_**
663
+ ```
664
+ q1 = Select(
665
+ 'SocialMedia me', name=[ eq(MY_NAME), Field ]
666
+ )
667
+ q2 = Select(
668
+ 'SocialMedia you' name=Field, id=Where.formula('{af} = n.friend')
669
+ )
670
+ print( Recursive('Network', [q1, q2]) )
671
+ ```
672
+ The result is...
673
+ ```
674
+ WITH RECURSIVE Network AS (
675
+ SELECT me.name FROM SocialMedia me WHERE me.name = 'Júlio Cascalles'
676
+ UNION ALL
677
+ SELECT you.name FROM SocialMedia you , Network n WHERE you.id = n.friend
678
+ )SELECT * FROM Network
679
+ ```
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=fTEX1TEkjr-uUg_aLVmkwVM5Ul3UQyvsW6Vw7z_TCQQ,54398
3
+ sql_blocks-1.25.30011511.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.25.30011511.dist-info/METADATA,sha256=Hqo-6uZ0zLVyhvw7H9E1Hc7vGT5Ny6Kjxy2nzTqIEn0,16187
5
+ sql_blocks-1.25.30011511.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.25.30011511.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.25.30011511.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=nMIOewFh6X7vC0UNKs4mdqz4F_gpCSvtF0oAkocEGHw,52542
3
- sql_blocks-1.25.19011849.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.25.19011849.dist-info/METADATA,sha256=jp-OY-e66KmW-tXSklUT3l5-01CX0jfCCPJ6iANNpSY,15031
5
- sql_blocks-1.25.19011849.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.25.19011849.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.25.19011849.dist-info/RECORD,,