quasardb 3.14.2.dev1__cp311-cp311-macosx_11_0_arm64.whl → 3.14.2.dev3__cp311-cp311-macosx_11_0_arm64.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.

Potentially problematic release.


This version of quasardb might be problematic. Click here for more details.

Files changed (58) hide show
  1. quasardb/CMakeFiles/CMakeDirectoryInformation.cmake +1 -1
  2. quasardb/CMakeLists.txt +19 -12
  3. quasardb/Makefile +10 -10
  4. quasardb/__init__.py +33 -4
  5. quasardb/cluster.cpp +14 -1
  6. quasardb/cluster.hpp +114 -72
  7. quasardb/concepts.hpp +56 -12
  8. quasardb/continuous.cpp +84 -34
  9. quasardb/continuous.hpp +10 -7
  10. quasardb/convert/array.hpp +23 -6
  11. quasardb/convert/value.hpp +78 -7
  12. quasardb/date/CMakeFiles/CMakeDirectoryInformation.cmake +1 -1
  13. quasardb/date/CMakeFiles/Export/a52b05f964b070ee926bcad51d3288af/dateTargets.cmake +12 -12
  14. quasardb/date/Makefile +10 -10
  15. quasardb/date/dateConfigVersion.cmake +9 -2
  16. quasardb/date/dateTargets.cmake +3 -7
  17. quasardb/detail/invoke.hpp +0 -0
  18. quasardb/detail/retry.cpp +30 -0
  19. quasardb/detail/retry.hpp +147 -0
  20. quasardb/detail/sleep.hpp +53 -0
  21. quasardb/{writer.cpp → detail/writer.cpp} +68 -162
  22. quasardb/detail/writer.hpp +550 -0
  23. quasardb/error.hpp +76 -1
  24. quasardb/libqdb_api.dylib +0 -0
  25. quasardb/masked_array.hpp +9 -2
  26. quasardb/module.cpp +20 -4
  27. quasardb/node.hpp +17 -8
  28. quasardb/numpy/__init__.py +58 -10
  29. quasardb/object_tracker.hpp +2 -3
  30. quasardb/options.hpp +32 -3
  31. quasardb/pandas/__init__.py +59 -102
  32. quasardb/properties.cpp +41 -0
  33. quasardb/properties.hpp +85 -0
  34. quasardb/pybind11/CMakeFiles/CMakeDirectoryInformation.cmake +1 -1
  35. quasardb/pybind11/Makefile +10 -10
  36. quasardb/quasardb.cpython-311-darwin.so +0 -0
  37. quasardb/range-v3/CMakeFiles/CMakeDirectoryInformation.cmake +1 -1
  38. quasardb/range-v3/CMakeFiles/Export/d94ef200eca10a819b5858b33e808f5b/range-v3-targets.cmake +12 -12
  39. quasardb/range-v3/CMakeFiles/range.v3.headers.dir/DependInfo.cmake +6 -2
  40. quasardb/range-v3/CMakeFiles/range.v3.headers.dir/build.make +2 -2
  41. quasardb/range-v3/Makefile +10 -10
  42. quasardb/range-v3/cmake_install.cmake +12 -0
  43. quasardb/range-v3/range-v3-config-version.cmake +9 -2
  44. quasardb/range-v3/range-v3-config.cmake +3 -7
  45. quasardb/reader.cpp +282 -0
  46. quasardb/reader.hpp +256 -0
  47. quasardb/table.cpp +4 -36
  48. quasardb/table.hpp +69 -28
  49. quasardb/traits.hpp +23 -0
  50. quasardb/writer.hpp +245 -287
  51. {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/METADATA +7 -7
  52. {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/RECORD +55 -49
  53. {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/WHEEL +1 -1
  54. quasardb/reader/ts_row.hpp +0 -281
  55. quasardb/reader/ts_value.hpp +0 -245
  56. quasardb/table_reader.hpp +0 -220
  57. {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/LICENSE.md +0 -0
  58. {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/top_level.txt +0 -0
quasardb/module.cpp CHANGED
@@ -2,12 +2,26 @@
2
2
  #include "cluster.hpp"
3
3
  #include "metrics.hpp"
4
4
  #include "node.hpp"
5
+ #include "reader.hpp"
5
6
  #include "writer.hpp"
6
7
  #include <functional>
7
8
  #include <list>
8
9
 
10
+ #ifndef QDB_TESTS_ENABLED
11
+ # include "detail/sleep.hpp"
12
+ # include "detail/writer.hpp"
13
+ using push_strategy_t = qdb::detail::default_writer_push_strategy;
14
+ using sleep_strategy_t = qdb::detail::default_sleep_strategy<>;
15
+ #else
16
+ # include "../tests/detail/sleep.hpp"
17
+ # include "../tests/detail/writer.hpp"
18
+ using push_strategy_t = qdb::detail::mock_failure_writer_push_strategy;
19
+ using sleep_strategy_t = qdb::detail::mock_sleep_strategy<>;
20
+ #endif
21
+
9
22
  namespace qdb
10
23
  {
24
+
11
25
  /**
12
26
  * This approach is inspired from pybind11, where we keep a global static variable
13
27
  * of initializers for components/modules that which to register with our global
@@ -45,6 +59,7 @@ PYBIND11_MODULE(quasardb, m)
45
59
  qdb::register_cluster(m);
46
60
  qdb::register_node(m);
47
61
  qdb::register_options(m);
62
+ qdb::register_properties(m);
48
63
  qdb::register_perf(m);
49
64
  qdb::register_entry(m);
50
65
  qdb::register_double(m);
@@ -60,14 +75,15 @@ PYBIND11_MODULE(quasardb, m)
60
75
  qdb::register_table(m);
61
76
  qdb::register_batch_column(m);
62
77
  qdb::register_batch_inserter(m);
63
- qdb::register_table_reader(m);
64
78
  qdb::register_masked_array(m);
65
- qdb::register_writer(m);
79
+ qdb::register_reader(m);
80
+
81
+ qdb::register_writer<push_strategy_t, sleep_strategy_t>(m);
82
+
66
83
  qdb::register_metrics(m);
67
84
 
68
85
  qdb::detail::register_ts_column(m);
69
- qdb::reader::register_ts_value(m);
70
- qdb::reader::register_ts_row(m);
86
+ qdb::detail::register_retry_options(m);
71
87
 
72
88
  for (const auto & initializer : qdb::initializers())
73
89
  {
quasardb/node.hpp CHANGED
@@ -51,13 +51,19 @@ public:
51
51
  const std::string & user_private_key = {},
52
52
  const std::string & cluster_public_key = {},
53
53
  const std::string & user_security_file = {},
54
- const std::string & cluster_public_key_file = {})
54
+ const std::string & cluster_public_key_file = {},
55
+ bool enable_encryption = false)
55
56
  : _uri{node_uri}
56
57
  , _handle{make_handle_ptr()}
57
58
  , _direct_handle{make_direct_handle_ptr()}
58
59
  {
59
60
  qdb::options{_handle}.apply_credentials(user_name, user_private_key, cluster_public_key,
60
61
  user_security_file, cluster_public_key_file);
62
+
63
+ if (enable_encryption) {
64
+ qdb::options{_handle}.set_encryption(qdb_crypt_aes_gcm_256);
65
+ }
66
+
61
67
  _direct_handle->connect(_handle, node_uri);
62
68
  }
63
69
 
@@ -108,13 +114,16 @@ static inline void register_node(Module & m)
108
114
 
109
115
  py::class_<qdb::node>(m, "Node")
110
116
  .def(py::init<std::string const &, std::string const &, std::string const &, std::string const &,
111
- std::string const &, std::string const &>(),
112
- py::arg("uri"), //
113
- py::arg("user_name") = std::string{}, //
114
- py::arg("user_private_key") = std::string{}, //
115
- py::arg("cluster_public_key") = std::string{}, //
116
- py::arg("user_security_file") = std::string{}, //
117
- py::arg("cluster_public_key_file") = std::string{}) //
117
+ std::string const &, std::string const &, bool>(),
118
+ py::arg("uri"), //
119
+ py::arg("user_name") = std::string{}, //
120
+ py::arg("user_private_key") = std::string{}, //
121
+ py::arg("cluster_public_key") = std::string{}, //
122
+ py::kw_only(), //
123
+ py::arg("user_security_file") = std::string{}, //
124
+ py::arg("cluster_public_key_file") = std::string{}, //
125
+ py::arg("enable_encryption") = false //
126
+ ) //
118
127
  .def("prefix_get", &qdb::node::prefix_get)
119
128
  .def("blob", &qdb::node::blob)
120
129
  .def("integer", &qdb::node::integer);
@@ -414,6 +414,17 @@ def _ensure_list(xs, cinfos):
414
414
  return ret
415
415
 
416
416
 
417
+ def _coerce_retries(retries) -> quasardb.RetryOptions:
418
+ if retries is None:
419
+ return quasardb.RetryOptions()
420
+ elif isinstance(retries, int):
421
+ return quasardb.RetryOptions(retries=retries)
422
+ elif isinstance(retries, quasardb.RetryOptions):
423
+ return retries
424
+ else:
425
+ raise TypeError("retries should either be an integer or quasardb.RetryOptions, got: " + type(retries))
426
+
427
+
417
428
  def ensure_ma(xs, dtype=None):
418
429
  if isinstance(dtype, list):
419
430
  assert(isinstance(xs, list) == True)
@@ -556,20 +567,25 @@ def write_array(
556
567
  logger.info("Writing array (%d rows of dtype %s) to columns %s.%s (type %s)", len(data), data.dtype, table.get_name(), column, ctype)
557
568
  write_with[ctype](column, index, data)
558
569
 
559
-
560
570
  def write_arrays(
561
571
  data,
562
572
  cluster,
563
573
  table = None,
574
+ *,
564
575
  dtype = None,
565
576
  index = None,
566
577
  _async = False,
567
578
  fast = False,
568
579
  truncate = False,
569
- deduplicate=False,
570
- deduplication_mode='drop',
580
+ deduplicate = False,
581
+ deduplication_mode = 'drop',
571
582
  infer_types = True,
572
- writer = None):
583
+ writer = None,
584
+ write_through = False,
585
+ retries = 3,
586
+
587
+ # We accept additional kwargs that will be passed through the writer.push() methods
588
+ **kwargs):
573
589
  """
574
590
  Write multiple aligned numpy arrays to a table.
575
591
 
@@ -652,12 +668,25 @@ def write_arrays(
652
668
 
653
669
  Defaults to False.
654
670
 
671
+ write_through: optional bool
672
+ If True, data is not cached after write.
673
+ By default is False, in which case caching is left at the discretion of the server.
674
+
655
675
  writer: optional quasardb.Writer
656
676
  Allows you to explicitly provide a Writer to use, which is expected to be
657
677
  initialized with the `table`.
658
678
 
659
679
  Reuse of the Writer allows for some performance improvements.
660
680
 
681
+ retries: optional int or quasardb.RetryOptions
682
+ Number of times to retry in case of a push failure. This is useful in case of async
683
+ pipeline failures, or when doing transactional inserts that may occasionally cause
684
+ transaction conflicts.
685
+
686
+ Retries with exponential backoff, starts at 3 seconds, and doubles every retry attempt.
687
+
688
+ Alternatively, a quasardb.RetryOptions object can be passed to more carefully fine-tune
689
+ retry behavior.
661
690
  """
662
691
 
663
692
  if table:
@@ -673,7 +702,10 @@ def write_arrays(
673
702
  deduplicate=deduplicate,
674
703
  deduplication_mode=deduplication_mode,
675
704
  infer_types=infer_types,
676
- writer=writer)
705
+ write_through=write_through,
706
+ writer=writer,
707
+ retries=retries,
708
+ **kwargs)
677
709
 
678
710
 
679
711
  ret = []
@@ -738,19 +770,35 @@ def write_arrays(
738
770
  n_rows += len(index_)
739
771
  ret.append(table)
740
772
 
773
+ retries = _coerce_retries(retries)
774
+
775
+ # By default, we push all additional kwargs to the writer.push() function. This allows transparent propagation
776
+ # arguments.
777
+ #
778
+ # The initial use case was that so we can add additional parameters for test mocks, e.g. `mock_failures` so that
779
+ # we can validate the retry functionality.
780
+ push_kwargs = kwargs
781
+ push_kwargs['deduplicate'] = deduplicate
782
+ push_kwargs['deduplication_mode'] = deduplication_mode
783
+ push_kwargs['write_through'] = write_through
784
+ push_kwargs['retries'] = retries
785
+
741
786
  logger.debug("pushing %d rows", n_rows)
742
787
  start = time.time()
743
788
 
744
789
  if fast is True:
745
- writer.push_fast(push_data, deduplicate=deduplicate, deduplication_mode=deduplication_mode)
790
+ push_kwargs['push_mode'] = quasardb.WriterPushMode.Fast
746
791
  elif truncate is True:
747
- writer.push_truncate(push_data, deduplicate=deduplicate, deduplication_mode=deduplication_mode)
792
+ push_kwargs['push_mode'] = quasardb.WriterPushMode.Truncate
748
793
  elif isinstance(truncate, tuple):
749
- writer.push_truncate(push_data, range=truncate, deduplicate=deduplicate, deduplication_mode=deduplication_mode)
794
+ push_kwargs['push_mode'] = quasardb.WriterPushMode.Truncate
795
+ push_kwargs['range'] = truncate
750
796
  elif _async is True:
751
- writer.push_async(push_data, deduplicate=deduplicate, deduplication_mode=deduplication_mode)
797
+ push_kwargs['push_mode'] = quasardb.WriterPushMode.Async
752
798
  else:
753
- writer.push(push_data, deduplicate=deduplicate, deduplication_mode=deduplication_mode)
799
+ push_kwargs['push_mode'] = quasardb.WriterPushMode.Transactional
800
+
801
+ writer.push(push_data, **push_kwargs)
754
802
 
755
803
  logger.debug("pushed %d rows in %s seconds",
756
804
  n_rows, (time.time() - start))
@@ -92,8 +92,7 @@ private:
92
92
  using container_t = std::vector<trackable_object>;
93
93
 
94
94
  public:
95
- repository() = default;
96
- ~repository() = default;
95
+ repository() = default;
97
96
 
98
97
  repository(repository const &) = delete;
99
98
  repository(repository && other) = delete;
@@ -135,7 +134,7 @@ public:
135
134
  };
136
135
 
137
136
  template <typename T>
138
- requires(!std::is_pointer_v<T>) [[nodiscard]] inline T * track(T * x) noexcept
137
+ [[nodiscard]] inline T * track(T * x) noexcept
139
138
  {
140
139
  return static_cast<T *>(track(make_trackable_object(std::unique_ptr<T>{x})));
141
140
  };
quasardb/options.hpp CHANGED
@@ -32,6 +32,7 @@
32
32
 
33
33
  #include "handle.hpp"
34
34
  #include <qdb/option.h>
35
+ #include "detail/qdb_resource.hpp"
35
36
  #include <chrono>
36
37
 
37
38
  namespace qdb
@@ -81,6 +82,21 @@ public:
81
82
  }
82
83
  };
83
84
 
85
+ void set_timezone(std::string const & tz)
86
+ {
87
+ qdb::qdb_throw_if_error(*_handle, qdb_option_set_timezone(*_handle, tz.c_str()));
88
+ }
89
+
90
+ std::string get_timezone()
91
+ {
92
+ detail::qdb_resource<char const> tz{*_handle};
93
+ qdb::qdb_throw_if_error(*_handle, qdb_option_get_timezone(*_handle, &tz));
94
+
95
+ std::string ret{tz.get()};
96
+
97
+ return ret;
98
+ }
99
+
84
100
  void set_timeout(std::chrono::milliseconds ms)
85
101
  {
86
102
  qdb::qdb_throw_if_error(
@@ -96,6 +112,16 @@ public:
96
112
  return std::chrono::milliseconds{ms};
97
113
  }
98
114
 
115
+ void enable_user_properties()
116
+ {
117
+ qdb::qdb_throw_if_error(*_handle, qdb_option_enable_user_properties(*_handle));
118
+ }
119
+
120
+ void disable_user_properties()
121
+ {
122
+ qdb::qdb_throw_if_error(*_handle, qdb_option_disable_user_properties(*_handle));
123
+ }
124
+
99
125
  void set_client_soft_memory_limit(std::size_t limit)
100
126
  {
101
127
  qdb::qdb_throw_if_error(*_handle,
@@ -211,7 +237,8 @@ static inline void register_options(Module & m)
211
237
  py::enum_<qdb_compression_t>{o, "Compression", py::arithmetic(), "Compression type"} //
212
238
  .value("Disabled", qdb_comp_none) //
213
239
  .value("Fast", qdb_comp_fast) //
214
- .value("Best", qdb_comp_best); //
240
+ .value("Best", qdb_comp_best) //
241
+ .value("Balanced", qdb_comp_balanced); //
215
242
 
216
243
  py::enum_<qdb_encryption_t>{o, "Encryption", py::arithmetic(), "Encryption type"} //
217
244
  .value("Disabled", qdb_crypt_none) //
@@ -220,6 +247,10 @@ static inline void register_options(Module & m)
220
247
  o.def(py::init<qdb::handle_ptr>()) //
221
248
  .def("set_timeout", &qdb::options::set_timeout) //
222
249
  .def("get_timeout", &qdb::options::get_timeout) //
250
+ .def("set_timezone", &qdb::options::set_timezone) //
251
+ .def("get_timezone", &qdb::options::get_timezone) //
252
+ .def("enable_user_properties", &qdb::options::enable_user_properties) //
253
+ .def("disable_user_properties", &qdb::options::disable_user_properties) //
223
254
  .def("set_stabilization_max_wait", &qdb::options::set_stabilization_max_wait) //
224
255
  .def("get_stabilization_max_wait", &qdb::options::get_stabilization_max_wait) //
225
256
  .def("set_max_cardinality", &qdb::options::set_max_cardinality) //
@@ -230,8 +261,6 @@ static inline void register_options(Module & m)
230
261
  .def("set_client_max_in_buf_size", &qdb::options::set_client_max_in_buf_size) //
231
262
  .def("get_client_max_in_buf_size", &qdb::options::get_client_max_in_buf_size) //
232
263
  .def("get_cluster_max_in_buf_size", &qdb::options::get_cluster_max_in_buf_size) //
233
- .def("set_client_max_parallelism", &qdb::options::set_client_max_parallelism, //
234
- py::arg("parallelism")) //
235
264
  .def("get_client_max_parallelism", &qdb::options::get_client_max_parallelism) //
236
265
  .def("set_query_max_length", &qdb::options::set_query_max_length, //
237
266
  py::arg("query_max_length")) //
@@ -205,9 +205,10 @@ def query(cluster: quasardb.Cluster,
205
205
  df.set_index(index, inplace=True)
206
206
  return df
207
207
 
208
- def read_dataframe(table, row_index=False, columns=None, ranges=None):
208
+ def stream_dataframe(table : quasardb.Table, *, batch_size : int = 0, column_names : list = None, ranges : list = None):
209
209
  """
210
- Read a Pandas Dataframe from a QuasarDB Timeseries table.
210
+ Read a Pandas Dataframe from a QuasarDB Timeseries table. Returns a generator with dataframes of size `batch_size`, which is useful
211
+ when traversing a large dataset which does not fit into memory.
211
212
 
212
213
  Parameters:
213
214
  -----------
@@ -215,52 +216,65 @@ def read_dataframe(table, row_index=False, columns=None, ranges=None):
215
216
  table : quasardb.Timeseries
216
217
  QuasarDB Timeseries table object, e.g. qdb_cluster.table('my_table')
217
218
 
218
- columns : optional list
219
+ batch_size : int
220
+ The amount of rows to fetch in a single read operation. If unset, or 0, will
221
+ return all data as an entire dataframe. Otherwise, returns a generator which
222
+ yields on dataframe at a time.
223
+
224
+ column_names : optional list
219
225
  List of columns to read in dataframe. The timestamp column '$timestamp' is
220
226
  always read.
221
227
 
222
228
  Defaults to all columns.
223
229
 
224
- row_index: boolean
225
- Whether or not to index by rows rather than timestamps. Set to true if your
226
- dataset may contains null values and multiple rows may use the same timestamps.
227
- Note: using row_index is significantly slower.
228
- Defaults to false.
229
-
230
230
  ranges: optional list
231
231
  A list of time ranges to read, represented as tuples of Numpy datetime64[ns] objects.
232
232
  Defaults to the entire table.
233
233
 
234
234
  """
235
+ # Sanitize batch_size
236
+ if batch_size == None:
237
+ batch_size = 0
238
+ elif not isinstance(batch_size, int):
239
+ raise TypeError("batch_size should be an integer, but got: {} with value {}".format(type(batch_size), str(batch_size)))
235
240
 
236
- if columns is None:
237
- columns = list(c.name for c in table.list_columns())
241
+ kwargs = {}
238
242
 
239
- if not row_index:
240
- xs = dict((c, (read_series(table, c, ranges))) for c in columns)
241
- return DataFrame(data=xs)
243
+ if column_names:
244
+ kwargs['column_names'] = column_names
242
245
 
243
- kwargs = {
244
- 'columns': columns
245
- }
246
246
  if ranges:
247
247
  kwargs['ranges'] = ranges
248
248
 
249
- logger.debug("reading DataFrame from bulk reader")
249
+ with table.reader(**kwargs) as reader:
250
+ for batch in reader:
251
+ # We always expect the timestamp column, and set this as the index
252
+ assert '$timestamp' in batch
253
+
254
+ idx = pd.Index(batch.pop('$timestamp'), copy=False, name='$timestamp')
255
+ df = pd.DataFrame(batch, index=idx)
256
+
257
+ yield df
258
+
250
259
 
251
- reader = table.reader(**kwargs)
252
- xs = []
253
- for row in reader:
254
- xs.append(row.copy())
260
+ def read_dataframe(table, **kwargs):
261
+ """
262
+ Read a Pandas Dataframe from a QuasarDB Timeseries table. Wraps around stream_dataframe(), and
263
+ returns everything as a single dataframe. batch_size is always explicitly set to 0.
264
+ """
255
265
 
256
- columns.insert(0, '$timestamp')
266
+ if 'batch_size' in kwargs and kwargs['batch_size'] != 0 and kwargs['batch_size'] != None:
267
+ logger.warn("Providing a batch size with read_dataframe is unsupported, overriding batch_size to 0.")
268
+ logger.warn("If you wish to traverse the data in smaller batches, please use: stream_dataframe().")
269
+ kwargs['batch_size'] = 0
257
270
 
258
- logger.debug(
259
- "read %d rows, returning as DataFrame with %d columns",
260
- len(xs),
261
- len(columns))
271
+ # Note that this is *lazy*, dfs is a generator, not a list -- as such, dataframes will be
272
+ # fetched on-demand, which means that an error could occur in the middle of processing
273
+ # dataframes.
274
+ dfs = stream_dataframe(table, **kwargs)
275
+
276
+ return pd.concat(dfs)
262
277
 
263
- return DataFrame(data=xs, columns=columns)
264
278
 
265
279
  def _extract_columns(df, cinfos):
266
280
  """
@@ -288,88 +302,39 @@ def _extract_columns(df, cinfos):
288
302
  def write_dataframes(
289
303
  dfs,
290
304
  cluster,
291
- dtype=None,
292
- create=False,
293
- shard_size=None,
294
- _async=False,
295
- fast=False,
296
- truncate=False,
297
- deduplicate=False,
298
- deduplication_mode='drop',
299
- infer_types=True,
300
- writer=None):
305
+ *,
306
+ create = False,
307
+ shard_size = None,
308
+ **kwargs):
301
309
  """
302
- Store a dataframe into a table with the pin column API.
310
+ Store dataframes into a table. Any additional parameters not documented here
311
+ are passed to numpy.write_arrays(). Please consult the pydoc of that function
312
+ for additional accepted parameters.
303
313
 
304
314
  Parameters:
305
315
  -----------
306
316
 
307
- df: pandas.DataFrame
308
- The pandas dataframe to store.
317
+ dfs: dict[str | quasardb.Table, pd.DataFrame] | list[tuple[str | quasardb.Table, pd.DataFrame]]
318
+ This can be either a dict that maps table (either objects or names) to a dataframe, or a list
319
+ of table<>dataframe tuples.
309
320
 
310
321
  cluster: quasardb.Cluster
311
322
  Active connection to the QuasarDB cluster
312
323
 
313
- table: quasardb.Timeseries or str
314
- Either a string or a reference to a QuasarDB Timeseries table object.
315
- For example, 'my_table' or cluster.table('my_table') are both valid values.
316
-
317
- dtype: optional dtype, list of dtype, or dict of dtype
318
- Optional data type to force. If a single dtype, will force that dtype to all
319
- columns. If list-like, will map dtypes to dataframe columns by their offset.
320
- If dict-like, will map dtypes to dataframe columns by their label.
321
-
322
- If a dtype for a column is provided in this argument, and infer_types is also
323
- True, this argument takes precedence.
324
-
325
324
  create: optional bool
326
- Whether to create the table. Defaults to false.
325
+ Whether to create the table. Defaults to False.
327
326
 
328
327
  shard_size: optional datetime.timedelta
329
- The shard size of the timeseries you wish to create.
330
-
331
- deduplicate: bool or list[str]
332
- Enables server-side deduplication of data when it is written into the table.
333
- When True, automatically deduplicates rows when all values of a row are identical.
334
- When a list of strings is provided, deduplicates only based on the values of
335
- these columns.
336
-
337
- Defaults to False.
338
-
339
- infer_types: optional bool
340
- If true, will attemp to convert types from Python to QuasarDB natives types if
341
- the provided dataframe has incompatible types. For example, a dataframe with integers
342
- will automatically convert these to doubles if the QuasarDB table expects it.
343
-
344
- **Important**: as conversions are expensive and often the majority of time spent
345
- when inserting data into QuasarDB, we strongly recommend setting this to ``False``
346
- for performance-sensitive code.
347
-
348
- truncate: optional bool
349
- Truncate (also referred to as upsert) the data in-place. Will detect time range to truncate
350
- from the time range inside the dataframe.
351
-
352
- Defaults to False.
353
-
354
- _async: optional bool
355
- If true, uses asynchronous insertion API where commits are buffered server-side and
356
- acknowledged before they are written to disk. If you insert to the same table from
357
- multiple processes, setting this to True may improve performance.
358
-
359
- Defaults to False.
360
-
361
- fast: optional bool
362
- Whether to use 'fast push'. If you incrementally add small batches of data to table,
363
- you may see better performance if you set this to True.
364
-
365
- Defaults to False.
366
-
328
+ The shard size of the timeseries you wish to create when `create` is True.
367
329
  """
368
330
 
369
331
  # If dfs is a dict, we convert it to a list of tuples.
370
332
  if isinstance(dfs, dict):
371
333
  dfs = dfs.items()
372
334
 
335
+ if shard_size is not None and create == False:
336
+ raise ValueError("Invalid argument: shard size provided while create is False")
337
+
373
338
  # If the tables are provided as strings, we look them up.
374
339
  dfs_ = []
375
340
  for (table, df) in dfs:
@@ -378,11 +343,10 @@ def write_dataframes(
378
343
 
379
344
  dfs_.append((table, df))
380
345
 
381
-
382
346
  data_by_table = []
383
347
 
384
348
  for table, df in dfs_:
385
- logger.info("quasardb.pandas.write_dataframe, create = %s, dtype = %s", create, dtype)
349
+ logger.debug("quasardb.pandas.write_dataframe, create = %s", create)
386
350
  assert isinstance(df, pd.DataFrame)
387
351
 
388
352
  # Create table if requested
@@ -409,14 +373,7 @@ def write_dataframes(
409
373
  return qdbnp.write_arrays(data_by_table, cluster,
410
374
  table=None,
411
375
  index=None,
412
- dtype=dtype,
413
- _async=_async,
414
- fast=fast,
415
- truncate=truncate,
416
- deduplicate=deduplicate,
417
- deduplication_mode=deduplication_mode,
418
- infer_types=infer_types,
419
- writer=writer)
376
+ **kwargs)
420
377
 
421
378
  def write_dataframe(
422
379
  df,
@@ -0,0 +1,41 @@
1
+ #include "properties.hpp"
2
+ #include <qdb/properties.h>
3
+ #include "detail/qdb_resource.hpp"
4
+
5
+ namespace qdb
6
+ {
7
+
8
+ std::optional<std::string> properties::get(std::string const & key)
9
+ {
10
+ detail::qdb_resource<char const> ret{*handle_};
11
+
12
+ auto err = qdb_user_properties_get(*handle_, key.c_str(), &ret);
13
+
14
+ if (err == qdb_e_alias_not_found) [[unlikely]]
15
+ {
16
+ return {};
17
+ }
18
+
19
+ qdb::qdb_throw_if_error(*handle_, err);
20
+
21
+ assert(ret != nullptr);
22
+
23
+ return std::string{ret};
24
+ }
25
+
26
+ void properties::put(std::string const & key, std::string const & value)
27
+ {
28
+ qdb::qdb_throw_if_error(*handle_, qdb_user_properties_put(*handle_, key.c_str(), value.c_str()));
29
+ }
30
+
31
+ void properties::remove(std::string const & key)
32
+ {
33
+ qdb::qdb_throw_if_error(*handle_, qdb_user_properties_remove(*handle_, key.c_str()));
34
+ }
35
+
36
+ void properties::clear()
37
+ {
38
+ qdb::qdb_throw_if_error(*handle_, qdb_user_properties_remove_all(*handle_));
39
+ }
40
+
41
+ }; // namespace qdb