meerschaum 2.7.2__py3-none-any.whl → 2.7.3__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 +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
|
|