sql-blocks 1.25.6139999999999__py3-none-any.whl → 1.2025.627__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
@@ -847,8 +847,15 @@ class Rows:
847
847
  )
848
848
 
849
849
 
850
+ class DescOrderBy:
851
+ @classmethod
852
+ def add(cls, name: str, main: SQLObject):
853
+ name = Clause.format(name, main)
854
+ main.values.setdefault(ORDER_BY, []).append(name + SortType.DESC.value)
855
+
850
856
  class OrderBy(Clause):
851
857
  sort: SortType = SortType.ASC
858
+ DESC = DescOrderBy
852
859
 
853
860
  @classmethod
854
861
  def add(cls, name: str, main: SQLObject):
@@ -1973,13 +1980,12 @@ class NotSelectIN(SelectIN):
1973
1980
 
1974
1981
  class CTE(Select):
1975
1982
  prefix = ''
1983
+ show_query = True
1976
1984
 
1977
- def __init__(self, table_name: str, query_list: list[Select]):
1985
+ def __init__(self, table_name: str, query_list: list[Select]=[]):
1978
1986
  super().__init__(table_name)
1979
- for query in query_list:
1980
- query.break_lines = False
1981
1987
  self.query_list = query_list
1982
- self.break_lines = False
1988
+ self.break_lines = False
1983
1989
 
1984
1990
  def __str__(self) -> str:
1985
1991
  size = 0
@@ -1989,6 +1995,7 @@ class CTE(Select):
1989
1995
  self.break_lines = True
1990
1996
  # ---------------------------------------------------------
1991
1997
  def justify(query: Select) -> str:
1998
+ query.break_lines = False
1992
1999
  result, line = [], ''
1993
2000
  keywords = '|'.join(KEYWORD)
1994
2001
  for word in re.split(fr'({keywords}|AND|OR|,)', str(query)):
@@ -2002,9 +2009,9 @@ class CTE(Select):
2002
2009
  # ---------------------------------------------------------
2003
2010
  return 'WITH {}{} AS (\n {}\n){}'.format(
2004
2011
  self.prefix, self.table_name,
2005
- '\nUNION ALL\n '.join(
2012
+ '\n\tUNION ALL\n '.join(
2006
2013
  justify(q) for q in self.query_list
2007
- ), super().__str__()
2014
+ ), super().__str__() if self.show_query else ''
2008
2015
  )
2009
2016
 
2010
2017
  def join(self, pattern: str, fields: list | str, format: str=''):
@@ -2058,6 +2065,76 @@ class Recursive(CTE):
2058
2065
  return self
2059
2066
 
2060
2067
 
2068
+ MAIN_TAG = '__main__'
2069
+
2070
+ class CTEFactory:
2071
+ def __init__(self, txt: str):
2072
+ """
2073
+ Syntax:
2074
+ ---
2075
+ **SELECT ...
2076
+ FROM** ( `sub_query1` ) **AS** `alias_1`
2077
+ JOIN ( `sub_query2` ) **AS** `alias_2` **ON** `__join__`
2078
+ """
2079
+ summary = self.extract_subqueries(txt)
2080
+ self.main = detect( summary.pop(MAIN_TAG) )
2081
+ self.cte_list = [
2082
+ CTE(alias, [
2083
+ Select.parse(query)[0]
2084
+ for query in elements
2085
+ ])
2086
+ for alias, elements in summary.items()
2087
+ ]
2088
+
2089
+ def __str__(self):
2090
+ CTE.show_query = False
2091
+ lines = [str(cte) for cte in self.cte_list]
2092
+ return ',\n'.join(lines) + '\n' + str(self.main)
2093
+
2094
+ @staticmethod
2095
+ def extract_subqueries(txt: str) -> dict:
2096
+ result = {}
2097
+ # ---------------------------------------------------
2098
+ def clean_subquery(source: list) -> str:
2099
+ while source:
2100
+ if source[0].upper() == 'SELECT':
2101
+ break
2102
+ word = source.pop(0)
2103
+ if word.upper() in ('FROM', 'JOIN'):
2104
+ result[MAIN_TAG] += f' {word}'
2105
+ return ' '.join(source)
2106
+ def balanced_parentheses(expr: str) -> bool:
2107
+ return expr.count('(') == expr.count(')')
2108
+ # ---------------------------------------------------
2109
+ for found in re.finditer(r'(FROM|JOIN)\s*[(]\s*SELECT', txt, re.IGNORECASE):
2110
+ start = found.start()
2111
+ alias = ''
2112
+ pos = start
2113
+ while not alias:
2114
+ found = re.search(r'[)]\s*AS\s+\w+', txt[pos:], re.IGNORECASE)
2115
+ if not found:
2116
+ break
2117
+ end = found.end() + pos
2118
+ last = end
2119
+ if balanced_parentheses(txt[start: end]):
2120
+ pos += found.start()
2121
+ alias = re.findall(r'\s*(\w+)$', txt[pos: end])[0]
2122
+ end = pos
2123
+ pos = end
2124
+ if not result:
2125
+ result[MAIN_TAG] = txt[:start]
2126
+ query_list = [
2127
+ clean_subquery( expr.split() )
2128
+ for expr in re.split(
2129
+ r'\bUNION\b', txt[start: end], re.IGNORECASE
2130
+ )
2131
+ ]
2132
+ result[MAIN_TAG] += f' {alias} {alias}'
2133
+ result[alias] = query_list
2134
+ result[MAIN_TAG] += txt[last:]
2135
+ return result
2136
+
2137
+
2061
2138
  # ----- Rules -----
2062
2139
 
2063
2140
  class RulePutLimit(Rule):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.25.6139999999999
3
+ Version: 1.2025.627
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
@@ -888,3 +888,49 @@ R2 = Recursive.create(
888
888
 
889
889
  >> Note: Comments added later.
890
890
  ---
891
+
892
+ ### CTEFactory class
893
+ CTEFactory exchanges subqueries for CTEs, simply by passing the text of the "dirty" query:
894
+
895
+ *Example*:
896
+ ```
897
+ print(
898
+ CTEFactory("""
899
+ SELECT u001.name, agg_sales.total
900
+ FROM (
901
+ SELECT * FROM Users u
902
+ WHERE u.status = 'active'
903
+ ) AS u001
904
+ JOIN (
905
+ SELECT s.user_id, Sum(s.value) as total
906
+ FROM Sales s
907
+ GROUP BY s.user_id
908
+ )
909
+ As agg_sales
910
+ ON u001.id = agg_sales.user_id
911
+ ORDER BY u001.name
912
+ """)
913
+ )
914
+ ```
915
+ results...
916
+ ```
917
+ WITH u001 AS (
918
+ SELECT * FROM Users u
919
+ WHERE u.status = 'active'
920
+ ),
921
+ WITH agg_sales AS (
922
+ SELECT s.user_id, Sum(s.value) as total
923
+ FROM Sales s
924
+ GROUP BY s.user_id
925
+ )
926
+ SELECT
927
+ u001.name,
928
+ agg_sales.total
929
+ FROM
930
+ u001 u001
931
+ JOIN agg_sales agg_sales ON
932
+ (u001.id = agg_sales.user_id)
933
+ ORDER BY
934
+ u001.name
935
+ ```
936
+ ---
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=Eq2EMaVALy1IBGcjyjh7SaE1WZBPNvXCi748iCi6AtM,77469
3
+ sql_blocks-1.2025.627.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.2025.627.dist-info/METADATA,sha256=n1Ju94eliblEldBRpj-VzmXg4_09K_vgT5hszgqqVS0,23328
5
+ sql_blocks-1.2025.627.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.2025.627.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.2025.627.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=qDwpII5rYY8tqpp93bKKHW9udOqI3SRb-0wfxjAsYD4,74694
3
- sql_blocks-1.25.6139999999999.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.25.6139999999999.dist-info/METADATA,sha256=Pmk2nqJ3hV7PZVGE8V0bqw8TnSa1_rVIiylJad-nDCk,22236
5
- sql_blocks-1.25.6139999999999.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.25.6139999999999.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.25.6139999999999.dist-info/RECORD,,