sql-blocks 1.25.111__py3-none-any.whl → 1.25.113__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
@@ -70,6 +70,10 @@ class SQLObject:
70
70
  appendix = {WHERE: r'\s+and\s+|', FROM: r'\s+join\s+|\s+JOIN\s+'}
71
71
  return KEYWORD[key][0].format(appendix.get(key, ''))
72
72
 
73
+ @staticmethod
74
+ def is_named_field(fld: str, key: str) -> bool:
75
+ return key == SELECT and re.search(r'\s+as\s+|\s+AS\s+', fld)
76
+
73
77
  def diff(self, key: str, search_list: list, exact: bool=False) -> set:
74
78
  def disassemble(source: list) -> list:
75
79
  if not exact:
@@ -82,12 +86,10 @@ class SQLObject:
82
86
  if exact:
83
87
  fld = fld.lower()
84
88
  return fld.strip()
85
- def is_named_field(fld: str) -> bool:
86
- return key == SELECT and re.search(r'\s+as\s+|\s+AS\s+', fld)
87
89
  def field_set(source: list) -> set:
88
90
  return set(
89
91
  (
90
- fld if is_named_field(fld) else
92
+ fld if self.is_named_field(fld, key) else
91
93
  re.sub(pattern, '', cleanup(fld))
92
94
  )
93
95
  for string in disassemble(source)
@@ -105,13 +107,16 @@ class SQLObject:
105
107
  return s1.symmetric_difference(s2)
106
108
  return s1 - s2
107
109
 
108
- def delete(self, search: str, keys: list=USUAL_KEYS):
110
+ def delete(self, search: str, keys: list=USUAL_KEYS, exact: bool=False):
111
+ if exact:
112
+ not_match = lambda item: not re.search(fr'\w*[.]*{search}$', item)
113
+ else:
114
+ not_match = lambda item: search not in item
109
115
  for key in keys:
110
- result = []
111
- for item in self.values.get(key, []):
112
- if search not in item:
113
- result.append(item)
114
- self.values[key] = result
116
+ self.values[key] = [
117
+ item for item in self.values.get(key, [])
118
+ if not_match(item)
119
+ ]
115
120
 
116
121
 
117
122
  SQL_CONST_SYSDATE = 'SYSDATE'
@@ -266,23 +271,38 @@ class Current_Date(Function):
266
271
  return super().get_pattern()
267
272
  # --------------------------------------------------------
268
273
 
269
- class Aggregate:
274
+ class Frame:
270
275
  break_lines: bool = True
271
276
 
272
277
  def over(self, **args):
273
- keywords = ' '.join(
274
- '{}{} BY {}'.format(
275
- '\n\t\t' if self.break_lines else '',
276
- key.upper(), args[key]
277
- ) for key in ('partition', 'order')
278
- if key in args
279
- )
278
+ """
279
+ How to use:
280
+ over(field1=OrderBy, field2=Partition)
281
+ """
282
+ keywords = ''
283
+ for field, obj in args.items():
284
+ is_valid = any([
285
+ obj is class_type # or isinstance(obj, class_type)
286
+ for class_type in (OrderBy, Partition)
287
+ ])
288
+ if not is_valid:
289
+ continue
290
+ keywords += '{}{} {}'.format(
291
+ '\n\t\t' if self.break_lines else ' ',
292
+ obj.cls_to_str(), field
293
+ )
280
294
  if keywords and self.break_lines:
281
295
  keywords += '\n\t'
282
296
  self.pattern = self.get_pattern() + f' OVER({keywords})'
283
297
  return self
284
298
 
285
299
 
300
+ class Aggregate(Frame):
301
+ ...
302
+
303
+ class Window(Frame):
304
+ ...
305
+
286
306
  # ---- Aggregate Functions: -------------------------------
287
307
  class Avg(Aggregate, Function):
288
308
  ...
@@ -295,6 +315,17 @@ class Sum(Aggregate, Function):
295
315
  class Count(Aggregate, Function):
296
316
  ...
297
317
 
318
+ # ---- Window Functions: -----------------------------------
319
+ class Row_Number(Window, Function):
320
+ ...
321
+ class Rank(Window, Function):
322
+ ...
323
+ class Lag(Window, Function):
324
+ ...
325
+ class Lead(Window, Function):
326
+ ...
327
+
328
+
298
329
  # ---- Conversions and other Functions: ---------------------
299
330
  class Coalesce(Function):
300
331
  ...
@@ -539,6 +570,16 @@ class OrderBy(Clause):
539
570
  name = cls.format(name, main)
540
571
  main.values.setdefault(ORDER_BY, []).append(name+cls.sort.value)
541
572
 
573
+ @classmethod
574
+ def cls_to_str(cls) -> str:
575
+ return ORDER_BY
576
+
577
+ PARTITION_BY = 'PARTITION BY'
578
+ class Partition:
579
+ @classmethod
580
+ def cls_to_str(cls) -> str:
581
+ return PARTITION_BY
582
+
542
583
 
543
584
  class GroupBy(Clause):
544
585
  @classmethod
@@ -925,7 +966,7 @@ class SQLParser(Parser):
925
966
  obj.values[key] = [
926
967
  Field.format(fld, obj)
927
968
  for fld in re.split(separator, values[key])
928
- if (fld != '*' and len(tables) == 1) or obj.match(fld)
969
+ if (fld != '*' and len(tables) == 1) or obj.match(fld, key)
929
970
  ]
930
971
  result[obj.alias] = obj
931
972
  self.queries = list( result.values() )
@@ -990,8 +1031,9 @@ class CypherParser(Parser):
990
1031
  if func_name == 'count':
991
1032
  if not token:
992
1033
  token = 'count_1'
993
- NamedField(token, Count).add('*', self.queries[-1])
994
- class_list = []
1034
+ pk_field = self.queries[-1].key_field or 'id'
1035
+ Count().As(token, extra_classes).add(pk_field, self.queries[-1])
1036
+ return
995
1037
  else:
996
1038
  FUNCTION_CLASS = {f.__name__.lower(): f for f in Function.__subclasses__()}
997
1039
  class_list = [ FUNCTION_CLASS[func_name] ]
@@ -1012,7 +1054,7 @@ class CypherParser(Parser):
1012
1054
  if not last.values.get(SELECT):
1013
1055
  raise IndexError(f'Primary Key not found for {last.table_name}.')
1014
1056
  pk_field = last.values[SELECT][-1].split('.')[-1]
1015
- last.delete(pk_field, [SELECT])
1057
+ last.delete(pk_field, [SELECT], exact=True)
1016
1058
  if '{}' in token:
1017
1059
  foreign_fld = token.format(
1018
1060
  last.table_name.lower()
@@ -1027,12 +1069,11 @@ class CypherParser(Parser):
1027
1069
  if fld not in curr.values.get(GROUP_BY, [])
1028
1070
  ]
1029
1071
  foreign_fld = fields[0].split('.')[-1]
1030
- curr.delete(foreign_fld, [SELECT])
1072
+ curr.delete(foreign_fld, [SELECT], exact=True)
1031
1073
  if curr.join_type == JoinType.RIGHT:
1032
1074
  pk_field, foreign_fld = foreign_fld, pk_field
1033
1075
  if curr.join_type == JoinType.RIGHT:
1034
1076
  curr, last = last, curr
1035
- # pk_field, foreign_fld = foreign_fld, pk_field
1036
1077
  k = ForeignKey.get_key(curr, last)
1037
1078
  ForeignKey.references[k] = (foreign_fld, pk_field)
1038
1079
 
@@ -1273,8 +1314,17 @@ class Select(SQLObject):
1273
1314
  self.values.setdefault(LIMIT, result)
1274
1315
  return self
1275
1316
 
1276
- def match(self, expr: str) -> bool:
1277
- return re.findall(f'\b*{self.alias}[.]', expr) != []
1317
+ def match(self, field: str, key: str) -> bool:
1318
+ '''
1319
+ Recognizes if the field is from the current table
1320
+ '''
1321
+ if key in (ORDER_BY, GROUP_BY) and '.' not in field:
1322
+ return any(
1323
+ self.is_named_field(fld, SELECT)
1324
+ for fld in self.values[SELECT]
1325
+ if field in fld
1326
+ )
1327
+ return re.findall(f'\b*{self.alias}[.]', field) != []
1278
1328
 
1279
1329
  @classmethod
1280
1330
  def parse(cls, txt: str, parser: Parser = SQLParser) -> list[SQLObject]:
@@ -1395,12 +1445,13 @@ class RuleReplaceJoinBySubselect(Rule):
1395
1445
  more_relations = any([
1396
1446
  ref[0] == query.table_name for ref in ForeignKey.references
1397
1447
  ])
1398
- invalid = any([
1448
+ keep_join = any([
1399
1449
  len( query.values.get(SELECT, []) ) > 0,
1400
1450
  len( query.values.get(WHERE, []) ) == 0,
1401
1451
  not fk_field, more_relations
1402
1452
  ])
1403
- if invalid:
1453
+ if keep_join:
1454
+ query.add(fk_field, main)
1404
1455
  continue
1405
1456
  query.__class__ = SubSelect
1406
1457
  Field.add(primary_k, query)
@@ -1446,14 +1497,16 @@ def detect(text: str) -> Select:
1446
1497
  return result
1447
1498
 
1448
1499
 
1449
- if __name__ == "__main__":
1450
- query = Select(
1451
- 'Installments i', due_date=Field, customer=Select(
1452
- 'Customer c', id=PrimaryKey,
1453
- name=contains('Smith', Position.EndsWith)
1454
- )
1455
- )
1500
+ if __name__ == '__main__':
1501
+ p, c, a = Select.parse('''
1502
+ Professor(?nome="Júlio Cascalles", id)
1503
+ <- Curso@disciplina(professor, aluno) ->
1504
+ Aluno(id ^count$qtd_alunos)
1505
+ ''', CypherParser)
1506
+ query = p + c + a
1507
+ print('#######################################')
1456
1508
  print(query)
1457
- print('-----')
1509
+ print('***************************************')
1458
1510
  query.optimize([RuleReplaceJoinBySubselect])
1459
1511
  print(query)
1512
+ print('#######################################')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.25.111
3
+ Version: 1.25.113
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
@@ -614,6 +614,12 @@ You may use this functions:
614
614
  * Max
615
615
  * Sum
616
616
  * Count
617
+ * Lag
618
+ * Lead
619
+ * Row_Number
620
+ * Rank
621
+ * Coalesce
622
+ * Cast
617
623
  > Some of these functions may vary in syntax depending on the database.
618
624
  For example, if your query is going to run on Oracle, do the following:
619
625
 
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=Saj3MqmF0OHY60bhc_aSl8fL-PhXM2apmiPiE6KspOc,50553
3
+ sql_blocks-1.25.113.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.25.113.dist-info/METADATA,sha256=td1IZl9O3m2U0rWFk6gyecqWnkr0cMDW94PfeqhzP8Y,14638
5
+ sql_blocks-1.25.113.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.25.113.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.25.113.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=rARGbJxL3Di9mOXRBS4aEFvclO922-dzDOXPy1vSTGg,49018
3
- sql_blocks-1.25.111.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.25.111.dist-info/METADATA,sha256=Lm7JKPIdZSJT5dkWUc2K7fpY4r0MQ-TeTyv0pbiMy24,14581
5
- sql_blocks-1.25.111.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.25.111.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.25.111.dist-info/RECORD,,