meerschaum 3.0.0rc3__py3-none-any.whl → 3.0.0rc7__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/_parser.py +14 -2
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +434 -0
- meerschaum/_internal/docs/index.py +1 -2
- meerschaum/_internal/entry.py +44 -8
- meerschaum/_internal/shell/Shell.py +113 -19
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +3 -1
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +39 -11
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +27 -8
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +13 -7
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +69 -4
- meerschaum/actions/start.py +135 -14
- meerschaum/actions/stop.py +36 -3
- meerschaum/actions/sync.py +6 -1
- meerschaum/api/__init__.py +35 -13
- meerschaum/api/_events.py +7 -2
- meerschaum/api/_oauth2.py +47 -4
- meerschaum/api/dash/callbacks/dashboard.py +103 -97
- meerschaum/api/dash/callbacks/jobs.py +3 -2
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +136 -57
- meerschaum/api/dash/callbacks/register.py +9 -2
- meerschaum/api/dash/callbacks/tokens.py +2 -1
- meerschaum/api/dash/components.py +6 -7
- meerschaum/api/dash/keys.py +17 -1
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +14 -4
- meerschaum/api/dash/pipes.py +186 -65
- meerschaum/api/dash/tokens.py +1 -1
- meerschaum/api/dash/webterm.py +14 -6
- meerschaum/api/models/_pipes.py +7 -1
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +1 -0
- meerschaum/api/routes/_jobs.py +23 -11
- meerschaum/api/routes/_login.py +73 -5
- meerschaum/api/routes/_pipes.py +6 -4
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +60 -13
- meerschaum/config/_default.py +89 -61
- meerschaum/config/_edit.py +10 -8
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +4 -2
- meerschaum/config/_paths.py +127 -12
- meerschaum/config/_read_config.py +20 -10
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +7 -5
- meerschaum/connectors/_Connector.py +1 -2
- meerschaum/connectors/__init__.py +37 -2
- meerschaum/connectors/api/_APIConnector.py +1 -1
- meerschaum/connectors/api/_jobs.py +11 -0
- meerschaum/connectors/api/_pipes.py +7 -1
- meerschaum/connectors/instance/_plugins.py +9 -1
- meerschaum/connectors/instance/_tokens.py +20 -3
- meerschaum/connectors/instance/_users.py +8 -1
- meerschaum/connectors/parse.py +1 -1
- meerschaum/connectors/sql/_create_engine.py +3 -0
- meerschaum/connectors/sql/_pipes.py +98 -79
- meerschaum/connectors/sql/_users.py +8 -1
- meerschaum/connectors/sql/tables/__init__.py +20 -3
- meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
- meerschaum/connectors/valkey/_pipes.py +7 -5
- meerschaum/core/Pipe/__init__.py +62 -72
- meerschaum/core/Pipe/_attributes.py +66 -90
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +0 -50
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_sync.py +12 -18
- meerschaum/core/Plugin/_Plugin.py +7 -1
- meerschaum/core/Token/_Token.py +1 -1
- meerschaum/core/User/_User.py +1 -2
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +135 -35
- meerschaum/jobs/systemd.py +7 -2
- meerschaum/plugins/__init__.py +277 -81
- meerschaum/utils/_get_pipes.py +30 -4
- meerschaum/utils/daemon/Daemon.py +195 -41
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +18 -5
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/debug.py +34 -4
- meerschaum/utils/dtypes/__init__.py +5 -1
- meerschaum/utils/formatting/__init__.py +4 -1
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +47 -46
- meerschaum/utils/formatting/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +16 -6
- meerschaum/utils/misc.py +18 -38
- meerschaum/utils/packages/__init__.py +15 -13
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/pipes.py +39 -7
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +171 -144
- meerschaum/utils/sql.py +12 -2
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/venv/__init__.py +2 -0
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/METADATA +3 -1
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/RECORD +125 -119
- meerschaum/config/_environment.py +0 -145
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/top_level.txt +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/zip-safe +0 -0
@@ -146,8 +146,10 @@ def fetch_pipes_keys(
|
|
146
146
|
location_keys: Optional[List[str]] = None,
|
147
147
|
tags: Optional[List[str]] = None,
|
148
148
|
params: Optional[Dict[str, Any]] = None,
|
149
|
-
debug: bool = False
|
150
|
-
) -> List[
|
149
|
+
debug: bool = False,
|
150
|
+
) -> List[
|
151
|
+
Tuple[str, str, Union[str, None], Dict[str, Any]]
|
152
|
+
]:
|
151
153
|
"""
|
152
154
|
Return a list of tuples corresponding to the parameters provided.
|
153
155
|
|
@@ -174,7 +176,7 @@ def fetch_pipes_keys(
|
|
174
176
|
|
175
177
|
Returns
|
176
178
|
-------
|
177
|
-
A list of tuples of pipes' keys (connector_keys, metric_key, location_key).
|
179
|
+
A list of tuples of pipes' keys and parameters (connector_keys, metric_key, location_key, parameters).
|
178
180
|
"""
|
179
181
|
from meerschaum.utils.packages import attempt_import
|
180
182
|
from meerschaum.utils.misc import separate_negation_values
|
@@ -249,11 +251,18 @@ def fetch_pipes_keys(
|
|
249
251
|
) for key, val in _params.items()
|
250
252
|
if not isinstance(val, (list, tuple)) and key in pipes_tbl.c
|
251
253
|
]
|
254
|
+
if self.flavor in json_flavors:
|
255
|
+
sqlalchemy_dialects = mrsm.attempt_import('sqlalchemy.dialects', lazy=False)
|
256
|
+
JSONB = sqlalchemy_dialects.postgresql.JSONB
|
257
|
+
else:
|
258
|
+
JSONB = sqlalchemy.String
|
259
|
+
|
252
260
|
select_cols = (
|
253
261
|
[
|
254
262
|
pipes_tbl.c.connector_keys,
|
255
263
|
pipes_tbl.c.metric_key,
|
256
264
|
pipes_tbl.c.location_key,
|
265
|
+
pipes_tbl.c.parameters,
|
257
266
|
]
|
258
267
|
)
|
259
268
|
|
@@ -271,14 +280,12 @@ def fetch_pipes_keys(
|
|
271
280
|
|
272
281
|
ors, nands = [], []
|
273
282
|
if self.flavor in json_flavors:
|
274
|
-
from sqlalchemy.dialects import postgresql
|
275
283
|
for _in_tags, _ex_tags in in_ex_tag_groups:
|
276
284
|
if _in_tags:
|
277
285
|
ors.append(
|
278
286
|
sqlalchemy.and_(
|
279
|
-
pipes_tbl.c['parameters'].cast(postgresql.JSONB).has_key('tags'),
|
280
287
|
pipes_tbl.c['parameters']['tags'].cast(
|
281
|
-
|
288
|
+
JSONB
|
282
289
|
).contains(_in_tags)
|
283
290
|
)
|
284
291
|
)
|
@@ -286,9 +293,8 @@ def fetch_pipes_keys(
|
|
286
293
|
nands.append(
|
287
294
|
sqlalchemy.not_(
|
288
295
|
sqlalchemy.and_(
|
289
|
-
pipes_tbl.c['parameters'].cast(postgresql.JSONB).has_key('tags'),
|
290
296
|
pipes_tbl.c['parameters']['tags'].cast(
|
291
|
-
|
297
|
+
JSONB
|
292
298
|
).contains([xt])
|
293
299
|
)
|
294
300
|
)
|
@@ -340,7 +346,7 @@ def fetch_pipes_keys(
|
|
340
346
|
except Exception as e:
|
341
347
|
error(str(e))
|
342
348
|
|
343
|
-
return
|
349
|
+
return rows
|
344
350
|
|
345
351
|
|
346
352
|
def create_pipe_indices(
|
@@ -371,6 +377,9 @@ def create_indices(
|
|
371
377
|
"""
|
372
378
|
Create a pipe's indices.
|
373
379
|
"""
|
380
|
+
if pipe.__dict__.get('_skip_check_indices', False):
|
381
|
+
return True
|
382
|
+
|
374
383
|
if debug:
|
375
384
|
dprint(f"Creating indices for {pipe}...")
|
376
385
|
|
@@ -380,7 +389,7 @@ def create_indices(
|
|
380
389
|
|
381
390
|
cols_to_include = set((columns or []) + (indices or [])) or None
|
382
391
|
|
383
|
-
|
392
|
+
pipe._clear_cache_key('_columns_indices', debug=debug)
|
384
393
|
ix_queries = {
|
385
394
|
col: queries
|
386
395
|
for col, queries in self.get_create_index_queries(pipe, debug=debug).items()
|
@@ -456,7 +465,7 @@ def get_pipe_index_names(self, pipe: mrsm.Pipe) -> Dict[str, str]:
|
|
456
465
|
-------
|
457
466
|
A dictionary of index keys to column names.
|
458
467
|
"""
|
459
|
-
from meerschaum.utils.sql import DEFAULT_SCHEMA_FLAVORS
|
468
|
+
from meerschaum.utils.sql import DEFAULT_SCHEMA_FLAVORS, truncate_item_name
|
460
469
|
_parameters = pipe.parameters
|
461
470
|
_index_template = _parameters.get('index_template', "IX_{schema_str}{target}_{column_names}")
|
462
471
|
_schema = self.get_pipe_schema(pipe)
|
@@ -497,7 +506,7 @@ def get_pipe_index_names(self, pipe: mrsm.Pipe) -> Dict[str, str]:
|
|
497
506
|
continue
|
498
507
|
seen_index_names[index_name] = ix
|
499
508
|
return {
|
500
|
-
ix: index_name
|
509
|
+
ix: truncate_item_name(index_name, flavor=self.flavor)
|
501
510
|
for index_name, ix in seen_index_names.items()
|
502
511
|
}
|
503
512
|
|
@@ -1498,7 +1507,7 @@ def get_pipe_attributes(
|
|
1498
1507
|
"""
|
1499
1508
|
from meerschaum.connectors.sql.tables import get_tables
|
1500
1509
|
from meerschaum.utils.packages import attempt_import
|
1501
|
-
sqlalchemy = attempt_import('sqlalchemy')
|
1510
|
+
sqlalchemy = attempt_import('sqlalchemy', lazy=False)
|
1502
1511
|
|
1503
1512
|
if pipe.get_id(debug=debug) is None:
|
1504
1513
|
return {}
|
@@ -1509,16 +1518,16 @@ def get_pipe_attributes(
|
|
1509
1518
|
q = sqlalchemy.select(pipes_tbl).where(pipes_tbl.c.pipe_id == pipe.id)
|
1510
1519
|
if debug:
|
1511
1520
|
dprint(q)
|
1512
|
-
|
1513
|
-
|
1521
|
+
rows = (
|
1522
|
+
self.exec(q, silent=True, debug=debug).mappings().all()
|
1514
1523
|
if self.flavor != 'duckdb'
|
1515
|
-
else self.read(q, debug=debug).to_dict(orient='records')
|
1524
|
+
else self.read(q, debug=debug).to_dict(orient='records')
|
1516
1525
|
)
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1526
|
+
if not rows:
|
1527
|
+
return {}
|
1528
|
+
attributes = dict(rows[0])
|
1529
|
+
except Exception:
|
1530
|
+
warn(traceback.format_exc())
|
1522
1531
|
return {}
|
1523
1532
|
|
1524
1533
|
### handle non-PostgreSQL databases (text vs JSON)
|
@@ -1673,8 +1682,9 @@ def sync_pipe(
|
|
1673
1682
|
UPDATE_QUERIES,
|
1674
1683
|
get_reset_autoincrement_queries,
|
1675
1684
|
)
|
1676
|
-
from meerschaum.utils.dtypes import
|
1685
|
+
from meerschaum.utils.dtypes import get_current_timestamp
|
1677
1686
|
from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
|
1687
|
+
from meerschaum.utils.dataframe import get_special_cols
|
1678
1688
|
from meerschaum import Pipe
|
1679
1689
|
import time
|
1680
1690
|
import copy
|
@@ -1686,6 +1696,7 @@ def sync_pipe(
|
|
1686
1696
|
|
1687
1697
|
start = time.perf_counter()
|
1688
1698
|
pipe_name = sql_item_name(pipe.target, self.flavor, schema=self.get_pipe_schema(pipe))
|
1699
|
+
dtypes = pipe.get_dtypes(debug=debug)
|
1689
1700
|
|
1690
1701
|
if not pipe.temporary and not pipe.get_id(debug=debug):
|
1691
1702
|
register_tuple = pipe.register(debug=debug)
|
@@ -1702,6 +1713,7 @@ def sync_pipe(
|
|
1702
1713
|
df,
|
1703
1714
|
chunksize=chunksize,
|
1704
1715
|
safe_copy=kw.get('safe_copy', False),
|
1716
|
+
dtypes=dtypes,
|
1705
1717
|
debug=debug,
|
1706
1718
|
)
|
1707
1719
|
|
@@ -1714,35 +1726,18 @@ def sync_pipe(
|
|
1714
1726
|
### Check for new columns.
|
1715
1727
|
add_cols_queries = self.get_add_columns_queries(pipe, df, debug=debug)
|
1716
1728
|
if add_cols_queries:
|
1717
|
-
|
1718
|
-
|
1729
|
+
pipe._clear_cache_key('_columns_types', debug=debug)
|
1730
|
+
pipe._clear_cache_key('_columns_indices', debug=debug)
|
1719
1731
|
if not self.exec_queries(add_cols_queries, debug=debug):
|
1720
1732
|
warn(f"Failed to add new columns to {pipe}.")
|
1721
1733
|
|
1722
1734
|
alter_cols_queries = self.get_alter_columns_queries(pipe, df, debug=debug)
|
1723
1735
|
if alter_cols_queries:
|
1724
|
-
|
1725
|
-
|
1736
|
+
pipe._clear_cache_key('_columns_types', debug=debug)
|
1737
|
+
pipe._clear_cache_key('_columns_types', debug=debug)
|
1726
1738
|
if not self.exec_queries(alter_cols_queries, debug=debug):
|
1727
1739
|
warn(f"Failed to alter columns for {pipe}.")
|
1728
1740
|
|
1729
|
-
### NOTE: Oracle SQL < 23c (2023) and SQLite does not support booleans,
|
1730
|
-
### so infer bools and persist them to `dtypes`.
|
1731
|
-
if self.flavor in ('oracle', 'sqlite', 'mysql', 'mariadb'):
|
1732
|
-
pipe_dtypes = pipe.get_dtypes(infer=False, debug=debug)
|
1733
|
-
new_bool_cols = {
|
1734
|
-
col: 'bool[pyarrow]'
|
1735
|
-
for col, typ in df.dtypes.items()
|
1736
|
-
if col not in pipe_dtypes
|
1737
|
-
and are_dtypes_equal(str(typ), 'bool')
|
1738
|
-
}
|
1739
|
-
pipe_dtypes.update(new_bool_cols)
|
1740
|
-
pipe.dtypes = pipe_dtypes
|
1741
|
-
if new_bool_cols and not pipe.temporary:
|
1742
|
-
infer_bool_success, infer_bool_msg = pipe.edit(debug=debug)
|
1743
|
-
if not infer_bool_success:
|
1744
|
-
return infer_bool_success, infer_bool_msg
|
1745
|
-
|
1746
1741
|
upsert = pipe.parameters.get('upsert', False) and (self.flavor + '-upsert') in UPDATE_QUERIES
|
1747
1742
|
if upsert:
|
1748
1743
|
check_existing = False
|
@@ -1771,7 +1766,7 @@ def sync_pipe(
|
|
1771
1766
|
if 'name' in kw:
|
1772
1767
|
kw.pop('name')
|
1773
1768
|
|
1774
|
-
### Insert new data into
|
1769
|
+
### Insert new data into the target table.
|
1775
1770
|
unseen_kw = copy.deepcopy(kw)
|
1776
1771
|
unseen_kw.update({
|
1777
1772
|
'name': pipe.target,
|
@@ -1792,7 +1787,7 @@ def sync_pipe(
|
|
1792
1787
|
is_new
|
1793
1788
|
and primary_key
|
1794
1789
|
and primary_key
|
1795
|
-
not in
|
1790
|
+
not in dtypes
|
1796
1791
|
and primary_key not in unseen_df.columns
|
1797
1792
|
)
|
1798
1793
|
)
|
@@ -1892,6 +1887,14 @@ def sync_pipe(
|
|
1892
1887
|
label=('update' if not upsert else 'upsert'),
|
1893
1888
|
)
|
1894
1889
|
self._log_temporary_tables_creation(temp_target, create=(not pipe.temporary), debug=debug)
|
1890
|
+
update_dtypes = {
|
1891
|
+
**{
|
1892
|
+
col: str(typ)
|
1893
|
+
for col, typ in update_df.dtypes.items()
|
1894
|
+
},
|
1895
|
+
**get_special_cols(update_df)
|
1896
|
+
}
|
1897
|
+
|
1895
1898
|
temp_pipe = Pipe(
|
1896
1899
|
pipe.connector_keys.replace(':', '_') + '_', pipe.metric_key, pipe.location_key,
|
1897
1900
|
instance=pipe.instance_keys,
|
@@ -1900,34 +1903,30 @@ def sync_pipe(
|
|
1900
1903
|
for ix_key, ix in pipe.columns.items()
|
1901
1904
|
if ix and ix in update_df.columns
|
1902
1905
|
},
|
1903
|
-
dtypes=
|
1904
|
-
col: typ
|
1905
|
-
for col, typ in pipe.dtypes.items()
|
1906
|
-
if col in update_df.columns
|
1907
|
-
},
|
1906
|
+
dtypes=update_dtypes,
|
1908
1907
|
target=temp_target,
|
1909
1908
|
temporary=True,
|
1910
1909
|
enforce=False,
|
1911
1910
|
static=True,
|
1912
1911
|
autoincrement=False,
|
1912
|
+
cache=False,
|
1913
1913
|
parameters={
|
1914
1914
|
'schema': self.internal_schema,
|
1915
1915
|
'hypertable': False,
|
1916
1916
|
},
|
1917
1917
|
)
|
1918
|
-
|
1919
|
-
col: get_db_type_from_pd_type(
|
1920
|
-
|
1921
|
-
self.flavor,
|
1922
|
-
)
|
1923
|
-
for col, typ in update_df.dtypes.items()
|
1918
|
+
_temp_columns_types = {
|
1919
|
+
col: get_db_type_from_pd_type(typ, self.flavor)
|
1920
|
+
for col, typ in update_dtypes.items()
|
1924
1921
|
}
|
1925
|
-
|
1926
|
-
temp_pipe.
|
1927
|
-
|
1922
|
+
temp_pipe._cache_value('_columns_types', _temp_columns_types, memory_only=True, debug=debug)
|
1923
|
+
temp_pipe._cache_value('_skip_check_indices', True, memory_only=True, debug=debug)
|
1924
|
+
now_ts = get_current_timestamp('ms', as_int=True) / 1000
|
1925
|
+
temp_pipe._cache_value('_columns_types_timestamp', now_ts, memory_only=True, debug=debug)
|
1928
1926
|
temp_success, temp_msg = temp_pipe.sync(update_df, check_existing=False, debug=debug)
|
1929
1927
|
if not temp_success:
|
1930
1928
|
return temp_success, temp_msg
|
1929
|
+
|
1931
1930
|
existing_cols = pipe.get_columns_types(debug=debug)
|
1932
1931
|
join_cols = [
|
1933
1932
|
col
|
@@ -1950,6 +1949,8 @@ def sync_pipe(
|
|
1950
1949
|
upsert=upsert,
|
1951
1950
|
schema=self.get_pipe_schema(pipe),
|
1952
1951
|
patch_schema=self.internal_schema,
|
1952
|
+
target_cols_types=pipe.get_columns_types(debug=debug),
|
1953
|
+
patch_cols_types=_temp_columns_types,
|
1953
1954
|
datetime_col=(dt_col if dt_col in update_df.columns else None),
|
1954
1955
|
identity_insert=(autoincrement and primary_key in update_df.columns),
|
1955
1956
|
null_indices=pipe.null_indices,
|
@@ -2237,13 +2238,13 @@ def sync_pipe_inplace(
|
|
2237
2238
|
|
2238
2239
|
add_cols_queries = self.get_add_columns_queries(pipe, new_cols, debug=debug)
|
2239
2240
|
if add_cols_queries:
|
2240
|
-
|
2241
|
-
|
2241
|
+
pipe._clear_cache_key('_columns_types', debug=debug)
|
2242
|
+
pipe._clear_cache_key('_columns_indices', debug=debug)
|
2242
2243
|
self.exec_queries(add_cols_queries, debug=debug)
|
2243
2244
|
|
2244
2245
|
alter_cols_queries = self.get_alter_columns_queries(pipe, new_cols, debug=debug)
|
2245
2246
|
if alter_cols_queries:
|
2246
|
-
|
2247
|
+
pipe._clear_cache_key('_columns_types', debug=debug)
|
2247
2248
|
self.exec_queries(alter_cols_queries, debug=debug)
|
2248
2249
|
|
2249
2250
|
insert_queries = [
|
@@ -2546,6 +2547,8 @@ def sync_pipe_inplace(
|
|
2546
2547
|
upsert=upsert,
|
2547
2548
|
schema=self.get_pipe_schema(pipe),
|
2548
2549
|
patch_schema=internal_schema,
|
2550
|
+
target_cols_types=pipe.get_columns_types(debug=debug),
|
2551
|
+
patch_cols_types=delta_cols_types,
|
2549
2552
|
datetime_col=pipe.columns.get('datetime', None),
|
2550
2553
|
flavor=self.flavor,
|
2551
2554
|
null_indices=pipe.null_indices,
|
@@ -3036,6 +3039,7 @@ def get_pipe_table(
|
|
3036
3039
|
from meerschaum.utils.sql import get_sqlalchemy_table
|
3037
3040
|
if not pipe.exists(debug=debug):
|
3038
3041
|
return None
|
3042
|
+
|
3039
3043
|
return get_sqlalchemy_table(
|
3040
3044
|
pipe.target,
|
3041
3045
|
connector=self,
|
@@ -3093,9 +3097,11 @@ def get_pipe_columns_types(
|
|
3093
3097
|
pipe_table = self.get_pipe_table(pipe, debug=debug)
|
3094
3098
|
if pipe_table is None:
|
3095
3099
|
return {}
|
3100
|
+
|
3096
3101
|
if debug:
|
3097
|
-
dprint(
|
3102
|
+
dprint("Found columns:")
|
3098
3103
|
mrsm.pprint(dict(pipe_table.columns))
|
3104
|
+
|
3099
3105
|
for col in pipe_table.columns:
|
3100
3106
|
table_columns[str(col.name)] = str(col.type)
|
3101
3107
|
except Exception as e:
|
@@ -3127,6 +3133,7 @@ def get_pipe_columns_indices(
|
|
3127
3133
|
"""
|
3128
3134
|
if pipe.__dict__.get('_skip_check_indices', False):
|
3129
3135
|
return {}
|
3136
|
+
|
3130
3137
|
from meerschaum.utils.sql import get_table_cols_indices
|
3131
3138
|
return get_table_cols_indices(
|
3132
3139
|
pipe.target,
|
@@ -3181,7 +3188,6 @@ def get_add_columns_queries(
|
|
3181
3188
|
get_db_type_from_pd_type,
|
3182
3189
|
)
|
3183
3190
|
from meerschaum.utils.misc import flatten_list
|
3184
|
-
table_obj = self.get_pipe_table(pipe, debug=debug)
|
3185
3191
|
is_dask = 'dask' in df.__module__ if not isinstance(df, dict) else False
|
3186
3192
|
if is_dask:
|
3187
3193
|
df = df.partitions[0].compute()
|
@@ -3205,9 +3211,6 @@ def get_add_columns_queries(
|
|
3205
3211
|
elif isinstance(val, str):
|
3206
3212
|
df_cols_types[col] = 'str'
|
3207
3213
|
db_cols_types = {
|
3208
|
-
col: get_pd_type_from_db_type(str(typ.type))
|
3209
|
-
for col, typ in table_obj.columns.items()
|
3210
|
-
} if table_obj is not None else {
|
3211
3214
|
col: get_pd_type_from_db_type(typ)
|
3212
3215
|
for col, typ in get_table_cols_types(
|
3213
3216
|
pipe.target,
|
@@ -3303,7 +3306,6 @@ def get_alter_columns_queries(
|
|
3303
3306
|
get_db_type_from_pd_type,
|
3304
3307
|
)
|
3305
3308
|
from meerschaum.utils.misc import flatten_list, generate_password, items_str
|
3306
|
-
table_obj = self.get_pipe_table(pipe, debug=debug)
|
3307
3309
|
target = pipe.target
|
3308
3310
|
session_id = generate_password(3)
|
3309
3311
|
numeric_cols = (
|
@@ -3324,9 +3326,6 @@ def get_alter_columns_queries(
|
|
3324
3326
|
else df
|
3325
3327
|
)
|
3326
3328
|
db_cols_types = {
|
3327
|
-
col: get_pd_type_from_db_type(str(typ.type))
|
3328
|
-
for col, typ in table_obj.columns.items()
|
3329
|
-
} if table_obj is not None else {
|
3330
3329
|
col: get_pd_type_from_db_type(typ)
|
3331
3330
|
for col, typ in get_table_cols_types(
|
3332
3331
|
pipe.target,
|
@@ -3335,7 +3334,7 @@ def get_alter_columns_queries(
|
|
3335
3334
|
debug=debug,
|
3336
3335
|
).items()
|
3337
3336
|
}
|
3338
|
-
pipe_dtypes = pipe.
|
3337
|
+
pipe_dtypes = pipe.get_dtypes(debug=debug)
|
3339
3338
|
pipe_bool_cols = [col for col, typ in pipe_dtypes.items() if are_dtypes_equal(str(typ), 'bool')]
|
3340
3339
|
pd_db_df_aliases = {
|
3341
3340
|
'int': 'bool',
|
@@ -3347,6 +3346,7 @@ def get_alter_columns_queries(
|
|
3347
3346
|
pd_db_df_aliases.update({
|
3348
3347
|
'int': 'numeric',
|
3349
3348
|
'date': 'datetime',
|
3349
|
+
'numeric': 'int',
|
3350
3350
|
})
|
3351
3351
|
|
3352
3352
|
altered_cols = {
|
@@ -3357,14 +3357,32 @@ def get_alter_columns_queries(
|
|
3357
3357
|
}
|
3358
3358
|
|
3359
3359
|
if debug and altered_cols:
|
3360
|
-
dprint(
|
3360
|
+
dprint("Columns to be altered:")
|
3361
3361
|
mrsm.pprint(altered_cols)
|
3362
3362
|
|
3363
|
+
### NOTE: Special columns (numerics, bools, etc.) are captured and cached upon detection.
|
3364
|
+
new_special_cols = pipe._get_cached_value('new_special_cols', debug=debug) or {}
|
3365
|
+
new_special_db_cols_types = {
|
3366
|
+
col: (db_cols_types.get(col, 'object'), typ)
|
3367
|
+
for col, typ in new_special_cols.items()
|
3368
|
+
}
|
3369
|
+
if debug:
|
3370
|
+
dprint("Cached new special columns:")
|
3371
|
+
mrsm.pprint(new_special_cols)
|
3372
|
+
dprint("New special columns db types:")
|
3373
|
+
mrsm.pprint(new_special_db_cols_types)
|
3374
|
+
|
3375
|
+
altered_cols.update(new_special_db_cols_types)
|
3376
|
+
|
3363
3377
|
### NOTE: Sometimes bools are coerced into ints or floats.
|
3364
3378
|
altered_cols_to_ignore = set()
|
3365
3379
|
for col, (db_typ, df_typ) in altered_cols.items():
|
3366
3380
|
for db_alias, df_alias in pd_db_df_aliases.items():
|
3367
|
-
if
|
3381
|
+
if (
|
3382
|
+
db_alias in db_typ.lower()
|
3383
|
+
and df_alias in df_typ.lower()
|
3384
|
+
and col not in new_special_cols
|
3385
|
+
):
|
3368
3386
|
altered_cols_to_ignore.add(col)
|
3369
3387
|
|
3370
3388
|
### Oracle's bool handling sometimes mixes NUMBER and INT.
|
@@ -3387,7 +3405,7 @@ def get_alter_columns_queries(
|
|
3387
3405
|
altered_cols_to_ignore.add(bool_col)
|
3388
3406
|
|
3389
3407
|
if debug and altered_cols_to_ignore:
|
3390
|
-
dprint(
|
3408
|
+
dprint("Ignoring the following altered columns (false positives).")
|
3391
3409
|
mrsm.pprint(altered_cols_to_ignore)
|
3392
3410
|
|
3393
3411
|
for col in altered_cols_to_ignore:
|
@@ -3434,12 +3452,12 @@ def get_alter_columns_queries(
|
|
3434
3452
|
+ sql_item_name(target, self.flavor, self.get_pipe_schema(pipe))
|
3435
3453
|
+ " (\n"
|
3436
3454
|
)
|
3437
|
-
for col_name,
|
3455
|
+
for col_name, col_typ in db_cols_types.items():
|
3438
3456
|
create_query += (
|
3439
3457
|
sql_item_name(col_name, self.flavor, None)
|
3440
3458
|
+ " "
|
3441
3459
|
+ (
|
3442
|
-
|
3460
|
+
col_typ
|
3443
3461
|
if col_name not in altered_cols
|
3444
3462
|
else altered_cols_types[col_name]
|
3445
3463
|
)
|
@@ -3453,12 +3471,12 @@ def get_alter_columns_queries(
|
|
3453
3471
|
+ ' ('
|
3454
3472
|
+ ', '.join([
|
3455
3473
|
sql_item_name(col_name, self.flavor, None)
|
3456
|
-
for col_name
|
3474
|
+
for col_name in db_cols_types
|
3457
3475
|
])
|
3458
3476
|
+ ')'
|
3459
3477
|
+ "\nSELECT\n"
|
3460
3478
|
)
|
3461
|
-
for col_name
|
3479
|
+
for col_name in db_cols_types:
|
3462
3480
|
new_col_str = (
|
3463
3481
|
sql_item_name(col_name, self.flavor, None)
|
3464
3482
|
if col_name not in altered_cols
|
@@ -3471,6 +3489,7 @@ def get_alter_columns_queries(
|
|
3471
3489
|
)
|
3472
3490
|
)
|
3473
3491
|
insert_query += new_col_str + ",\n"
|
3492
|
+
|
3474
3493
|
insert_query = insert_query[:-2] + (
|
3475
3494
|
f"\nFROM {sql_item_name(temp_table_name, self.flavor, self.get_pipe_schema(pipe))}"
|
3476
3495
|
)
|
@@ -16,9 +16,15 @@ def get_users_pipe(self) -> mrsm.Pipe:
|
|
16
16
|
"""
|
17
17
|
Return the internal metadata pipe for users management.
|
18
18
|
"""
|
19
|
-
|
19
|
+
if '_users_pipe' in self.__dict__:
|
20
|
+
return self._users_pipe
|
21
|
+
|
22
|
+
cache_connector = self.__dict__.get('_cache_connector', None)
|
23
|
+
self._users_pipe = mrsm.Pipe(
|
20
24
|
'mrsm', 'users',
|
21
25
|
temporary=True,
|
26
|
+
cache=True,
|
27
|
+
cache_connector_keys=cache_connector,
|
22
28
|
static=True,
|
23
29
|
null_indices=False,
|
24
30
|
enforce=False,
|
@@ -36,6 +42,7 @@ def get_users_pipe(self) -> mrsm.Pipe:
|
|
36
42
|
'unique': 'username',
|
37
43
|
},
|
38
44
|
)
|
45
|
+
return self._users_pipe
|
39
46
|
|
40
47
|
|
41
48
|
def register_user(
|
@@ -24,6 +24,7 @@ _skip_index_names_flavors = {'mssql',}
|
|
24
24
|
def get_tables(
|
25
25
|
mrsm_instance: Optional[Union[str, InstanceConnector]] = None,
|
26
26
|
create: Optional[bool] = None,
|
27
|
+
refresh: bool = False,
|
27
28
|
debug: bool = False,
|
28
29
|
) -> Union[Dict[str, 'sqlalchemy.Table'], bool]:
|
29
30
|
"""
|
@@ -37,6 +38,9 @@ def get_tables(
|
|
37
38
|
create: Optional[bool], default None
|
38
39
|
If `True`, create the tables if they don't exist.
|
39
40
|
|
41
|
+
refresh: bool, default False
|
42
|
+
If `True`, invalidate and rebuild any cache.
|
43
|
+
|
40
44
|
debug: bool, default False
|
41
45
|
Verbosity Toggle.
|
42
46
|
|
@@ -67,7 +71,15 @@ def get_tables(
|
|
67
71
|
else: ### NOTE: mrsm_instance MUST BE a SQL Connector for this to work!
|
68
72
|
conn = mrsm_instance
|
69
73
|
|
70
|
-
cache_expired =
|
74
|
+
cache_expired = refresh or (
|
75
|
+
(
|
76
|
+
_check_create_cache(conn, debug=debug)
|
77
|
+
if conn.flavor != 'sqlite'
|
78
|
+
else True
|
79
|
+
)
|
80
|
+
if conn.type == 'sql'
|
81
|
+
else False
|
82
|
+
)
|
71
83
|
create = create or cache_expired
|
72
84
|
|
73
85
|
### Skip if the connector is not a SQL connector.
|
@@ -75,6 +87,10 @@ def get_tables(
|
|
75
87
|
return {}
|
76
88
|
|
77
89
|
conn_key = str(conn)
|
90
|
+
|
91
|
+
if refresh:
|
92
|
+
_ = connector_tables.pop(conn_key, None)
|
93
|
+
|
78
94
|
if conn_key in connector_tables:
|
79
95
|
return connector_tables[conn_key]
|
80
96
|
|
@@ -248,8 +264,9 @@ def get_tables(
|
|
248
264
|
|
249
265
|
_write_create_cache(mrsm.get_connector(str(mrsm_instance)), debug=debug)
|
250
266
|
|
251
|
-
|
252
|
-
|
267
|
+
if conn.flavor != 'sqlite':
|
268
|
+
with open(pickle_path, 'wb') as f:
|
269
|
+
pickle.dump(conn.metadata, f)
|
253
270
|
|
254
271
|
connector_tables[conn_key] = _tables
|
255
272
|
return connector_tables[conn_key]
|
@@ -140,13 +140,13 @@ class ValkeyConnector(InstanceConnector):
|
|
140
140
|
|
141
141
|
return uri
|
142
142
|
|
143
|
-
def set(self, key: str, value: Any, **kwargs: Any) ->
|
143
|
+
def set(self, key: str, value: Any, **kwargs: Any) -> bool:
|
144
144
|
"""
|
145
145
|
Set the `key` to `value`.
|
146
146
|
"""
|
147
147
|
return self.client.set(key, value, **kwargs)
|
148
148
|
|
149
|
-
def get(self, key: str) -> Union[str, None]:
|
149
|
+
def get(self, key: str, decode: bool = True) -> Union[str, None]:
|
150
150
|
"""
|
151
151
|
Get the value for `key`.
|
152
152
|
"""
|
@@ -154,7 +154,7 @@ class ValkeyConnector(InstanceConnector):
|
|
154
154
|
if val is None:
|
155
155
|
return None
|
156
156
|
|
157
|
-
return val.decode('utf-8')
|
157
|
+
return val.decode('utf-8') if decode else val
|
158
158
|
|
159
159
|
def test_connection(self) -> bool:
|
160
160
|
"""
|
@@ -888,7 +888,9 @@ def fetch_pipes_keys(
|
|
888
888
|
tags: Optional[List[str]] = None,
|
889
889
|
params: Optional[Dict[str, Any]] = None,
|
890
890
|
debug: bool = False
|
891
|
-
) ->
|
891
|
+
) -> List[
|
892
|
+
Tuple[str, str, Union[str, None], Dict[str, Any]]
|
893
|
+
]:
|
892
894
|
"""
|
893
895
|
Return the keys for the registered pipes.
|
894
896
|
"""
|
@@ -919,6 +921,7 @@ def fetch_pipes_keys(
|
|
919
921
|
doc['connector_keys'],
|
920
922
|
doc['metric_key'],
|
921
923
|
doc['location_key'],
|
924
|
+
doc.get('parameters', {})
|
922
925
|
)
|
923
926
|
for doc in df.to_dict(orient='records')
|
924
927
|
]
|
@@ -929,9 +932,8 @@ def fetch_pipes_keys(
|
|
929
932
|
in_ex_tag_groups = [separate_negation_values(tag_group) for tag_group in tag_groups]
|
930
933
|
|
931
934
|
filtered_keys = []
|
932
|
-
for ck, mk, lk in keys:
|
933
|
-
|
934
|
-
pipe_tags = set(pipe.tags)
|
935
|
+
for ck, mk, lk, parameters in keys:
|
936
|
+
pipe_tags = set(parameters.get('tags', []))
|
935
937
|
|
936
938
|
include_pipe = True
|
937
939
|
for in_tags, ex_tags in in_ex_tag_groups:
|
@@ -943,6 +945,6 @@ def fetch_pipes_keys(
|
|
943
945
|
continue
|
944
946
|
|
945
947
|
if include_pipe:
|
946
|
-
filtered_keys.append((ck, mk, lk))
|
948
|
+
filtered_keys.append((ck, mk, lk, parameters))
|
947
949
|
|
948
950
|
return filtered_keys
|