meerschaum 2.4.10__py3-none-any.whl → 2.4.12__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.
Files changed (33) hide show
  1. meerschaum/_internal/arguments/_parse_arguments.py +15 -1
  2. meerschaum/_internal/docs/index.py +1 -0
  3. meerschaum/_internal/shell/Shell.py +19 -9
  4. meerschaum/_internal/shell/ShellCompleter.py +11 -6
  5. meerschaum/actions/bootstrap.py +120 -15
  6. meerschaum/actions/clear.py +41 -30
  7. meerschaum/actions/edit.py +89 -0
  8. meerschaum/actions/start.py +3 -2
  9. meerschaum/actions/sync.py +3 -2
  10. meerschaum/api/dash/callbacks/dashboard.py +2 -1
  11. meerschaum/api/dash/callbacks/jobs.py +53 -7
  12. meerschaum/api/dash/callbacks/pipes.py +1 -1
  13. meerschaum/api/dash/jobs.py +86 -60
  14. meerschaum/api/dash/pages/__init__.py +1 -0
  15. meerschaum/api/dash/pages/job.py +21 -0
  16. meerschaum/api/routes/_jobs.py +3 -3
  17. meerschaum/config/_version.py +1 -1
  18. meerschaum/connectors/sql/_fetch.py +65 -61
  19. meerschaum/connectors/sql/_pipes.py +36 -29
  20. meerschaum/core/Pipe/_data.py +1 -1
  21. meerschaum/utils/formatting/__init__.py +32 -16
  22. meerschaum/utils/formatting/_pipes.py +1 -1
  23. meerschaum/utils/formatting/_shell.py +4 -3
  24. meerschaum/utils/prompt.py +16 -15
  25. meerschaum/utils/sql.py +107 -35
  26. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/METADATA +1 -1
  27. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/RECORD +33 -32
  28. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/WHEEL +1 -1
  29. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/LICENSE +0 -0
  30. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/NOTICE +0 -0
  31. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/entry_points.txt +0 -0
  32. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/top_level.txt +0 -0
  33. {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/zip-safe +0 -0
@@ -2096,7 +2096,7 @@ def get_pipe_rowcount(
2096
2096
  An `int` for the number of rows if the `pipe` exists, otherwise `None`.
2097
2097
 
2098
2098
  """
2099
- from meerschaum.utils.sql import dateadd_str, sql_item_name, NO_CTE_FLAVORS
2099
+ from meerschaum.utils.sql import dateadd_str, sql_item_name, wrap_query_with_cte
2100
2100
  from meerschaum.connectors.sql._fetch import get_pipe_query
2101
2101
  if remote:
2102
2102
  msg = f"'fetch:definition' must be an attribute of {pipe} to get a remote rowcount."
@@ -2175,20 +2175,10 @@ def get_pipe_rowcount(
2175
2175
  if not remote
2176
2176
  else get_pipe_query(pipe)
2177
2177
  )
2178
- query = (
2179
- f"""
2180
- WITH src AS ({src})
2181
- SELECT COUNT(*)
2182
- FROM src
2183
- """
2184
- ) if self.flavor not in ('mysql', 'mariadb') else (
2185
- f"""
2186
- SELECT COUNT(*)
2187
- FROM ({src}) AS src
2188
- """
2189
- )
2178
+ parent_query = f"SELECT COUNT(*)\nFROM {sql_item_name('src', self.flavor)}"
2179
+ query = wrap_query_with_cte(src, parent_query, self.flavor)
2190
2180
  if begin is not None or end is not None:
2191
- query += "WHERE"
2181
+ query += "\nWHERE"
2192
2182
  if begin is not None:
2193
2183
  query += f"""
2194
2184
  {dt} >= {dateadd_str(self.flavor, datepart='minute', number=0, begin=begin)}
@@ -2330,10 +2320,10 @@ def clear_pipe(
2330
2320
 
2331
2321
 
2332
2322
  def get_pipe_table(
2333
- self,
2334
- pipe: mrsm.Pipe,
2335
- debug: bool = False,
2336
- ) -> sqlalchemy.Table:
2323
+ self,
2324
+ pipe: mrsm.Pipe,
2325
+ debug: bool = False,
2326
+ ) -> Union['sqlalchemy.Table', None]:
2337
2327
  """
2338
2328
  Return the `sqlalchemy.Table` object for a `mrsm.Pipe`.
2339
2329
 
@@ -2352,18 +2342,18 @@ def get_pipe_table(
2352
2342
  return None
2353
2343
  return get_sqlalchemy_table(
2354
2344
  pipe.target,
2355
- connector = self,
2356
- schema = self.get_pipe_schema(pipe),
2357
- debug = debug,
2358
- refresh = True,
2345
+ connector=self,
2346
+ schema=self.get_pipe_schema(pipe),
2347
+ debug=debug,
2348
+ refresh=True,
2359
2349
  )
2360
2350
 
2361
2351
 
2362
2352
  def get_pipe_columns_types(
2363
- self,
2364
- pipe: mrsm.Pipe,
2365
- debug: bool = False,
2366
- ) -> Dict[str, str]:
2353
+ self,
2354
+ pipe: mrsm.Pipe,
2355
+ debug: bool = False,
2356
+ ) -> Dict[str, str]:
2367
2357
  """
2368
2358
  Get the pipe's columns and types.
2369
2359
 
@@ -2394,8 +2384,8 @@ def get_pipe_columns_types(
2394
2384
  return get_table_cols_types(
2395
2385
  pipe.target,
2396
2386
  self,
2397
- flavor = self.flavor,
2398
- schema = self.schema,
2387
+ flavor=self.flavor,
2388
+ schema=self.get_pipe_schema(pipe),
2399
2389
  )
2400
2390
 
2401
2391
  table_columns = {}
@@ -2448,6 +2438,7 @@ def get_add_columns_queries(
2448
2438
  from meerschaum.utils.sql import (
2449
2439
  sql_item_name,
2450
2440
  SINGLE_ALTER_TABLE_FLAVORS,
2441
+ get_table_cols_types,
2451
2442
  )
2452
2443
  from meerschaum.utils.dtypes.sql import (
2453
2444
  get_pd_type_from_db_type,
@@ -2480,6 +2471,14 @@ def get_add_columns_queries(
2480
2471
  db_cols_types = {
2481
2472
  col: get_pd_type_from_db_type(str(typ.type))
2482
2473
  for col, typ in table_obj.columns.items()
2474
+ } if table_obj is not None else {
2475
+ col: get_pd_type_from_db_type(typ)
2476
+ for col, typ in get_table_cols_types(
2477
+ pipe.target,
2478
+ self,
2479
+ schema=self.get_pipe_schema(pipe),
2480
+ debug=debug,
2481
+ ).items()
2483
2482
  }
2484
2483
  new_cols = set(df_cols_types) - set(db_cols_types)
2485
2484
  if not new_cols:
@@ -2552,7 +2551,7 @@ def get_alter_columns_queries(
2552
2551
  """
2553
2552
  if not pipe.exists(debug=debug):
2554
2553
  return []
2555
- from meerschaum.utils.sql import sql_item_name, DROP_IF_EXISTS_FLAVORS
2554
+ from meerschaum.utils.sql import sql_item_name, DROP_IF_EXISTS_FLAVORS, get_table_cols_types
2556
2555
  from meerschaum.utils.dataframe import get_numeric_cols
2557
2556
  from meerschaum.utils.dtypes import are_dtypes_equal
2558
2557
  from meerschaum.utils.dtypes.sql import (
@@ -2583,6 +2582,14 @@ def get_alter_columns_queries(
2583
2582
  db_cols_types = {
2584
2583
  col: get_pd_type_from_db_type(str(typ.type))
2585
2584
  for col, typ in table_obj.columns.items()
2585
+ } if table_obj is not None else {
2586
+ col: get_pd_type_from_db_type(typ)
2587
+ for col, typ in get_table_cols_types(
2588
+ pipe.target,
2589
+ self,
2590
+ schema=self.get_pipe_schema(pipe),
2591
+ debug=debug,
2592
+ ).items()
2586
2593
  }
2587
2594
  pipe_bool_cols = [col for col, typ in pipe.dtypes.items() if are_dtypes_equal(str(typ), 'bool')]
2588
2595
  pd_db_df_aliases = {
@@ -453,7 +453,7 @@ def get_backtrack_data(
453
453
  backtrack_interval = self.get_backtrack_interval(debug=debug)
454
454
  if backtrack_minutes is None:
455
455
  backtrack_minutes = (
456
- (backtrack_interval.total_seconds() * 60)
456
+ (backtrack_interval.total_seconds() / 60)
457
457
  if isinstance(backtrack_interval, timedelta)
458
458
  else backtrack_interval
459
459
  )
@@ -11,7 +11,7 @@ import platform
11
11
  import os
12
12
  import sys
13
13
  import meerschaum as mrsm
14
- from meerschaum.utils.typing import Optional, Union, Any, Dict
14
+ from meerschaum.utils.typing import Optional, Union, Any, Dict, Iterable
15
15
  from meerschaum.utils.formatting._shell import make_header
16
16
  from meerschaum.utils.formatting._pprint import pprint
17
17
  from meerschaum.utils.formatting._pipes import (
@@ -322,14 +322,14 @@ def format_success_tuple(
322
322
  from meerschaum.config import get_config
323
323
  status_config = get_config('formatting', status, patch=True)
324
324
 
325
- msg = (' ' * left_padding) + status_config[CHARSET]['icon'] + ' ' + str(tup[1])
325
+ msg = (' ' * left_padding) + status_config[CHARSET]['icon'] + ' ' + str(highlight_pipes(tup[1]))
326
326
  lines = msg.split('\n')
327
327
  lines = [lines[0]] + [
328
328
  ((' ' + line if not line.startswith(' ') else line))
329
329
  for line in lines[1:]
330
330
  ]
331
331
  if ANSI:
332
- lines[0] = fill_ansi(highlight_pipes(lines[0]), **status_config['ansi']['rich'])
332
+ lines[0] = fill_ansi(lines[0], **status_config['ansi']['rich'])
333
333
 
334
334
  msg = '\n'.join(lines)
335
335
  msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding)
@@ -337,7 +337,7 @@ def format_success_tuple(
337
337
 
338
338
 
339
339
  def print_options(
340
- options: Optional[Dict[str, Any]] = None,
340
+ options: Optional[Iterable[Any]] = None,
341
341
  nopretty: bool = False,
342
342
  no_rich: bool = False,
343
343
  name: str = 'options',
@@ -345,6 +345,7 @@ def print_options(
345
345
  num_cols: Optional[int] = None,
346
346
  adjust_cols: bool = True,
347
347
  sort_options: bool = False,
348
+ number_options: bool = False,
348
349
  **kw
349
350
  ) -> None:
350
351
  """
@@ -373,6 +374,12 @@ def print_options(
373
374
  adjust_cols: bool, default True
374
375
  If `True`, adjust the number of columns depending on the terminal size.
375
376
 
377
+ sort_options: bool, default False
378
+ If `True`, print the options in sorted order.
379
+
380
+ number_options: bool, default False
381
+ If `True`, print the option's number in the list (1 index).
382
+
376
383
  """
377
384
  import os
378
385
  from meerschaum.utils.packages import import_rich
@@ -398,9 +405,10 @@ def print_options(
398
405
  print()
399
406
  print(make_header(_header))
400
407
  ### print actions
401
- for option in _options:
408
+ for i, option in enumerate(_options):
409
+ marker = '-' if not number_options else (str(i + 1) + '.')
402
410
  if not nopretty:
403
- print(" - ", end="")
411
+ print(f" {marker} ", end="")
404
412
  print(option)
405
413
  if not nopretty:
406
414
  print()
@@ -434,11 +442,11 @@ def print_options(
434
442
 
435
443
  if _header is not None:
436
444
  table = Table(
437
- title = _header,
438
- box = box.SIMPLE,
439
- show_header = False,
440
- show_footer = False,
441
- title_style = '',
445
+ title=_header,
446
+ box=box.SIMPLE,
447
+ show_header=False,
448
+ show_footer=False,
449
+ title_style='',
442
450
  expand = True,
443
451
  )
444
452
  else:
@@ -447,18 +455,26 @@ def print_options(
447
455
  table.add_column()
448
456
 
449
457
  if len(_options) < 12:
450
- # If fewer than 12 items, use a single column
451
- for option in _options:
452
- table.add_row(Text.from_ansi(highlight_pipes(option)))
458
+ ### If fewer than 12 items, use a single column
459
+ for i, option in enumerate(_options):
460
+ item = highlight_pipes(option)
461
+ if number_options:
462
+ item = str(i + 1) + '. ' + item
463
+ table.add_row(Text.from_ansi(item))
453
464
  else:
454
- # Otherwise, use multiple columns as before
465
+ ### Otherwise, use multiple columns as before
455
466
  num_rows = (len(_options) + num_cols - 1) // num_cols
467
+ item_ix = 0
456
468
  for i in range(num_rows):
457
469
  row = []
458
470
  for j in range(num_cols):
459
471
  index = i + j * num_rows
460
472
  if index < len(_options):
461
- row.append(Text.from_ansi(highlight_pipes(_options[index])))
473
+ item = highlight_pipes(_options[index])
474
+ if number_options:
475
+ item = str(i + 1) + '. ' + item
476
+ row.append(Text.from_ansi(item))
477
+ item_ix += 1
462
478
  else:
463
479
  row.append('')
464
480
  table.add_row(*row)
@@ -323,7 +323,7 @@ def pipe_repr(
323
323
  )
324
324
  if as_rich_text:
325
325
  return text_obj
326
- return rich_text_to_str(text_obj)
326
+ return rich_text_to_str(text_obj).replace('\n', '')
327
327
 
328
328
 
329
329
  def highlight_pipes(message: str) -> str:
@@ -10,10 +10,11 @@ from re import sub
10
10
  from meerschaum.utils.threading import Lock
11
11
  _locks = {'_tried_clear_command': Lock()}
12
12
 
13
+
13
14
  def make_header(
14
- message : str,
15
- ruler : str = '─',
16
- ) -> str:
15
+ message: str,
16
+ ruler: str = '─',
17
+ ) -> str:
17
18
  """Format a message string with a ruler.
18
19
  Length of the ruler is the length of the longest word.
19
20
 
@@ -10,17 +10,18 @@ from __future__ import annotations
10
10
  import os
11
11
  from meerschaum.utils.typing import Any, Union, Optional, Tuple, List
12
12
 
13
+
13
14
  def prompt(
14
- question: str,
15
- icon: bool = True,
16
- default: Union[str, Tuple[str, str], None] = None,
17
- default_editable: Optional[str] = None,
18
- detect_password: bool = True,
19
- is_password: bool = False,
20
- wrap_lines: bool = True,
21
- noask: bool = False,
22
- **kw: Any
23
- ) -> str:
15
+ question: str,
16
+ icon: bool = True,
17
+ default: Union[str, Tuple[str, str], None] = None,
18
+ default_editable: Optional[str] = None,
19
+ detect_password: bool = True,
20
+ is_password: bool = False,
21
+ wrap_lines: bool = True,
22
+ noask: bool = False,
23
+ **kw: Any
24
+ ) -> str:
24
25
  """
25
26
  Ask the user a question and return the answer.
26
27
  Wrapper around `prompt_toolkit.prompt()` with modified behavior.
@@ -75,7 +76,7 @@ def prompt(
75
76
  ### if a default is provided, append it to the question.
76
77
  default_answer = default
77
78
  if default is not None:
78
- question += f" (default: "
79
+ question += " (default: "
79
80
  if isinstance(default, tuple) and len(default) > 1:
80
81
  question += f"{default[0]} [{default[1]}]"
81
82
  default_answer = default[0]
@@ -86,7 +87,7 @@ def prompt(
86
87
  ### detect password
87
88
  if (detect_password and 'password' in question.lower()) or is_password:
88
89
  kw['is_password'] = True
89
-
90
+
90
91
  ### Add the icon and only color the first line.
91
92
  lines = question.split('\n')
92
93
  first_line = lines[0]
@@ -107,15 +108,15 @@ def prompt(
107
108
  answer = (
108
109
  prompt_toolkit.prompt(
109
110
  prompt_toolkit.formatted_text.ANSI(question),
110
- wrap_lines = wrap_lines,
111
- default = default_editable or '',
111
+ wrap_lines=wrap_lines,
112
+ default=default_editable or '',
112
113
  **filter_keywords(prompt_toolkit.prompt, **kw)
113
114
  ) if not noask else ''
114
115
  )
115
116
  else:
116
117
  print(question, end='\n', flush=True)
117
118
  try:
118
- answer = input()
119
+ answer = input() if not noask else ''
119
120
  except EOFError:
120
121
  answer = ''
121
122
  if noask:
meerschaum/utils/sql.py CHANGED
@@ -758,11 +758,11 @@ def build_where(
758
758
 
759
759
 
760
760
  def table_exists(
761
- table: str,
762
- connector: mrsm.connectors.sql.SQLConnector,
763
- schema: Optional[str] = None,
764
- debug: bool = False,
765
- ) -> bool:
761
+ table: str,
762
+ connector: mrsm.connectors.sql.SQLConnector,
763
+ schema: Optional[str] = None,
764
+ debug: bool = False,
765
+ ) -> bool:
766
766
  """Check if a table exists.
767
767
 
768
768
  Parameters
@@ -793,12 +793,12 @@ def table_exists(
793
793
 
794
794
 
795
795
  def get_sqlalchemy_table(
796
- table: str,
797
- connector: Optional[meerschaum.connectors.sql.SQLConnector] = None,
798
- schema: Optional[str] = None,
799
- refresh: bool = False,
800
- debug: bool = False,
801
- ) -> 'sqlalchemy.Table':
796
+ table: str,
797
+ connector: Optional[mrsm.connectors.sql.SQLConnector] = None,
798
+ schema: Optional[str] = None,
799
+ refresh: bool = False,
800
+ debug: bool = False,
801
+ ) -> Union['sqlalchemy.Table', None]:
802
802
  """
803
803
  Construct a SQLAlchemy table from its name.
804
804
 
@@ -829,6 +829,9 @@ def get_sqlalchemy_table(
829
829
  from meerschaum import get_connector
830
830
  connector = get_connector('sql')
831
831
 
832
+ if connector.flavor == 'duckdb':
833
+ return None
834
+
832
835
  from meerschaum.connectors.sql.tables import get_tables
833
836
  from meerschaum.utils.packages import attempt_import
834
837
  from meerschaum.utils.warnings import warn
@@ -1333,11 +1336,11 @@ def get_rename_table_queries(
1333
1336
 
1334
1337
 
1335
1338
  def get_create_table_query(
1336
- query: str,
1337
- new_table: str,
1338
- flavor: str,
1339
- schema: Optional[str] = None,
1340
- ) -> str:
1339
+ query: str,
1340
+ new_table: str,
1341
+ flavor: str,
1342
+ schema: Optional[str] = None,
1343
+ ) -> str:
1341
1344
  """
1342
1345
  Return a query to create a new table from a `SELECT` query.
1343
1346
 
@@ -1365,10 +1368,8 @@ def get_create_table_query(
1365
1368
  new_table_name = sql_item_name(new_table, flavor, schema)
1366
1369
  if flavor in ('mssql',):
1367
1370
  query = query.lstrip()
1368
- original_query = query
1369
1371
  if 'with ' in query.lower():
1370
1372
  final_select_ix = query.lower().rfind('select')
1371
- def_name = query[len('WITH '):].split(' ', maxsplit=1)[0]
1372
1373
  return (
1373
1374
  query[:final_select_ix].rstrip() + ',\n'
1374
1375
  + f"{create_cte_name} AS (\n"
@@ -1405,12 +1406,86 @@ def get_create_table_query(
1405
1406
  return textwrap.dedent(create_table_query)
1406
1407
 
1407
1408
 
1409
+ def wrap_query_with_cte(
1410
+ sub_query: str,
1411
+ parent_query: str,
1412
+ flavor: str,
1413
+ cte_name: str = "src",
1414
+ ) -> str:
1415
+ """
1416
+ Wrap a subquery in a CTE and append an encapsulating query.
1417
+
1418
+ Parameters
1419
+ ----------
1420
+ sub_query: str
1421
+ The query to be referenced. This may itself contain CTEs.
1422
+ Unless `cte_name` is provided, this will be aliased as `src`.
1423
+
1424
+ parent_query: str
1425
+ The larger query to append which references the subquery.
1426
+ This must not contain CTEs.
1427
+
1428
+ flavor: str
1429
+ The database flavor, e.g. `'mssql'`.
1430
+
1431
+ cte_name: str, default 'src'
1432
+ The CTE alias, defaults to `src`.
1433
+
1434
+ Returns
1435
+ -------
1436
+ An encapsulating query which allows you to treat `sub_query` as a temporary table.
1437
+
1438
+ Examples
1439
+ --------
1440
+
1441
+ ```python
1442
+ from meerschaum.utils.sql import wrap_query_with_cte
1443
+ sub_query = "WITH foo AS (SELECT 1 AS val) SELECT (val * 2) AS newval FROM foo"
1444
+ parent_query = "SELECT newval * 3 FROM src"
1445
+ query = wrap_query_with_cte(sub_query, parent_query, 'mssql')
1446
+ print(query)
1447
+ # WITH foo AS (SELECT 1 AS val),
1448
+ # [src] AS (
1449
+ # SELECT (val * 2) AS newval FROM foo
1450
+ # )
1451
+ # SELECT newval * 3 FROM src
1452
+ ```
1453
+
1454
+ """
1455
+ sub_query = sub_query.lstrip()
1456
+ cte_name_quoted = sql_item_name(cte_name, flavor, None)
1457
+
1458
+ if flavor in NO_CTE_FLAVORS:
1459
+ return (
1460
+ parent_query
1461
+ .replace(cte_name_quoted, '--MRSM_SUBQUERY--')
1462
+ .replace(cte_name, '--MRSM_SUBQUERY--')
1463
+ .replace('--MRSM_SUBQUERY--', f"(\n{sub_query}\n) AS {cte_name_quoted}")
1464
+ )
1465
+
1466
+ if 'with ' in sub_query.lower():
1467
+ final_select_ix = sub_query.lower().rfind('select')
1468
+ return (
1469
+ sub_query[:final_select_ix].rstrip() + ',\n'
1470
+ + f"{cte_name_quoted} AS (\n"
1471
+ + ' ' + sub_query[final_select_ix:]
1472
+ + "\n)\n"
1473
+ + parent_query
1474
+ )
1475
+
1476
+ return (
1477
+ f"WITH {cte_name_quoted} AS (\n"
1478
+ f" {sub_query}\n"
1479
+ f")\n{parent_query}"
1480
+ )
1481
+
1482
+
1408
1483
  def format_cte_subquery(
1409
- sub_query: str,
1410
- flavor: str,
1411
- sub_name: str = 'src',
1412
- cols_to_select: Union[List[str], str] = '*',
1413
- ) -> str:
1484
+ sub_query: str,
1485
+ flavor: str,
1486
+ sub_name: str = 'src',
1487
+ cols_to_select: Union[List[str], str] = '*',
1488
+ ) -> str:
1414
1489
  """
1415
1490
  Given a subquery, build a wrapper query that selects from the CTE subquery.
1416
1491
 
@@ -1434,28 +1509,25 @@ def format_cte_subquery(
1434
1509
  -------
1435
1510
  A wrapper query that selects from the CTE.
1436
1511
  """
1437
- import textwrap
1438
1512
  quoted_sub_name = sql_item_name(sub_name, flavor, None)
1439
1513
  cols_str = (
1440
1514
  cols_to_select
1441
1515
  if isinstance(cols_to_select, str)
1442
1516
  else ', '.join([sql_item_name(col, flavor, None) for col in cols_to_select])
1443
1517
  )
1444
- return textwrap.dedent(
1445
- f"""
1446
- SELECT {cols_str}
1447
- FROM ({sub_query})"""
1448
- + (f' AS {quoted_sub_name}' if flavor != 'oracle' else '') + """
1449
- """
1518
+ parent_query = (
1519
+ f"SELECT {cols_str}\n"
1520
+ f"FROM {quoted_sub_name}"
1450
1521
  )
1522
+ return wrap_query_with_cte(sub_query, parent_query, flavor, cte_name=sub_name)
1451
1523
 
1452
1524
 
1453
1525
  def session_execute(
1454
- session: 'sqlalchemy.orm.session.Session',
1455
- queries: Union[List[str], str],
1456
- with_results: bool = False,
1457
- debug: bool = False,
1458
- ) -> Union[mrsm.SuccessTuple, Tuple[mrsm.SuccessTuple, List['sqlalchemy.sql.ResultProxy']]]:
1526
+ session: 'sqlalchemy.orm.session.Session',
1527
+ queries: Union[List[str], str],
1528
+ with_results: bool = False,
1529
+ debug: bool = False,
1530
+ ) -> Union[mrsm.SuccessTuple, Tuple[mrsm.SuccessTuple, List['sqlalchemy.sql.ResultProxy']]]:
1459
1531
  """
1460
1532
  Similar to `SQLConnector.exec_queries()`, execute a list of queries
1461
1533
  and roll back when one fails.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.4.10
3
+ Version: 2.4.12
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares