meerschaum 2.6.17__py3-none-any.whl → 2.7.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.
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
  )