meerschaum 2.4.7__py3-none-any.whl → 2.4.8__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/actions/sql.py CHANGED
@@ -59,7 +59,7 @@ def sql(
59
59
  - `sql local exec "INSERT INTO table (id) VALUES (1)"
60
60
  Execute the above query on `sql:local`.
61
61
  """
62
- from meerschaum.utils.dataframe impor to_json
62
+ from meerschaum.utils.dataframe import to_json
63
63
 
64
64
  if action is None:
65
65
  action = []
@@ -408,8 +408,8 @@ def get_pipe_data(
408
408
  _params = None
409
409
  if not isinstance(_params, dict):
410
410
  raise fastapi.HTTPException(
411
- status_code = 409,
412
- detail = "Params must be a valid JSON-encoded dictionary.",
411
+ status_code=409,
412
+ detail="Params must be a valid JSON-encoded dictionary.",
413
413
  )
414
414
 
415
415
  _select_columns = []
@@ -422,8 +422,8 @@ def get_pipe_data(
422
422
  _select_columns = None
423
423
  if not isinstance(_select_columns, list):
424
424
  raise fastapi.HTTPException(
425
- status_code = 409,
426
- detail = "Selected columns must be a JSON-encoded list."
425
+ status_code=409,
426
+ detail="Selected columns must be a JSON-encoded list."
427
427
  )
428
428
 
429
429
  _omit_columns = []
@@ -436,35 +436,35 @@ def get_pipe_data(
436
436
  _omit_columns = None
437
437
  if _omit_columns is None:
438
438
  raise fastapi.HTTPException(
439
- status_code = 409,
440
- detail = "Omitted columns must be a JSON-encoded list.",
439
+ status_code=409,
440
+ detail="Omitted columns must be a JSON-encoded list.",
441
441
  )
442
442
 
443
443
  pipe = get_pipe(connector_keys, metric_key, location_key)
444
444
  if not is_pipe_registered(pipe, pipes(refresh=True)):
445
445
  raise fastapi.HTTPException(
446
- status_code = 409,
447
- detail = "Pipe must be registered with the datetime column specified."
446
+ status_code=409,
447
+ detail="Pipe must be registered with the datetime column specified."
448
448
  )
449
449
 
450
450
  if pipe.target in ('users', 'plugins', 'pipes'):
451
451
  raise fastapi.HTTPException(
452
- status_code = 409,
453
- detail = f"Cannot retrieve data from protected table '{pipe.target}'.",
452
+ status_code=409,
453
+ detail=f"Cannot retrieve data from protected table '{pipe.target}'.",
454
454
  )
455
455
 
456
456
  df = pipe.get_data(
457
- select_columns = _select_columns,
458
- omit_columns = _omit_columns,
459
- begin = begin,
460
- end = end,
461
- params = _params,
462
- debug = debug,
457
+ select_columns=_select_columns,
458
+ omit_columns=_omit_columns,
459
+ begin=begin,
460
+ end=end,
461
+ params=_params,
462
+ debug=debug,
463
463
  )
464
464
  if df is None:
465
465
  raise fastapi.HTTPException(
466
- status_code = 400,
467
- detail = f"Could not fetch data with the given parameters.",
466
+ status_code=400,
467
+ detail="Could not fetch data with the given parameters.",
468
468
  )
469
469
 
470
470
  ### NaN cannot be JSON-serialized.
@@ -482,16 +482,16 @@ def get_pipe_data(
482
482
 
483
483
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/csv', tags=['Pipes'])
484
484
  def get_pipe_csv(
485
- connector_keys: str,
486
- metric_key: str,
487
- location_key: str,
488
- begin: Union[str, int, None] = None,
489
- end: Union[str, int, None] = None,
490
- params: Optional[str] = None,
491
- curr_user = (
492
- fastapi.Depends(manager) if not no_auth else None
493
- ),
494
- ) -> str:
485
+ connector_keys: str,
486
+ metric_key: str,
487
+ location_key: str,
488
+ begin: Union[str, int, None] = None,
489
+ end: Union[str, int, None] = None,
490
+ params: Optional[str] = None,
491
+ curr_user = (
492
+ fastapi.Depends(manager) if not no_auth else None
493
+ ),
494
+ ) -> str:
495
495
  """
496
496
  Get a Pipe's data as a CSV file. Optionally set query boundaries.
497
497
  """
@@ -518,8 +518,8 @@ def get_pipe_csv(
518
518
 
519
519
  if not isinstance(_params, dict):
520
520
  raise fastapi.HTTPException(
521
- status_code = 409,
522
- detail = "Params must be a valid JSON-encoded dictionary.",
521
+ status_code=409,
522
+ detail="Params must be a valid JSON-encoded dictionary.",
523
523
  )
524
524
 
525
525
  p = get_pipe(connector_keys, metric_key, location_key)
@@ -529,7 +529,7 @@ def get_pipe_csv(
529
529
  detail = "Pipe must be registered with the datetime column specified."
530
530
  )
531
531
 
532
- dt_col = pipe.columns.get('datetime', None)
532
+ dt_col = p.columns.get('datetime', None)
533
533
  if dt_col:
534
534
  if begin is None:
535
535
  begin = p.get_sync_time(round_down=False, newest=False)
@@ -552,13 +552,13 @@ def get_pipe_csv(
552
552
 
553
553
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/id', tags=['Pipes'])
554
554
  def get_pipe_id(
555
- connector_keys: str,
556
- metric_key: str,
557
- location_key: str,
558
- curr_user = (
559
- fastapi.Depends(manager) if not no_auth else None
560
- ),
561
- ) -> int:
555
+ connector_keys: str,
556
+ metric_key: str,
557
+ location_key: str,
558
+ curr_user = (
559
+ fastapi.Depends(manager) if not no_auth else None
560
+ ),
561
+ ) -> int:
562
562
  """
563
563
  Get a Pipe's ID.
564
564
  """
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.4.7"
5
+ __version__ = "2.4.8"
@@ -346,14 +346,14 @@ def get_pipe_data(
346
346
  try:
347
347
  response = self.get(
348
348
  r_url + "/data",
349
- params = {
349
+ params={
350
350
  'select_columns': json.dumps(select_columns),
351
351
  'omit_columns': json.dumps(omit_columns),
352
352
  'begin': begin,
353
353
  'end': end,
354
- 'params': json.dumps(params)
354
+ 'params': json.dumps(params, default=str)
355
355
  },
356
- debug = debug
356
+ debug=debug
357
357
  )
358
358
  if not response.ok:
359
359
  return None
@@ -272,7 +272,7 @@ class SQLConnector(Connector):
272
272
  """
273
273
  Return whether this connector may be multithreaded.
274
274
  """
275
- if self.flavor == 'duckdb':
275
+ if self.flavor in ('duckdb', 'oracle'):
276
276
  return False
277
277
  if self.flavor == 'sqlite':
278
278
  return ':memory:' not in self.URI
@@ -15,12 +15,12 @@ from meerschaum.utils.warnings import warn
15
15
 
16
16
  _in_memory_temp_tables: Dict[str, bool] = {}
17
17
  def _log_temporary_tables_creation(
18
- self,
19
- tables: Union[str, List[str]],
20
- ready_to_drop: bool = False,
21
- create: bool = True,
22
- debug: bool = False,
23
- ) -> SuccessTuple:
18
+ self,
19
+ tables: Union[str, List[str]],
20
+ ready_to_drop: bool = False,
21
+ create: bool = True,
22
+ debug: bool = False,
23
+ ) -> SuccessTuple:
24
24
  """
25
25
  Log a temporary table's creation for later deletion.
26
26
  """
@@ -58,15 +58,15 @@ def _log_temporary_tables_creation(
58
58
 
59
59
 
60
60
  def _drop_temporary_table(
61
- self,
62
- table: str,
63
- debug: bool = False,
64
- ) -> SuccessTuple:
61
+ self,
62
+ table: str,
63
+ debug: bool = False,
64
+ ) -> SuccessTuple:
65
65
  """
66
66
  Drop a temporary table and clear it from the internal table.
67
67
  """
68
- from meerschaum.utils.sql import sql_item_name, table_exists, SKIP_IF_EXISTS_FLAVORS
69
- if_exists = "IF EXISTS" if self.flavor not in SKIP_IF_EXISTS_FLAVORS else ""
68
+ from meerschaum.utils.sql import sql_item_name, table_exists, DROP_IF_EXISTS_FLAVORS
69
+ if_exists = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
70
70
  if not if_exists:
71
71
  if not table_exists(table, self, self.internal_schema, debug=debug):
72
72
  return True, "Success"
@@ -459,6 +459,11 @@ def get_create_index_queries(
459
459
  + 'if_not_exists => true, '
460
460
  + "migrate_data => true);"
461
461
  )
462
+ elif self.flavor == 'mssql':
463
+ dt_query = (
464
+ f"CREATE CLUSTERED INDEX {_datetime_index_name} "
465
+ f"ON {_pipe_name} ({_datetime_name})"
466
+ )
462
467
  else: ### mssql, sqlite, etc.
463
468
  dt_query = (
464
469
  f"CREATE INDEX {_datetime_index_name} "
@@ -563,7 +568,12 @@ def get_drop_index_queries(
563
568
  return {}
564
569
  if not pipe.exists(debug=debug):
565
570
  return {}
566
- from meerschaum.utils.sql import sql_item_name, table_exists, hypertable_queries
571
+ from meerschaum.utils.sql import (
572
+ sql_item_name,
573
+ table_exists,
574
+ hypertable_queries,
575
+ DROP_IF_EXISTS_FLAVORS,
576
+ )
567
577
  drop_queries = {}
568
578
  schema = self.get_pipe_schema(pipe)
569
579
  schema_prefix = (schema + '_') if schema else ''
@@ -580,16 +590,17 @@ def get_drop_index_queries(
580
590
  is_hypertable_query = hypertable_queries[self.flavor].format(table_name=pipe_name)
581
591
  is_hypertable = self.value(is_hypertable_query, silent=True, debug=debug) is not None
582
592
 
593
+ if_exists_str = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
583
594
  if is_hypertable:
584
595
  nuke_queries = []
585
596
  temp_table = '_' + pipe.target + '_temp_migration'
586
597
  temp_table_name = sql_item_name(temp_table, self.flavor, self.get_pipe_schema(pipe))
587
598
 
588
599
  if table_exists(temp_table, self, schema=self.get_pipe_schema(pipe), debug=debug):
589
- nuke_queries.append(f"DROP TABLE {temp_table_name}")
600
+ nuke_queries.append(f"DROP TABLE {if_exists_str} {temp_table_name}")
590
601
  nuke_queries += [
591
602
  f"SELECT * INTO {temp_table_name} FROM {pipe_name}",
592
- f"DROP TABLE {pipe_name}",
603
+ f"DROP TABLE {if_exists_str} {pipe_name}",
593
604
  f"ALTER TABLE {temp_table_name} RENAME TO {pipe_name_no_schema}",
594
605
  ]
595
606
  nuke_ix_keys = ('datetime', 'id')
@@ -811,7 +822,7 @@ def get_pipe_data(
811
822
  parse_df_datetimes(
812
823
  c,
813
824
  ignore_cols=ignore_dt_cols,
814
- chunksize = kw.get('chunksize', None),
825
+ chunksize=kw.get('chunksize', None),
815
826
  debug=debug,
816
827
  )
817
828
  for c in df
@@ -1017,7 +1028,7 @@ def get_pipe_data_query(
1017
1028
  if _dt and _dt in existing_cols:
1018
1029
  order_by += dt + ' ' + order + ','
1019
1030
  for key, quoted_col_name in quoted_indices.items():
1020
- if key == 'datetime':
1031
+ if dt == quoted_col_name:
1021
1032
  continue
1022
1033
  order_by += ' ' + quoted_col_name + ' ' + order + ','
1023
1034
  order_by = order_by[:-1]
@@ -1034,7 +1045,7 @@ def get_pipe_data_query(
1034
1045
  )
1035
1046
  else:
1036
1047
  query += f"\nLIMIT {limit}"
1037
-
1048
+
1038
1049
  if debug:
1039
1050
  to_print = (
1040
1051
  []
@@ -1315,7 +1326,7 @@ def sync_pipe(
1315
1326
  ) if dt_col else None
1316
1327
 
1317
1328
  transact_id = generate_password(3)
1318
- temp_target = '-' + transact_id + '_' + pipe.target
1329
+ temp_target = '##' + transact_id + '_' + pipe.target
1319
1330
  self._log_temporary_tables_creation(temp_target, create=(not pipe.temporary), debug=debug)
1320
1331
  temp_pipe = Pipe(
1321
1332
  pipe.connector_keys.replace(':', '_') + '_', pipe.metric_key, pipe.location_key,
@@ -1721,7 +1732,7 @@ def sync_pipe_inplace(
1721
1732
 
1722
1733
  delta_cols_types = get_table_cols_types(
1723
1734
  temp_tables['delta'],
1724
- connectable = connectable,
1735
+ connectable=connectable,
1725
1736
  flavor=self.flavor,
1726
1737
  schema=internal_schema,
1727
1738
  database=database,
@@ -1779,7 +1790,7 @@ def sync_pipe_inplace(
1779
1790
  create_joined_success, create_joined_msg = session_execute(
1780
1791
  session,
1781
1792
  create_joined_query,
1782
- debug = debug,
1793
+ debug=debug,
1783
1794
  ) if on_cols and not upsert else (True, "Success")
1784
1795
  if not create_joined_success:
1785
1796
  _ = clean_up_temp_tables()
@@ -1790,14 +1801,14 @@ def sync_pipe_inplace(
1790
1801
  + (', '.join([
1791
1802
  (
1792
1803
  "CASE\n WHEN " + sql_item_name(c + '_delta', self.flavor, None)
1793
- + " != " + get_null_replacement(typ, self.flavor)
1804
+ + " != " + get_null_replacement(typ, self.flavor)
1794
1805
  + " THEN " + sql_item_name(c + '_delta', self.flavor, None)
1795
1806
  + "\n ELSE NULL\nEND "
1796
1807
  + " AS " + sql_item_name(c, self.flavor, None)
1797
1808
  ) for c, typ in delta_cols.items()
1798
1809
  ]))
1799
1810
  + f"\nFROM {temp_table_names['joined']}\n"
1800
- + f"WHERE "
1811
+ + "WHERE "
1801
1812
  + '\nAND\n'.join([
1802
1813
  (
1803
1814
  sql_item_name(c + '_backtrack', self.flavor, None) + ' IS NULL'
@@ -1813,8 +1824,8 @@ def sync_pipe_inplace(
1813
1824
  (create_unseen_success, create_unseen_msg), create_unseen_results = session_execute(
1814
1825
  session,
1815
1826
  create_unseen_query,
1816
- with_results = True,
1817
- debug = debug
1827
+ with_results=True,
1828
+ debug=debug
1818
1829
  ) if not upsert else (True, "Success"), None
1819
1830
  if not create_unseen_success:
1820
1831
  _ = clean_up_temp_tables()
@@ -1832,7 +1843,7 @@ def sync_pipe_inplace(
1832
1843
  ) for c, typ in delta_cols.items()
1833
1844
  ]))
1834
1845
  + f"\nFROM {temp_table_names['joined']}\n"
1835
- + f"WHERE "
1846
+ + "WHERE "
1836
1847
  + '\nOR\n'.join([
1837
1848
  (
1838
1849
  sql_item_name(c + '_backtrack', self.flavor, None) + ' IS NOT NULL'
@@ -1849,8 +1860,8 @@ def sync_pipe_inplace(
1849
1860
  (create_update_success, create_update_msg), create_update_results = session_execute(
1850
1861
  session,
1851
1862
  create_update_query,
1852
- with_results = True,
1853
- debug = debug,
1863
+ with_results=True,
1864
+ debug=debug,
1854
1865
  ) if on_cols and not upsert else ((True, "Success"), [])
1855
1866
  apply_update_queries = (
1856
1867
  get_update_queries(
@@ -1858,12 +1869,12 @@ def sync_pipe_inplace(
1858
1869
  temp_tables['update'],
1859
1870
  session,
1860
1871
  on_cols,
1861
- upsert = upsert,
1862
- schema = self.get_pipe_schema(pipe),
1863
- patch_schema = internal_schema,
1864
- datetime_col = pipe.columns.get('datetime', None),
1865
- flavor = self.flavor,
1866
- debug = debug
1872
+ upsert=upsert,
1873
+ schema=self.get_pipe_schema(pipe),
1874
+ patch_schema=internal_schema,
1875
+ datetime_col=pipe.columns.get('datetime', None),
1876
+ flavor=self.flavor,
1877
+ debug=debug,
1867
1878
  )
1868
1879
  if on_cols else []
1869
1880
  )
@@ -1883,8 +1894,8 @@ def sync_pipe_inplace(
1883
1894
  (apply_unseen_success, apply_unseen_msg), apply_unseen_results = session_execute(
1884
1895
  session,
1885
1896
  apply_unseen_queries,
1886
- with_results = True,
1887
- debug = debug,
1897
+ with_results=True,
1898
+ debug=debug,
1888
1899
  ) if not upsert else (True, "Success"), None
1889
1900
  if not apply_unseen_success:
1890
1901
  _ = clean_up_temp_tables()
@@ -1894,8 +1905,8 @@ def sync_pipe_inplace(
1894
1905
  (apply_update_success, apply_update_msg), apply_update_results = session_execute(
1895
1906
  session,
1896
1907
  apply_update_queries,
1897
- with_results = True,
1898
- debug = debug,
1908
+ with_results=True,
1909
+ debug=debug,
1899
1910
  )
1900
1911
  if not apply_update_success:
1901
1912
  _ = clean_up_temp_tables()
@@ -2198,7 +2209,7 @@ def get_pipe_rowcount(
2198
2209
  else 'WHERE'
2199
2210
  )
2200
2211
  )
2201
-
2212
+
2202
2213
  result = self.value(query, debug=debug, silent=True)
2203
2214
  try:
2204
2215
  return int(result)
@@ -2207,11 +2218,11 @@ def get_pipe_rowcount(
2207
2218
 
2208
2219
 
2209
2220
  def drop_pipe(
2210
- self,
2211
- pipe: mrsm.Pipe,
2212
- debug: bool = False,
2213
- **kw
2214
- ) -> SuccessTuple:
2221
+ self,
2222
+ pipe: mrsm.Pipe,
2223
+ debug: bool = False,
2224
+ **kw
2225
+ ) -> SuccessTuple:
2215
2226
  """
2216
2227
  Drop a pipe's tables but maintain its registration.
2217
2228
 
@@ -2219,30 +2230,36 @@ def drop_pipe(
2219
2230
  ----------
2220
2231
  pipe: mrsm.Pipe
2221
2232
  The pipe to drop.
2222
-
2233
+
2234
+ Returns
2235
+ -------
2236
+ A `SuccessTuple` indicated success.
2223
2237
  """
2224
- from meerschaum.utils.sql import table_exists, sql_item_name
2238
+ from meerschaum.utils.sql import table_exists, sql_item_name, DROP_IF_EXISTS_FLAVORS
2225
2239
  success = True
2226
2240
  target = pipe.target
2227
2241
  target_name = (
2228
2242
  sql_item_name(target, self.flavor, self.get_pipe_schema(pipe))
2229
2243
  )
2230
2244
  if table_exists(target, self, debug=debug):
2231
- success = self.exec(f"DROP TABLE {target_name}", silent=True, debug=debug) is not None
2245
+ if_exists_str = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
2246
+ success = self.exec(
2247
+ f"DROP TABLE {if_exists_str} {target_name}", silent=True, debug=debug
2248
+ ) is not None
2232
2249
 
2233
2250
  msg = "Success" if success else f"Failed to drop {pipe}."
2234
2251
  return success, msg
2235
2252
 
2236
2253
 
2237
2254
  def clear_pipe(
2238
- self,
2239
- pipe: mrsm.Pipe,
2240
- begin: Union[datetime, int, None] = None,
2241
- end: Union[datetime, int, None] = None,
2242
- params: Optional[Dict[str, Any]] = None,
2243
- debug: bool = False,
2244
- **kw
2245
- ) -> SuccessTuple:
2255
+ self,
2256
+ pipe: mrsm.Pipe,
2257
+ begin: Union[datetime, int, None] = None,
2258
+ end: Union[datetime, int, None] = None,
2259
+ params: Optional[Dict[str, Any]] = None,
2260
+ debug: bool = False,
2261
+ **kw
2262
+ ) -> SuccessTuple:
2246
2263
  """
2247
2264
  Delete a pipe's data within a bounded or unbounded interval without dropping the table.
2248
2265
 
@@ -2535,7 +2552,7 @@ def get_alter_columns_queries(
2535
2552
  """
2536
2553
  if not pipe.exists(debug=debug):
2537
2554
  return []
2538
- from meerschaum.utils.sql import sql_item_name
2555
+ from meerschaum.utils.sql import sql_item_name, DROP_IF_EXISTS_FLAVORS
2539
2556
  from meerschaum.utils.dataframe import get_numeric_cols
2540
2557
  from meerschaum.utils.dtypes import are_dtypes_equal
2541
2558
  from meerschaum.utils.dtypes.sql import (
@@ -2691,7 +2708,9 @@ def get_alter_columns_queries(
2691
2708
  f"\nFROM {sql_item_name(temp_table_name, self.flavor, self.get_pipe_schema(pipe))}"
2692
2709
  )
2693
2710
 
2694
- drop_query = "DROP TABLE " + sql_item_name(
2711
+ if_exists_str = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
2712
+
2713
+ drop_query = f"DROP TABLE {if_exists_str}" + sql_item_name(
2695
2714
  temp_table_name, self.flavor, self.get_pipe_schema(pipe)
2696
2715
  )
2697
2716
  return [
@@ -2882,6 +2901,7 @@ def deduplicate_pipe(
2882
2901
  NO_CTE_FLAVORS,
2883
2902
  get_rename_table_queries,
2884
2903
  NO_SELECT_INTO_FLAVORS,
2904
+ DROP_IF_EXISTS_FLAVORS,
2885
2905
  get_create_table_query,
2886
2906
  format_cte_subquery,
2887
2907
  get_null_replacement,
@@ -3012,6 +3032,7 @@ def deduplicate_pipe(
3012
3032
  ) + f"""
3013
3033
  ORDER BY {index_list_str_ordered}
3014
3034
  """
3035
+ if_exists_str = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
3015
3036
  alter_queries = flatten_list([
3016
3037
  get_rename_table_queries(
3017
3038
  pipe.target, temp_old_table, self.flavor, schema=self.get_pipe_schema(pipe)
@@ -3020,7 +3041,7 @@ def deduplicate_pipe(
3020
3041
  dedup_table, pipe.target, self.flavor, schema=self.get_pipe_schema(pipe)
3021
3042
  ),
3022
3043
  f"""
3023
- DROP TABLE {temp_old_table_name}
3044
+ DROP TABLE {if_exists_str} {temp_old_table_name}
3024
3045
  """,
3025
3046
  ])
3026
3047
 
@@ -3030,9 +3051,9 @@ def deduplicate_pipe(
3030
3051
 
3031
3052
  results = self.exec_queries(
3032
3053
  alter_queries,
3033
- break_on_error = True,
3034
- rollback = True,
3035
- debug = debug,
3054
+ break_on_error=True,
3055
+ rollback=True,
3056
+ debug=debug,
3036
3057
  )
3037
3058
 
3038
3059
  fail_query = None
@@ -753,6 +753,7 @@ def to_sql(
753
753
  table_exists,
754
754
  json_flavors,
755
755
  truncate_item_name,
756
+ DROP_IF_EXISTS_FLAVORS,
756
757
  )
757
758
  from meerschaum.utils.dataframe import get_json_cols, get_numeric_cols, get_uuid_cols
758
759
  from meerschaum.utils.dtypes import are_dtypes_equal, quantize_decimal
@@ -827,12 +828,13 @@ def to_sql(
827
828
  'parallel': True,
828
829
  })
829
830
 
831
+ if_exists_str = "IF EXISTS" if self.flavor in DROP_IF_EXISTS_FLAVORS else ""
830
832
  if self.flavor == 'oracle':
831
833
  ### For some reason 'replace' doesn't work properly in pandas,
832
834
  ### so try dropping first.
833
835
  if if_exists == 'replace' and table_exists(name, self, schema=schema, debug=debug):
834
836
  success = self.exec(
835
- "DROP TABLE " + sql_item_name(name, 'oracle', schema)
837
+ f"DROP TABLE {if_exists_str}" + sql_item_name(name, 'oracle', schema)
836
838
  ) is not None
837
839
  if not success:
838
840
  warn(f"Unable to drop {name}")
@@ -295,7 +295,7 @@ def sync(
295
295
 
296
296
  ### CHECKPOINT: Retrieved the DataFrame.
297
297
  _checkpoint(**kw)
298
-
298
+
299
299
  ### Allow for dataframe generators or iterables.
300
300
  if df_is_chunk_generator(df):
301
301
  kw['workers'] = p.get_num_workers(kw.get('workers', None))
@@ -61,12 +61,10 @@ def add_missing_cols_to_df(
61
61
  if set(df.columns) == set(dtypes):
62
62
  return df
63
63
 
64
- import traceback
65
- from meerschaum.utils.packages import import_pandas, attempt_import
66
- from meerschaum.utils.warnings import warn
64
+ from meerschaum.utils.packages import attempt_import
67
65
  from meerschaum.utils.dtypes import to_pandas_dtype
68
66
  pandas = attempt_import('pandas')
69
-
67
+
70
68
  def build_series(dtype: str):
71
69
  return pandas.Series([], dtype=to_pandas_dtype(dtype))
72
70
 
@@ -75,7 +73,10 @@ def add_missing_cols_to_df(
75
73
  for col, typ in dtypes.items()
76
74
  if col not in df.columns
77
75
  }
78
- return df.assign(**assign_kwargs)
76
+ df_with_cols = df.assign(**assign_kwargs)
77
+ for col in assign_kwargs:
78
+ df_with_cols[col] = df_with_cols[col].fillna(pandas.NA)
79
+ return df_with_cols
79
80
 
80
81
 
81
82
  def filter_unseen_df(
@@ -152,6 +153,7 @@ def filter_unseen_df(
152
153
  is_dask = 'dask' in new_df.__module__
153
154
  if is_dask:
154
155
  pandas = attempt_import('pandas')
156
+ _ = attempt_import('partd', lazy=False)
155
157
  dd = attempt_import('dask.dataframe')
156
158
  merge = dd.merge
157
159
  NA = pandas.NA
@@ -301,21 +303,28 @@ def filter_unseen_df(
301
303
  lambda x: f'{x:f}' if isinstance(x, Decimal) else x
302
304
  )
303
305
 
306
+ old_dt_cols = [
307
+ col
308
+ for col, typ in old_df.dtypes.items()
309
+ if are_dtypes_equal(str(typ), 'datetime')
310
+ ]
311
+ for col in old_dt_cols:
312
+ old_df[col] = coerce_timezone(old_df[col])
313
+
314
+ new_dt_cols = [
315
+ col
316
+ for col, typ in old_df.dtypes.items()
317
+ if are_dtypes_equal(str(typ), 'datetime')
318
+ ]
319
+ for col in new_dt_cols:
320
+ new_df[col] = coerce_timezone(new_df[col])
321
+
304
322
  old_uuid_cols = get_uuid_cols(old_df)
305
323
  new_uuid_cols = get_uuid_cols(new_df)
306
324
  uuid_cols = set(new_uuid_cols + old_uuid_cols)
307
- for uuid_col in old_uuid_cols:
308
- old_df[uuid_col] = old_df[uuid_col].apply(
309
- lambda x: f'{x}' if isinstance(x, UUID) else x
310
- )
311
- for uuid_col in new_uuid_cols:
312
- new_df[uuid_col] = new_df[uuid_col].apply(
313
- lambda x: f'{x}' if isinstance(x, UUID) else x
314
- )
315
-
316
325
  joined_df = merge(
317
- new_df.fillna(NA),
318
- old_df.fillna(NA),
326
+ new_df.infer_objects(copy=False).fillna(NA),
327
+ old_df.infer_objects(copy=False).fillna(NA),
319
328
  how='left',
320
329
  on=None,
321
330
  indicator=True,
@@ -558,10 +567,10 @@ def get_json_cols(df: 'pd.DataFrame') -> List[str]:
558
567
  -------
559
568
  A list of columns to be encoded as JSON.
560
569
  """
561
- is_dask = 'dask' in df.__module__
570
+ is_dask = 'dask' in df.__module__ if hasattr(df, '__module__') else False
562
571
  if is_dask:
563
572
  df = get_first_valid_dask_partition(df)
564
-
573
+
565
574
  if len(df) == 0:
566
575
  return []
567
576
 
@@ -699,6 +708,7 @@ def enforce_dtypes(
699
708
  is_dtype_numeric,
700
709
  attempt_cast_to_numeric,
701
710
  attempt_cast_to_uuid,
711
+ coerce_timezone,
702
712
  )
703
713
  if safe_copy:
704
714
  df = df.copy()
@@ -1065,6 +1075,7 @@ def get_first_valid_dask_partition(ddf: 'dask.dataframe.DataFrame') -> Union['pd
1065
1075
  continue
1066
1076
  if len(pdf) > 0:
1067
1077
  return pdf
1078
+ _ = mrsm.attempt_import('partd', lazy=False)
1068
1079
  return ddf.compute()
1069
1080
 
1070
1081
 
@@ -1171,9 +1182,9 @@ def query_df(
1171
1182
  dtypes = {col: str(typ) for col, typ in df.dtypes.items()}
1172
1183
 
1173
1184
  if inplace:
1174
- df.fillna(NA, inplace=True)
1185
+ df.infer_objects(copy=False).fillna(NA, inplace=True)
1175
1186
  else:
1176
- df = df.fillna(NA)
1187
+ df = df.infer_objects(copy=False).fillna(NA)
1177
1188
 
1178
1189
  if isinstance(begin, str):
1179
1190
  begin = dateutil_parser.parse(begin)
@@ -1346,7 +1357,7 @@ def to_json(
1346
1357
  df = df.copy()
1347
1358
  for col in uuid_cols:
1348
1359
  df[col] = df[col].astype(str)
1349
- return df.fillna(pd.NA).to_json(
1360
+ return df.infer_objects(copy=False).fillna(pd.NA).to_json(
1350
1361
  date_format=date_format,
1351
1362
  date_unit=date_unit,
1352
1363
  orient=orient,
@@ -82,7 +82,10 @@ DB_TO_PD_DTYPES: Dict[str, Union[str, Dict[str, str]]] = {
82
82
  'TIMESTAMPTZ': 'datetime64[ns, UTC]',
83
83
  'DATE': 'datetime64[ns]',
84
84
  'DATETIME': 'datetime64[ns]',
85
+ 'DATETIME2': 'datetime64[ns]',
86
+ 'DATETIMEOFFSET': 'datetime64[ns, UTC]',
85
87
  'TEXT': 'string[pyarrow]',
88
+ 'VARCHAR': 'string[pyarrow]',
86
89
  'CLOB': 'string[pyarrow]',
87
90
  'BOOL': 'bool[pyarrow]',
88
91
  'BOOLEAN': 'bool[pyarrow]',
@@ -156,7 +159,7 @@ PD_TO_DB_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
156
159
  'postgresql': 'TIMESTAMP',
157
160
  'mariadb': 'DATETIME',
158
161
  'mysql': 'DATETIME',
159
- 'mssql': 'DATETIME',
162
+ 'mssql': 'DATETIME2',
160
163
  'oracle': 'DATE',
161
164
  'sqlite': 'DATETIME',
162
165
  'duckdb': 'TIMESTAMP',
@@ -169,7 +172,7 @@ PD_TO_DB_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
169
172
  'postgresql': 'TIMESTAMP',
170
173
  'mariadb': 'TIMESTAMP',
171
174
  'mysql': 'TIMESTAMP',
172
- 'mssql': 'TIMESTAMP',
175
+ 'mssql': 'DATETIMEOFFSET',
173
176
  'oracle': 'TIMESTAMP',
174
177
  'sqlite': 'TIMESTAMP',
175
178
  'duckdb': 'TIMESTAMP',
@@ -245,13 +248,13 @@ PD_TO_DB_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
245
248
  'uuid': {
246
249
  'timescaledb': 'UUID',
247
250
  'postgresql': 'UUID',
248
- 'mariadb': 'CHAR(32)',
249
- 'mysql': 'CHAR(32)',
251
+ 'mariadb': 'CHAR(36)',
252
+ 'mysql': 'CHAR(36)',
250
253
  'mssql': 'UNIQUEIDENTIFIER',
251
254
  ### I know this is too much space, but erring on the side of caution.
252
255
  'oracle': 'NVARCHAR(2000)',
253
256
  'sqlite': 'TEXT',
254
- 'duckdb': 'UUID',
257
+ 'duckdb': 'VARCHAR',
255
258
  'citus': 'UUID',
256
259
  'cockroachdb': 'UUID',
257
260
  'default': 'TEXT',
@@ -289,7 +292,7 @@ PD_TO_SQLALCHEMY_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
289
292
  'postgresql': 'DateTime',
290
293
  'mariadb': 'DateTime',
291
294
  'mysql': 'DateTime',
292
- 'mssql': 'DateTime',
295
+ 'mssql': 'sqlalchemy.dialects.mssql.DATETIME2',
293
296
  'oracle': 'DateTime',
294
297
  'sqlite': 'DateTime',
295
298
  'duckdb': 'DateTime',
@@ -302,7 +305,7 @@ PD_TO_SQLALCHEMY_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
302
305
  'postgresql': 'DateTime',
303
306
  'mariadb': 'DateTime',
304
307
  'mysql': 'DateTime',
305
- 'mssql': 'DateTime',
308
+ 'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET',
306
309
  'oracle': 'DateTime',
307
310
  'sqlite': 'DateTime',
308
311
  'duckdb': 'DateTime',
@@ -350,16 +353,16 @@ PD_TO_SQLALCHEMY_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
350
353
  'default': 'UnicodeText',
351
354
  },
352
355
  'json': {
353
- 'timescaledb': 'JSONB',
354
- 'postgresql': 'JSONB',
356
+ 'timescaledb': 'sqlalchemy.dialects.postgresql.JSONB',
357
+ 'postgresql': 'sqlalchemy.dialects.postgresql.JSONB',
355
358
  'mariadb': 'UnicodeText',
356
359
  'mysql': 'UnicodeText',
357
360
  'mssql': 'UnicodeText',
358
361
  'oracle': 'UnicodeText',
359
362
  'sqlite': 'UnicodeText',
360
363
  'duckdb': 'TEXT',
361
- 'citus': 'JSONB',
362
- 'cockroachdb': 'JSONB',
364
+ 'citus': 'sqlalchemy.dialects.postgresql.JSONB',
365
+ 'cockroachdb': 'sqlalchemy.dialects.postgresql.JSONB',
363
366
  'default': 'UnicodeText',
364
367
  },
365
368
  'numeric': {
@@ -378,12 +381,12 @@ PD_TO_SQLALCHEMY_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
378
381
  'uuid': {
379
382
  'timescaledb': 'Uuid',
380
383
  'postgresql': 'Uuid',
381
- 'mariadb': 'Uuid',
382
- 'mysql': 'Uuid',
384
+ 'mariadb': 'sqlalchemy.dialects.mysql.CHAR(36)',
385
+ 'mysql': 'sqlalchemy.dialects.mysql.CHAR(36)',
383
386
  'mssql': 'Uuid',
384
387
  'oracle': 'UnicodeText',
385
- 'sqlite': 'Uuid',
386
- 'duckdb': 'Uuid',
388
+ 'sqlite': 'UnicodeText',
389
+ 'duckdb': 'UnicodeText',
387
390
  'citus': 'Uuid',
388
391
  'cockroachdb': 'Uuid',
389
392
  'default': 'Uuid',
@@ -453,6 +456,7 @@ def get_db_type_from_pd_type(
453
456
  The database data type for the incoming Pandas data type.
454
457
  If nothing can be found, a warning will be thrown and 'TEXT' will be returned.
455
458
  """
459
+ import ast
456
460
  from meerschaum.utils.warnings import warn
457
461
  from meerschaum.utils.packages import attempt_import
458
462
  from meerschaum.utils.dtypes import are_dtypes_equal
@@ -505,9 +509,19 @@ def get_db_type_from_pd_type(
505
509
  db_type = flavor_types.get(flavor, default_flavor_type)
506
510
  if not as_sqlalchemy:
507
511
  return db_type
508
- if db_type == 'JSONB':
509
- sqlalchemy_dialects_postgresql = attempt_import('sqlalchemy.dialects.postgresql')
510
- return sqlalchemy_dialects_postgresql.JSONB
512
+
513
+ if db_type.startswith('sqlalchemy.dialects'):
514
+ dialect, typ_class_name = db_type.replace('sqlalchemy.dialects.', '').split('.', maxsplit=2)
515
+ arg = None
516
+ if '(' in typ_class_name:
517
+ typ_class_name, arg_str = typ_class_name.split('(', maxsplit=1)
518
+ arg = ast.literal_eval(arg_str.rstrip(')'))
519
+ sqlalchemy_dialects_flavor_module = attempt_import(f'sqlalchemy.dialects.{dialect}')
520
+ cls = getattr(sqlalchemy_dialects_flavor_module, typ_class_name)
521
+ if arg is None:
522
+ return cls
523
+ return cls(arg)
524
+
511
525
  if 'numeric' in db_type.lower():
512
526
  numeric_type_str = PD_TO_DB_DTYPES_FLAVORS['numeric'].get(flavor, 'NUMERIC')
513
527
  if flavor not in NUMERIC_PRECISION_FLAVORS:
@@ -128,7 +128,8 @@ packages['sql'] = {
128
128
  'numpy' : 'numpy>=1.18.5',
129
129
  'pandas' : 'pandas[parquet]>=2.0.1',
130
130
  'pyarrow' : 'pyarrow>=16.1.0',
131
- 'dask' : 'dask[dataframe]>=2024.5.1',
131
+ 'dask' : 'dask[complete]>=2024.5.1',
132
+ 'partd' : 'partd>=1.4.2',
132
133
  'pytz' : 'pytz',
133
134
  'joblib' : 'joblib>=0.17.0',
134
135
  'sqlalchemy' : 'SQLAlchemy>=2.0.5',
meerschaum/utils/sql.py CHANGED
@@ -38,6 +38,9 @@ version_queries = {
38
38
  'oracle': "SELECT version from PRODUCT_COMPONENT_VERSION WHERE rownum = 1",
39
39
  }
40
40
  SKIP_IF_EXISTS_FLAVORS = {'mssql', 'oracle'}
41
+ DROP_IF_EXISTS_FLAVORS = {
42
+ 'timescaledb', 'postgresql', 'citus', 'mssql', 'mysql', 'mariadb', 'sqlite',
43
+ }
41
44
  COALESCE_UNIQUE_INDEX_FLAVORS = {'timescaledb', 'postgresql', 'citus'}
42
45
  update_queries = {
43
46
  'default': """
@@ -1284,11 +1287,11 @@ def get_db_version(conn: 'SQLConnector', debug: bool = False) -> Union[str, None
1284
1287
 
1285
1288
 
1286
1289
  def get_rename_table_queries(
1287
- old_table: str,
1288
- new_table: str,
1289
- flavor: str,
1290
- schema: Optional[str] = None,
1291
- ) -> List[str]:
1290
+ old_table: str,
1291
+ new_table: str,
1292
+ flavor: str,
1293
+ schema: Optional[str] = None,
1294
+ ) -> List[str]:
1292
1295
  """
1293
1296
  Return queries to alter a table's name.
1294
1297
 
@@ -1317,12 +1320,13 @@ def get_rename_table_queries(
1317
1320
  if flavor == 'mssql':
1318
1321
  return [f"EXEC sp_rename '{old_table}', '{new_table}'"]
1319
1322
 
1323
+ if_exists_str = "IF EXISTS" if flavor in DROP_IF_EXISTS_FLAVORS else ""
1320
1324
  if flavor == 'duckdb':
1321
1325
  return [
1322
1326
  get_create_table_query(f"SELECT * FROM {old_table_name}", tmp_table, 'duckdb', schema),
1323
1327
  get_create_table_query(f"SELECT * FROM {tmp_table_name}", new_table, 'duckdb', schema),
1324
- f"DROP TABLE {tmp_table_name}",
1325
- f"DROP TABLE {old_table_name}",
1328
+ f"DROP TABLE {if_exists_str} {tmp_table_name}",
1329
+ f"DROP TABLE {if_exists_str} {old_table_name}",
1326
1330
  ]
1327
1331
 
1328
1332
  return [f"ALTER TABLE {old_table_name} RENAME TO {new_table_name}"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.4.7
3
+ Version: 2.4.8
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -44,7 +44,8 @@ Requires-Dist: valkey >=6.0.0 ; extra == 'api'
44
44
  Requires-Dist: numpy >=1.18.5 ; extra == 'api'
45
45
  Requires-Dist: pandas[parquet] >=2.0.1 ; extra == 'api'
46
46
  Requires-Dist: pyarrow >=16.1.0 ; extra == 'api'
47
- Requires-Dist: dask[dataframe] >=2024.5.1 ; extra == 'api'
47
+ Requires-Dist: dask[complete] >=2024.5.1 ; extra == 'api'
48
+ Requires-Dist: partd >=1.4.2 ; extra == 'api'
48
49
  Requires-Dist: pytz ; extra == 'api'
49
50
  Requires-Dist: joblib >=0.17.0 ; extra == 'api'
50
51
  Requires-Dist: SQLAlchemy >=2.0.5 ; extra == 'api'
@@ -237,7 +238,8 @@ Requires-Dist: pycparser >=2.21.0 ; extra == 'full'
237
238
  Requires-Dist: numpy >=1.18.5 ; extra == 'full'
238
239
  Requires-Dist: pandas[parquet] >=2.0.1 ; extra == 'full'
239
240
  Requires-Dist: pyarrow >=16.1.0 ; extra == 'full'
240
- Requires-Dist: dask[dataframe] >=2024.5.1 ; extra == 'full'
241
+ Requires-Dist: dask[complete] >=2024.5.1 ; extra == 'full'
242
+ Requires-Dist: partd >=1.4.2 ; extra == 'full'
241
243
  Requires-Dist: pytz ; extra == 'full'
242
244
  Requires-Dist: joblib >=0.17.0 ; extra == 'full'
243
245
  Requires-Dist: SQLAlchemy >=2.0.5 ; extra == 'full'
@@ -277,7 +279,8 @@ Provides-Extra: sql
277
279
  Requires-Dist: numpy >=1.18.5 ; extra == 'sql'
278
280
  Requires-Dist: pandas[parquet] >=2.0.1 ; extra == 'sql'
279
281
  Requires-Dist: pyarrow >=16.1.0 ; extra == 'sql'
280
- Requires-Dist: dask[dataframe] >=2024.5.1 ; extra == 'sql'
282
+ Requires-Dist: dask[complete] >=2024.5.1 ; extra == 'sql'
283
+ Requires-Dist: partd >=1.4.2 ; extra == 'sql'
281
284
  Requires-Dist: pytz ; extra == 'sql'
282
285
  Requires-Dist: joblib >=0.17.0 ; extra == 'sql'
283
286
  Requires-Dist: SQLAlchemy >=2.0.5 ; extra == 'sql'
@@ -42,7 +42,7 @@ meerschaum/actions/restart.py,sha256=6ffp3-X9eTEgunVSdD41HnOwqp71yjuSAmXJ5j39ONI
42
42
  meerschaum/actions/setup.py,sha256=KkAGWcgwzl_L6A19fTmTX1KtBjW2FwD8QenLjPy0mQQ,3205
43
43
  meerschaum/actions/sh.py,sha256=fLfTJaacKu4sjLTRqEzzYlT2WbbdZBEczsKb6F-qAek,2026
44
44
  meerschaum/actions/show.py,sha256=uNXOfh1Z44i6YpmAcE_uM_6s9r3lNh8CnstpvS6tTjE,28179
45
- meerschaum/actions/sql.py,sha256=g64Qr1uDlyqXmOOOY4-2pvIGQ_V5VNk1_fUC5wO7h9M,4262
45
+ meerschaum/actions/sql.py,sha256=8BSvlnccfEqLrscLq67Toa5D4FJ7I598IdxEe_yzmV8,4263
46
46
  meerschaum/actions/stack.py,sha256=ZwrCTGJ0x3jjZkRieWcvqasQHYCqNtB1HYvTX-r3Z3g,5996
47
47
  meerschaum/actions/start.py,sha256=7B6zLHh-DNWnJta1h_RV9ccGJ4EnbEDu3k-GdwAsU8M,19208
48
48
  meerschaum/actions/stop.py,sha256=5fdUw70YN-yuUWrC-NhA88cxr9FZ5NbssbQ8xXO8nFU,4632
@@ -123,7 +123,7 @@ meerschaum/api/routes/_index.py,sha256=QI6CBo6pI2Zi0a6fJHDjZfiLa9f4okb0BGe3A_JD0
123
123
  meerschaum/api/routes/_jobs.py,sha256=vO6CJYUme7bHIOy9gAv6VyEM1M8EKdJmtfw2nSnZ24Q,11639
124
124
  meerschaum/api/routes/_login.py,sha256=ti2onNOemOGLHLoPubAQOYtD7eq7FA1jOlbOSVSjXVo,2466
125
125
  meerschaum/api/routes/_misc.py,sha256=05--9ZVFeaCgZrHER2kA3SYdK4TyfkEXOCjLvPbum-w,2469
126
- meerschaum/api/routes/_pipes.py,sha256=1l5BB9MqGfYI4UtWHm8T1nGy3JKEtZrFLpsQOgqIw2E,21509
126
+ meerschaum/api/routes/_pipes.py,sha256=g88AU_NUM6tlX3bFl4EOGiQWZYqvDxDFlaLIYbYn1c4,21397
127
127
  meerschaum/api/routes/_plugins.py,sha256=vR6-uTJraY1YEJMuRvds1-xFLB2mexxnp2dJwN_0rVo,6216
128
128
  meerschaum/api/routes/_users.py,sha256=SfAkZFKrKnGjpzj8SFIKzPemzQJOH3oB72h19EaUvcQ,7204
129
129
  meerschaum/api/routes/_version.py,sha256=2t-nw_9IxCVZCNEar0LOwmut2zsClRVHjiOOUx16cu0,825
@@ -142,7 +142,7 @@ meerschaum/config/_preprocess.py,sha256=-AEA8m_--KivZwTQ1sWN6LTn5sio_fUr2XZ51BO6
142
142
  meerschaum/config/_read_config.py,sha256=oxnLjuhy6JBBld886FkBX07wUdkpzEzTItYMUa9qw1Q,14688
143
143
  meerschaum/config/_shell.py,sha256=46_m49Txc5q1rGfCgO49ca48BODx45DQJi8D0zz1R18,4245
144
144
  meerschaum/config/_sync.py,sha256=jHcWRkxd82_BgX8Xo8agsWvf7BSbv3qHLWmYl6ehp_0,4242
145
- meerschaum/config/_version.py,sha256=I_aoflRLDtNWUKaI2yojmN_vGeUIecat83x1jZQwx3E,71
145
+ meerschaum/config/_version.py,sha256=1oWpjLUymKmrkEXNSCmeb0HaJEmpVYs4n7VzxwE3Z6M,71
146
146
  meerschaum/config/paths.py,sha256=JjibeGN3YAdSNceRwsd42aNmeUrIgM6ndzC8qZAmNI0,621
147
147
  meerschaum/config/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
148
  meerschaum/config/stack/__init__.py,sha256=gGVxXgNnGb9u25iF__IiNPlZt1BLUVmHmFJ0jvnJg3Q,10548
@@ -162,22 +162,22 @@ meerschaum/connectors/api/_fetch.py,sha256=Khq9AFr1nk8Dsmcedb77aWhAuHw0JGgVeahDG
162
162
  meerschaum/connectors/api/_jobs.py,sha256=N5lpHFGG10jlVgaJeWAOTuLBQw3AdgjXsEPpp1YwZQE,11270
163
163
  meerschaum/connectors/api/_login.py,sha256=5GsD-B214vr5EYfM3XrTUs1sTFApxZA-9dNxq8oNSyg,2050
164
164
  meerschaum/connectors/api/_misc.py,sha256=OZRZBYOokKIEjmQaR8jUYgu6ZRn9VzXBChzR8CfDv_w,1092
165
- meerschaum/connectors/api/_pipes.py,sha256=8Y_UaBCW_fPjaLrz73M12rjM6i6D_ZiEHK-vauhUifs,20540
165
+ meerschaum/connectors/api/_pipes.py,sha256=njBUzhX88EzZ0YJNBNepnTGcv1huXzAnnT0EwaABaBA,20549
166
166
  meerschaum/connectors/api/_plugins.py,sha256=z04tPjfZZWwa7T60mogZH3X3wDmeLdnoN5Oh8m_YsU8,5217
167
167
  meerschaum/connectors/api/_request.py,sha256=Wc4Y70t0VxEj3_ch5fAeeoSAeFMuRnyNLOV-aUFInjY,6996
168
168
  meerschaum/connectors/api/_uri.py,sha256=HWxqGx4R1cHZ3ywy9Ro9ePbFxxusw4RLaC3hpGt9Z6I,1469
169
169
  meerschaum/connectors/api/_users.py,sha256=kzb7ENgXwQ19OJYKOuuWzx2rwVuUZCly9dTnyvVuT2Q,5275
170
170
  meerschaum/connectors/plugin/PluginConnector.py,sha256=aQ1QaB7MordCFimZqoGLb0R12PfDUN_nWks2J5mzeAs,2084
171
171
  meerschaum/connectors/plugin/__init__.py,sha256=pwF7TGY4WNz2_HaVdmK4rPQ9ZwTOEuPHgzOqsGcoXJw,198
172
- meerschaum/connectors/sql/_SQLConnector.py,sha256=Q1M6byhApNxQcyDJoC9tmYk1KXF431zyblf7KKa-A08,11817
172
+ meerschaum/connectors/sql/_SQLConnector.py,sha256=QlC2Af7AM7bIlPHjUO57mTyzot3zU7o83jegADMxnBE,11829
173
173
  meerschaum/connectors/sql/__init__.py,sha256=3cqYiDkVasn7zWdtOTAZbT4bo95AuvGOmDD2TkaAxtw,205
174
174
  meerschaum/connectors/sql/_cli.py,sha256=1SgnWeMIAihoxp4FzbNrcq1npXf0dSOQnCntpU9hUXA,4405
175
175
  meerschaum/connectors/sql/_create_engine.py,sha256=pZPjy-ne8DtVfu-wqMJopIGkgm8vul-y3E9d4tUyTgM,10215
176
176
  meerschaum/connectors/sql/_fetch.py,sha256=NYYWDoEd-aGIS337KwH-D9_3KVWVCZOHAspGLfdEuUE,13086
177
- meerschaum/connectors/sql/_instance.py,sha256=r_S96vzSuKnBVGROSKQAPl-DnFnOOEPUkz1KFDFPHFQ,6509
178
- meerschaum/connectors/sql/_pipes.py,sha256=3pAIKh4W0w74iON9N6hoiEgKSc8y2nGhiWlUraa-4TU,99874
177
+ meerschaum/connectors/sql/_instance.py,sha256=zXPZnEqvOAeOUPMeh6CcfkB1pOjjwJxdUOwXccRbuwk,6465
178
+ meerschaum/connectors/sql/_pipes.py,sha256=m82E9GdBxdfcYCtFWECqmC3HjaZ7Y5ukhQ9LBlEymc4,100605
179
179
  meerschaum/connectors/sql/_plugins.py,sha256=wbxcNxqTtjfDsxPvdVGTllasYf6NHHzODaQ72hEUSBQ,8135
180
- meerschaum/connectors/sql/_sql.py,sha256=k3VkzqWCtM-DGnspyt1e9SNr6RnP_v7qSbn6bF5AUm0,36252
180
+ meerschaum/connectors/sql/_sql.py,sha256=zEh6fbOLJhfLOF-4x9OTQ5Fi3NMSVES3oixmnGYcNG8,36381
181
181
  meerschaum/connectors/sql/_uri.py,sha256=0BrhQtqQdzg9mR04gWBZINs_BbPFtSlTECXT_TCUwik,3460
182
182
  meerschaum/connectors/sql/_users.py,sha256=FJjYeJGhr-TDHziNc6p_5mupGRtGjezKPIYgHFEVSnY,9956
183
183
  meerschaum/connectors/sql/tools.py,sha256=jz8huOaRCwGlYdtGfAqAh7SoK8uydYBrasKQba9FT38,187
@@ -204,7 +204,7 @@ meerschaum/core/Pipe/_edit.py,sha256=HrKWe9vhqKaNOjOcJzW5BNbaUBPIbgNAhJEK8OMsy7c
204
204
  meerschaum/core/Pipe/_fetch.py,sha256=LtYqZSN2kwc5Tl2gQ5kSmGN7Ombv86k1zDNTP3SUF1k,5417
205
205
  meerschaum/core/Pipe/_register.py,sha256=Sd5xaAW8H7uLTIoommcKb-6kHPRuHJLWNSbPnt2UbvA,2240
206
206
  meerschaum/core/Pipe/_show.py,sha256=nG50y8eBT9TVuKkRgAKtNDNIxysJvMNxfu__lkL1F9k,1352
207
- meerschaum/core/Pipe/_sync.py,sha256=MkiFz6ULfT1qHQl4xbOEx4Bi8EYnMDgUZyBnjjwNG1M,33050
207
+ meerschaum/core/Pipe/_sync.py,sha256=rEP6Ijs3PG_cTtffqijqxabyyhZFqJicQCNj5Ea3NHw,33042
208
208
  meerschaum/core/Pipe/_verify.py,sha256=sPtY1DgUJ2E5FJp0hJ_IzrQQe1DmfAqJFWS24e2wr3w,14202
209
209
  meerschaum/core/Plugin/__init__.py,sha256=UXg64EvJPgI1PCxkY_KM02-ZmBm4FZpLPIQR_uSJJDc,137
210
210
  meerschaum/core/User/_User.py,sha256=JZ9Y1tsjZe-cgD24m9YfZ6ZwSOKn_sHc4rbQ7KblBz8,6592
@@ -218,7 +218,7 @@ meerschaum/plugins/__init__.py,sha256=trMQ53qgP7ikJhhV_uXzqJw6X1NDz2rPOGXFk40bb1
218
218
  meerschaum/plugins/bootstrap.py,sha256=qg9MQ1YAU8ShwGqWDl38WjiXLIxDPl95pSIGDLN9rOw,11423
219
219
  meerschaum/utils/__init__.py,sha256=QrK1K9hIbPCRCM5k2nZGFqGnrqhA0Eh-iSmCU7FG6Cs,612
220
220
  meerschaum/utils/_get_pipes.py,sha256=tu4xKPoDn79Dz2kWM13cXTP4DSCkn-3G9M8KiLftopw,11073
221
- meerschaum/utils/dataframe.py,sha256=fRQB-Y1XNAcjP0YsAIzV9kcYR1bzIVZJTyp3bV3B9NI,41502
221
+ meerschaum/utils/dataframe.py,sha256=G5rD3998mYAVeTlIat7gTPZKvhlpHSMvLo9EHTXocgM,41946
222
222
  meerschaum/utils/debug.py,sha256=GyIzJmunkoPnOcZNYVQdT4Sgd-aOb5MI2VbIgATOjIQ,3695
223
223
  meerschaum/utils/interactive.py,sha256=t-6jWozXSqL7lYGDHuwiOjTgr-UKhdcg61q_eR5mikI,3196
224
224
  meerschaum/utils/misc.py,sha256=ws-8v7gvD2uwmdIDDSRSUVL10XV7YZGUCEFBllC-xbY,46365
@@ -227,7 +227,7 @@ meerschaum/utils/pool.py,sha256=vkE42af4fjrTEJTxf6Ek3xGucm1MtEkpsSEiaVzNKHs,2655
227
227
  meerschaum/utils/process.py,sha256=o7UtTQX87YGkg2dItPhlvN7gNQPkElXTYSzKf5Ro8Uc,7474
228
228
  meerschaum/utils/prompt.py,sha256=0asF_ndumQIN7p5kEOzK-ldsdE4m8FFapcT3-4wgPi8,19010
229
229
  meerschaum/utils/schedule.py,sha256=6I2TFGa1aPRU9wTdd3YFrJq-DCPpnl-sTWeFEnrINYA,10886
230
- meerschaum/utils/sql.py,sha256=eTo-t6k5fI6FCNJggQEU6OiB_o30raYp6B3j1VH7Utw,47795
230
+ meerschaum/utils/sql.py,sha256=vZx6HyvaFKrvWDMZYSzHZaSQWMMWQOUmK-LLW5Kx7Lg,47993
231
231
  meerschaum/utils/threading.py,sha256=3N8JXPAnwqJiSjuQcbbJg3Rv9-CCUMJpeQRfKFR7MaA,2489
232
232
  meerschaum/utils/typing.py,sha256=U3MC347sh1umpa3Xr1k71eADyDmk4LB6TnVCpq8dVzI,2830
233
233
  meerschaum/utils/warnings.py,sha256=IDiwYspsfjIi1gtk3V9cSo9vNLckB9bCsHhRClpPJTc,6639
@@ -239,22 +239,22 @@ meerschaum/utils/daemon/StdinFile.py,sha256=J6tyUReM8NEp3bBQAxMfe8mjJG5mWi6CzHN4
239
239
  meerschaum/utils/daemon/__init__.py,sha256=o9jWb4lRTIyny4EPt7fPXFgV_vIf1mUofsTwoE1ZecA,8751
240
240
  meerschaum/utils/daemon/_names.py,sha256=d2ZwTxBoTAqXZkCfZ5LuX2XrkQmLNUq1OTlUqfoH5dA,4515
241
241
  meerschaum/utils/dtypes/__init__.py,sha256=LawV4XrCLZRhyGquUen3i0HvK2IRHG-Ud4MYi3L4phA,7391
242
- meerschaum/utils/dtypes/sql.py,sha256=wk_r-EEjZca1CoaesxetXSIe5sIjtSwwoJmITy7AJXY,15466
242
+ meerschaum/utils/dtypes/sql.py,sha256=K0pginy3U5UvgtM9af-HRoiqdvFlwiAmKNQBPGChIUA,16267
243
243
  meerschaum/utils/formatting/__init__.py,sha256=GLx3fvTQi7EnC9fo31WggpMRpmR7yTWIuZmHdZgqvuM,15370
244
244
  meerschaum/utils/formatting/_jobs.py,sha256=izsqPJhTtUkXUUtWnbXtReYsUYwulXtci3pBj72Ne64,6637
245
245
  meerschaum/utils/formatting/_pipes.py,sha256=wy0iWJFsFl3X2VloaiA_gp9Yx9w6tD3FQZvAQAqef4A,19492
246
246
  meerschaum/utils/formatting/_pprint.py,sha256=tgrT3FyGyu5CWJYysqK3kX1xdZYorlbOk9fcU_vt9Qg,3096
247
247
  meerschaum/utils/formatting/_shell.py,sha256=OMFh3cSZNr83z8m265irkS_JtEWHwjkEY2ybnMIOllY,3774
248
248
  meerschaum/utils/packages/__init__.py,sha256=m3HLTkKJxXco1g-h75q2l5skBwKXWaJtNmfQOsijchI,63965
249
- meerschaum/utils/packages/_packages.py,sha256=kwXoGePzmaNVRB5YQP6JJOWQqnPiUpM-TPtb38DrPXg,8265
249
+ meerschaum/utils/packages/_packages.py,sha256=8Ox9fiQTVmmKAmlxZBV-7Wtq_jhPnnf3AMXvktGE-KY,8319
250
250
  meerschaum/utils/packages/lazy_loader.py,sha256=VHnph3VozH29R4JnSSBfwtA5WKZYZQFT_GeQSShCnuc,2540
251
251
  meerschaum/utils/venv/_Venv.py,sha256=sBnlmxHdAh2bx8btfVoD79-H9-cYsv5lP02IIXkyECs,3553
252
252
  meerschaum/utils/venv/__init__.py,sha256=G3KXL4ByWNqVxBRLs_RaJbO3h3tOKXkazkAYuoUW568,24420
253
- meerschaum-2.4.7.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
254
- meerschaum-2.4.7.dist-info/METADATA,sha256=Qy8mE0KQp-XlyINH997P2nEK9d3s96SXvi4srA96mqw,24683
255
- meerschaum-2.4.7.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
256
- meerschaum-2.4.7.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
257
- meerschaum-2.4.7.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
258
- meerschaum-2.4.7.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
259
- meerschaum-2.4.7.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
260
- meerschaum-2.4.7.dist-info/RECORD,,
253
+ meerschaum-2.4.8.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
254
+ meerschaum-2.4.8.dist-info/METADATA,sha256=9y7GjQHhEDP9mdJWBFuIfZwZ7vtHV0N4Wk0MqF6xuDg,24819
255
+ meerschaum-2.4.8.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
256
+ meerschaum-2.4.8.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
257
+ meerschaum-2.4.8.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
258
+ meerschaum-2.4.8.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
259
+ meerschaum-2.4.8.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
260
+ meerschaum-2.4.8.dist-info/RECORD,,