sql-blocks 1.25.33022255__py3-none-any.whl → 1.25.37022003__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
@@ -82,8 +82,14 @@ class SQLObject:
82
82
  return KEYWORD[key][0].format(appendix.get(key, ''))
83
83
 
84
84
  @staticmethod
85
- def is_named_field(fld: str, key: str) -> bool:
86
- return key == SELECT and re.search(r'\s+as\s+|\s+AS\s+', fld)
85
+ def is_named_field(fld: str, name: str='') -> bool:
86
+ return re.search(fr'(\s+as\s+|\s+AS\s+){name}', fld)
87
+
88
+ def has_named_field(self, name: str) -> bool:
89
+ return any(
90
+ self.is_named_field(fld, name)
91
+ for fld in self.values.get(SELECT, [])
92
+ )
87
93
 
88
94
  def diff(self, key: str, search_list: list, exact: bool=False) -> set:
89
95
  def disassemble(source: list) -> list:
@@ -100,7 +106,9 @@ class SQLObject:
100
106
  def field_set(source: list) -> set:
101
107
  return set(
102
108
  (
103
- fld if self.is_named_field(fld, key) else
109
+ fld
110
+ if key == SELECT and self.is_named_field(fld, key)
111
+ else
104
112
  re.sub(pattern, '', cleanup(fld))
105
113
  )
106
114
  for string in disassemble(source)
@@ -150,7 +158,7 @@ class Field:
150
158
  name = name.strip()
151
159
  if name in ('_', '*'):
152
160
  name = '*'
153
- elif not is_const():
161
+ elif not is_const() and not main.has_named_field(name):
154
162
  name = f'{main.alias}.{name}'
155
163
  if Function in cls.__bases__:
156
164
  name = f'{cls.__name__}({name})'
@@ -190,13 +198,15 @@ class Dialect(Enum):
190
198
 
191
199
  class Function:
192
200
  dialect = Dialect.ANSI
201
+ need_params = True
202
+ separator = ', '
193
203
 
194
204
  def __init__(self, *params: list):
195
205
  # --- Replace class methods by instance methods: ------
196
206
  self.add = self.__add
197
207
  self.format = self.__format
198
208
  # -----------------------------------------------------
199
- self.params = [str(p) for p in params]
209
+ self.params = list(params)
200
210
  self.field_class = Field
201
211
  self.pattern = self.get_pattern()
202
212
  self.extra = {}
@@ -213,14 +223,24 @@ class Function:
213
223
  def __str__(self) -> str:
214
224
  return self.pattern.format(
215
225
  func_name=self.__class__.__name__,
216
- params=', '.join(self.params)
226
+ params=self.separator.join(str(p) for p in self.params)
217
227
  )
218
228
 
229
+ def set_main_param(self, name: str, main: SQLObject) -> bool:
230
+ nested_functions = [
231
+ param for param in self.params if isinstance(param, Function)
232
+ ]
233
+ for func in nested_functions:
234
+ if func.need_params:
235
+ func.set_main_param(name, main)
236
+ return
237
+ self.params = [
238
+ Field.format(name, main)
239
+ ] + self.params
240
+
219
241
  def __format(self, name: str, main: SQLObject) -> str:
220
242
  if name not in '*_':
221
- self.params = [
222
- Field.format(name, main)
223
- ] + self.params
243
+ self.set_main_param(name, main)
224
244
  return str(self)
225
245
 
226
246
  @classmethod
@@ -254,10 +274,11 @@ class DateDiff(Function):
254
274
  def get_pattern(self) -> str:
255
275
  def is_field_or_func(name: str) -> bool:
256
276
  return re.sub('[()]', '', name).isidentifier()
277
+ params = [str(p) for p in self.params]
257
278
  if self.dialect != Dialect.SQL_SERVER:
258
279
  return ' - '.join(
259
280
  p if is_field_or_func(p) else f"'{p}'"
260
- for p in self.params
281
+ for p in params
261
282
  ) # <==== Date subtract
262
283
  return super().get_pattern()
263
284
 
@@ -272,6 +293,8 @@ class Year(Function):
272
293
  return super().get_pattern()
273
294
 
274
295
  class Current_Date(Function):
296
+ need_params = False
297
+
275
298
  def get_pattern(self) -> str:
276
299
  database_type = {
277
300
  Dialect.ORACLE: SQL_CONST_SYSDATE,
@@ -343,7 +366,7 @@ class Lead(Window, Function):
343
366
  class Coalesce(Function):
344
367
  ...
345
368
  class Cast(Function):
346
- ...
369
+ separator = ' As '
347
370
 
348
371
 
349
372
  FUNCTION_CLASS = {f.__name__.lower(): f for f in Function.__subclasses__()}
@@ -516,14 +539,9 @@ class Where:
516
539
 
517
540
  def add(self, name: str, main: SQLObject):
518
541
  func_type = FUNCTION_CLASS.get(name.lower())
519
- exists = any(
520
- main.is_named_field(fld, SELECT)
521
- for fld in main.values.get(SELECT, [])
522
- if name in fld
523
- )
524
542
  if func_type:
525
543
  name = func_type.format('*', main)
526
- elif not exists:
544
+ elif not main.has_named_field(name):
527
545
  name = Field.format(name, main)
528
546
  main.values.setdefault(WHERE, []).append('{}{} {}'.format(
529
547
  self.prefix, name, self.content
@@ -604,6 +622,14 @@ class Between:
604
622
  Where.gte(self.start).add(name, main),
605
623
  Where.lte(self.end).add(name, main)
606
624
 
625
+ class SameDay(Between):
626
+ def __init__(self, date: str):
627
+ super().__init__(
628
+ f'{date} 00:00:00',
629
+ f'{date} 23:59:59',
630
+ )
631
+
632
+
607
633
 
608
634
  class Clause:
609
635
  @classmethod
@@ -1438,11 +1464,7 @@ class Select(SQLObject):
1438
1464
  Recognizes if the field is from the current table
1439
1465
  '''
1440
1466
  if key in (ORDER_BY, GROUP_BY) and '.' not in field:
1441
- return any(
1442
- self.is_named_field(fld, SELECT)
1443
- for fld in self.values[SELECT]
1444
- if field in fld
1445
- )
1467
+ return main.has_named_field(field)
1446
1468
  return re.findall(f'\b*{self.alias}[.]', field) != []
1447
1469
 
1448
1470
  @classmethod
@@ -1455,12 +1477,10 @@ class Select(SQLObject):
1455
1477
  for rule in rules:
1456
1478
  rule.apply(self)
1457
1479
 
1458
- def add_fields(self, fields: list, order_by: bool=False, group_by:bool=False):
1459
- class_types = [Field]
1460
- if order_by:
1461
- class_types += [OrderBy]
1462
- if group_by:
1463
- class_types += [GroupBy]
1480
+ def add_fields(self, fields: list, class_types=None):
1481
+ if not class_types:
1482
+ class_types = []
1483
+ class_types += [Field]
1464
1484
  FieldList(fields, class_types).add('', self)
1465
1485
 
1466
1486
  def translate_to(self, language: QueryLanguage) -> str:
@@ -1491,12 +1511,17 @@ class CTE(Select):
1491
1511
  self.break_lines = False
1492
1512
 
1493
1513
  def __str__(self) -> str:
1514
+ size = 0
1515
+ for key in USUAL_KEYS:
1516
+ size += sum(len(v) for v in self.values.get(key, []) if '\n' not in v)
1517
+ if size > 70:
1518
+ self.break_lines = True
1494
1519
  # ---------------------------------------------------------
1495
1520
  def justify(query: Select) -> str:
1496
1521
  result, line = [], ''
1497
1522
  keywords = '|'.join(KEYWORD)
1498
1523
  for word in re.split(fr'({keywords}|AND|OR|,)', str(query)):
1499
- if len(line) >= 65:
1524
+ if len(line) >= 60:
1500
1525
  result.append(line)
1501
1526
  line = ''
1502
1527
  line += word
@@ -1510,7 +1535,8 @@ class CTE(Select):
1510
1535
  justify(q) for q in self.query_list
1511
1536
  ), super().__str__()
1512
1537
  )
1513
- def join(self, pattern: str, fields: list | str, format: str):
1538
+
1539
+ def join(self, pattern: str, fields: list | str, format: str=''):
1514
1540
  if isinstance(fields, str):
1515
1541
  count = len( fields.split(',') )
1516
1542
  else:
@@ -1702,11 +1728,4 @@ def detect(text: str, join_queries: bool = True, format: str='') -> Select | lis
1702
1728
  for query in query_list[1:]:
1703
1729
  result += query
1704
1730
  return result
1705
-
1706
-
1707
- if __name__ == "__main__":
1708
- CAMPO_MEDIA = 'MEDIA_SALARIAL_DEPTO'
1709
- employees = detect(
1710
- f'Employees@department_id(avg$salary:{CAMPO_MEDIA})'
1711
- )
1712
- print(employees)
1731
+ # ===========================================================================================//
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.25.33022255
3
+ Version: 1.25.37022003
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
@@ -67,6 +67,16 @@ You can specify your own alias: `a = Select('Actor a')`
67
67
  3.1 -- If you want to filter the field on a range of values:
68
68
 
69
69
  `a = Select( 'Actor a', age=Between(45, 69) )`
70
+ ...but if it is a time slot within the same day, you can do it like this:
71
+ `Select(..., event_date=SameDay("2024-10-03"))`
72
+ This results in
73
+ ```
74
+ SELECT ...
75
+ WHERE
76
+ event_date >= '2024-10-03 00:00:00' AND
77
+ event_date <= '2024-10-03 23:59:59'
78
+ ```
79
+ ---
70
80
 
71
81
  3.2 -- Sub-queries:
72
82
  ```
@@ -668,6 +678,21 @@ For example, if your query is going to run on Oracle, do the following:
668
678
 
669
679
  `Function.dialect = Dialect.ORACLE`
670
680
 
681
+
682
+ > Most of this functions you can use nested inside each other.
683
+ *Example:*
684
+ ```
685
+ Select(...
686
+ event_date=Substring(
687
+ Cast("CHAR"), 12, 19
688
+ ).As('time')
689
+ )
690
+ ```
691
+ Results...
692
+ ```
693
+ SELECT ...
694
+ SubString(Cast(event_date As char), 12, 19) as time
695
+ ```
671
696
  ---
672
697
 
673
698
  ### 17 - CTE and Recursive classes
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=kaI6nCcq1X3CenGFP4HkKkk3rsDt_jts8xErg-ml7dk,58217
3
+ sql_blocks-1.25.37022003.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.25.37022003.dist-info/METADATA,sha256=AUa01HeqYtgwVObYWMTkqixy0RNoxwNQXurTMEWjjQ8,20146
5
+ sql_blocks-1.25.37022003.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.25.37022003.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.25.37022003.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=orYGCD6mnYmPZT4xzLXMy8Na_nwUdAe0HxsVPeas6LM,57510
3
- sql_blocks-1.25.33022255.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.25.33022255.dist-info/METADATA,sha256=mOP6W7LDKvEHlfrZUVooyBwYs1A9a55K9bpLQpVt2N0,19574
5
- sql_blocks-1.25.33022255.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.25.33022255.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.25.33022255.dist-info/RECORD,,