sql-blocks 1.20250709__py3-none-any.whl → 1.20250711__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
@@ -221,11 +221,12 @@ class Code:
221
221
  def As(self, field_alias: str, modifiers=None):
222
222
  if modifiers:
223
223
  self.extra[field_alias] = TO_LIST(modifiers)
224
- self.field_class = NamedField(field_alias)
224
+ if field_alias:
225
+ self.field_class = NamedField(field_alias)
225
226
  return self
226
227
 
227
228
  def format(self, name: str, main: SQLObject) -> str:
228
- raise NotImplementedError('Use child classes instead of this one')
229
+ return Field.format(name, main)
229
230
 
230
231
  def __add(self, name: str, main: SQLObject):
231
232
  name = self.format(name, main)
@@ -624,8 +625,9 @@ class Where:
624
625
  ))
625
626
 
626
627
  @classmethod
627
- def join(cls, query: SQLObject):
628
+ def join(cls, query: SQLObject, pairs: dict=None):
628
629
  where = cls(query)
630
+ where.pairs = pairs
629
631
  where.add = where.add_join
630
632
  return where
631
633
 
@@ -634,11 +636,18 @@ class Where:
634
636
  main.values[FROM].append(f',{query.table_name} {query.alias}')
635
637
  for key in USUAL_KEYS:
636
638
  main.update_values(key, query.values.get(key, []))
637
- if query.key_field:
638
- main.values.setdefault(WHERE, []).append('({a1}.{f1} = {a2}.{f2})'.format(
639
- a1=main.alias, f1=name,
640
- a2=query.alias, f2=query.key_field
641
- ))
639
+ if not self.pairs:
640
+ if not query.key_field:
641
+ return
642
+ self.pairs = {name: query.key_field}
643
+ a1, a2 = main.alias, query.alias
644
+ main.has_named_field
645
+ for f1, f2 in self.pairs.items():
646
+ if main.has_named_field(f1):
647
+ expr = f'({a2}.{f2} = {f1})'
648
+ else:
649
+ expr = f'({a2}.{f2} = {a1}.{f1})'
650
+ main.values.setdefault(WHERE, []).append(expr)
642
651
 
643
652
  def add(self, name: str, main: SQLObject):
644
653
  func_type = FUNCTION_CLASS.get(name.lower())
@@ -970,6 +979,8 @@ class GroupBy(Clause):
970
979
  query: Select = obj
971
980
  fields += query.values.get(SELECT, [])
972
981
  query.add(alias, main)
982
+ elif obj == Field:
983
+ fields += [alias]
973
984
  if not func:
974
985
  fields += [self.format(name, main)]
975
986
  for field in fields:
@@ -1664,38 +1675,51 @@ class CypherParser(Parser):
1664
1675
  Where(' '.join(condition)).add(field, query)
1665
1676
 
1666
1677
  def add_order(self, token: str):
1667
- self.add_field(token, [OrderBy])
1678
+ self.add_field(token, sorted=True)
1668
1679
 
1669
- def add_field(self, token: str, extra_classes: list['type']=[]):
1680
+ def add_field(self, token: str, sorted: bool = False):
1670
1681
  if token in self.TOKEN_METHODS:
1671
1682
  return
1672
- class_list = [Field]
1673
1683
  if '*' in token:
1674
1684
  token = token.replace('*', '')
1675
1685
  self.queries[-1].key_field = token
1676
1686
  return
1677
- elif '$' in token:
1678
- func_name, token = token.split('$')
1679
- if func_name == 'count':
1680
- if not token:
1681
- token = 'count_1'
1682
- pk_field = self.queries[-1].key_field or 'id'
1683
- Count().As(token, extra_classes).add(pk_field, self.queries[-1])
1684
- return
1685
- else:
1686
- class_type = FUNCTION_CLASS.get(func_name)
1687
+ # -------------------------------------------------------
1688
+ def field_params() -> dict:
1689
+ ROLE_OF_SEPARATOR = {
1690
+ '$': 'function',
1691
+ ':': 'alias',
1692
+ '@': 'group',
1693
+ '!': 'field',
1694
+ }
1695
+ REGEX_FIELD = r'([{}])'.format(''.join(ROLE_OF_SEPARATOR))
1696
+ elements = re.split(REGEX_FIELD, token+'!')
1697
+ return {
1698
+ ROLE_OF_SEPARATOR[k]: v
1699
+ for k, v in zip(elements[1::2], elements[::2])
1700
+ }
1701
+ def run(function: str='', alias: str='', group: str='', field: str=''):
1702
+ is_count = function == 'count'
1703
+ if alias or is_count:
1704
+ field, alias = alias, field
1705
+ extra_classes = [OrderBy] if sorted else []
1706
+ if group:
1707
+ if not field:
1708
+ field = group
1709
+ extra_classes += [GroupBy]
1710
+ if function:
1711
+ if is_count and not field:
1712
+ field = self.queries[-1].key_field or 'id'
1713
+ class_type = FUNCTION_CLASS.get(function)
1687
1714
  if not class_type:
1688
- raise ValueError(f'Unknown function `{func_name}`.')
1689
- if ':' in token:
1690
- token, field_alias = token.split(':')
1691
- if extra_classes == [OrderBy]:
1692
- class_type = class_type().As(field_alias, OrderBy)
1693
- extra_classes = []
1694
- else:
1695
- class_type = class_type().As(field_alias)
1696
- class_list = [class_type]
1697
- class_list += extra_classes
1698
- FieldList(token, class_list).add('', self.queries[-1])
1715
+ raise ValueError(f'Unknown function `{function}`.')
1716
+ class_list = [ class_type().As(alias or group, extra_classes) ]
1717
+ else:
1718
+ class_list = [Field] + extra_classes
1719
+ FieldList(field, class_list).add('', self.queries[-1])
1720
+ # -------------------------------------------------------
1721
+ run( **field_params() )
1722
+ # -------------------------------------------------------
1699
1723
 
1700
1724
  def left_ftable(self, token: str):
1701
1725
  if self.queries:
@@ -2107,7 +2131,7 @@ class CTE(Select):
2107
2131
  else:
2108
2132
  count = len(fields)
2109
2133
  queries = detect(
2110
- pattern*count, join_queries=False, format=format
2134
+ pattern*count, join_method=None, format=format
2111
2135
  )
2112
2136
  FieldList(fields, queries, ziped=True).add('', self)
2113
2137
  self.break_lines = True
@@ -2128,7 +2152,7 @@ class Recursive(CTE):
2128
2152
  def get_field(obj: SQLObject, pos: int) -> str:
2129
2153
  return obj.values[SELECT][pos].split('.')[-1]
2130
2154
  t1, t2 = detect(
2131
- pattern*2, join_queries=False, format=format
2155
+ pattern*2, join_method=None, format=format
2132
2156
  )
2133
2157
  pk_field = get_field(t1, 0)
2134
2158
  foreign_key = ''
@@ -2163,6 +2187,14 @@ class CTEFactory:
2163
2187
  FROM** ( `sub_query1` ) **AS** `alias_1`
2164
2188
  JOIN ( `sub_query2` ) **AS** `alias_2` **ON** `__join__`
2165
2189
  """
2190
+ if parser_class(txt) == CypherParser:
2191
+ query_list = Select.parse(txt, CypherParser)
2192
+ alias = '_'.join(query.table_name for query in query_list)
2193
+ self.main = Select(alias)
2194
+ self.main.break_lines = False
2195
+ query = join_queries(query_list)
2196
+ self.cte_list = [CTE(alias, [query])]
2197
+ return
2166
2198
  summary = self.extract_subqueries(txt)
2167
2199
  self.main = detect( summary.pop(MAIN_TAG) )
2168
2200
  self.cte_list = [
@@ -2341,8 +2373,13 @@ def parser_class(text: str) -> Parser:
2341
2373
  return class_type
2342
2374
  return None
2343
2375
 
2376
+ def join_queries(query_list: list) -> Select:
2377
+ result = query_list[0]
2378
+ for query in query_list[1:]:
2379
+ result += query
2380
+ return result
2344
2381
 
2345
- def detect(text: str, join_queries: bool = True, format: str='') -> Select | list[Select]:
2382
+ def detect(text: str, join_method = join_queries, format: str='') -> Select | list[Select]:
2346
2383
  from collections import Counter
2347
2384
  parser = parser_class(text)
2348
2385
  if not parser:
@@ -2357,14 +2394,28 @@ def detect(text: str, join_queries: bool = True, format: str='') -> Select | lis
2357
2394
  Select.EQUIVALENT_NAMES[new_name] = table
2358
2395
  text = text[:begin] + new_name + '(' + text[end:]
2359
2396
  count -= 1
2360
- query_list = Select.parse(text, parser)
2397
+ result = Select.parse(text, parser)
2361
2398
  if format:
2362
- for query in query_list:
2399
+ for query in result:
2363
2400
  query.set_file_format(format)
2364
- if not join_queries:
2365
- return query_list
2366
- result = query_list[0]
2367
- for query in query_list[1:]:
2368
- result += query
2401
+ if join_method:
2402
+ result = join_method(result)
2369
2403
  return result
2370
2404
  # ===========================================================================================//
2405
+
2406
+ if __name__ == "__main__":
2407
+ cte = CTEFactory(
2408
+ "Sales(year$ref_date:ref_year@, sum$quantity:qty_sold, vendor) <- Vendor(id, name@)"
2409
+ # ^^^ ^^^ ^^^
2410
+ # | | | ^^^ ^^^
2411
+ # | | | | |
2412
+ # | | | Relaciona Sales com Vendor ----+ |
2413
+ # | | | |
2414
+ # | | +---- Chama de `ref_year` e agrupa |
2415
+ # | | |
2416
+ # | +-- Extrai o ano do campo `ref_date` |
2417
+ # | |
2418
+ # +--- Tabela de vendas |
2419
+ # Agrupa também pelo nome do vendedor -------------+
2420
+ )
2421
+ print(cte)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.20250709
3
+ Version: 1.20250711
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
@@ -197,6 +197,7 @@ FROM
197
197
  WHERE
198
198
  (album.artist_id = artist.id)
199
199
 
200
+ (*) --> For more than one relationship, use the **pairs** parameter.
200
201
 
201
202
 
202
203
  ---
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=JHQ0CzciUEodZoA0WjUYKDpC2VZWcFlDB9C2wrNc4rA,83004
3
+ sql_blocks-1.20250711.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.20250711.dist-info/METADATA,sha256=fci6o5QrXStVrXylRQ7QaCpO9jid0P6eON6xMK4Rob0,24486
5
+ sql_blocks-1.20250711.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.20250711.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.20250711.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=xyJGWgUf0nG7Vxcfy0DIjrvuCMGLRU3PGeg4UsZrRnY,80533
3
- sql_blocks-1.20250709.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.20250709.dist-info/METADATA,sha256=QzoGQNftNY_wHmpm3EkMcMDqgrA1OMtN79-f8jS0XV4,24416
5
- sql_blocks-1.20250709.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.20250709.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.20250709.dist-info/RECORD,,