sql-blocks 1.20250715__py3-none-any.whl → 1.20250718__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
@@ -1994,7 +1994,7 @@ class Select(SQLObject):
1994
1994
  from copy import deepcopy
1995
1995
  return deepcopy(self)
1996
1996
 
1997
- def no_relation_error(self, other: SQLObject):
1997
+ def relation_error(self, other: SQLObject):
1998
1998
  raise ValueError(f'No relationship found between {self.table_name} and {other.table_name}.')
1999
1999
 
2000
2000
  def __add__(self, other: SQLObject):
@@ -2011,7 +2011,7 @@ class Select(SQLObject):
2011
2011
  PrimaryKey.add(primary_key, query)
2012
2012
  query.add(foreign_field, other)
2013
2013
  return other
2014
- self.no_relation_error(other) # === raise ERROR ... ===
2014
+ self.relation_error(other) # === raise ERROR ... ===
2015
2015
  elif primary_key:
2016
2016
  PrimaryKey.add(primary_key, other)
2017
2017
  other.add(foreign_field, query)
@@ -2040,7 +2040,7 @@ class Select(SQLObject):
2040
2040
  else:
2041
2041
  fk_field, primary_k = ForeignKey.find(other, self)
2042
2042
  if not fk_field:
2043
- self.no_relation_error(other) # === raise ERROR ... ===
2043
+ self.relation_error(other) # === raise ERROR ... ===
2044
2044
  query = other.copy()
2045
2045
  other = self.copy()
2046
2046
  query.__class__ = NotSelectIN
@@ -2127,8 +2127,8 @@ class CTE(Select):
2127
2127
  query.break_lines = False
2128
2128
  result, line = [], ''
2129
2129
  keywords = '|'.join(KEYWORD)
2130
- for word in re.split(fr'({keywords}|AND|OR|,)', str(query)):
2131
- if len(line) >= 50:
2130
+ for word in re.split(fr'({keywords}|AND|OR|JOIN|,)', str(query)):
2131
+ if len(line) >= 30:
2132
2132
  result.append(line)
2133
2133
  line = ''
2134
2134
  line += word
@@ -2197,7 +2197,8 @@ class Recursive(CTE):
2197
2197
  MAIN_TAG = '__main__'
2198
2198
 
2199
2199
  class CTEFactory:
2200
-
2200
+ TEMPLATE_FIELD_FUNC = lambda t: t.lower()[:3] + '_id'
2201
+
2201
2202
  def __init__(self, txt: str, template: str = ''):
2202
2203
  """
2203
2204
  SQL syntax:
@@ -2208,35 +2209,61 @@ class CTEFactory:
2208
2209
 
2209
2210
  Cypher syntax:
2210
2211
  ---
2211
- Table1(field, `function$`field`:alias`, `group@`) <- Table2(field)
2212
- `...`MainTable(field)
2212
+ `cte_name`[
2213
+ Table1(field, `function$`field`:alias`, `group@`) <- Table2(field)
2214
+ ]
2213
2215
  """
2216
+ def put_parentheses():
2217
+ result = txt
2218
+ for found in re.findall(r'\[\d+\][^(]', result):
2219
+ item = found.strip()[:3]
2220
+ result = result.replace(item, f'{item}()')
2221
+ return result
2222
+ txt, *negative = re.split(r'(\[-\d+\])', txt)
2223
+ txt = put_parentheses()
2224
+ txt, *suffix = re.split(r'(\[\d+\])', txt, maxsplit=1)
2214
2225
  if template:
2215
2226
  for table in re.findall(r'[#](\w+)', txt):
2216
- txt = txt.replace(f'#{table}', template.format(t=table))
2217
- if parser_class(txt) == CypherParser:
2218
- txt, *main_script = txt.split('...')
2219
- query_list = Select.parse(txt, CypherParser)
2220
- if main_script:
2221
- main_script = self.replace_wildcards(
2222
- ''.join(main_script), query_list
2223
- )
2224
- self.main = detect(main_script)
2225
- alias = self.main.table_name
2226
- else:
2227
+ txt = txt.replace( f'#{table}', template.format(
2228
+ t=table, f=CTEFactory.TEMPLATE_FIELD_FUNC(table)
2229
+ ) )
2230
+ self.cte_list = []
2231
+ self.main = None
2232
+ for script in txt.split(']'):
2233
+ if '(' not in script:
2234
+ script += self.replace_wildcards(''.join(suffix))
2235
+ suffix = []
2236
+ self.build_ctes(script)
2237
+ if not self.cte_list:
2238
+ return
2239
+ if not suffix and negative:
2240
+ suffix = negative
2241
+ if suffix:
2242
+ self.main = detect( self.replace_wildcards(''.join(suffix)) )
2243
+ elif not self.main:
2244
+ self.main = Select(self.cte_list[0].table_name)
2245
+ self.main.break_lines = False
2246
+
2247
+ def build_ctes(self, script: str):
2248
+ alias, *body = script.split('[')
2249
+ if body:
2250
+ script = ''.join(body)
2251
+ if not re.sub( r'\s+', '', script):
2252
+ return
2253
+ if parser_class(script) == CypherParser:
2254
+ query_list = Select.parse(script, CypherParser)
2255
+ if not body:
2227
2256
  alias = '_'.join(query.table_name for query in query_list)
2228
- self.main = Select(alias)
2229
- self.main.break_lines = False
2230
2257
  related_tables = any([
2231
2258
  query.join_type.value for query in query_list
2232
2259
  ])
2233
2260
  if related_tables:
2234
2261
  query_list = [ join_queries(query_list) ]
2235
- self.cte_list = [CTE(alias, query_list)]
2262
+ self.cte_list += [CTE(alias, query_list)]
2236
2263
  return
2237
- summary = self.extract_subqueries(txt)
2264
+ summary = self.extract_subqueries(script)
2238
2265
  self.main = detect( summary.pop(MAIN_TAG) )
2239
- self.cte_list = [
2266
+ self.cte_list += [
2240
2267
  CTE(alias, [
2241
2268
  Select.parse(query)[0]
2242
2269
  for query in elements
@@ -2245,24 +2272,43 @@ class CTEFactory:
2245
2272
  ]
2246
2273
 
2247
2274
  def __str__(self):
2275
+ if not self.main:
2276
+ return ''
2248
2277
  CTE.show_query = False
2249
2278
  lines = [str(cte) for cte in self.cte_list]
2250
2279
  result = ',\n'.join(lines) + '\n' + str(self.main)
2251
2280
  CTE.show_query = True
2252
2281
  return result
2253
2282
 
2254
- @staticmethod
2255
- def replace_wildcards(txt: str, query_list: list) -> str:
2256
- if '(*)' in txt:
2257
- field_list = [
2258
- re.split(
2259
- r'\bas\b|\bAS\b', field
2260
- )[-1].strip()
2261
- for query in query_list
2262
- for field in query.values.get(SELECT, [])
2263
- ]
2264
- return txt.replace('(*)', '({}, *)'.format(
2265
- ','.join( set(field_list) )
2283
+ def replace_wildcards(self, txt: str) -> str:
2284
+ ALL_FIELDS_WILDCARD = '**'
2285
+ result = ''
2286
+ names = []
2287
+ query_list = []
2288
+ cte: CTE
2289
+ for cte in self.cte_list:
2290
+ names.append(cte.table_name)
2291
+ query_list += cte.query_list
2292
+ last = 0
2293
+ for found in re.finditer(r'\[[-]*\d+\]', txt):
2294
+ pos = int(re.sub(r'\[|\]', '', found.group()))
2295
+ if pos > 0:
2296
+ pos -= 1
2297
+ start = found.start()
2298
+ result += txt[last:start] + names[pos]
2299
+ last = found.end()
2300
+ txt = result + txt[last:]
2301
+ if ALL_FIELDS_WILDCARD in txt:
2302
+ field_list = []
2303
+ for query in query_list:
2304
+ for field in query.values.get(SELECT, []):
2305
+ new_item = re.split(
2306
+ r'\bas\b|\bAS\b|[.]', field
2307
+ )[-1].strip()
2308
+ if new_item not in field_list:
2309
+ field_list.append(new_item)
2310
+ return txt.replace(ALL_FIELDS_WILDCARD, '{}'.format(
2311
+ ','.join(field_list)
2266
2312
  ))
2267
2313
  return txt
2268
2314
 
@@ -2459,37 +2505,24 @@ def detect(text: str, join_method = join_queries, format: str='') -> Select | li
2459
2505
 
2460
2506
 
2461
2507
  if __name__ == "__main__":
2462
- OrderBy.sort = SortType.DESC
2508
+ # cte = CTEFactory("""
2509
+ # Sales(year$ref_date:ref_year@, sum$quantity:qty_sold, vendor) <- Vendor(id, name:vendors_name@)
2510
+ # """)
2463
2511
  cte = CTEFactory(
2464
- # "#Customer#Employee#Supplier...People_by_Type(*)",
2465
- # template = '{t}("{t[0]}":ptype, nome:person_name)'
2466
- """
2467
- SELECT u001.name, agg_sales.total
2468
- FROM (
2469
- SELECT * FROM Users u
2470
- WHERE u.status = 'active'
2471
- ) AS u001
2472
- JOIN (
2473
- SELECT s.user_id, Sum(s.value) as total
2474
- FROM Sales s
2475
- GROUP BY s.user_id
2476
- )
2477
- As agg_sales
2478
- ON u001.id = agg_sales.user_id
2479
- ORDER BY u001.name
2480
- """
2481
- # "Sales(year$ref_date:ref_year@, sum$quantity:qty_sold, vendor) <- Vendor(id, name:vendors_name@)"
2482
- # ^^^ ^^^ ^^^
2483
- # | | | ^^^ ^^^
2484
- # | | | | |
2485
- # | | | Relate Sales to Vendor --------+ |
2486
- # | | | |
2487
- # | | +---- Call it `ref_year` and group it |
2488
- # | | |
2489
- # | +-- Extracts the year from the `ref_date` field |
2490
- # | |
2491
- # +--- The Sales table |
2492
- # Also groups by vendor´s name ------------------+
2493
- # "...Annual_Sales_per_Vendor(*) -> Goal(^year, target)"
2512
+ txt='''
2513
+ #Empregado #Cliente #Fornecedor
2514
+
2515
+ Todas_as_pessoas[
2516
+ [1] [2] [3]
2517
+ ]
2518
+
2519
+ [-1](**, ano*) <- Meta(ano, qt_ideal)
2520
+ ''',
2521
+ template='''
2522
+ Vendas_por_{t}[
2523
+ Vendas(year$data:ano@, sum$quantidade:qt_vendida,
2524
+ {f}) -> {t}(id, nome:nome_pessoa@)
2525
+ ]
2526
+ '''
2494
2527
  )
2495
2528
  print(cte)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.20250715
3
+ Version: 1.20250718
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
@@ -995,26 +995,30 @@ results...
995
995
 
996
996
  #### 17.3.1 - You can also pass a Cypher script like in the example below:
997
997
 
998
- cte = CTEFactory(
999
- "Sales(year$ref_date:ref_year@, sum$quantity:qty_sold, vendor)"
1000
- " <- Vendor(id, name:vendors_name@)"
1001
- "...Annual_Sales_per_Vendor(*) -> Goal(year, target)"
1002
- )
1003
- print(cte)
998
+ cte = CTEFactory("""
999
+ Annual_Sales_per_Vendor[
1000
+ Sales(
1001
+ year$ref_date:ref_year@, sum$quantity:qty_sold,
1002
+ vendor) <- Vendor(id, name:vendors_name@)
1003
+ ]
1004
1004
 
1005
- ![image](https://raw.githubusercontent.com/julio-cascalles/sql_blocks/refs/heads/master/assets/CTEFactory.png)
1005
+ [-1](**, ref_year) -> Goal(year, target)
1006
+ """)
1007
+ print(cte)
1006
1008
 
1007
1009
  results...
1008
1010
  ```
1009
1011
  WITH Annual_Sales_per_Vendor AS (
1010
- SELECT ven.name as vendors_name, Year(sal.ref_date) as ref_year
1011
- , Sum(sal.quantity) as qty_sold FROM Vendor ven LEFT JOIN Sales sal ON (ven.id = sal.vendor)
1012
+ SELECT ven.name as vendors_name
1013
+ , Year(sal.ref_date) as ref_year
1014
+ , Sum(sal.quantity) as qty_sold
1015
+ FROM Vendor ven LEFT JOIN Sales sal ON (ven.id = sal.vendor
1012
1016
  GROUP BY ven.name, ref_year
1013
1017
  )
1014
1018
  SELECT
1019
+ aspv.vendors_name,
1015
1020
  aspv.ref_year,
1016
1021
  aspv.qty_sold,
1017
- aspv.vendors_name,
1018
1022
  goa.target
1019
1023
  FROM
1020
1024
  Annual_Sales_per_Vendor aspv
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=jsihV2ylEjZu_g5W4e6jtJlbmK2ZXqVJXcqKncGikNk,86290
3
+ sql_blocks-1.20250718.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.20250718.dist-info/METADATA,sha256=r4s-4ScclaImOIhaIo9t5F0gY6WkJdoEpGgq-YcNOeA,25345
5
+ sql_blocks-1.20250718.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.20250718.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.20250718.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=LNdU9qK7eROrpCzggnPxOOlTSKkuU-mVYJLNsZNjvdY,85746
3
- sql_blocks-1.20250715.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.20250715.dist-info/METADATA,sha256=pVsle1pwP1elpLwRZtpcXjL5uIi2iTiysunYaBj4N3I,25388
5
- sql_blocks-1.20250715.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.20250715.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.20250715.dist-info/RECORD,,