sql-blocks 1.25.30012339__py3-none-any.whl → 1.25.32011301__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
@@ -65,6 +65,11 @@ class SQLObject:
65
65
  def table_name(self) -> str:
66
66
  return self.values[FROM][0].split()[0]
67
67
 
68
+ def set_file_format(self, pattern: str):
69
+ if '{' not in pattern:
70
+ pattern = '{}' + pattern
71
+ self.values[FROM][0] = pattern.format(self.aka())
72
+
68
73
  @property
69
74
  def alias(self) -> str:
70
75
  if self.__alias:
@@ -366,15 +371,20 @@ class ExpressionField:
366
371
  class FieldList:
367
372
  separator = ','
368
373
 
369
- def __init__(self, fields: list=[], class_types = [Field]):
374
+ def __init__(self, fields: list=[], class_types = [Field], ziped: bool=False):
370
375
  if isinstance(fields, str):
371
376
  fields = [
372
377
  f.strip() for f in fields.split(self.separator)
373
378
  ]
374
379
  self.fields = fields
375
380
  self.class_types = class_types
381
+ self.ziped = ziped
376
382
 
377
383
  def add(self, name: str, main: SQLObject):
384
+ if self.ziped: # --- One class per field...
385
+ for field, class_type in zip(self.fields, self.class_types):
386
+ class_type.add(field, main)
387
+ return
378
388
  for field in self.fields:
379
389
  for class_type in self.class_types:
380
390
  class_type.add(field, main)
@@ -1112,7 +1122,11 @@ class CypherParser(Parser):
1112
1122
  if token in self.TOKEN_METHODS:
1113
1123
  return
1114
1124
  class_list = [Field]
1115
- if '$' in token:
1125
+ if '*' in token:
1126
+ token = token.replace('*', '')
1127
+ self.queries[-1].key_field = token
1128
+ return
1129
+ elif '$' in token:
1116
1130
  func_name, token = token.split('$')
1117
1131
  if func_name == 'count':
1118
1132
  if not token:
@@ -1136,10 +1150,13 @@ class CypherParser(Parser):
1136
1150
  def add_foreign_key(self, token: str, pk_field: str=''):
1137
1151
  curr, last = [self.queries[i] for i in (-1, -2)]
1138
1152
  if not pk_field:
1139
- if not last.values.get(SELECT):
1140
- raise IndexError(f'Primary Key not found for {last.table_name}.')
1141
- pk_field = last.values[SELECT][-1].split('.')[-1]
1142
- last.delete(pk_field, [SELECT], exact=True)
1153
+ if last.key_field:
1154
+ pk_field = last.key_field
1155
+ else:
1156
+ if not last.values.get(SELECT):
1157
+ raise IndexError(f'Primary Key not found for {last.table_name}.')
1158
+ pk_field = last.values[SELECT][-1].split('.')[-1]
1159
+ last.delete(pk_field, [SELECT], exact=True)
1143
1160
  if '{}' in token:
1144
1161
  foreign_fld = token.format(
1145
1162
  last.table_name.lower()
@@ -1465,24 +1482,25 @@ class CTE(Select):
1465
1482
  for query in query_list:
1466
1483
  query.break_lines = False
1467
1484
  self.query_list = query_list
1485
+ self.break_lines = False
1468
1486
 
1469
1487
  def __str__(self) -> str:
1470
1488
  # ---------------------------------------------------------
1471
1489
  def justify(query: Select) -> str:
1472
- LINE_MAX_SIZE = 50
1473
1490
  result, line = [], ''
1474
- for word in str(query).split(' '):
1475
- if len(line) >= LINE_MAX_SIZE and word in KEYWORD:
1491
+ keywords = '|'.join(KEYWORD)
1492
+ for word in re.split(fr'({keywords}|AND|OR|,)', str(query)):
1493
+ if len(line) >= 65:
1476
1494
  result.append(line)
1477
1495
  line = ''
1478
- line += word + ' '
1496
+ line += word
1479
1497
  if line:
1480
1498
  result.append(line)
1481
- return '\n\t'.join(result)
1499
+ return '\n '.join(result)
1482
1500
  # ---------------------------------------------------------
1483
- return 'WITH {}{} AS (\n\t{}\n){}'.format(
1501
+ return 'WITH {}{} AS (\n {}\n){}'.format(
1484
1502
  self.prefix, self.table_name,
1485
- '\nUNION ALL\n\t'.join(
1503
+ '\nUNION ALL\n '.join(
1486
1504
  justify(q) for q in self.query_list
1487
1505
  ), super().__str__()
1488
1506
  )
@@ -1496,6 +1514,45 @@ class Recursive(CTE):
1496
1514
  f', {self.table_name} {self.alias}')
1497
1515
  return super().__str__()
1498
1516
 
1517
+ @classmethod
1518
+ def create(cls, name: str, pattern: str, formula: str, init_value, format: str=''):
1519
+ SQLObject.ALIAS_FUNC = None
1520
+ def get_field(obj: SQLObject, pos: int) -> str:
1521
+ return obj.values[SELECT][pos].split('.')[-1]
1522
+ t1, t2 = detect(
1523
+ pattern*2, join_queries=False, format=format
1524
+ )
1525
+ pk_field = get_field(t1, 0)
1526
+ foreign_key = ''
1527
+ for num in re.findall(r'\[(\d+)\]', formula):
1528
+ num = int(num)
1529
+ if not foreign_key:
1530
+ foreign_key = get_field(t2, num-1)
1531
+ formula = formula.replace(f'[{num}]', '%')
1532
+ else:
1533
+ formula = formula.replace(f'[{num}]', get_field(t2, num-1))
1534
+ Where.eq(init_value).add(pk_field, t1)
1535
+ Where.formula(formula).add(foreign_key or pk_field, t2)
1536
+ return cls(name, [t1, t2])
1537
+
1538
+ def join(self, pattern: str, fields: list | str, format: str):
1539
+ if isinstance(fields, str):
1540
+ count = len( fields.split(',') )
1541
+ else:
1542
+ count = len(fields)
1543
+ queries = detect(
1544
+ pattern*count, join_queries=False, format=format
1545
+ )
1546
+ FieldList(fields, queries, ziped=True).add('', self)
1547
+ self.break_lines = True
1548
+
1549
+ def counter(self, name: str, start, increment: str='+1'):
1550
+ for i, query in enumerate(self.query_list):
1551
+ if i == 0:
1552
+ Field.add(f'{start} AS {name}', query)
1553
+ else:
1554
+ Field.add(f'({name}{increment}) AS {name}', query)
1555
+
1499
1556
 
1500
1557
  # ----- Rules -----
1501
1558
 
@@ -1613,7 +1670,7 @@ def parser_class(text: str) -> Parser:
1613
1670
  return None
1614
1671
 
1615
1672
 
1616
- def detect(text: str, join_queries: bool = True) -> Select | list[Select]:
1673
+ def detect(text: str, join_queries: bool = True, format: str='') -> Select | list[Select]:
1617
1674
  from collections import Counter
1618
1675
  parser = parser_class(text)
1619
1676
  if not parser:
@@ -1629,6 +1686,9 @@ def detect(text: str, join_queries: bool = True) -> Select | list[Select]:
1629
1686
  text = text[:begin] + new_name + '(' + text[end:]
1630
1687
  count -= 1
1631
1688
  query_list = Select.parse(text, parser)
1689
+ if format:
1690
+ for query in query_list:
1691
+ query.set_file_format(format)
1632
1692
  if not join_queries:
1633
1693
  return query_list
1634
1694
  result = query_list[0]
@@ -1636,3 +1696,4 @@ def detect(text: str, join_queries: bool = True) -> Select | list[Select]:
1636
1696
  result += query
1637
1697
  return result
1638
1698
 
1699
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.25.30012339
3
+ Version: 1.25.32011301
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
@@ -489,6 +489,7 @@ ORDER BY
489
489
  * `^` Put the field in the ORDER BY clause
490
490
  * `@` Immediately after the table name, it indicates the grouping field.
491
491
  * `$` For SQL functions like **avg**$_field_, **sum**$_field_, **count**$_field_...
492
+ * `*` Sets the primary key field.
492
493
 
493
494
 
494
495
  ---
@@ -671,7 +672,7 @@ For example, if your query is going to run on Oracle, do the following:
671
672
 
672
673
  ### 17 - CTE and Recursive classes
673
674
 
674
- * **_CTE class_**
675
+ * **17.1 - _CTE class_**
675
676
  ```
676
677
  query = Select(
677
678
  'SocialMedia s', post=Count, reaction=Sum, user=GroupBy
@@ -685,7 +686,7 @@ The result is...
685
686
  )SELECT * FROM Metrics
686
687
  ```
687
688
 
688
- * **_Recursive class_**
689
+ * **17.2 - _Recursive class_**
689
690
  ```
690
691
  q1 = Select(
691
692
  'SocialMedia me', name=[ eq(MY_NAME), Field ]
@@ -698,8 +699,90 @@ print( Recursive('Network', [q1, q2]) )
698
699
  The result is...
699
700
  ```
700
701
  WITH RECURSIVE Network AS (
701
- SELECT me.name FROM SocialMedia me WHERE me.name = 'Júlio Cascalles'
702
+ SELECT me.name FROM SocialMedia me WHERE
703
+ me.name = 'Júlio Cascalles'
702
704
  UNION ALL
703
- SELECT you.name FROM SocialMedia you , Network n WHERE you.id = n.friend
705
+ SELECT you.name FROM SocialMedia you , Network n
706
+ WHERE you.id = n.friend
704
707
  )SELECT * FROM Network
705
708
  ```
709
+
710
+ * **17.2.1 - The `create` method** ... parameters :
711
+ - name: The name of the CTE
712
+ - pattern: A cypher script that defines the tables used
713
+ - formula: The format for `Where.formula` method _(*)_
714
+ - init_value: The value for the condition in the first table
715
+ - format (optional): If tables are files or internet hiperlinks, you may especify the extension and/or folder...
716
+ > Example:
717
+ ```
718
+ R = Recursive.create(
719
+ 'Route R', 'Flyght(departure, arrival)',
720
+ '[2] = R.[1]', 'JFK', format='.csv'
721
+ ) # ^^^--- Flyghts from JFK airport
722
+ ```
723
+ _...Creates a recursive CTE called Route, using Flyght table, where the recursivity condition is Flyght.arrival equals to Route.departure_
724
+ >> (*) -- Note that [1] and [2] refers to first field and second field. 😉
725
+
726
+ Result:
727
+
728
+ WITH RECURSIVE Route AS (
729
+ SELECT f1.departure, f1.arrival
730
+ FROM Flyght.csv f1
731
+ WHERE f1.departure = 'JFK'
732
+ UNION ALL
733
+ SELECT f2.departure, f2.arrival
734
+ FROM Flyght.csv f2
735
+ , Route R
736
+ WHERE f2.arrival = R.departure
737
+ )SELECT * FROM Route R
738
+
739
+ **17.2.2 - The `join` method**
740
+
741
+ In the previous example, if you add this code...
742
+ `R.join('Airport(*id,name)', 'departure, arrival', format='.csv')`
743
+
744
+ ...The result would be:
745
+
746
+ WITH RECURSIVE Route AS (
747
+ SELECT f1.departure, f1.arrival
748
+ FROM Flyght.csv f1
749
+ WHERE f1.departure = 'JFK'
750
+ UNION ALL
751
+ SELECT f2.departure, f2.arrival
752
+ FROM Flyght.csv f2
753
+ , Route R
754
+ WHERE f2.arrival = R.departure
755
+ )SELECT
756
+ a1.name, a2.name
757
+ FROM
758
+ Route R
759
+ JOIN Airport.csv a2 ON (R.arrival = a2.id)
760
+ JOIN Airport.csv a1 ON (R.departure = a1.id)
761
+
762
+
763
+ **17.2.3 - The `counter` method**
764
+ Adds an increment field in queries inside CTE:
765
+ > Examples:
766
+ * `R.counter('stops', 0)` # -- counter starts with 0 and increment +1
767
+ * `R2.counter('generation', 5, '- 1')` # -- for the code below...
768
+ ```
769
+ R2 = Recursive.create(
770
+ 'Ancestors a', 'People(id,name,father,mother,birth)',
771
+ '(% = a.father OR % = a.mother)', 32630, '.parquet'
772
+ )
773
+ ```
774
+ ...Results:
775
+
776
+ WITH RECURSIVE Ancestors AS (
777
+ SELECT p1.id, p1.name, p1.father, p1.mother, p1.birth,
778
+ 5 AS generation /* <<---- Most current generation ------------*/
779
+ FROM People.parquet p1 WHERE p1.id = 32630
780
+ UNION ALL
781
+ SELECT p2.id, p2.name, p2.father, p2.mother, p2.birth,
782
+ (generation- 1) AS generation /* <<-- Previous generation -----*/
783
+ FROM People.parquet p2 , Ancestors a WHERE (p2.id = a.father OR p2.id = a.mother)
784
+ )SELECT * FROM Ancestors a
785
+
786
+
787
+ >> Note: Comments added later.
788
+ ---
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=VW8n2nWu7RREnns8dmYruhlvzGBCA3EeuBKiXCjClmE,56975
3
+ sql_blocks-1.25.32011301.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.25.32011301.dist-info/METADATA,sha256=g_Yr5mTcquKqG5PM6qSNeWsuZ4wbo88pdZUldp_rt9k,19574
5
+ sql_blocks-1.25.32011301.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.25.32011301.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.25.32011301.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=TSrqjpP41uyocxLpTePI2JzSJorxbFB_jjLfWkHmnJ0,54584
3
- sql_blocks-1.25.30012339.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.25.30012339.dist-info/METADATA,sha256=0nqWca0uwfqNns6xVIJAB_uR1KMjPnUUgrJHvBPX7bg,16753
5
- sql_blocks-1.25.30012339.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.25.30012339.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.25.30012339.dist-info/RECORD,,