meerschaum 2.6.0.dev1__py3-none-any.whl → 2.6.1__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/api/dash/pages/login.py +17 -17
- meerschaum/api/dash/pipes.py +13 -4
- meerschaum/api/routes/_pipes.py +162 -136
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +1 -0
- meerschaum/connectors/api/_APIConnector.py +1 -0
- meerschaum/connectors/api/_pipes.py +46 -13
- meerschaum/connectors/sql/_SQLConnector.py +4 -3
- meerschaum/connectors/sql/_fetch.py +4 -2
- meerschaum/connectors/sql/_pipes.py +496 -148
- meerschaum/connectors/sql/_sql.py +37 -16
- meerschaum/connectors/valkey/_ValkeyConnector.py +3 -2
- meerschaum/connectors/valkey/_pipes.py +13 -5
- meerschaum/core/Pipe/__init__.py +20 -0
- meerschaum/core/Pipe/_attributes.py +179 -9
- meerschaum/core/Pipe/_clear.py +10 -8
- meerschaum/core/Pipe/_copy.py +2 -0
- meerschaum/core/Pipe/_data.py +57 -28
- meerschaum/core/Pipe/_deduplicate.py +30 -28
- meerschaum/core/Pipe/_dtypes.py +12 -2
- meerschaum/core/Pipe/_fetch.py +11 -9
- meerschaum/core/Pipe/_sync.py +24 -7
- meerschaum/core/Pipe/_verify.py +51 -48
- meerschaum/utils/dataframe.py +16 -8
- meerschaum/utils/dtypes/__init__.py +9 -1
- meerschaum/utils/dtypes/sql.py +32 -6
- meerschaum/utils/misc.py +8 -8
- meerschaum/utils/sql.py +485 -16
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/METADATA +1 -1
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/RECORD +36 -36
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/LICENSE +0 -0
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/NOTICE +0 -0
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/WHEEL +0 -0
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.6.0.dev1.dist-info → meerschaum-2.6.1.dist-info}/zip-safe +0 -0
@@ -17,7 +17,7 @@ from meerschaum.utils.warnings import warn
|
|
17
17
|
### database flavors that can use bulk insert
|
18
18
|
_bulk_flavors = {'postgresql', 'timescaledb', 'citus'}
|
19
19
|
### flavors that do not support chunks
|
20
|
-
_disallow_chunks_flavors = []
|
20
|
+
_disallow_chunks_flavors = ['duckdb']
|
21
21
|
_max_chunks_flavors = {'sqlite': 1000}
|
22
22
|
SKIP_READ_TRANSACTION_FLAVORS: list[str] = ['mssql']
|
23
23
|
|
@@ -134,7 +134,7 @@ def read(
|
|
134
134
|
pd = import_pandas()
|
135
135
|
dd = None
|
136
136
|
is_dask = 'dask' in pd.__name__
|
137
|
-
|
137
|
+
pandas = attempt_import('pandas')
|
138
138
|
is_dask = dd is not None
|
139
139
|
npartitions = chunksize_to_npartitions(chunksize)
|
140
140
|
if is_dask:
|
@@ -498,6 +498,8 @@ def exec(
|
|
498
498
|
commit: Optional[bool] = None,
|
499
499
|
close: Optional[bool] = None,
|
500
500
|
with_connection: bool = False,
|
501
|
+
_connection=None,
|
502
|
+
_transaction=None,
|
501
503
|
**kw: Any
|
502
504
|
) -> Union[
|
503
505
|
sqlalchemy.engine.result.resultProxy,
|
@@ -508,7 +510,7 @@ def exec(
|
|
508
510
|
]:
|
509
511
|
"""
|
510
512
|
Execute SQL code and return the `sqlalchemy` result, e.g. when calling stored procedures.
|
511
|
-
|
513
|
+
|
512
514
|
If inserting data, please use bind variables to avoid SQL injection!
|
513
515
|
|
514
516
|
Parameters
|
@@ -565,15 +567,24 @@ def exec(
|
|
565
567
|
if not hasattr(query, 'compile'):
|
566
568
|
query = sqlalchemy.text(query)
|
567
569
|
|
568
|
-
connection = self.get_connection()
|
570
|
+
connection = _connection if _connection is not None else self.get_connection()
|
569
571
|
|
570
572
|
try:
|
571
|
-
transaction =
|
572
|
-
|
573
|
+
transaction = (
|
574
|
+
_transaction
|
575
|
+
if _transaction is not None else (
|
576
|
+
connection.begin()
|
577
|
+
if _commit
|
578
|
+
else None
|
579
|
+
)
|
580
|
+
)
|
581
|
+
except sqlalchemy.exc.InvalidRequestError as e:
|
582
|
+
if _connection is not None or _transaction is not None:
|
583
|
+
raise e
|
573
584
|
connection = self.get_connection(rebuild=True)
|
574
585
|
transaction = connection.begin()
|
575
586
|
|
576
|
-
if transaction is not None and not transaction.is_active:
|
587
|
+
if transaction is not None and not transaction.is_active and _transaction is not None:
|
577
588
|
connection = self.get_connection(rebuild=True)
|
578
589
|
transaction = connection.begin() if _commit else None
|
579
590
|
|
@@ -708,6 +719,8 @@ def to_sql(
|
|
708
719
|
debug: bool = False,
|
709
720
|
as_tuple: bool = False,
|
710
721
|
as_dict: bool = False,
|
722
|
+
_connection=None,
|
723
|
+
_transaction=None,
|
711
724
|
**kw
|
712
725
|
) -> Union[bool, SuccessTuple]:
|
713
726
|
"""
|
@@ -782,6 +795,7 @@ def to_sql(
|
|
782
795
|
from meerschaum.utils.dtypes.sql import (
|
783
796
|
NUMERIC_PRECISION_FLAVORS,
|
784
797
|
PD_TO_SQLALCHEMY_DTYPES_FLAVORS,
|
798
|
+
get_db_type_from_pd_type,
|
785
799
|
)
|
786
800
|
from meerschaum.connectors.sql._create_engine import flavor_configs
|
787
801
|
from meerschaum.utils.packages import attempt_import, import_pandas
|
@@ -849,6 +863,8 @@ def to_sql(
|
|
849
863
|
to_sql_kw.update({
|
850
864
|
'parallel': True,
|
851
865
|
})
|
866
|
+
elif _connection is not None:
|
867
|
+
to_sql_kw['con'] = _connection
|
852
868
|
|
853
869
|
if_exists_str = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
|
854
870
|
if self.flavor == 'oracle':
|
@@ -869,19 +885,24 @@ def to_sql(
|
|
869
885
|
elif are_dtypes_equal(str(typ), 'int'):
|
870
886
|
dtype[col] = sqlalchemy.types.INTEGER
|
871
887
|
to_sql_kw['dtype'] = dtype
|
872
|
-
elif self.flavor == 'mssql':
|
873
|
-
pass
|
874
|
-
### TODO clean this up
|
875
|
-
# dtype = to_sql_kw.get('dtype', {})
|
876
|
-
# for col, typ in df.dtypes.items():
|
877
|
-
# if are_dtypes_equal(str(typ), 'bool'):
|
878
|
-
# dtype[col] = sqlalchemy.types.INTEGER
|
879
|
-
# to_sql_kw['dtype'] = dtype
|
880
888
|
elif self.flavor == 'duckdb':
|
881
889
|
dtype = to_sql_kw.get('dtype', {})
|
882
890
|
dt_cols = [col for col, typ in df.dtypes.items() if are_dtypes_equal(str(typ), 'datetime')]
|
883
891
|
for col in dt_cols:
|
884
892
|
df[col] = coerce_timezone(df[col], strip_utc=False)
|
893
|
+
elif self.flavor == 'mssql':
|
894
|
+
dtype = to_sql_kw.get('dtype', {})
|
895
|
+
dt_cols = [col for col, typ in df.dtypes.items() if are_dtypes_equal(str(typ), 'datetime')]
|
896
|
+
new_dtype = {}
|
897
|
+
for col in dt_cols:
|
898
|
+
if col in dtype:
|
899
|
+
continue
|
900
|
+
dt_typ = get_db_type_from_pd_type(str(df.dtypes[col]), self.flavor, as_sqlalchemy=True)
|
901
|
+
if col not in dtype:
|
902
|
+
new_dtype[col] = dt_typ
|
903
|
+
|
904
|
+
dtype.update(new_dtype)
|
905
|
+
to_sql_kw['dtype'] = dtype
|
885
906
|
|
886
907
|
### Check for JSON columns.
|
887
908
|
if self.flavor not in json_flavors:
|
@@ -916,7 +937,7 @@ def to_sql(
|
|
916
937
|
|
917
938
|
try:
|
918
939
|
with warnings.catch_warnings():
|
919
|
-
warnings.filterwarnings('ignore'
|
940
|
+
warnings.filterwarnings('ignore')
|
920
941
|
df.to_sql(**to_sql_kw)
|
921
942
|
success = True
|
922
943
|
except Exception as e:
|
@@ -408,6 +408,7 @@ class ValkeyConnector(Connector):
|
|
408
408
|
-------
|
409
409
|
A list of dictionaries, where all keys and values are strings.
|
410
410
|
"""
|
411
|
+
from meerschaum.utils.dtypes import coerce_timezone
|
411
412
|
table_name = self.quote_table(table)
|
412
413
|
datetime_column_key = self.get_datetime_column_key(table)
|
413
414
|
datetime_column = self.get(datetime_column_key)
|
@@ -424,10 +425,10 @@ class ValkeyConnector(Connector):
|
|
424
425
|
dateutil_parser = mrsm.attempt_import('dateutil.parser')
|
425
426
|
|
426
427
|
if isinstance(begin, str):
|
427
|
-
begin = dateutil_parser.parse(begin)
|
428
|
+
begin = coerce_timezone(dateutil_parser.parse(begin))
|
428
429
|
|
429
430
|
if isinstance(end, str):
|
430
|
-
end = dateutil_parser.parse(end)
|
431
|
+
end = coerce_timezone(dateutil_parser.parse(end))
|
431
432
|
|
432
433
|
begin_ts = (
|
433
434
|
(
|
@@ -409,6 +409,7 @@ def get_pipe_data(
|
|
409
409
|
return None
|
410
410
|
|
411
411
|
from meerschaum.utils.dataframe import query_df, parse_df_datetimes
|
412
|
+
from meerschaum.utils.dtypes import are_dtypes_equal
|
412
413
|
|
413
414
|
valkey_dtypes = pipe.parameters.get('valkey', {}).get('dtypes', {})
|
414
415
|
dt_col = pipe.columns.get('datetime', None)
|
@@ -442,13 +443,14 @@ def get_pipe_data(
|
|
442
443
|
ignore_dt_cols = [
|
443
444
|
col
|
444
445
|
for col, dtype in pipe.dtypes.items()
|
445
|
-
if
|
446
|
+
if not are_dtypes_equal(str(dtype), 'datetime')
|
446
447
|
]
|
447
448
|
|
448
449
|
df = parse_df_datetimes(
|
449
450
|
docs,
|
450
451
|
ignore_cols=ignore_dt_cols,
|
451
452
|
chunksize=kwargs.get('chunksize', None),
|
453
|
+
strip_timezone=(pipe.tzinfo is None),
|
452
454
|
debug=debug,
|
453
455
|
)
|
454
456
|
for col, typ in valkey_dtypes.items():
|
@@ -501,6 +503,7 @@ def sync_pipe(
|
|
501
503
|
-------
|
502
504
|
A `SuccessTuple` indicating success.
|
503
505
|
"""
|
506
|
+
from meerschaum.utils.dtypes import are_dtypes_equal
|
504
507
|
dt_col = pipe.columns.get('datetime', None)
|
505
508
|
indices = [col for col in pipe.columns.values() if col]
|
506
509
|
table_name = self.quote_table(pipe.target)
|
@@ -508,6 +511,7 @@ def sync_pipe(
|
|
508
511
|
if is_dask:
|
509
512
|
df = df.compute()
|
510
513
|
upsert = pipe.parameters.get('upsert', False)
|
514
|
+
static = pipe.parameters.get('static', False)
|
511
515
|
|
512
516
|
def _serialize_indices_docs(_docs):
|
513
517
|
return [
|
@@ -526,7 +530,11 @@ def sync_pipe(
|
|
526
530
|
|
527
531
|
valkey_dtypes = pipe.parameters.get('valkey', {}).get('dtypes', {})
|
528
532
|
new_dtypes = {
|
529
|
-
str(key):
|
533
|
+
str(key): (
|
534
|
+
str(val)
|
535
|
+
if not are_dtypes_equal(str(val), 'datetime')
|
536
|
+
else 'datetime64[ns, UTC]'
|
537
|
+
)
|
530
538
|
for key, val in df.dtypes.items()
|
531
539
|
if str(key) not in valkey_dtypes
|
532
540
|
}
|
@@ -539,7 +547,7 @@ def sync_pipe(
|
|
539
547
|
new_dtypes[col] = 'string'
|
540
548
|
df[col] = df[col].astype('string')
|
541
549
|
|
542
|
-
if new_dtypes:
|
550
|
+
if new_dtypes and not static:
|
543
551
|
valkey_dtypes.update(new_dtypes)
|
544
552
|
if 'valkey' not in pipe.parameters:
|
545
553
|
pipe.parameters['valkey'] = {}
|
@@ -625,7 +633,7 @@ def get_pipe_columns_types(
|
|
625
633
|
|
626
634
|
from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
|
627
635
|
return {
|
628
|
-
col: get_db_type_from_pd_type(typ)
|
636
|
+
col: get_db_type_from_pd_type(typ, flavor='postgresql')
|
629
637
|
for col, typ in pipe.parameters.get('valkey', {}).get('dtypes', {}).items()
|
630
638
|
}
|
631
639
|
|
@@ -733,7 +741,7 @@ def get_sync_time(
|
|
733
741
|
return (
|
734
742
|
int(dt_val)
|
735
743
|
if are_dtypes_equal(dt_typ, 'int')
|
736
|
-
else dateutil_parser.parse(str(dt_val))
|
744
|
+
else dateutil_parser.parse(str(dt_val))
|
737
745
|
)
|
738
746
|
except Exception as e:
|
739
747
|
warn(f"Failed to parse sync time for {pipe}:\n{e}")
|
meerschaum/core/Pipe/__init__.py
CHANGED
@@ -92,6 +92,7 @@ class Pipe:
|
|
92
92
|
_get_data_as_iterator,
|
93
93
|
get_chunk_interval,
|
94
94
|
get_chunk_bounds,
|
95
|
+
parse_date_bounds,
|
95
96
|
)
|
96
97
|
from ._register import register
|
97
98
|
from ._attributes import (
|
@@ -101,8 +102,13 @@ class Pipe:
|
|
101
102
|
indices,
|
102
103
|
indexes,
|
103
104
|
dtypes,
|
105
|
+
autoincrement,
|
106
|
+
upsert,
|
107
|
+
static,
|
108
|
+
tzinfo,
|
104
109
|
get_columns,
|
105
110
|
get_columns_types,
|
111
|
+
get_columns_indices,
|
106
112
|
get_indices,
|
107
113
|
tags,
|
108
114
|
get_id,
|
@@ -154,6 +160,8 @@ class Pipe:
|
|
154
160
|
instance: Optional[Union[str, InstanceConnector]] = None,
|
155
161
|
temporary: bool = False,
|
156
162
|
upsert: Optional[bool] = None,
|
163
|
+
autoincrement: Optional[bool] = None,
|
164
|
+
static: Optional[bool] = None,
|
157
165
|
mrsm_instance: Optional[Union[str, InstanceConnector]] = None,
|
158
166
|
cache: bool = False,
|
159
167
|
debug: bool = False,
|
@@ -205,6 +213,12 @@ class Pipe:
|
|
205
213
|
upsert: Optional[bool], default None
|
206
214
|
If `True`, set `upsert` to `True` in the parameters.
|
207
215
|
|
216
|
+
autoincrement: Optional[bool], default None
|
217
|
+
If `True`, set `autoincrement` in the parameters.
|
218
|
+
|
219
|
+
static: Optional[bool], default None
|
220
|
+
If `True`, set `static` in the parameters.
|
221
|
+
|
208
222
|
temporary: bool, default False
|
209
223
|
If `True`, prevent instance tables (pipes, users, plugins) from being created.
|
210
224
|
|
@@ -299,6 +313,12 @@ class Pipe:
|
|
299
313
|
if isinstance(upsert, bool):
|
300
314
|
self._attributes['parameters']['upsert'] = upsert
|
301
315
|
|
316
|
+
if isinstance(autoincrement, bool):
|
317
|
+
self._attributes['parameters']['autoincrement'] = autoincrement
|
318
|
+
|
319
|
+
if isinstance(static, bool):
|
320
|
+
self._attributes['parameters']['static'] = static
|
321
|
+
|
302
322
|
### NOTE: The parameters dictionary is {} by default.
|
303
323
|
### A Pipe may be registered without parameters, then edited,
|
304
324
|
### or a Pipe may be registered with parameters set in-memory first.
|
@@ -7,9 +7,14 @@ Fetch and manipulate Pipes' attributes
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
11
|
+
from datetime import timezone
|
12
|
+
|
13
|
+
import meerschaum as mrsm
|
10
14
|
from meerschaum.utils.typing import Tuple, Dict, SuccessTuple, Any, Union, Optional, List
|
11
15
|
from meerschaum.utils.warnings import warn
|
12
16
|
|
17
|
+
|
13
18
|
@property
|
14
19
|
def attributes(self) -> Dict[str, Any]:
|
15
20
|
"""
|
@@ -50,6 +55,13 @@ def parameters(self) -> Optional[Dict[str, Any]]:
|
|
50
55
|
"""
|
51
56
|
if 'parameters' not in self.attributes:
|
52
57
|
self.attributes['parameters'] = {}
|
58
|
+
_parameters = self.attributes['parameters']
|
59
|
+
dt_col = _parameters.get('columns', {}).get('datetime', None)
|
60
|
+
dt_typ = _parameters.get('dtypes', {}).get(dt_col, None) if dt_col else None
|
61
|
+
if dt_col and not dt_typ:
|
62
|
+
if 'dtypes' not in _parameters:
|
63
|
+
self.attributes['parameters']['dtypes'] = {}
|
64
|
+
self.attributes['parameters']['dtypes'][dt_col] = 'datetime'
|
53
65
|
return self.attributes['parameters']
|
54
66
|
|
55
67
|
|
@@ -84,7 +96,7 @@ def columns(self, _columns: Union[Dict[str, str], List[str]]) -> None:
|
|
84
96
|
"""
|
85
97
|
if isinstance(_columns, (list, tuple)):
|
86
98
|
_columns = {col: col for col in _columns}
|
87
|
-
if not isinstance(
|
99
|
+
if not isinstance(_columns, dict):
|
88
100
|
warn(f"{self}.columns must be a dictionary, received {type(_columns)}.")
|
89
101
|
return
|
90
102
|
self.parameters['columns'] = _columns
|
@@ -108,7 +120,7 @@ def indices(self) -> Union[Dict[str, Union[str, List[str]]], None]:
|
|
108
120
|
if not isinstance(_indices, dict):
|
109
121
|
_indices = {}
|
110
122
|
self.parameters[indices_key] = _indices
|
111
|
-
unique_cols = (
|
123
|
+
unique_cols = list(set((
|
112
124
|
[dt_col]
|
113
125
|
if dt_col
|
114
126
|
else []
|
@@ -116,7 +128,7 @@ def indices(self) -> Union[Dict[str, Union[str, List[str]]], None]:
|
|
116
128
|
col
|
117
129
|
for col_ix, col in _columns.items()
|
118
130
|
if col_ix != 'datetime'
|
119
|
-
]
|
131
|
+
]))
|
120
132
|
return {
|
121
133
|
**({'unique': unique_cols} if len(unique_cols) > 1 else {}),
|
122
134
|
**_columns,
|
@@ -203,6 +215,80 @@ def dtypes(self, _dtypes: Dict[str, Any]) -> None:
|
|
203
215
|
self.parameters['dtypes'] = _dtypes
|
204
216
|
|
205
217
|
|
218
|
+
@property
|
219
|
+
def upsert(self) -> bool:
|
220
|
+
"""
|
221
|
+
Return whether `upsert` is set for the pipe.
|
222
|
+
"""
|
223
|
+
if 'upsert' not in self.parameters:
|
224
|
+
self.parameters['upsert'] = False
|
225
|
+
return self.parameters['upsert']
|
226
|
+
|
227
|
+
|
228
|
+
@upsert.setter
|
229
|
+
def upsert(self, _upsert: bool) -> None:
|
230
|
+
"""
|
231
|
+
Set the `upsert` parameter for the pipe.
|
232
|
+
"""
|
233
|
+
self.parameters['upsert'] = _upsert
|
234
|
+
|
235
|
+
|
236
|
+
@property
|
237
|
+
def static(self) -> bool:
|
238
|
+
"""
|
239
|
+
Return whether `static` is set for the pipe.
|
240
|
+
"""
|
241
|
+
if 'static' not in self.parameters:
|
242
|
+
self.parameters['static'] = False
|
243
|
+
return self.parameters['static']
|
244
|
+
|
245
|
+
|
246
|
+
@static.setter
|
247
|
+
def static(self, _static: bool) -> None:
|
248
|
+
"""
|
249
|
+
Set the `static` parameter for the pipe.
|
250
|
+
"""
|
251
|
+
self.parameters['static'] = _static
|
252
|
+
|
253
|
+
|
254
|
+
@property
|
255
|
+
def autoincrement(self) -> bool:
|
256
|
+
"""
|
257
|
+
Return the `autoincrement` parameter for the pipe.
|
258
|
+
"""
|
259
|
+
if 'autoincrement' not in self.parameters:
|
260
|
+
self.parameters['autoincrement'] = False
|
261
|
+
|
262
|
+
return self.parameters['autoincrement']
|
263
|
+
|
264
|
+
|
265
|
+
@autoincrement.setter
|
266
|
+
def autoincrement(self, _autoincrement: bool) -> None:
|
267
|
+
"""
|
268
|
+
Set the `autoincrement` parameter for the pipe.
|
269
|
+
"""
|
270
|
+
self.parameters['autoincrement'] = _autoincrement
|
271
|
+
|
272
|
+
|
273
|
+
@property
|
274
|
+
def tzinfo(self) -> Union[None, timezone]:
|
275
|
+
"""
|
276
|
+
Return `timezone.utc` if the pipe is timezone-aware.
|
277
|
+
"""
|
278
|
+
dt_col = self.columns.get('datetime', None)
|
279
|
+
if not dt_col:
|
280
|
+
return None
|
281
|
+
|
282
|
+
dt_typ = str(self.dtypes.get(dt_col, 'datetime64[ns, UTC]'))
|
283
|
+
if 'utc' in dt_typ.lower() or dt_typ == 'datetime':
|
284
|
+
return timezone.utc
|
285
|
+
|
286
|
+
if dt_typ == 'datetime64[ns]':
|
287
|
+
return None
|
288
|
+
|
289
|
+
return None
|
290
|
+
|
291
|
+
|
206
292
|
def get_columns(self, *args: str, error: bool = False) -> Union[str, Tuple[str]]:
|
207
293
|
"""
|
208
294
|
Check if the requested columns are defined.
|
@@ -248,12 +334,19 @@ def get_columns(self, *args: str, error: bool = False) -> Union[str, Tuple[str]]
|
|
248
334
|
return tuple(col_names)
|
249
335
|
|
250
336
|
|
251
|
-
def get_columns_types(
|
337
|
+
def get_columns_types(
|
338
|
+
self,
|
339
|
+
refresh: bool = False,
|
340
|
+
debug: bool = False,
|
341
|
+
) -> Union[Dict[str, str], None]:
|
252
342
|
"""
|
253
343
|
Get a dictionary of a pipe's column names and their types.
|
254
344
|
|
255
345
|
Parameters
|
256
346
|
----------
|
347
|
+
refresh: bool, default False
|
348
|
+
If `True`, invalidate the cache and fetch directly from the instance connector.
|
349
|
+
|
257
350
|
debug: bool, default False:
|
258
351
|
Verbosity toggle.
|
259
352
|
|
@@ -265,17 +358,91 @@ def get_columns_types(self, debug: bool = False) -> Union[Dict[str, str], None]:
|
|
265
358
|
--------
|
266
359
|
>>> pipe.get_columns_types()
|
267
360
|
{
|
268
|
-
'dt': 'TIMESTAMP
|
361
|
+
'dt': 'TIMESTAMP WITH TIMEZONE',
|
269
362
|
'id': 'BIGINT',
|
270
363
|
'val': 'DOUBLE PRECISION',
|
271
364
|
}
|
272
365
|
>>>
|
273
366
|
"""
|
274
|
-
|
367
|
+
import time
|
275
368
|
from meerschaum.connectors import get_connector_plugin
|
369
|
+
from meerschaum.config.static import STATIC_CONFIG
|
370
|
+
from meerschaum.utils.warnings import dprint
|
276
371
|
|
277
|
-
|
278
|
-
|
372
|
+
now = time.perf_counter()
|
373
|
+
cache_seconds = STATIC_CONFIG['pipes']['static_schema_cache_seconds']
|
374
|
+
static = self.parameters.get('static', False)
|
375
|
+
if not static:
|
376
|
+
refresh = True
|
377
|
+
if refresh:
|
378
|
+
_ = self.__dict__.pop('_columns_types_timestamp', None)
|
379
|
+
_ = self.__dict__.pop('_columns_types', None)
|
380
|
+
_columns_types = self.__dict__.get('_columns_types', None)
|
381
|
+
if _columns_types:
|
382
|
+
columns_types_timestamp = self.__dict__.get('_columns_types_timestamp', None)
|
383
|
+
if columns_types_timestamp is not None:
|
384
|
+
delta = now - columns_types_timestamp
|
385
|
+
if delta < cache_seconds:
|
386
|
+
if debug:
|
387
|
+
dprint(
|
388
|
+
f"Returning cached `columns_types` for {self} "
|
389
|
+
f"({round(delta, 2)} seconds old)."
|
390
|
+
)
|
391
|
+
return _columns_types
|
392
|
+
|
393
|
+
with mrsm.Venv(get_connector_plugin(self.instance_connector)):
|
394
|
+
_columns_types = (
|
395
|
+
self.instance_connector.get_pipe_columns_types(self, debug=debug)
|
396
|
+
if hasattr(self.instance_connector, 'get_pipe_columns_types')
|
397
|
+
else None
|
398
|
+
)
|
399
|
+
|
400
|
+
self.__dict__['_columns_types'] = _columns_types
|
401
|
+
self.__dict__['_columns_types_timestamp'] = now
|
402
|
+
return _columns_types or {}
|
403
|
+
|
404
|
+
|
405
|
+
def get_columns_indices(
|
406
|
+
self,
|
407
|
+
debug: bool = False,
|
408
|
+
refresh: bool = False,
|
409
|
+
) -> Dict[str, List[Dict[str, str]]]:
|
410
|
+
"""
|
411
|
+
Return a dictionary mapping columns to index information.
|
412
|
+
"""
|
413
|
+
import time
|
414
|
+
from meerschaum.connectors import get_connector_plugin
|
415
|
+
from meerschaum.config.static import STATIC_CONFIG
|
416
|
+
from meerschaum.utils.warnings import dprint
|
417
|
+
|
418
|
+
now = time.perf_counter()
|
419
|
+
exists_timeout_seconds = STATIC_CONFIG['pipes']['exists_timeout_seconds']
|
420
|
+
if refresh:
|
421
|
+
_ = self.__dict__.pop('_columns_indices_timestamp', None)
|
422
|
+
_ = self.__dict__.pop('_columns_indices', None)
|
423
|
+
_columns_indices = self.__dict__.get('_columns_indices', None)
|
424
|
+
if _columns_indices:
|
425
|
+
columns_indices_timestamp = self.__dict__.get('_columns_indices_timestamp', None)
|
426
|
+
if columns_indices_timestamp is not None:
|
427
|
+
delta = now - columns_indices_timestamp
|
428
|
+
if delta < exists_timeout_seconds:
|
429
|
+
if debug:
|
430
|
+
dprint(
|
431
|
+
f"Returning cached `columns_indices` for {self} "
|
432
|
+
f"({round(delta, 2)} seconds old)."
|
433
|
+
)
|
434
|
+
return _columns_indices
|
435
|
+
|
436
|
+
with mrsm.Venv(get_connector_plugin(self.instance_connector)):
|
437
|
+
_columns_indices = (
|
438
|
+
self.instance_connector.get_pipe_columns_indices(self, debug=debug)
|
439
|
+
if hasattr(self.instance_connector, 'get_pipe_columns_indices')
|
440
|
+
else None
|
441
|
+
)
|
442
|
+
|
443
|
+
self.__dict__['_columns_indices'] = _columns_indices
|
444
|
+
self.__dict__['_columns_indices_timestamp'] = now
|
445
|
+
return _columns_indices or {}
|
279
446
|
|
280
447
|
|
281
448
|
def get_id(self, **kw: Any) -> Union[int, None]:
|
@@ -289,7 +456,10 @@ def get_id(self, **kw: Any) -> Union[int, None]:
|
|
289
456
|
from meerschaum.connectors import get_connector_plugin
|
290
457
|
|
291
458
|
with Venv(get_connector_plugin(self.instance_connector)):
|
292
|
-
|
459
|
+
if hasattr(self.instance_connector, 'get_pipe_id'):
|
460
|
+
return self.instance_connector.get_pipe_id(self, **kw)
|
461
|
+
|
462
|
+
return None
|
293
463
|
|
294
464
|
|
295
465
|
@property
|
meerschaum/core/Pipe/_clear.py
CHANGED
@@ -58,12 +58,14 @@ def clear(
|
|
58
58
|
from meerschaum.utils.venv import Venv
|
59
59
|
from meerschaum.connectors import get_connector_plugin
|
60
60
|
|
61
|
+
begin, end = self.parse_date_bounds(begin, end)
|
62
|
+
|
61
63
|
if self.cache_pipe is not None:
|
62
64
|
success, msg = self.cache_pipe.clear(
|
63
|
-
begin
|
64
|
-
end
|
65
|
-
params
|
66
|
-
debug
|
65
|
+
begin=begin,
|
66
|
+
end=end,
|
67
|
+
params=params,
|
68
|
+
debug=debug,
|
67
69
|
**kwargs
|
68
70
|
)
|
69
71
|
if not success:
|
@@ -72,9 +74,9 @@ def clear(
|
|
72
74
|
with Venv(get_connector_plugin(self.instance_connector)):
|
73
75
|
return self.instance_connector.clear_pipe(
|
74
76
|
self,
|
75
|
-
begin
|
76
|
-
end
|
77
|
-
params
|
78
|
-
debug
|
77
|
+
begin=begin,
|
78
|
+
end=end,
|
79
|
+
params=params,
|
80
|
+
debug=debug,
|
79
81
|
**kwargs
|
80
82
|
)
|
meerschaum/core/Pipe/_copy.py
CHANGED