meerschaum 2.9.4__py3-none-any.whl → 3.0.0rc1__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/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +17 -1
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +372 -0
- meerschaum/actions/api.py +12 -2
- meerschaum/actions/bootstrap.py +7 -7
- meerschaum/actions/edit.py +142 -18
- meerschaum/actions/register.py +137 -6
- meerschaum/actions/show.py +117 -29
- meerschaum/actions/stop.py +4 -1
- meerschaum/actions/sync.py +1 -1
- meerschaum/actions/tag.py +9 -8
- meerschaum/api/__init__.py +9 -2
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -1
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +133 -18
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +1 -1
- meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
- meerschaum/api/dash/components.py +30 -8
- meerschaum/api/dash/keys.py +19 -93
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/settings/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/settings/tokens.py +55 -0
- meerschaum/api/dash/pipes.py +156 -58
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +606 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +4 -0
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +85 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/css/dash.css +16 -0
- meerschaum/api/resources/templates/termpage.html +12 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +14 -35
- meerschaum/api/routes/_login.py +49 -12
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +134 -111
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/config/__init__.py +43 -20
- meerschaum/config/_default.py +32 -5
- meerschaum/config/_edit.py +28 -24
- meerschaum/config/_environment.py +1 -1
- meerschaum/config/_patch.py +6 -6
- meerschaum/config/_paths.py +5 -1
- meerschaum/config/_read_config.py +65 -34
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +24 -5
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +10 -4
- meerschaum/connectors/__init__.py +4 -20
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +1 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +15 -14
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +151 -0
- meerschaum/connectors/instance/_tokens.py +296 -0
- meerschaum/connectors/instance/_users.py +181 -0
- meerschaum/connectors/parse.py +4 -1
- meerschaum/connectors/sql/_SQLConnector.py +8 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +6 -154
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +42 -31
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +9 -2
- meerschaum/connectors/sql/_users.py +29 -2
- meerschaum/connectors/sql/tables/__init__.py +1 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
- meerschaum/connectors/valkey/_pipes.py +9 -10
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +31 -14
- meerschaum/core/Pipe/_attributes.py +156 -58
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +41 -1
- meerschaum/core/Pipe/_dtypes.py +29 -14
- meerschaum/core/Pipe/_edit.py +12 -4
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +48 -53
- meerschaum/core/Pipe/_verify.py +1 -1
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +221 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +34 -8
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Job.py +3 -2
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +22 -7
- meerschaum/plugins/bootstrap.py +2 -1
- meerschaum/utils/_get_pipes.py +68 -27
- meerschaum/utils/daemon/Daemon.py +2 -1
- meerschaum/utils/daemon/__init__.py +30 -2
- meerschaum/utils/dataframe.py +96 -15
- meerschaum/utils/dtypes/__init__.py +93 -21
- meerschaum/utils/dtypes/sql.py +44 -0
- meerschaum/utils/formatting/__init__.py +1 -1
- meerschaum/utils/formatting/_pipes.py +5 -4
- meerschaum/utils/formatting/_shell.py +11 -9
- meerschaum/utils/misc.py +237 -80
- meerschaum/utils/packages/__init__.py +3 -6
- meerschaum/utils/packages/_packages.py +34 -32
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +1 -0
- meerschaum/utils/sql.py +115 -39
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
- meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.4.dist-info/RECORD +0 -263
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
@@ -7,171 +7,23 @@ This module contains the logic that builds the sqlalchemy engine string.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import traceback
|
10
|
+
|
10
11
|
import meerschaum as mrsm
|
11
12
|
from meerschaum.utils.debug import dprint
|
13
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
12
14
|
|
13
|
-
### determine driver and requirements from flavor
|
14
|
-
default_requirements = {
|
15
|
-
'username',
|
16
|
-
'password',
|
17
|
-
'host',
|
18
|
-
'database',
|
19
|
-
}
|
20
15
|
|
21
16
|
### NOTE: These are defined in the `system.json` config file and so this dictionary's values
|
22
17
|
### will all be overwritten if applicable.
|
23
|
-
|
24
|
-
|
25
|
-
'pool_size': 5,
|
26
|
-
'max_overflow': 10,
|
27
|
-
'pool_recycle': 3600,
|
28
|
-
'connect_args': {},
|
29
|
-
}
|
30
|
-
flavor_configs = {
|
31
|
-
'timescaledb': {
|
32
|
-
'engine': 'postgresql+psycopg',
|
33
|
-
'create_engine': default_create_engine_args,
|
34
|
-
'omit_create_engine': {'method',},
|
35
|
-
'to_sql': {},
|
36
|
-
'requirements': default_requirements,
|
37
|
-
'defaults': {
|
38
|
-
'port': 5432,
|
39
|
-
},
|
40
|
-
},
|
41
|
-
'postgresql': {
|
42
|
-
'engine': 'postgresql+psycopg',
|
43
|
-
'create_engine': default_create_engine_args,
|
44
|
-
'omit_create_engine': {'method',},
|
45
|
-
'to_sql': {},
|
46
|
-
'requirements': default_requirements,
|
47
|
-
'defaults': {
|
48
|
-
'port': 5432,
|
49
|
-
},
|
50
|
-
},
|
51
|
-
'postgis': {
|
52
|
-
'engine': 'postgresql+psycopg',
|
53
|
-
'create_engine': default_create_engine_args,
|
54
|
-
'omit_create_engine': {'method',},
|
55
|
-
'to_sql': {},
|
56
|
-
'requirements': default_requirements,
|
57
|
-
'defaults': {
|
58
|
-
'port': 5432,
|
59
|
-
},
|
60
|
-
},
|
61
|
-
'citus': {
|
62
|
-
'engine': 'postgresql+psycopg',
|
63
|
-
'create_engine': default_create_engine_args,
|
64
|
-
'omit_create_engine': {'method',},
|
65
|
-
'to_sql': {},
|
66
|
-
'requirements': default_requirements,
|
67
|
-
'defaults': {
|
68
|
-
'port': 5432,
|
69
|
-
},
|
70
|
-
},
|
71
|
-
'mssql': {
|
72
|
-
'engine': 'mssql+pyodbc',
|
73
|
-
'create_engine': {
|
74
|
-
'fast_executemany': True,
|
75
|
-
'use_insertmanyvalues': False,
|
76
|
-
'isolation_level': 'AUTOCOMMIT',
|
77
|
-
'use_setinputsizes': False,
|
78
|
-
'pool_pre_ping': True,
|
79
|
-
'ignore_no_transaction_on_rollback': True,
|
80
|
-
},
|
81
|
-
'omit_create_engine': {'method',},
|
82
|
-
'to_sql': {
|
83
|
-
'method': None,
|
84
|
-
},
|
85
|
-
'requirements': default_requirements,
|
86
|
-
'defaults': {
|
87
|
-
'port': 1433,
|
88
|
-
'options': (
|
89
|
-
"driver=ODBC Driver 18 for SQL Server"
|
90
|
-
"&UseFMTONLY=Yes"
|
91
|
-
"&TrustServerCertificate=yes"
|
92
|
-
"&Encrypt=no"
|
93
|
-
"&MARS_Connection=yes"
|
94
|
-
),
|
95
|
-
},
|
96
|
-
},
|
97
|
-
'mysql': {
|
98
|
-
'engine': 'mysql+pymysql',
|
99
|
-
'create_engine': default_create_engine_args,
|
100
|
-
'omit_create_engine': {'method',},
|
101
|
-
'to_sql': {
|
102
|
-
'method': 'multi',
|
103
|
-
},
|
104
|
-
'requirements': default_requirements,
|
105
|
-
'defaults': {
|
106
|
-
'port': 3306,
|
107
|
-
},
|
108
|
-
},
|
109
|
-
'mariadb': {
|
110
|
-
'engine': 'mysql+pymysql',
|
111
|
-
'create_engine': default_create_engine_args,
|
112
|
-
'omit_create_engine': {'method',},
|
113
|
-
'to_sql': {
|
114
|
-
'method': 'multi',
|
115
|
-
},
|
116
|
-
'requirements': default_requirements,
|
117
|
-
'defaults': {
|
118
|
-
'port': 3306,
|
119
|
-
},
|
120
|
-
},
|
121
|
-
'oracle': {
|
122
|
-
'engine': 'oracle+oracledb',
|
123
|
-
'create_engine': default_create_engine_args,
|
124
|
-
'omit_create_engine': {'method',},
|
125
|
-
'to_sql': {
|
126
|
-
'method': None,
|
127
|
-
},
|
128
|
-
'requirements': default_requirements,
|
129
|
-
'defaults': {
|
130
|
-
'port': 1521,
|
131
|
-
},
|
132
|
-
},
|
133
|
-
'sqlite': {
|
134
|
-
'engine': 'sqlite',
|
135
|
-
'create_engine': default_create_engine_args,
|
136
|
-
'omit_create_engine': {'method',},
|
137
|
-
'to_sql': {
|
138
|
-
'method': 'multi',
|
139
|
-
},
|
140
|
-
'requirements': {'database'},
|
141
|
-
'defaults': {},
|
142
|
-
},
|
143
|
-
'duckdb': {
|
144
|
-
'engine': 'duckdb',
|
145
|
-
'create_engine': {},
|
146
|
-
'omit_create_engine': {'ALL',},
|
147
|
-
'to_sql': {
|
148
|
-
'method': 'multi',
|
149
|
-
},
|
150
|
-
'requirements': '',
|
151
|
-
'defaults': {},
|
152
|
-
},
|
153
|
-
'cockroachdb': {
|
154
|
-
'engine': 'cockroachdb',
|
155
|
-
'omit_create_engine': {'method',},
|
156
|
-
'create_engine': default_create_engine_args,
|
157
|
-
'to_sql': {
|
158
|
-
'method': 'multi',
|
159
|
-
},
|
160
|
-
'requirements': {'host'},
|
161
|
-
'defaults': {
|
162
|
-
'port': 26257,
|
163
|
-
'database': 'defaultdb',
|
164
|
-
'username': 'root',
|
165
|
-
'password': 'admin',
|
166
|
-
},
|
167
|
-
},
|
168
|
-
}
|
18
|
+
|
19
|
+
flavor_configs = STATIC_CONFIG['sql']['create_engine_flavors']
|
169
20
|
install_flavor_drivers = {
|
170
21
|
'sqlite': ['aiosqlite'],
|
171
22
|
'duckdb': ['duckdb', 'duckdb_engine'],
|
172
23
|
'mysql': ['pymysql'],
|
173
24
|
'mariadb': ['pymysql'],
|
174
25
|
'timescaledb': ['psycopg'],
|
26
|
+
'timescaledb-ha': ['psycopg', 'geoalchemy'],
|
175
27
|
'postgresql': ['psycopg'],
|
176
28
|
'postgis': ['psycopg', 'geoalchemy'],
|
177
29
|
'citus': ['psycopg'],
|
@@ -268,7 +120,7 @@ def create_engine(
|
|
268
120
|
|
269
121
|
### Sometimes the timescaledb:// flavor can slip in.
|
270
122
|
if _uri and self.flavor in _uri:
|
271
|
-
if self.flavor in ('timescaledb', 'postgis'):
|
123
|
+
if self.flavor in ('timescaledb', 'timescaledb-ha', 'postgis'):
|
272
124
|
engine_str = engine_str.replace(self.flavor, 'postgresql', 1)
|
273
125
|
elif _uri.startswith('postgresql://'):
|
274
126
|
engine_str = engine_str.replace('postgresql://', 'postgresql+psycopg2://')
|
@@ -230,11 +230,9 @@ def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
|
|
230
230
|
- query
|
231
231
|
- sql
|
232
232
|
"""
|
233
|
-
import re
|
234
233
|
import textwrap
|
235
234
|
from meerschaum.utils.warnings import warn as _warn
|
236
|
-
from meerschaum.utils.
|
237
|
-
from meerschaum.utils.sql import sql_item_name
|
235
|
+
from meerschaum.utils.pipes import replace_pipes_syntax
|
238
236
|
if pipe.parameters.get('fetch', {}).get('definition', None):
|
239
237
|
definition = pipe.parameters['fetch']['definition']
|
240
238
|
elif pipe.parameters.get('definition', None):
|
@@ -251,21 +249,7 @@ def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
|
|
251
249
|
)
|
252
250
|
return None
|
253
251
|
|
254
|
-
|
255
|
-
try:
|
256
|
-
args_str = pipe_match.group(1)
|
257
|
-
args, kwargs = parse_arguments_str(args_str)
|
258
|
-
pipe = mrsm.Pipe(*args, **kwargs)
|
259
|
-
except Exception as e:
|
260
|
-
if warn:
|
261
|
-
_warn(f"Failed to parse pipe from SQL definition:\n{e}")
|
262
|
-
raise e
|
263
|
-
|
264
|
-
target = pipe.target
|
265
|
-
schema = pipe.instance_connector.get_pipe_schema(pipe)
|
266
|
-
return sql_item_name(target, pipe.instance_connector.flavor, schema)
|
267
|
-
|
268
|
-
definition = re.sub(r'\{\{Pipe\((.*?)\)\}\}', replace_pipe_match, definition)
|
252
|
+
definition = replace_pipes_syntax(definition)
|
269
253
|
return textwrap.dedent(definition.lstrip().rstrip())
|
270
254
|
|
271
255
|
|
@@ -45,7 +45,7 @@ def register_pipe(
|
|
45
45
|
### (which shouldn't be able to be registered anyway but that's an issue for later).
|
46
46
|
parameters = None
|
47
47
|
try:
|
48
|
-
parameters = pipe.
|
48
|
+
parameters = pipe.get_parameters(apply_symlinks=False)
|
49
49
|
except Exception as e:
|
50
50
|
if debug:
|
51
51
|
dprint(str(e))
|
@@ -76,7 +76,7 @@ def register_pipe(
|
|
76
76
|
|
77
77
|
def edit_pipe(
|
78
78
|
self,
|
79
|
-
pipe
|
79
|
+
pipe: mrsm.Pipe,
|
80
80
|
patch: bool = False,
|
81
81
|
debug: bool = False,
|
82
82
|
**kw : Any
|
@@ -108,10 +108,10 @@ def edit_pipe(
|
|
108
108
|
original_parameters = Pipe(
|
109
109
|
pipe.connector_keys, pipe.metric_key, pipe.location_key,
|
110
110
|
mrsm_instance=pipe.instance_keys
|
111
|
-
).
|
111
|
+
).get_parameters(apply_symlinks=False)
|
112
112
|
parameters = apply_patch_to_config(
|
113
113
|
original_parameters,
|
114
|
-
pipe.parameters
|
114
|
+
pipe._attributes['parameters']
|
115
115
|
)
|
116
116
|
|
117
117
|
### ensure pipes table exists
|
@@ -174,7 +174,7 @@ def fetch_pipes_keys(
|
|
174
174
|
from meerschaum.utils.packages import attempt_import
|
175
175
|
from meerschaum.utils.misc import separate_negation_values
|
176
176
|
from meerschaum.utils.sql import OMIT_NULLSFIRST_FLAVORS, table_exists
|
177
|
-
from meerschaum.
|
177
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
178
178
|
import json
|
179
179
|
from copy import deepcopy
|
180
180
|
sqlalchemy, sqlalchemy_sql_functions = attempt_import(
|
@@ -603,7 +603,10 @@ def get_create_index_queries(
|
|
603
603
|
### create datetime index
|
604
604
|
dt_query = None
|
605
605
|
if _datetime is not None:
|
606
|
-
if
|
606
|
+
if (
|
607
|
+
self.flavor in ('timescaledb', 'timescaledb-ha')
|
608
|
+
and pipe.parameters.get('hypertable', True)
|
609
|
+
):
|
607
610
|
_id_count = (
|
608
611
|
get_distinct_col_count(_id, f"SELECT {_id_name} FROM {_pipe_name}", self)
|
609
612
|
if (_id is not None and _create_space_partition) else None
|
@@ -719,7 +722,7 @@ def get_create_index_queries(
|
|
719
722
|
f"ADD CONSTRAINT {primary_key_constraint_name} PRIMARY KEY ({primary_key_name})"
|
720
723
|
)
|
721
724
|
])
|
722
|
-
elif self.flavor
|
725
|
+
elif self.flavor in ('timescaledb', 'timescaledb-ha'):
|
723
726
|
primary_queries.extend([
|
724
727
|
(
|
725
728
|
f"ALTER TABLE {_pipe_name}\n"
|
@@ -758,7 +761,7 @@ def get_create_index_queries(
|
|
758
761
|
|
759
762
|
### create id index
|
760
763
|
if _id_name is not None:
|
761
|
-
if self.flavor
|
764
|
+
if self.flavor in ('timescaledb', 'timescaledb-ha'):
|
762
765
|
### Already created indices via create_hypertable.
|
763
766
|
id_query = (
|
764
767
|
None if (_id is not None and _create_space_partition)
|
@@ -797,7 +800,7 @@ def get_create_index_queries(
|
|
797
800
|
|
798
801
|
cols_names_str = ", ".join(cols_names)
|
799
802
|
index_query_params_clause = f" ({cols_names_str})"
|
800
|
-
if self.flavor
|
803
|
+
if self.flavor in ('postgis', 'timescaledb-ha'):
|
801
804
|
for col in cols:
|
802
805
|
col_typ = existing_cols_pd_types.get(cols[0], 'object')
|
803
806
|
if col_typ != 'object' and are_dtypes_equal(col_typ, 'geometry'):
|
@@ -1073,14 +1076,14 @@ def get_pipe_data(
|
|
1073
1076
|
|
1074
1077
|
cols_types = pipe.get_columns_types(debug=debug) if pipe.enforce else {}
|
1075
1078
|
dtypes = {
|
1079
|
+
**{
|
1080
|
+
col: get_pd_type_from_db_type(typ)
|
1081
|
+
for col, typ in cols_types.items()
|
1082
|
+
},
|
1076
1083
|
**{
|
1077
1084
|
p_col: to_pandas_dtype(p_typ)
|
1078
1085
|
for p_col, p_typ in pipe.dtypes.items()
|
1079
1086
|
},
|
1080
|
-
**{
|
1081
|
-
col: get_pd_type_from_db_type(typ)
|
1082
|
-
for col, typ in cols_types.items()
|
1083
|
-
}
|
1084
1087
|
} if pipe.enforce else {}
|
1085
1088
|
if dtypes:
|
1086
1089
|
if self.flavor == 'sqlite':
|
@@ -1419,7 +1422,7 @@ def get_pipe_data_query(
|
|
1419
1422
|
if k in existing_cols or skip_existing_cols_check
|
1420
1423
|
}
|
1421
1424
|
if valid_params:
|
1422
|
-
where += build_where(valid_params, self).replace(
|
1425
|
+
where += ' ' + build_where(valid_params, self).lstrip().replace(
|
1423
1426
|
'WHERE', (' AND' if is_dt_bound else " ")
|
1424
1427
|
)
|
1425
1428
|
|
@@ -1648,8 +1651,8 @@ def sync_pipe(
|
|
1648
1651
|
self,
|
1649
1652
|
pipe: mrsm.Pipe,
|
1650
1653
|
df: Union[pd.DataFrame, str, Dict[Any, Any], None] = None,
|
1651
|
-
begin:
|
1652
|
-
end:
|
1654
|
+
begin: Union[datetime, int, None] = None,
|
1655
|
+
end: Union[datetime, int, None] = None,
|
1653
1656
|
chunksize: Optional[int] = -1,
|
1654
1657
|
check_existing: bool = True,
|
1655
1658
|
blocking: bool = True,
|
@@ -1669,11 +1672,11 @@ def sync_pipe(
|
|
1669
1672
|
An optional DataFrame or equivalent to sync into the pipe.
|
1670
1673
|
Defaults to `None`.
|
1671
1674
|
|
1672
|
-
begin:
|
1675
|
+
begin: Union[datetime, int, None], default None
|
1673
1676
|
Optionally specify the earliest datetime to search for data.
|
1674
1677
|
Defaults to `None`.
|
1675
1678
|
|
1676
|
-
end:
|
1679
|
+
end: Union[datetime, int, None], default None
|
1677
1680
|
Optionally specify the latest datetime to search for data.
|
1678
1681
|
Defaults to `None`.
|
1679
1682
|
|
@@ -1833,10 +1836,12 @@ def sync_pipe(
|
|
1833
1836
|
)
|
1834
1837
|
)
|
1835
1838
|
if autoincrement and autoincrement not in pipe.parameters:
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
|
1839
|
+
update_success, update_msg = pipe.update_parameters(
|
1840
|
+
{'autoincrement': autoincrement},
|
1841
|
+
debug=debug,
|
1842
|
+
)
|
1843
|
+
if not update_success:
|
1844
|
+
return update_success, update_msg
|
1840
1845
|
|
1841
1846
|
def _check_pk(_df_to_clear):
|
1842
1847
|
if _df_to_clear is None:
|
@@ -1969,7 +1974,11 @@ def sync_pipe(
|
|
1969
1974
|
if col and col in existing_cols
|
1970
1975
|
] if not primary_key or self.flavor == 'oracle' else (
|
1971
1976
|
[dt_col, primary_key]
|
1972
|
-
if
|
1977
|
+
if (
|
1978
|
+
self.flavor in ('timescaledb', 'timescaledb-ha')
|
1979
|
+
and dt_col
|
1980
|
+
and dt_col in update_df.columns
|
1981
|
+
)
|
1973
1982
|
else [primary_key]
|
1974
1983
|
)
|
1975
1984
|
update_queries = get_update_queries(
|
@@ -2833,7 +2842,6 @@ def get_pipe_rowcount(
|
|
2833
2842
|
error(msg)
|
2834
2843
|
return None
|
2835
2844
|
|
2836
|
-
|
2837
2845
|
flavor = self.flavor if not remote else pipe.connector.flavor
|
2838
2846
|
conn = self if not remote else pipe.connector
|
2839
2847
|
_pipe_name = sql_item_name(pipe.target, flavor, self.get_pipe_schema(pipe))
|
@@ -3412,12 +3420,13 @@ def get_alter_columns_queries(
|
|
3412
3420
|
|
3413
3421
|
if numeric_cols:
|
3414
3422
|
pipe.dtypes.update({col: 'numeric' for col in numeric_cols})
|
3415
|
-
|
3416
|
-
|
3417
|
-
|
3418
|
-
|
3419
|
-
|
3420
|
-
|
3423
|
+
if not pipe.temporary:
|
3424
|
+
edit_success, edit_msg = pipe.edit(debug=debug)
|
3425
|
+
if not edit_success:
|
3426
|
+
warn(
|
3427
|
+
f"Failed to update dtypes for numeric columns {items_str(numeric_cols)}:\n"
|
3428
|
+
+ f"{edit_msg}"
|
3429
|
+
)
|
3421
3430
|
else:
|
3422
3431
|
numeric_cols.extend([col for col, typ in pipe.dtypes.items() if typ.startswith('numeric')])
|
3423
3432
|
|
@@ -3881,13 +3890,15 @@ def get_pipe_schema(self, pipe: mrsm.Pipe) -> Union[str, None]:
|
|
3881
3890
|
-------
|
3882
3891
|
A schema string or `None` if nothing is configured.
|
3883
3892
|
"""
|
3893
|
+
if self.flavor == 'sqlite':
|
3894
|
+
return self.schema
|
3884
3895
|
return pipe.parameters.get('schema', self.schema)
|
3885
3896
|
|
3886
3897
|
|
3887
3898
|
@staticmethod
|
3888
3899
|
def get_temporary_target(
|
3889
3900
|
target: str,
|
3890
|
-
transact_id: Optional[str
|
3901
|
+
transact_id: Optional[str] = None,
|
3891
3902
|
label: Optional[str] = None,
|
3892
3903
|
separator: Optional[str] = None,
|
3893
3904
|
) -> str:
|
@@ -13,6 +13,35 @@ import json
|
|
13
13
|
import meerschaum as mrsm
|
14
14
|
from meerschaum.utils.typing import Optional, Any, List, SuccessTuple, Dict
|
15
15
|
|
16
|
+
|
17
|
+
def get_plugins_pipe(self) -> mrsm.Pipe:
|
18
|
+
"""
|
19
|
+
Return the internal metadata plugins pipe.
|
20
|
+
"""
|
21
|
+
users_pipe = self.get_users_pipe()
|
22
|
+
user_id_dtype = users_pipe.dtypes.get('user_id', 'int')
|
23
|
+
return mrsm.Pipe(
|
24
|
+
'mrsm', 'plugins',
|
25
|
+
instance=self,
|
26
|
+
temporary=True,
|
27
|
+
static=True,
|
28
|
+
null_indices=False,
|
29
|
+
columns={
|
30
|
+
'primary': 'plugin_id',
|
31
|
+
'user_id': 'user_id',
|
32
|
+
},
|
33
|
+
dtypes={
|
34
|
+
'plugin_name': 'string',
|
35
|
+
'user_id': user_id_dtype,
|
36
|
+
'attributes': 'json',
|
37
|
+
'version': 'string',
|
38
|
+
},
|
39
|
+
indices={
|
40
|
+
'unique': 'plugin_name',
|
41
|
+
},
|
42
|
+
)
|
43
|
+
|
44
|
+
|
16
45
|
def register_plugin(
|
17
46
|
self,
|
18
47
|
plugin: 'mrsm.core.Plugin',
|
@@ -17,7 +17,14 @@ from meerschaum.utils.debug import dprint
|
|
17
17
|
from meerschaum.utils.warnings import warn
|
18
18
|
|
19
19
|
### database flavors that can use bulk insert
|
20
|
-
_bulk_flavors = {
|
20
|
+
_bulk_flavors = {
|
21
|
+
'postgresql',
|
22
|
+
'postgis',
|
23
|
+
'timescaledb',
|
24
|
+
'timescaledb-ha',
|
25
|
+
'citus',
|
26
|
+
'mssql',
|
27
|
+
}
|
21
28
|
### flavors that do not support chunks
|
22
29
|
_disallow_chunks_flavors = ['duckdb']
|
23
30
|
_max_chunks_flavors = {'sqlite': 1000}
|
@@ -928,7 +935,7 @@ def to_sql(
|
|
928
935
|
df[col] = df[col].apply(
|
929
936
|
functools.partial(
|
930
937
|
serialize_geometry,
|
931
|
-
|
938
|
+
geometry_format=('wkt' if self.flavor == 'mssql' else 'wkb_hex'),
|
932
939
|
)
|
933
940
|
)
|
934
941
|
|
@@ -12,6 +12,32 @@ import meerschaum as mrsm
|
|
12
12
|
from meerschaum.utils.typing import SuccessTuple, Optional, Any, Dict, List, Union
|
13
13
|
|
14
14
|
|
15
|
+
def get_users_pipe(self) -> mrsm.Pipe:
|
16
|
+
"""
|
17
|
+
Return the internal metadata pipe for users management.
|
18
|
+
"""
|
19
|
+
return mrsm.Pipe(
|
20
|
+
'mrsm', 'users',
|
21
|
+
temporary=True,
|
22
|
+
static=True,
|
23
|
+
null_indices=False,
|
24
|
+
enforce=False,
|
25
|
+
autoincrement=True,
|
26
|
+
columns={
|
27
|
+
'primary': 'user_id',
|
28
|
+
},
|
29
|
+
dtypes={
|
30
|
+
'user_id': 'int',
|
31
|
+
'username': 'string',
|
32
|
+
'attributes': 'json',
|
33
|
+
'user_type': 'string',
|
34
|
+
},
|
35
|
+
indices={
|
36
|
+
'unique': 'username',
|
37
|
+
},
|
38
|
+
)
|
39
|
+
|
40
|
+
|
15
41
|
def register_user(
|
16
42
|
self,
|
17
43
|
user: mrsm.core.User,
|
@@ -64,7 +90,7 @@ def register_user(
|
|
64
90
|
|
65
91
|
def valid_username(username: str) -> SuccessTuple:
|
66
92
|
"""Verify that a given username is valid."""
|
67
|
-
from meerschaum.
|
93
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
68
94
|
fail_reasons = []
|
69
95
|
|
70
96
|
min_length = STATIC_CONFIG['users']['min_username_length']
|
@@ -104,6 +130,7 @@ def edit_user(
|
|
104
130
|
) -> SuccessTuple:
|
105
131
|
"""Update an existing user's metadata."""
|
106
132
|
from meerschaum.utils.packages import attempt_import
|
133
|
+
from meerschaum.utils.sql import json_flavors
|
107
134
|
sqlalchemy = attempt_import('sqlalchemy', lazy=False)
|
108
135
|
from meerschaum.connectors.sql.tables import get_tables
|
109
136
|
users_tbl = get_tables(mrsm_instance=self, debug=debug)['users']
|
@@ -131,7 +158,7 @@ def edit_user(
|
|
131
158
|
bind_variables['email'] = user.email
|
132
159
|
if user.attributes is not None and user.attributes != {}:
|
133
160
|
bind_variables['attributes'] = (
|
134
|
-
json.dumps(user.attributes) if self.flavor in
|
161
|
+
json.dumps(user.attributes) if self.flavor not in json_flavors
|
135
162
|
else user.attributes
|
136
163
|
)
|
137
164
|
if user.type != '':
|
@@ -231,7 +231,7 @@ def create_schemas(
|
|
231
231
|
"""
|
232
232
|
Create the internal Meerschaum schema on the database.
|
233
233
|
"""
|
234
|
-
from meerschaum.
|
234
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
235
235
|
from meerschaum.utils.packages import attempt_import
|
236
236
|
from meerschaum.utils.sql import sql_item_name, NO_SCHEMA_FLAVORS, SKIP_IF_EXISTS_FLAVORS
|
237
237
|
if conn.flavor in NO_SCHEMA_FLAVORS:
|
@@ -10,19 +10,18 @@ import json
|
|
10
10
|
from datetime import datetime, timezone
|
11
11
|
|
12
12
|
import meerschaum as mrsm
|
13
|
-
from meerschaum.connectors import
|
13
|
+
from meerschaum.connectors import InstanceConnector, make_connector
|
14
14
|
from meerschaum.utils.typing import List, Dict, Any, Optional, Union
|
15
15
|
from meerschaum.utils.warnings import dprint
|
16
16
|
|
17
17
|
|
18
18
|
@make_connector
|
19
|
-
class ValkeyConnector(
|
19
|
+
class ValkeyConnector(InstanceConnector):
|
20
20
|
"""
|
21
21
|
Manage a Valkey instance.
|
22
22
|
|
23
23
|
Build a `ValkeyConnector` from connection attributes or a URI string.
|
24
24
|
"""
|
25
|
-
IS_INSTANCE: bool = True
|
26
25
|
REQUIRED_ATTRIBUTES: List[str] = ['host']
|
27
26
|
OPTIONAL_ATTRIBUTES: List[str] = [
|
28
27
|
'port', 'username', 'password', 'db', 'socket_timeout',
|
@@ -80,7 +79,6 @@ class ValkeyConnector(Connector):
|
|
80
79
|
get_plugin_user_id,
|
81
80
|
get_plugin_username,
|
82
81
|
get_plugin_attributes,
|
83
|
-
get_plugins,
|
84
82
|
delete_plugin,
|
85
83
|
)
|
86
84
|
|
@@ -13,7 +13,7 @@ from meerschaum.utils.typing import SuccessTuple, Any, Union, Optional, Dict, Li
|
|
13
13
|
from meerschaum.utils.misc import string_to_dict
|
14
14
|
from meerschaum.utils.dtypes import json_serialize_value
|
15
15
|
from meerschaum.utils.warnings import warn, dprint
|
16
|
-
from meerschaum.
|
16
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
17
17
|
|
18
18
|
PIPES_TABLE: str = 'mrsm_pipes'
|
19
19
|
PIPES_COUNTER: str = 'mrsm_pipes:counter'
|
@@ -276,7 +276,7 @@ def edit_pipe(
|
|
276
276
|
return False, f"{pipe} is not registered."
|
277
277
|
|
278
278
|
parameters_key = get_pipe_parameters_key(pipe)
|
279
|
-
parameters_str = json.dumps(pipe.
|
279
|
+
parameters_str = json.dumps(pipe.get_parameters(apply_symlinks=False), separators=(',', ':'))
|
280
280
|
self.set(parameters_key, parameters_str)
|
281
281
|
return True, "Success"
|
282
282
|
|
@@ -337,7 +337,7 @@ def drop_pipe(
|
|
337
337
|
if 'valkey' not in pipe.parameters:
|
338
338
|
return True, "Success"
|
339
339
|
|
340
|
-
pipe.parameters['valkey']['dtypes'] = {}
|
340
|
+
pipe._attributes['parameters']['valkey']['dtypes'] = {}
|
341
341
|
if not pipe.temporary:
|
342
342
|
edit_success, edit_msg = pipe.edit(debug=debug)
|
343
343
|
if not edit_success:
|
@@ -577,13 +577,12 @@ def sync_pipe(
|
|
577
577
|
|
578
578
|
if new_dtypes and (not static or not valkey_dtypes):
|
579
579
|
valkey_dtypes.update(new_dtypes)
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
return edit_success, edit_msg
|
580
|
+
update_success, update_msg = pipe.update_parameters(
|
581
|
+
{'valkey': {'dtypes': valkey_dtypes}},
|
582
|
+
debug=debug,
|
583
|
+
)
|
584
|
+
if not update_success:
|
585
|
+
return False, update_msg
|
587
586
|
|
588
587
|
unseen_df, update_df, delta_df = (
|
589
588
|
pipe.filter_existing(df, include_unchanged_columns=True, debug=debug)
|
@@ -127,7 +127,8 @@ def get_plugin_id(
|
|
127
127
|
"""
|
128
128
|
Return a plugin's ID.
|
129
129
|
"""
|
130
|
-
|
130
|
+
user_id = self.get_plugin_user_id(plugin, debug=debug)
|
131
|
+
return plugin.name if user_id is not None else None
|
131
132
|
|
132
133
|
|
133
134
|
def get_plugin_version(
|
@@ -199,31 +200,6 @@ def get_plugin_attributes(
|
|
199
200
|
return {}
|
200
201
|
|
201
202
|
|
202
|
-
def get_plugins(
|
203
|
-
self,
|
204
|
-
user_id: Optional[int] = None,
|
205
|
-
search_term: Optional[str] = None,
|
206
|
-
debug: bool = False,
|
207
|
-
**kw: Any
|
208
|
-
) -> List[str]:
|
209
|
-
"""
|
210
|
-
Return a list of plugin names.
|
211
|
-
"""
|
212
|
-
plugins_pipe = self.get_plugins_pipe()
|
213
|
-
params = {}
|
214
|
-
if user_id:
|
215
|
-
params['user_id'] = user_id
|
216
|
-
|
217
|
-
df = plugins_pipe.get_data(['plugin_name'], params=params, debug=debug)
|
218
|
-
docs = df.to_dict(orient='records')
|
219
|
-
|
220
|
-
return [
|
221
|
-
doc['plugin_name']
|
222
|
-
for doc in docs
|
223
|
-
if (plugin_name := doc['plugin_name']).startswith(search_term or '')
|
224
|
-
]
|
225
|
-
|
226
|
-
|
227
203
|
def delete_plugin(
|
228
204
|
self,
|
229
205
|
plugin: 'mrsm.core.Plugin',
|