meerschaum 2.7.2__py3-none-any.whl → 2.7.3__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- meerschaum/_internal/arguments/_parse_arguments.py +2 -0
- meerschaum/_internal/arguments/_parser.py +17 -11
- meerschaum/actions/clear.py +1 -1
- meerschaum/actions/edit.py +1 -1
- meerschaum/actions/verify.py +18 -21
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/sql/_fetch.py +45 -26
- meerschaum/connectors/sql/_instance.py +4 -4
- meerschaum/connectors/sql/_pipes.py +135 -103
- meerschaum/core/Pipe/_attributes.py +1 -1
- meerschaum/core/Pipe/_dtypes.py +9 -9
- meerschaum/core/Pipe/_fetch.py +2 -3
- meerschaum/core/Pipe/_sync.py +11 -3
- meerschaum/core/Pipe/_verify.py +9 -5
- meerschaum/jobs/__init__.py +1 -1
- meerschaum/utils/dataframe.py +10 -2
- meerschaum/utils/dtypes/sql.py +1 -1
- meerschaum/utils/formatting/__init__.py +5 -25
- meerschaum/utils/formatting/_pipes.py +9 -6
- meerschaum/utils/sql.py +156 -87
- meerschaum/utils/venv/__init__.py +44 -6
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/METADATA +1 -1
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/RECORD +29 -29
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/LICENSE +0 -0
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/NOTICE +0 -0
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/WHEEL +0 -0
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/top_level.txt +0 -0
- {meerschaum-2.7.2.dist-info → meerschaum-2.7.3.dist-info}/zip-safe +0 -0
meerschaum/utils/sql.py
CHANGED
@@ -45,12 +45,12 @@ DROP_IF_EXISTS_FLAVORS = {
|
|
45
45
|
}
|
46
46
|
SKIP_AUTO_INCREMENT_FLAVORS = {'citus', 'duckdb'}
|
47
47
|
COALESCE_UNIQUE_INDEX_FLAVORS = {'timescaledb', 'postgresql', 'citus'}
|
48
|
-
|
48
|
+
UPDATE_QUERIES = {
|
49
49
|
'default': """
|
50
50
|
UPDATE {target_table_name} AS f
|
51
51
|
{sets_subquery_none}
|
52
52
|
FROM {target_table_name} AS t
|
53
|
-
INNER JOIN (SELECT
|
53
|
+
INNER JOIN (SELECT {patch_cols_str} FROM {patch_table_name}) AS p
|
54
54
|
ON
|
55
55
|
{and_subquery_t}
|
56
56
|
WHERE
|
@@ -84,7 +84,7 @@ update_queries = {
|
|
84
84
|
""",
|
85
85
|
'mysql': """
|
86
86
|
UPDATE {target_table_name} AS f
|
87
|
-
JOIN (SELECT
|
87
|
+
JOIN (SELECT {patch_cols_str} FROM {patch_table_name}) AS p
|
88
88
|
ON
|
89
89
|
{and_subquery_f}
|
90
90
|
{sets_subquery_f}
|
@@ -100,7 +100,7 @@ update_queries = {
|
|
100
100
|
""",
|
101
101
|
'mariadb': """
|
102
102
|
UPDATE {target_table_name} AS f
|
103
|
-
JOIN (SELECT
|
103
|
+
JOIN (SELECT {patch_cols_str} FROM {patch_table_name}) AS p
|
104
104
|
ON
|
105
105
|
{and_subquery_f}
|
106
106
|
{sets_subquery_f}
|
@@ -179,13 +179,13 @@ update_queries = {
|
|
179
179
|
WHERE ROWID IN (
|
180
180
|
SELECT t.ROWID
|
181
181
|
FROM {target_table_name} AS t
|
182
|
-
INNER JOIN (SELECT
|
182
|
+
INNER JOIN (SELECT * FROM {patch_table_name}) AS p
|
183
183
|
ON {and_subquery_t}
|
184
184
|
);
|
185
185
|
""",
|
186
186
|
"""
|
187
187
|
INSERT INTO {target_table_name} AS f
|
188
|
-
SELECT
|
188
|
+
SELECT {patch_cols_str} FROM {patch_table_name} AS p
|
189
189
|
""",
|
190
190
|
],
|
191
191
|
}
|
@@ -573,21 +573,21 @@ def dateadd_str(
|
|
573
573
|
Examples
|
574
574
|
--------
|
575
575
|
>>> dateadd_str(
|
576
|
-
... flavor
|
577
|
-
... begin
|
578
|
-
... number
|
576
|
+
... flavor='mssql',
|
577
|
+
... begin=datetime(2022, 1, 1, 0, 0),
|
578
|
+
... number=1,
|
579
579
|
... )
|
580
580
|
"DATEADD(day, 1, CAST('2022-01-01 00:00:00' AS DATETIME2))"
|
581
581
|
>>> dateadd_str(
|
582
|
-
... flavor
|
583
|
-
... begin
|
584
|
-
... number
|
582
|
+
... flavor='postgresql',
|
583
|
+
... begin=datetime(2022, 1, 1, 0, 0),
|
584
|
+
... number=1,
|
585
585
|
... )
|
586
586
|
"CAST('2022-01-01 00:00:00' AS TIMESTAMP) + INTERVAL '1 day'"
|
587
587
|
|
588
588
|
"""
|
589
589
|
from meerschaum.utils.packages import attempt_import
|
590
|
-
from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
|
590
|
+
from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type, get_pd_type_from_db_type
|
591
591
|
dateutil_parser = attempt_import('dateutil.parser')
|
592
592
|
if 'int' in str(type(begin)).lower():
|
593
593
|
return str(begin)
|
@@ -619,7 +619,14 @@ def dateadd_str(
|
|
619
619
|
else f"'{begin}'"
|
620
620
|
)
|
621
621
|
|
622
|
-
dt_is_utc =
|
622
|
+
dt_is_utc = (
|
623
|
+
begin_time.tzinfo is not None
|
624
|
+
if begin_time is not None
|
625
|
+
else ('+' in str(begin) or '-' in str(begin).split(':', maxsplit=1)[-1])
|
626
|
+
)
|
627
|
+
if db_type:
|
628
|
+
db_type_is_utc = 'utc' in get_pd_type_from_db_type(db_type).lower()
|
629
|
+
dt_is_utc = dt_is_utc or db_type_is_utc
|
623
630
|
db_type = db_type or get_db_type_from_pd_type(
|
624
631
|
('datetime64[ns, UTC]' if dt_is_utc else 'datetime64[ns]'),
|
625
632
|
flavor=flavor,
|
@@ -629,22 +636,32 @@ def dateadd_str(
|
|
629
636
|
if flavor in ('postgresql', 'timescaledb', 'cockroachdb', 'citus'):
|
630
637
|
begin = (
|
631
638
|
f"CAST({begin} AS {db_type})" if begin != 'now'
|
632
|
-
else "CAST(NOW() AT TIME ZONE 'utc' AS {db_type})"
|
639
|
+
else f"CAST(NOW() AT TIME ZONE 'utc' AS {db_type})"
|
633
640
|
)
|
641
|
+
if dt_is_utc:
|
642
|
+
begin += " AT TIME ZONE 'UTC'"
|
634
643
|
da = begin + (f" + INTERVAL '{number} {datepart}'" if number != 0 else '')
|
635
644
|
|
636
645
|
elif flavor == 'duckdb':
|
637
646
|
begin = f"CAST({begin} AS {db_type})" if begin != 'now' else 'NOW()'
|
647
|
+
if dt_is_utc:
|
648
|
+
begin += " AT TIME ZONE 'UTC'"
|
638
649
|
da = begin + (f" + INTERVAL '{number} {datepart}'" if number != 0 else '')
|
639
650
|
|
640
651
|
elif flavor in ('mssql',):
|
641
652
|
if begin_time and begin_time.microsecond != 0 and not dt_is_utc:
|
642
653
|
begin = begin[:-4] + "'"
|
643
654
|
begin = f"CAST({begin} AS {db_type})" if begin != 'now' else 'GETUTCDATE()'
|
655
|
+
if dt_is_utc:
|
656
|
+
begin += " AT TIME ZONE 'UTC'"
|
644
657
|
da = f"DATEADD({datepart}, {number}, {begin})" if number != 0 else begin
|
645
658
|
|
646
659
|
elif flavor in ('mysql', 'mariadb'):
|
647
|
-
begin =
|
660
|
+
begin = (
|
661
|
+
f"CAST({begin} AS DATETIME(6))"
|
662
|
+
if begin != 'now'
|
663
|
+
else 'UTC_TIMESTAMP(6)'
|
664
|
+
)
|
648
665
|
da = (f"DATE_ADD({begin}, INTERVAL {number} {datepart})" if number != 0 else begin)
|
649
666
|
|
650
667
|
elif flavor == 'sqlite':
|
@@ -653,10 +670,10 @@ def dateadd_str(
|
|
653
670
|
elif flavor == 'oracle':
|
654
671
|
if begin == 'now':
|
655
672
|
begin = str(
|
656
|
-
datetime.now(timezone.utc).replace(tzinfo=None).strftime('%Y:%m:%d %M:%S.%f')
|
673
|
+
datetime.now(timezone.utc).replace(tzinfo=None).strftime(r'%Y:%m:%d %M:%S.%f')
|
657
674
|
)
|
658
675
|
elif begin_time:
|
659
|
-
begin = str(begin_time.strftime('%Y-%m-%d %H:%M:%S.%f'))
|
676
|
+
begin = str(begin_time.strftime(r'%Y-%m-%d %H:%M:%S.%f'))
|
660
677
|
dt_format = 'YYYY-MM-DD HH24:MI:SS.FF'
|
661
678
|
_begin = f"'{begin}'" if begin_time else begin
|
662
679
|
da = (
|
@@ -691,7 +708,7 @@ def test_connection(
|
|
691
708
|
warnings.filterwarnings('ignore', 'Could not')
|
692
709
|
try:
|
693
710
|
return retry_connect(**_default_kw)
|
694
|
-
except Exception
|
711
|
+
except Exception:
|
695
712
|
return False
|
696
713
|
|
697
714
|
|
@@ -784,7 +801,7 @@ def sql_item_name(item: str, flavor: str, schema: Optional[str] = None) -> str:
|
|
784
801
|
### NOTE: System-reserved words must be quoted.
|
785
802
|
if truncated_item.lower() in (
|
786
803
|
'float', 'varchar', 'nvarchar', 'clob',
|
787
|
-
'boolean', 'integer', 'table',
|
804
|
+
'boolean', 'integer', 'table', 'row',
|
788
805
|
):
|
789
806
|
wrappers = ('"', '"')
|
790
807
|
else:
|
@@ -1494,9 +1511,9 @@ def get_update_queries(
|
|
1494
1511
|
):
|
1495
1512
|
flavor = 'sqlite_delete_insert'
|
1496
1513
|
flavor_key = (f'{flavor}-upsert' if upsert else flavor)
|
1497
|
-
base_queries =
|
1514
|
+
base_queries = UPDATE_QUERIES.get(
|
1498
1515
|
flavor_key,
|
1499
|
-
|
1516
|
+
UPDATE_QUERIES['default']
|
1500
1517
|
)
|
1501
1518
|
if not isinstance(base_queries, list):
|
1502
1519
|
base_queries = [base_queries]
|
@@ -1577,6 +1594,12 @@ def get_update_queries(
|
|
1577
1594
|
if not value_cols:
|
1578
1595
|
return ''
|
1579
1596
|
|
1597
|
+
utc_value_cols = {
|
1598
|
+
c_name
|
1599
|
+
for c_name, c_type in value_cols
|
1600
|
+
if ('utc' in get_pd_type_from_db_type(c_type).lower())
|
1601
|
+
} if flavor not in TIMEZONE_NAIVE_FLAVORS else set()
|
1602
|
+
|
1580
1603
|
cast_func_cols = {
|
1581
1604
|
c_name: (
|
1582
1605
|
('', '', '')
|
@@ -1585,7 +1608,11 @@ def get_update_queries(
|
|
1585
1608
|
and are_dtypes_equal(get_pd_type_from_db_type(c_type), 'bytes')
|
1586
1609
|
)
|
1587
1610
|
else (
|
1588
|
-
('CAST(', f" AS {c_type.replace('_', ' ')}", ')'
|
1611
|
+
('CAST(', f" AS {c_type.replace('_', ' ')}", ')' + (
|
1612
|
+
" AT TIME ZONE 'UTC'"
|
1613
|
+
if c_name in utc_value_cols
|
1614
|
+
else ''
|
1615
|
+
))
|
1589
1616
|
if flavor != 'sqlite'
|
1590
1617
|
else ('', '', '')
|
1591
1618
|
)
|
@@ -1865,6 +1892,7 @@ def get_create_table_queries(
|
|
1865
1892
|
flavor: str,
|
1866
1893
|
schema: Optional[str] = None,
|
1867
1894
|
primary_key: Optional[str] = None,
|
1895
|
+
primary_key_db_type: Optional[str] = None,
|
1868
1896
|
autoincrement: bool = False,
|
1869
1897
|
datetime_column: Optional[str] = None,
|
1870
1898
|
) -> List[str]:
|
@@ -1889,6 +1917,9 @@ def get_create_table_queries(
|
|
1889
1917
|
primary_key: Optional[str], default None
|
1890
1918
|
If provided, designate this column as the primary key in the new table.
|
1891
1919
|
|
1920
|
+
primary_key_db_type: Optional[str], default None
|
1921
|
+
If provided, alter the primary key to this type (to set NOT NULL constraint).
|
1922
|
+
|
1892
1923
|
autoincrement: bool, default False
|
1893
1924
|
If `True` and `primary_key` is provided, create the `primary_key` column
|
1894
1925
|
as an auto-incrementing integer column.
|
@@ -1915,6 +1946,7 @@ def get_create_table_queries(
|
|
1915
1946
|
flavor,
|
1916
1947
|
schema=schema,
|
1917
1948
|
primary_key=primary_key,
|
1949
|
+
primary_key_db_type=primary_key_db_type,
|
1918
1950
|
autoincrement=(autoincrement and flavor not in SKIP_AUTO_INCREMENT_FLAVORS),
|
1919
1951
|
datetime_column=datetime_column,
|
1920
1952
|
)
|
@@ -1926,6 +1958,7 @@ def _get_create_table_query_from_dtypes(
|
|
1926
1958
|
flavor: str,
|
1927
1959
|
schema: Optional[str] = None,
|
1928
1960
|
primary_key: Optional[str] = None,
|
1961
|
+
primary_key_db_type: Optional[str] = None,
|
1929
1962
|
autoincrement: bool = False,
|
1930
1963
|
datetime_column: Optional[str] = None,
|
1931
1964
|
) -> List[str]:
|
@@ -2015,6 +2048,7 @@ def _get_create_table_query_from_cte(
|
|
2015
2048
|
flavor: str,
|
2016
2049
|
schema: Optional[str] = None,
|
2017
2050
|
primary_key: Optional[str] = None,
|
2051
|
+
primary_key_db_type: Optional[str] = None,
|
2018
2052
|
autoincrement: bool = False,
|
2019
2053
|
datetime_column: Optional[str] = None,
|
2020
2054
|
) -> List[str]:
|
@@ -2035,7 +2069,11 @@ def _get_create_table_query_from_cte(
|
|
2035
2069
|
if primary_key
|
2036
2070
|
else None
|
2037
2071
|
)
|
2038
|
-
primary_key_clustered =
|
2072
|
+
primary_key_clustered = (
|
2073
|
+
"CLUSTERED"
|
2074
|
+
if not datetime_column or datetime_column == primary_key
|
2075
|
+
else "NONCLUSTERED"
|
2076
|
+
)
|
2039
2077
|
datetime_column_name = (
|
2040
2078
|
sql_item_name(datetime_column, flavor)
|
2041
2079
|
if datetime_column
|
@@ -2045,80 +2083,106 @@ def _get_create_table_query_from_cte(
|
|
2045
2083
|
query = query.lstrip()
|
2046
2084
|
if query.lower().startswith('with '):
|
2047
2085
|
final_select_ix = query.lower().rfind('select')
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2086
|
+
create_table_queries = [
|
2087
|
+
(
|
2088
|
+
query[:final_select_ix].rstrip() + ',\n'
|
2089
|
+
+ f"{create_cte_name} AS (\n"
|
2090
|
+
+ textwrap.indent(query[final_select_ix:], ' ')
|
2091
|
+
+ "\n)\n"
|
2092
|
+
+ f"SELECT *\nINTO {new_table_name}\nFROM {create_cte_name}"
|
2093
|
+
),
|
2094
|
+
]
|
2055
2095
|
else:
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2096
|
+
create_table_queries = [
|
2097
|
+
(
|
2098
|
+
"SELECT *\n"
|
2099
|
+
f"INTO {new_table_name}\n"
|
2100
|
+
f"FROM (\n{textwrap.indent(query, ' ')}\n) AS {create_cte_name}"
|
2101
|
+
),
|
2102
|
+
]
|
2103
|
+
|
2104
|
+
alter_type_queries = []
|
2105
|
+
if primary_key_db_type:
|
2106
|
+
alter_type_queries.extend([
|
2107
|
+
(
|
2108
|
+
f"ALTER TABLE {new_table_name}\n"
|
2109
|
+
f"ALTER COLUMN {primary_key_name} {primary_key_db_type} NOT NULL"
|
2110
|
+
),
|
2111
|
+
])
|
2112
|
+
alter_type_queries.extend([
|
2113
|
+
(
|
2114
|
+
f"ALTER TABLE {new_table_name}\n"
|
2115
|
+
f"ADD CONSTRAINT {primary_key_constraint_name} "
|
2116
|
+
f"PRIMARY KEY {primary_key_clustered} ({primary_key_name})"
|
2117
|
+
),
|
2118
|
+
])
|
2066
2119
|
elif flavor in (None,):
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2120
|
+
create_table_queries = [
|
2121
|
+
(
|
2122
|
+
f"WITH {create_cte_name} AS (\n{textwrap.index(query, ' ')}\n)\n"
|
2123
|
+
f"CREATE TABLE {new_table_name} AS\n"
|
2124
|
+
"SELECT *\n"
|
2125
|
+
f"FROM {create_cte_name}"
|
2126
|
+
),
|
2127
|
+
]
|
2073
2128
|
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2129
|
+
alter_type_queries = [
|
2130
|
+
(
|
2131
|
+
f"ALTER TABLE {new_table_name}\n"
|
2132
|
+
f"ADD PRIMARY KEY ({primary_key_name})"
|
2133
|
+
),
|
2134
|
+
]
|
2078
2135
|
elif flavor in ('sqlite', 'mysql', 'mariadb', 'duckdb', 'oracle'):
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2136
|
+
create_table_queries = [
|
2137
|
+
(
|
2138
|
+
f"CREATE TABLE {new_table_name} AS\n"
|
2139
|
+
"SELECT *\n"
|
2140
|
+
f"FROM (\n{textwrap.indent(query, ' ')}\n)"
|
2141
|
+
+ (f" AS {create_cte_name}" if flavor != 'oracle' else '')
|
2142
|
+
),
|
2143
|
+
]
|
2084
2144
|
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2145
|
+
alter_type_queries = [
|
2146
|
+
(
|
2147
|
+
f"ALTER TABLE {new_table_name}\n"
|
2148
|
+
"ADD PRIMARY KEY ({primary_key_name})"
|
2149
|
+
),
|
2150
|
+
]
|
2089
2151
|
elif flavor == 'timescaledb' and datetime_column and datetime_column != primary_key:
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2152
|
+
create_table_queries = [
|
2153
|
+
(
|
2154
|
+
"SELECT *\n"
|
2155
|
+
f"INTO {new_table_name}\n"
|
2156
|
+
f"FROM (\n{textwrap.indent(query, ' ')}\n) AS {create_cte_name}\n"
|
2157
|
+
),
|
2158
|
+
]
|
2095
2159
|
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2099
|
-
|
2160
|
+
alter_type_queries = [
|
2161
|
+
(
|
2162
|
+
f"ALTER TABLE {new_table_name}\n"
|
2163
|
+
f"ADD PRIMARY KEY ({datetime_column_name}, {primary_key_name})"
|
2164
|
+
),
|
2165
|
+
]
|
2100
2166
|
else:
|
2101
|
-
|
2102
|
-
|
2103
|
-
|
2104
|
-
|
2105
|
-
|
2167
|
+
create_table_queries = [
|
2168
|
+
(
|
2169
|
+
"SELECT *\n"
|
2170
|
+
f"INTO {new_table_name}\n"
|
2171
|
+
f"FROM (\n{textwrap.indent(query, ' ')}\n) AS {create_cte_name}"
|
2172
|
+
),
|
2173
|
+
]
|
2106
2174
|
|
2107
|
-
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2175
|
+
alter_type_queries = [
|
2176
|
+
(
|
2177
|
+
f"ALTER TABLE {new_table_name}\n"
|
2178
|
+
f"ADD PRIMARY KEY ({primary_key_name})"
|
2179
|
+
),
|
2180
|
+
]
|
2111
2181
|
|
2112
|
-
create_table_query = textwrap.dedent(create_table_query).lstrip().rstrip()
|
2113
2182
|
if not primary_key:
|
2114
|
-
return
|
2115
|
-
|
2116
|
-
alter_type_query = textwrap.dedent(alter_type_query).lstrip().rstrip()
|
2183
|
+
return create_table_queries
|
2117
2184
|
|
2118
|
-
return
|
2119
|
-
create_table_query,
|
2120
|
-
alter_type_query,
|
2121
|
-
]
|
2185
|
+
return create_table_queries + alter_type_queries
|
2122
2186
|
|
2123
2187
|
|
2124
2188
|
def wrap_query_with_cte(
|
@@ -2167,6 +2231,7 @@ def wrap_query_with_cte(
|
|
2167
2231
|
```
|
2168
2232
|
|
2169
2233
|
"""
|
2234
|
+
import textwrap
|
2170
2235
|
sub_query = sub_query.lstrip()
|
2171
2236
|
cte_name_quoted = sql_item_name(cte_name, flavor, None)
|
2172
2237
|
|
@@ -2190,7 +2255,7 @@ def wrap_query_with_cte(
|
|
2190
2255
|
|
2191
2256
|
return (
|
2192
2257
|
f"WITH {cte_name_quoted} AS (\n"
|
2193
|
-
f"
|
2258
|
+
f"{textwrap.indent(sub_query, ' ')}\n"
|
2194
2259
|
f")\n{parent_query}"
|
2195
2260
|
)
|
2196
2261
|
|
@@ -2269,6 +2334,8 @@ def session_execute(
|
|
2269
2334
|
queries = [queries]
|
2270
2335
|
successes, msgs, results = [], [], []
|
2271
2336
|
for query in queries:
|
2337
|
+
if debug:
|
2338
|
+
dprint(query)
|
2272
2339
|
query_text = sqlalchemy.text(query)
|
2273
2340
|
fail_msg = "Failed to execute queries."
|
2274
2341
|
try:
|
@@ -2283,6 +2350,8 @@ def session_execute(
|
|
2283
2350
|
msgs.append(query_msg)
|
2284
2351
|
results.append(result)
|
2285
2352
|
if not query_success:
|
2353
|
+
if debug:
|
2354
|
+
dprint("Rolling back session.")
|
2286
2355
|
session.rollback()
|
2287
2356
|
break
|
2288
2357
|
success, msg = all(successes), '\n'.join(msgs)
|
@@ -67,7 +67,6 @@ def activate_venv(
|
|
67
67
|
return True
|
68
68
|
import sys
|
69
69
|
import os
|
70
|
-
from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
|
71
70
|
if venv is not None:
|
72
71
|
init_venv(venv=venv, debug=debug)
|
73
72
|
with LOCKS['active_venvs']:
|
@@ -379,22 +378,47 @@ def init_venv(
|
|
379
378
|
import os
|
380
379
|
import pathlib
|
381
380
|
import shutil
|
381
|
+
import time
|
382
382
|
|
383
383
|
from meerschaum.config.static import STATIC_CONFIG
|
384
|
-
from meerschaum.config._paths import
|
384
|
+
from meerschaum.config._paths import (
|
385
|
+
VIRTENV_RESOURCES_PATH,
|
386
|
+
VENVS_CACHE_RESOURCES_PATH,
|
387
|
+
)
|
385
388
|
from meerschaum.utils.packages import is_uv_enabled
|
386
389
|
|
387
390
|
venv_path = VIRTENV_RESOURCES_PATH / venv
|
388
391
|
vtp = venv_target_path(venv=venv, allow_nonexistent=True, debug=debug)
|
389
392
|
docker_home_venv_path = pathlib.Path('/home/meerschaum/venvs/mrsm')
|
390
|
-
|
393
|
+
lock_path = VENVS_CACHE_RESOURCES_PATH / (venv + '.lock')
|
391
394
|
work_dir_env_var = STATIC_CONFIG['environment']['work_dir']
|
395
|
+
|
396
|
+
def update_lock(active: bool):
|
397
|
+
try:
|
398
|
+
if active:
|
399
|
+
lock_path.unlink()
|
400
|
+
else:
|
401
|
+
lock_path.touch()
|
402
|
+
except Exception:
|
403
|
+
pass
|
404
|
+
|
405
|
+
def wait_for_lock():
|
406
|
+
max_lock_seconds = 1.0
|
407
|
+
step_sleep_seconds = 0.1
|
408
|
+
init_venv_check_start = time.perf_counter()
|
409
|
+
while (time.perf_counter() - init_venv_check_start < max_lock_seconds):
|
410
|
+
if not lock_path.exists():
|
411
|
+
continue
|
412
|
+
time.sleep(step_sleep_seconds)
|
413
|
+
update_lock(False)
|
414
|
+
|
392
415
|
if (
|
393
416
|
not force
|
394
417
|
and venv == 'mrsm'
|
395
418
|
and os.environ.get(work_dir_env_var, None) is not None
|
396
419
|
and docker_home_venv_path.exists()
|
397
420
|
):
|
421
|
+
wait_for_lock()
|
398
422
|
shutil.move(docker_home_venv_path, venv_path)
|
399
423
|
if verify:
|
400
424
|
verify_venv(venv, debug=debug)
|
@@ -422,6 +446,9 @@ def init_venv(
|
|
422
446
|
except FileExistsError:
|
423
447
|
pass
|
424
448
|
|
449
|
+
wait_for_lock()
|
450
|
+
update_lock(True)
|
451
|
+
|
425
452
|
if uv is not None:
|
426
453
|
_venv_success = run_python_package(
|
427
454
|
'uv',
|
@@ -437,7 +464,9 @@ def init_venv(
|
|
437
464
|
_venv_success = run_python_package(
|
438
465
|
'venv',
|
439
466
|
[venv_path.as_posix()] + (
|
440
|
-
['--symlinks']
|
467
|
+
['--symlinks']
|
468
|
+
if platform.system() != 'Windows'
|
469
|
+
else []
|
441
470
|
),
|
442
471
|
venv=None, debug=debug
|
443
472
|
) == 0
|
@@ -447,8 +476,14 @@ def init_venv(
|
|
447
476
|
_venv = None
|
448
477
|
if not _venv_success:
|
449
478
|
virtualenv = attempt_import(
|
450
|
-
'virtualenv',
|
451
|
-
|
479
|
+
'virtualenv',
|
480
|
+
venv=None,
|
481
|
+
lazy=False,
|
482
|
+
install=(not tried_virtualenv),
|
483
|
+
warn=False,
|
484
|
+
check_update=False,
|
485
|
+
color=False,
|
486
|
+
debug=debug,
|
452
487
|
)
|
453
488
|
if virtualenv is None:
|
454
489
|
print(
|
@@ -457,6 +492,7 @@ def init_venv(
|
|
457
492
|
)
|
458
493
|
if rename_vtp and temp_vtp.exists():
|
459
494
|
temp_vtp.rename(vtp)
|
495
|
+
update_lock(False)
|
460
496
|
return False
|
461
497
|
|
462
498
|
tried_virtualenv = True
|
@@ -495,6 +531,7 @@ def init_venv(
|
|
495
531
|
traceback.print_exc()
|
496
532
|
if rename_vtp and temp_vtp.exists():
|
497
533
|
temp_vtp.rename(vtp)
|
534
|
+
update_lock(False)
|
498
535
|
return False
|
499
536
|
if verify:
|
500
537
|
verify_venv(venv, debug=debug)
|
@@ -503,6 +540,7 @@ def init_venv(
|
|
503
540
|
if rename_vtp and temp_vtp.exists():
|
504
541
|
temp_vtp.rename(vtp)
|
505
542
|
|
543
|
+
update_lock(False)
|
506
544
|
return True
|
507
545
|
|
508
546
|
|