meerschaum 2.6.17__py3-none-any.whl → 2.7.0rc1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. meerschaum/actions/delete.py +65 -69
  2. meerschaum/actions/install.py +1 -2
  3. meerschaum/config/_default.py +1 -1
  4. meerschaum/config/_paths.py +2 -1
  5. meerschaum/config/_version.py +1 -1
  6. meerschaum/connectors/api/_pipes.py +4 -3
  7. meerschaum/connectors/sql/_pipes.py +63 -25
  8. meerschaum/connectors/sql/_sql.py +6 -1
  9. meerschaum/connectors/valkey/_pipes.py +12 -1
  10. meerschaum/core/Pipe/__init__.py +23 -13
  11. meerschaum/core/Pipe/_attributes.py +19 -0
  12. meerschaum/core/Pipe/_dtypes.py +1 -1
  13. meerschaum/core/Pipe/_sync.py +61 -21
  14. meerschaum/core/Pipe/_verify.py +8 -7
  15. meerschaum/plugins/_Plugin.py +11 -14
  16. meerschaum/utils/daemon/Daemon.py +18 -11
  17. meerschaum/utils/dataframe.py +175 -13
  18. meerschaum/utils/dtypes/__init__.py +103 -14
  19. meerschaum/utils/dtypes/sql.py +26 -0
  20. meerschaum/utils/misc.py +8 -8
  21. meerschaum/utils/sql.py +64 -11
  22. meerschaum/utils/venv/_Venv.py +4 -4
  23. meerschaum/utils/venv/__init__.py +33 -13
  24. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/METADATA +1 -1
  25. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/RECORD +31 -31
  26. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/LICENSE +0 -0
  27. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/NOTICE +0 -0
  28. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/WHEEL +0 -0
  29. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/entry_points.txt +0 -0
  30. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/top_level.txt +0 -0
  31. {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/zip-safe +0 -0
@@ -9,6 +9,7 @@ Functions for deleting elements.
9
9
  from __future__ import annotations
10
10
  from meerschaum.utils.typing import Any, SuccessTuple, Union, Optional, List
11
11
 
12
+
12
13
  def delete(
13
14
  action: Optional[List[str]] = None,
14
15
  **kw: Any
@@ -21,7 +22,6 @@ def delete(
21
22
 
22
23
  """
23
24
  from meerschaum.actions import choose_subaction
24
- from meerschaum.utils.debug import dprint
25
25
  options = {
26
26
  'config' : _delete_config,
27
27
  'pipes' : _delete_pipes,
@@ -65,19 +65,18 @@ def _complete_delete(
65
65
 
66
66
 
67
67
  def _delete_pipes(
68
- debug: bool = False,
69
- yes: bool = False,
70
- force: bool = False,
71
- noask: bool = False,
72
- **kw: Any
73
- ) -> SuccessTuple:
68
+ debug: bool = False,
69
+ yes: bool = False,
70
+ force: bool = False,
71
+ noask: bool = False,
72
+ **kw: Any
73
+ ) -> SuccessTuple:
74
74
  """
75
75
  Drop pipes and delete their registrations.
76
-
77
76
  """
78
77
  from meerschaum import get_pipes
79
78
  from meerschaum.utils.prompt import yes_no
80
- from meerschaum.utils.formatting import pprint, highlight_pipes
79
+ from meerschaum.utils.formatting import highlight_pipes
81
80
  from meerschaum.utils.warnings import warn
82
81
  from meerschaum.actions import actions
83
82
  pipes = get_pipes(as_list=True, debug=debug, **kw)
@@ -126,21 +125,22 @@ def _delete_pipes(
126
125
 
127
126
  return successes > 0, msg
128
127
 
128
+
129
129
  def _delete_config(
130
- action: Optional[List[str]] = None,
131
- yes: bool = False,
132
- force: bool = False,
133
- noask: bool = False,
134
- debug: bool = False,
135
- **kw: Any
136
- ) -> SuccessTuple:
130
+ action: Optional[List[str]] = None,
131
+ yes: bool = False,
132
+ force: bool = False,
133
+ noask: bool = False,
134
+ debug: bool = False,
135
+ **kw: Any
136
+ ) -> SuccessTuple:
137
137
  """
138
138
  Delete configuration files.
139
-
140
139
  """
141
- import os, shutil
140
+ import os
141
+ import shutil
142
142
  from meerschaum.utils.prompt import yes_no
143
- from meerschaum.config._paths import CONFIG_DIR_PATH, STACK_COMPOSE_PATH, DEFAULT_CONFIG_DIR_PATH
143
+ from meerschaum.config._paths import STACK_COMPOSE_PATH, DEFAULT_CONFIG_DIR_PATH
144
144
  from meerschaum.config._read_config import get_possible_keys, get_keyfile_path
145
145
  from meerschaum.utils.debug import dprint
146
146
  paths = [p for p in [STACK_COMPOSE_PATH, DEFAULT_CONFIG_DIR_PATH] if p.exists()]
@@ -157,7 +157,7 @@ def _delete_config(
157
157
  else:
158
158
  sep = '\n' + ' - '
159
159
  answer = yes_no(
160
- f"Are you sure you want to delete the following configuration files?" +
160
+ "Are you sure you want to delete the following configuration files?" +
161
161
  f"{sep + sep.join([str(p) for p in paths])}\n",
162
162
  default='n', noask=noask, yes=yes
163
163
  )
@@ -176,18 +176,18 @@ def _delete_config(
176
176
 
177
177
  return success, msg
178
178
 
179
+
179
180
  def _delete_plugins(
180
- action: Optional[List[str]] = None,
181
- repository: Optional[str] = None,
182
- yes: bool = False,
183
- force: bool = False,
184
- noask: bool = False,
185
- debug: bool = False,
186
- **kw: Any
187
- ) -> SuccessTuple:
181
+ action: Optional[List[str]] = None,
182
+ repository: Optional[str] = None,
183
+ yes: bool = False,
184
+ force: bool = False,
185
+ noask: bool = False,
186
+ debug: bool = False,
187
+ **kw: Any
188
+ ) -> SuccessTuple:
188
189
  """
189
190
  Delete plugins from a Meerschaum repository.
190
-
191
191
  """
192
192
  from meerschaum.utils.warnings import info
193
193
  from meerschaum.plugins import reload_plugins
@@ -206,7 +206,7 @@ def _delete_plugins(
206
206
  ) if not force else True
207
207
 
208
208
  if not answer:
209
- return False, f"No plugins deleted."
209
+ return False, "No plugins deleted."
210
210
 
211
211
  successes = {}
212
212
  for name in action:
@@ -218,27 +218,24 @@ def _delete_plugins(
218
218
  reload_plugins(debug=debug)
219
219
  return True, "Success"
220
220
 
221
+
221
222
  def _delete_users(
222
- action: Optional[List[str]] = None,
223
- mrsm_instance: Optional[str] = None,
224
- yes: bool = False,
225
- force: bool = False,
226
- noask: bool = False,
227
- shell: bool = False,
228
- debug: bool = False,
229
- **kw
230
- ) -> SuccessTuple:
223
+ action: Optional[List[str]] = None,
224
+ mrsm_instance: Optional[str] = None,
225
+ yes: bool = False,
226
+ force: bool = False,
227
+ noask: bool = False,
228
+ shell: bool = False,
229
+ debug: bool = False,
230
+ **kw
231
+ ) -> SuccessTuple:
231
232
  """
232
233
  Delete users from a Meerschaum instance. Adequate permissions are required.
233
-
234
234
  """
235
- from meerschaum import get_connector
236
235
  from meerschaum.connectors.parse import parse_instance_keys
237
- from meerschaum.utils.prompt import yes_no, prompt
238
- from meerschaum.utils.debug import dprint
239
- from meerschaum.utils.warnings import warn, error, info
236
+ from meerschaum.utils.prompt import yes_no
237
+ from meerschaum.utils.warnings import info
240
238
  from meerschaum.core import User
241
- from meerschaum.connectors.api import APIConnector
242
239
  from meerschaum.utils.formatting import print_tuple
243
240
  instance_connector = parse_instance_keys(mrsm_instance)
244
241
 
@@ -292,24 +289,25 @@ def _delete_users(
292
289
  )
293
290
  return True, msg
294
291
 
292
+
295
293
  def _delete_connectors(
296
- action: Optional[List[str]] = None,
297
- connector_keys: Optional[List[str]] = None,
298
- yes: bool = False,
299
- force: bool = False,
300
- noask: bool = False,
301
- debug: bool = False,
302
- **kw: Any
303
- ) -> SuccessTuple:
294
+ action: Optional[List[str]] = None,
295
+ connector_keys: Optional[List[str]] = None,
296
+ yes: bool = False,
297
+ force: bool = False,
298
+ noask: bool = False,
299
+ debug: bool = False,
300
+ **kw: Any
301
+ ) -> SuccessTuple:
304
302
  """
305
303
  Delete configured connectors.
306
304
 
307
305
  Example:
308
306
  `delete connectors sql:test`
309
-
310
307
  """
311
- import os, pathlib
312
- from meerschaum.utils.prompt import yes_no, prompt
308
+ import os
309
+ import pathlib
310
+ from meerschaum.utils.prompt import yes_no
313
311
  from meerschaum.connectors.parse import parse_connector_keys
314
312
  from meerschaum.config import _config
315
313
  from meerschaum.config._edit import write_config
@@ -329,7 +327,7 @@ def _delete_connectors(
329
327
  for ck in _keys:
330
328
  try:
331
329
  conn = parse_connector_keys(ck, debug=debug)
332
- except Exception as e:
330
+ except Exception:
333
331
  warn(f"Could not parse connector '{ck}'. Skipping...", stack=False)
334
332
  continue
335
333
 
@@ -357,26 +355,27 @@ def _delete_connectors(
357
355
  ):
358
356
  try:
359
357
  os.remove(c.database)
360
- except Exception as e:
358
+ except Exception:
361
359
  warn(
362
360
  "Failed to delete database file for connector "
363
361
  + f"'{c}'. Ignoring...", stack=False
364
362
  )
365
- except Exception as e:
363
+ except Exception:
366
364
  pass
367
365
  try:
368
366
  del cf['meerschaum']['connectors'][c.type][c.label]
369
- except Exception as e:
367
+ except Exception:
370
368
  warn(f"Failed to delete connector '{c}' from configuration. Skipping...", stack=False)
371
369
 
372
370
  write_config(cf, debug=debug)
373
371
  return True, "Success"
374
372
 
373
+
375
374
  def _complete_delete_connectors(
376
- action: Optional[List[str]] = None,
377
- line: str = '',
378
- **kw: Any
379
- ) -> List[str]:
375
+ action: Optional[List[str]] = None,
376
+ line: str = '',
377
+ **kw: Any
378
+ ) -> List[str]:
380
379
  from meerschaum.config import get_config
381
380
  from meerschaum.utils.misc import get_connector_labels
382
381
  types = list(get_config('meerschaum', 'connectors').keys())
@@ -401,10 +400,8 @@ def _delete_jobs(
401
400
  Remove a job's log files and delete the job's ID.
402
401
 
403
402
  If the job is running, ask to kill the job first.
404
-
405
403
  """
406
404
  from meerschaum.jobs import (
407
- Job,
408
405
  get_running_jobs,
409
406
  get_stopped_jobs,
410
407
  get_filtered_jobs,
@@ -460,7 +457,7 @@ def _delete_jobs(
460
457
  ### Ensure the running jobs are dead.
461
458
  if get_running_jobs(executor_keys, jobs, debug=debug):
462
459
  return False, (
463
- f"Failed to kill running jobs. Please stop these jobs before deleting."
460
+ "Failed to kill running jobs. Please stop these jobs before deleting."
464
461
  )
465
462
  _to_delete.update(to_stop_jobs)
466
463
 
@@ -475,7 +472,7 @@ def _delete_jobs(
475
472
  pprint_jobs(_to_delete, nopretty=nopretty)
476
473
  if not yes_no(
477
474
  "Are you sure you want to delete these jobs?",
478
- yes=yes, noask=noask, default='y',
475
+ yes=yes, noask=noask, default='n',
479
476
  ):
480
477
  return False, "No jobs were deleted."
481
478
 
@@ -569,7 +566,6 @@ def _delete_venvs(
569
566
  from meerschaum.utils.venv import venv_exists
570
567
  from meerschaum.utils.prompt import yes_no
571
568
  from meerschaum.utils.misc import print_options
572
- from meerschaum.utils.warnings import warn
573
569
 
574
570
  venvs_to_skip = ['mrsm']
575
571
  venvs = [
@@ -175,7 +175,7 @@ def _install_packages(
175
175
  + f" into the virtual environment '{venv}'."
176
176
  )
177
177
  return False, (
178
- f"Failed to install package" + ("s" if len(action) != 1 else '') + f" {items_str(action)}."
178
+ "Failed to install package" + ("s" if len(action) != 1 else '') + f" {items_str(action)}."
179
179
  )
180
180
 
181
181
 
@@ -200,7 +200,6 @@ def _install_required(
200
200
  from meerschaum.core import Plugin
201
201
  from meerschaum.utils.warnings import warn, info
202
202
  from meerschaum.connectors.parse import parse_repo_keys
203
- from meerschaum.utils.formatting import print_tuple
204
203
  from meerschaum.plugins import get_plugins_names
205
204
  repo_connector = parse_repo_keys(repository)
206
205
 
@@ -137,7 +137,7 @@ default_pipes_config = {
137
137
  },
138
138
  'verify': {
139
139
  'bound_days': 366,
140
- 'chunk_minutes': 1440,
140
+ 'chunk_minutes': 43200,
141
141
  },
142
142
  },
143
143
  'attributes': {
@@ -103,7 +103,7 @@ if ENVIRONMENT_VENVS_DIR in os.environ:
103
103
  if not _VENVS_DIR_PATH.exists():
104
104
  try:
105
105
  _VENVS_DIR_PATH.mkdir(parents=True, exist_ok=True)
106
- except Exception as e:
106
+ except Exception:
107
107
  print(
108
108
  f"Invalid path set for environment variable '{ENVIRONMENT_VENVS_DIR}':\n"
109
109
  + f"{_VENVS_DIR_PATH}"
@@ -148,6 +148,7 @@ paths = {
148
148
  'CACHE_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', '.cache'),
149
149
  'PIPES_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'pipes'),
150
150
  'USERS_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'users'),
151
+ 'VENVS_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'venvs'),
151
152
 
152
153
  'PLUGINS_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'plugins'),
153
154
  'PLUGINS_INTERNAL_LOCK_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'plugins.lock'),
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.6.17"
5
+ __version__ = "2.7.0rc1"
@@ -15,7 +15,8 @@ from datetime import datetime
15
15
  import meerschaum as mrsm
16
16
  from meerschaum.utils.debug import dprint
17
17
  from meerschaum.utils.warnings import warn, error
18
- from meerschaum.utils.typing import SuccessTuple, Union, Any, Optional, Mapping, List, Dict, Tuple
18
+ from meerschaum.utils.typing import SuccessTuple, Union, Any, Optional, List, Dict, Tuple
19
+
19
20
 
20
21
  def pipe_r_url(
21
22
  pipe: mrsm.Pipe
@@ -30,6 +31,7 @@ def pipe_r_url(
30
31
  + f"{pipe.connector_keys}/{pipe.metric_key}/{location_key}"
31
32
  )
32
33
 
34
+
33
35
  def register_pipe(
34
36
  self,
35
37
  pipe: mrsm.Pipe,
@@ -39,7 +41,6 @@ def register_pipe(
39
41
  Returns a tuple of (success_bool, response_dict).
40
42
  """
41
43
  from meerschaum.utils.debug import dprint
42
- from meerschaum.config.static import STATIC_CONFIG
43
44
  ### NOTE: if `parameters` is supplied in the Pipe constructor,
44
45
  ### then `pipe.parameters` will exist and not be fetched from the database.
45
46
  r_url = pipe_r_url(pipe)
@@ -180,7 +181,7 @@ def sync_pipe(
180
181
  from meerschaum.utils.misc import json_serialize_datetime, items_str
181
182
  from meerschaum.config import get_config
182
183
  from meerschaum.utils.packages import attempt_import
183
- from meerschaum.utils.dataframe import get_numeric_cols, to_json
184
+ from meerschaum.utils.dataframe import get_numeric_cols, to_json, get_bytes_cols
184
185
  begin = time.time()
185
186
  more_itertools = attempt_import('more_itertools')
186
187
  if df is None:
@@ -460,10 +460,16 @@ def get_create_index_queries(
460
460
  else None
461
461
  )
462
462
  primary_key_constraint_name = (
463
- sql_item_name(f'pk_{pipe.target}', self.flavor, None)
463
+ sql_item_name(f'PK_{pipe.target}', self.flavor, None)
464
464
  if primary_key is not None
465
465
  else None
466
466
  )
467
+ primary_key_clustered = "CLUSTERED" if _datetime is None else "NONCLUSTERED"
468
+ datetime_clustered = (
469
+ "CLUSTERED"
470
+ if not existing_primary_keys and _datetime is not None
471
+ else "NONCLUSTERED"
472
+ )
467
473
 
468
474
  _id_index_name = (
469
475
  sql_item_name(index_names['id'], self.flavor, None)
@@ -474,6 +480,7 @@ def get_create_index_queries(
474
480
  _create_space_partition = get_config('system', 'experimental', 'space')
475
481
 
476
482
  ### create datetime index
483
+ dt_query = None
477
484
  if _datetime is not None:
478
485
  if self.flavor == 'timescaledb' and pipe.parameters.get('hypertable', True):
479
486
  _id_count = (
@@ -504,19 +511,19 @@ def get_create_index_queries(
504
511
  + 'if_not_exists => true, '
505
512
  + "migrate_data => true);"
506
513
  )
507
- elif self.flavor == 'mssql':
508
- dt_query = (
509
- "CREATE "
510
- + ("CLUSTERED " if not primary_key else '')
511
- + f"INDEX {_datetime_index_name} "
512
- + f"ON {_pipe_name} ({_datetime_name})"
513
- )
514
- else: ### mssql, sqlite, etc.
515
- dt_query = (
516
- f"CREATE INDEX {_datetime_index_name} "
517
- + f"ON {_pipe_name} ({_datetime_name})"
518
- )
514
+ elif _datetime_index_name:
515
+ if self.flavor == 'mssql':
516
+ dt_query = (
517
+ f"CREATE {datetime_clustered} INDEX {_datetime_index_name} "
518
+ f"ON {_pipe_name} ({_datetime_name})"
519
+ )
520
+ else:
521
+ dt_query = (
522
+ f"CREATE INDEX {_datetime_index_name} "
523
+ + f"ON {_pipe_name} ({_datetime_name})"
524
+ )
519
525
 
526
+ if dt_query:
520
527
  index_queries[_datetime] = [dt_query]
521
528
 
522
529
  primary_queries = []
@@ -623,7 +630,7 @@ def get_create_index_queries(
623
630
  ),
624
631
  (
625
632
  f"ALTER TABLE {_pipe_name}\n"
626
- f"ADD CONSTRAINT {primary_key_constraint_name} PRIMARY KEY ({primary_key_name})"
633
+ f"ADD CONSTRAINT {primary_key_constraint_name} PRIMARY KEY {primary_key_clustered} ({primary_key_name})"
627
634
  ),
628
635
  ])
629
636
  index_queries[primary_key] = primary_queries
@@ -875,6 +882,7 @@ def get_pipe_data(
875
882
  from meerschaum.utils.dtypes import (
876
883
  attempt_cast_to_numeric,
877
884
  attempt_cast_to_uuid,
885
+ attempt_cast_to_bytes,
878
886
  are_dtypes_equal,
879
887
  )
880
888
  from meerschaum.utils.dtypes.sql import get_pd_type_from_db_type
@@ -891,17 +899,15 @@ def get_pipe_data(
891
899
  col: get_pd_type_from_db_type(typ)
892
900
  for col, typ in cols_types.items()
893
901
  }
894
- }
902
+ } if pipe.enforce else {}
895
903
  if dtypes:
896
904
  if self.flavor == 'sqlite':
897
905
  if not pipe.columns.get('datetime', None):
898
906
  _dt = pipe.guess_datetime()
899
907
  dt = sql_item_name(_dt, self.flavor, None) if _dt else None
900
- is_guess = True
901
908
  else:
902
909
  _dt = pipe.get_columns('datetime')
903
910
  dt = sql_item_name(_dt, self.flavor, None)
904
- is_guess = False
905
911
 
906
912
  if _dt:
907
913
  dt_type = dtypes.get(_dt, 'object').lower()
@@ -929,7 +935,7 @@ def get_pipe_data(
929
935
  col: to_pandas_dtype(typ)
930
936
  for col, typ in dtypes.items()
931
937
  if col in select_columns and col not in (omit_columns or [])
932
- }
938
+ } if pipe.enforce else {}
933
939
  query = self.get_pipe_data_query(
934
940
  pipe,
935
941
  select_columns=select_columns,
@@ -959,6 +965,11 @@ def get_pipe_data(
959
965
  for col, typ in pipe.dtypes.items()
960
966
  if typ == 'uuid' and col in dtypes
961
967
  ]
968
+ bytes_columns = [
969
+ col
970
+ for col, typ in pipe.dtypes.items()
971
+ if typ == 'bytes' and col in dtypes
972
+ ]
962
973
 
963
974
  kw['coerce_float'] = kw.get('coerce_float', (len(numeric_columns) == 0))
964
975
 
@@ -978,6 +989,11 @@ def get_pipe_data(
978
989
  continue
979
990
  df[col] = df[col].apply(attempt_cast_to_uuid)
980
991
 
992
+ for col in bytes_columns:
993
+ if col not in df.columns:
994
+ continue
995
+ df[col] = df[col].apply(attempt_cast_to_bytes)
996
+
981
997
  if self.flavor == 'sqlite':
982
998
  ignore_dt_cols = [
983
999
  col
@@ -1339,7 +1355,13 @@ def create_pipe_table_from_df(
1339
1355
  """
1340
1356
  Create a pipe's table from its configured dtypes and an incoming dataframe.
1341
1357
  """
1342
- from meerschaum.utils.dataframe import get_json_cols, get_numeric_cols, get_uuid_cols
1358
+ from meerschaum.utils.dataframe import (
1359
+ get_json_cols,
1360
+ get_numeric_cols,
1361
+ get_uuid_cols,
1362
+ get_datetime_cols,
1363
+ get_bytes_cols,
1364
+ )
1343
1365
  from meerschaum.utils.sql import get_create_table_queries, sql_item_name
1344
1366
  primary_key = pipe.columns.get('primary', None)
1345
1367
  dt_col = pipe.columns.get('datetime', None)
@@ -1365,6 +1387,18 @@ def create_pipe_table_from_df(
1365
1387
  col: 'numeric'
1366
1388
  for col in get_numeric_cols(df)
1367
1389
  },
1390
+ **{
1391
+ col: 'bytes'
1392
+ for col in get_bytes_cols(df)
1393
+ },
1394
+ **{
1395
+ col: 'datetime64[ns, UTC]'
1396
+ for col in get_datetime_cols(df, timezone_aware=True, timezone_naive=False)
1397
+ },
1398
+ **{
1399
+ col: 'datetime64[ns]'
1400
+ for col in get_datetime_cols(df, timezone_aware=False, timezone_naive=True)
1401
+ },
1368
1402
  **pipe.dtypes
1369
1403
  }
1370
1404
  autoincrement = (
@@ -1455,11 +1489,9 @@ def sync_pipe(
1455
1489
  get_update_queries,
1456
1490
  sql_item_name,
1457
1491
  update_queries,
1458
- get_create_table_queries,
1459
1492
  get_reset_autoincrement_queries,
1460
1493
  )
1461
1494
  from meerschaum.utils.misc import generate_password
1462
- from meerschaum.utils.dataframe import get_json_cols, get_numeric_cols, get_uuid_cols
1463
1495
  from meerschaum.utils.dtypes import are_dtypes_equal
1464
1496
  from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
1465
1497
  from meerschaum import Pipe
@@ -1572,6 +1604,7 @@ def sync_pipe(
1572
1604
  'schema': self.get_pipe_schema(pipe),
1573
1605
  })
1574
1606
 
1607
+ dt_col = pipe.columns.get('datetime', None)
1575
1608
  primary_key = pipe.columns.get('primary', None)
1576
1609
  autoincrement = (
1577
1610
  pipe.parameters.get('autoincrement', False)
@@ -1714,7 +1747,11 @@ def sync_pipe(
1714
1747
  col
1715
1748
  for col_key, col in pipe.columns.items()
1716
1749
  if col and col in existing_cols
1717
- ]
1750
+ ] if not primary_key else (
1751
+ [dt_col, primary_key]
1752
+ if self.flavor == 'timescaledb' and dt_col and dt_col in update_df.columns
1753
+ else [primary_key]
1754
+ )
1718
1755
  update_queries = get_update_queries(
1719
1756
  pipe.target,
1720
1757
  temp_target,
@@ -1723,7 +1760,8 @@ def sync_pipe(
1723
1760
  upsert=upsert,
1724
1761
  schema=self.get_pipe_schema(pipe),
1725
1762
  patch_schema=self.internal_schema,
1726
- datetime_col=pipe.columns.get('datetime', None),
1763
+ datetime_col=(dt_col if dt_col in update_df.columns else None),
1764
+ identity_insert=(autoincrement and primary_key in update_df.columns),
1727
1765
  debug=debug,
1728
1766
  )
1729
1767
  update_success = all(
@@ -1841,7 +1879,6 @@ def sync_pipe_inplace(
1841
1879
  session_execute,
1842
1880
  update_queries,
1843
1881
  )
1844
- from meerschaum.utils.dtypes import are_dtypes_equal
1845
1882
  from meerschaum.utils.dtypes.sql import (
1846
1883
  get_pd_type_from_db_type,
1847
1884
  )
@@ -2061,6 +2098,7 @@ def sync_pipe_inplace(
2061
2098
  ) if not (upsert or static) else new_cols_types
2062
2099
 
2063
2100
  common_cols = [col for col in new_cols if col in backtrack_cols_types]
2101
+ primary_key = pipe.columns.get('primary', None)
2064
2102
  on_cols = {
2065
2103
  col: new_cols.get(col)
2066
2104
  for col_key, col in pipe.columns.items()
@@ -2071,7 +2109,7 @@ def sync_pipe_inplace(
2071
2109
  and col in backtrack_cols_types
2072
2110
  and col in new_cols
2073
2111
  )
2074
- }
2112
+ } if not primary_key else {primary_key: new_cols.get(primary_key)}
2075
2113
 
2076
2114
  null_replace_new_cols_str = (
2077
2115
  ', '.join([
@@ -790,7 +790,12 @@ def to_sql(
790
790
  truncate_item_name,
791
791
  DROP_IF_EXISTS_FLAVORS,
792
792
  )
793
- from meerschaum.utils.dataframe import get_json_cols, get_numeric_cols, get_uuid_cols
793
+ from meerschaum.utils.dataframe import (
794
+ get_json_cols,
795
+ get_numeric_cols,
796
+ get_uuid_cols,
797
+ get_bytes_cols,
798
+ )
794
799
  from meerschaum.utils.dtypes import are_dtypes_equal, quantize_decimal, coerce_timezone
795
800
  from meerschaum.utils.dtypes.sql import (
796
801
  NUMERIC_PRECISION_FLAVORS,
@@ -46,9 +46,20 @@ def serialize_document(doc: Dict[str, Any]) -> str:
46
46
  -------
47
47
  A serialized string for the document.
48
48
  """
49
+ from meerschaum.utils.dtypes import serialize_bytes
49
50
  return json.dumps(
50
51
  doc,
51
- default=(lambda x: json_serialize_datetime(x) if hasattr(x, 'tzinfo') else str(x)),
52
+ default=(
53
+ lambda x: (
54
+ json_serialize_datetime(x)
55
+ if hasattr(x, 'tzinfo')
56
+ else (
57
+ serialize_bytes(x)
58
+ if isinstance(x, bytes)
59
+ else str(x)
60
+ )
61
+ )
62
+ ),
52
63
  separators=(',', ':'),
53
64
  sort_keys=True,
54
65
  )