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.
- meerschaum/_internal/arguments/_parse_arguments.py +15 -1
- meerschaum/_internal/docs/index.py +1 -0
- meerschaum/_internal/shell/Shell.py +19 -9
- meerschaum/_internal/shell/ShellCompleter.py +11 -6
- meerschaum/actions/bootstrap.py +120 -15
- meerschaum/actions/clear.py +41 -30
- meerschaum/actions/edit.py +89 -0
- meerschaum/actions/start.py +3 -2
- meerschaum/actions/sync.py +3 -2
- meerschaum/api/dash/callbacks/dashboard.py +2 -1
- meerschaum/api/dash/callbacks/jobs.py +53 -7
- meerschaum/api/dash/callbacks/pipes.py +1 -1
- meerschaum/api/dash/jobs.py +86 -60
- meerschaum/api/dash/pages/__init__.py +1 -0
- meerschaum/api/dash/pages/job.py +21 -0
- meerschaum/api/routes/_jobs.py +3 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/sql/_fetch.py +65 -61
- meerschaum/connectors/sql/_pipes.py +36 -29
- meerschaum/core/Pipe/_data.py +1 -1
- meerschaum/utils/formatting/__init__.py +32 -16
- meerschaum/utils/formatting/_pipes.py +1 -1
- meerschaum/utils/formatting/_shell.py +4 -3
- meerschaum/utils/prompt.py +16 -15
- meerschaum/utils/sql.py +107 -35
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/METADATA +1 -1
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/RECORD +33 -32
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/WHEEL +1 -1
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/LICENSE +0 -0
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/NOTICE +0 -0
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.4.10.dist-info → meerschaum-2.4.12.dist-info}/top_level.txt +0 -0
- {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,
|
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
|
-
|
2179
|
-
|
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 += "
|
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
|
-
|
2334
|
-
|
2335
|
-
|
2336
|
-
|
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
|
2356
|
-
schema
|
2357
|
-
debug
|
2358
|
-
refresh
|
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
|
-
|
2364
|
-
|
2365
|
-
|
2366
|
-
|
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
|
2398
|
-
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 = {
|
meerschaum/core/Pipe/_data.py
CHANGED
@@ -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()
|
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(
|
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[
|
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("
|
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
|
438
|
-
box
|
439
|
-
show_header
|
440
|
-
show_footer
|
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
|
-
|
451
|
-
for option in _options:
|
452
|
-
|
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
|
-
|
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
|
-
|
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)
|
@@ -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
|
-
|
15
|
-
|
16
|
-
|
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
|
|
meerschaum/utils/prompt.py
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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 +=
|
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
|
111
|
-
default
|
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
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
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
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
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
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
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
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
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
|
-
|
1445
|
-
f""
|
1446
|
-
|
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
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
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.
|