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/writer.hpp CHANGED
@@ -30,349 +30,262 @@
30
30
  */
31
31
  #pragma once
32
32
 
33
- #include "batch_column.hpp"
34
- #include "dispatch.hpp"
33
+ #include "concepts.hpp"
35
34
  #include "error.hpp"
36
35
  #include "logger.hpp"
36
+ #include "metrics.hpp"
37
37
  #include "object_tracker.hpp"
38
- #include "table.hpp"
39
- #include "utils.hpp"
40
- #include <variant>
38
+ #include "detail/writer.hpp"
41
39
  #include <vector>
42
40
 
43
41
  namespace qdb
44
42
  {
45
-
46
- namespace detail
47
- {
48
-
49
- using deduplicate = std::variant<std::vector<std::string>, bool>;
50
-
51
- enum deduplication_mode_t
52
- {
53
- deduplication_mode_drop,
54
- deduplication_mode_upsert
55
-
56
- };
57
-
58
- constexpr inline qdb_exp_batch_push_options_t to_push_options(enum detail::deduplication_mode_t mode)
59
- {
60
- switch (mode)
61
- {
62
- case deduplication_mode_drop:
63
- return qdb_exp_batch_option_unique_drop;
64
- case deduplication_mode_upsert:
65
- return qdb_exp_batch_option_unique_upsert;
66
- default:
67
- return qdb_exp_batch_option_standard;
68
- }
69
- }
70
-
71
- struct deduplicate_options
72
- {
73
- detail::deduplicate columns_;
74
- deduplication_mode_t mode_;
75
-
76
- deduplicate_options()
77
- {
78
- columns_ = false;
79
- mode_ = deduplication_mode_drop;
80
- };
81
-
82
- deduplicate_options(deduplication_mode_t mode, detail::deduplicate columns)
83
- : columns_{columns}
84
- , mode_{mode} {};
85
- };
86
-
87
- using int64_column = std::vector<qdb_int_t>;
88
- using double_column = std::vector<double>;
89
- using timestamp_column = std::vector<qdb_timespec_t>;
90
- using blob_column = std::vector<qdb_blob_t>;
91
- using string_column = std::vector<qdb_string_t>;
92
-
93
- using any_column =
94
- std::variant<int64_column, double_column, timestamp_column, blob_column, string_column>;
95
-
96
- template <qdb_ts_column_type_t T>
97
- struct column_of_type;
98
-
99
- template <qdb_ts_column_type_t T>
100
- struct make_column;
101
-
102
- #define COLUMN_OF_TYPE_DECL(TYPE, COLUMN) \
103
- template <> \
104
- struct column_of_type<TYPE> \
105
- { \
106
- using value_type = COLUMN; \
107
- }; \
108
- \
109
- template <> \
110
- struct make_column<TYPE> \
111
- { \
112
- any_column inline operator()() \
113
- { \
114
- return COLUMN{}; \
115
- }; \
116
- };
117
-
118
- COLUMN_OF_TYPE_DECL(qdb_ts_column_int64, int64_column);
119
- COLUMN_OF_TYPE_DECL(qdb_ts_column_double, double_column);
120
- COLUMN_OF_TYPE_DECL(qdb_ts_column_timestamp, timestamp_column);
121
- COLUMN_OF_TYPE_DECL(qdb_ts_column_blob, blob_column);
122
- COLUMN_OF_TYPE_DECL(qdb_ts_column_string, string_column);
123
- COLUMN_OF_TYPE_DECL(qdb_ts_column_symbol, string_column);
124
-
125
- #undef COLUMN_OF_TYPE_DECL
126
-
127
- template <qdb_ts_column_type_t T>
128
- std::vector<typename traits::qdb_column<T>::value_type> & access_column(
129
- std::vector<any_column> & columns, size_t index)
130
- {
131
- using column_type = typename column_of_type<T>::value_type;
132
- try
133
- {
134
- return std::get<column_type>(columns[index]);
135
- }
136
- catch (std::bad_variant_access const & /*e*/)
137
- {
138
- throw qdb::incompatible_type_exception{};
139
- }
140
- }
141
-
142
- template <qdb_ts_column_type_t T>
143
- std::size_t column_size(std::vector<any_column> & columns, size_t index)
43
+ class writer
144
44
  {
145
- return access_column<T>(columns, index).size();
146
- }
147
45
 
148
- template <qdb_ts_column_type_t T>
149
- struct clear_column
150
- {
151
- void operator()(any_column & xs)
152
- {
153
- using value_type = typename detail::column_of_type<T>::value_type;
154
- std::get<value_type>(xs).clear();
155
- }
156
- };
46
+ using int64_column = detail::int64_column;
47
+ using double_column = detail::double_column;
48
+ using timestamp_column = detail::timestamp_column;
49
+ using blob_column = detail::blob_column;
50
+ using string_column = detail::string_column;
51
+ using any_column = detail::any_column;
157
52
 
158
- class staged_table
159
- {
160
53
  public:
161
- staged_table(qdb::table const & table)
54
+ public:
55
+ writer(qdb::handle_ptr h)
162
56
  : _logger("quasardb.writer")
163
- , _table_name(table.get_name())
164
- {
165
- _column_infos = table.list_columns();
166
-
167
- std::transform(std::cbegin(_column_infos), std::cend(_column_infos),
168
- std::back_inserter(_columns),
169
-
170
- [](auto const & col) { return dispatch::by_column_type<detail::make_column>(col.type); });
171
- }
172
-
173
- ~staged_table()
174
- {
175
- clear();
176
- }
177
-
178
- void set_index(py::array const & timestamps);
179
- void set_blob_column(std::size_t index, const masked_array & xs);
180
- void set_string_column(std::size_t index, const masked_array & xs);
181
- void set_double_column(std::size_t index, masked_array_t<traits::float64_dtype> const & xs);
182
- void set_int64_column(std::size_t index, masked_array_t<traits::int64_dtype> const & xs);
183
- void set_timestamp_column(
184
- std::size_t index, masked_array_t<traits::datetime64_ns_dtype> const & xs);
57
+ , _handle{h}
58
+ {}
185
59
 
186
- std::vector<qdb_exp_batch_push_column_t> const & prepare_columns();
60
+ // prevent copy because of the table object, use a unique_ptr of the batch in cluster
61
+ // to return the object
62
+ writer(const writer &) = delete;
187
63
 
188
- void prepare_table_data(qdb_exp_batch_push_table_data_t & table_data);
64
+ ~writer()
65
+ {}
189
66
 
190
- void prepare_batch(qdb_exp_batch_push_mode_t mode,
191
- detail::deduplicate_options const & deduplicate_options,
192
- qdb_ts_range_t * ranges,
193
- qdb_exp_batch_push_table_t & batch);
67
+ const std::vector<qdb_exp_batch_push_column_t> & prepare_columns();
194
68
 
195
- static inline void _set_push_options(
196
- enum detail::deduplication_mode_t mode, bool columns, qdb_exp_batch_push_table_t & out)
69
+ template < //
70
+ qdb::concepts::writer_push_strategy PushStrategy, //
71
+ qdb::concepts::sleep_strategy SleepStrategy> //
72
+ void push(detail::writer_data const & data, py::kwargs kwargs)
197
73
  {
198
- out.options = (columns == true ? detail::to_push_options(mode) : qdb_exp_batch_option_standard);
199
- }
74
+ qdb::object_tracker::scoped_capture capture{_object_tracker};
200
75
 
201
- static inline void _set_push_options(enum detail::deduplication_mode_t mode,
202
- std::vector<std::string> const & columns,
203
- qdb_exp_batch_push_table_t & out)
204
- {
205
- auto where_duplicate = std::make_unique<qdb_string_t[]>(columns.size());
206
-
207
- std::transform(std::cbegin(columns), std::cend(columns), where_duplicate.get(),
208
- [](std::string const & column) -> qdb_string_t {
209
- // Note, we're not copying any strings here. This works,
210
- // because we pass the vector by reference, and it is "owned"
211
- // by _push_impl, so the lifetime is longer.
212
- return qdb_string_t{column.c_str(), column.size()};
213
- });
214
-
215
- out.options = detail::to_push_options(mode);
216
- out.where_duplicate = where_duplicate.release();
217
- out.where_duplicate_count = columns.size();
76
+ // We always want to have a push mode at this point
77
+ kwargs = detail::batch_push_mode::ensure(kwargs);
78
+
79
+ _push_impl<PushStrategy, SleepStrategy>( //
80
+ detail::staged_tables::index(data), //
81
+ kwargs //
82
+ ); //
218
83
  }
219
84
 
220
- inline void clear()
85
+ template < //
86
+ qdb::concepts::writer_push_strategy PushStrategy, //
87
+ qdb::concepts::sleep_strategy SleepStrategy> //
88
+ void push_async(detail::writer_data const & data, py::kwargs kwargs)
221
89
  {
222
- _index.clear();
223
- for (size_t index = 0; index < _columns.size(); ++index)
224
- {
225
- dispatch::by_column_type<detail::clear_column>(_column_infos[index].type, _columns[index]);
226
- }
90
+ _logger.warn("writer.push_async() is deprecated, please invoke writer.push() directly and "
91
+ "provide the push mode as a kwarg");
227
92
 
228
- _table_name.clear();
229
- _column_infos.clear();
230
- _columns_data.clear();
93
+ return push<PushStrategy, SleepStrategy>(
94
+ data, detail::batch_push_mode::set(kwargs, qdb_exp_batch_push_async));
231
95
  }
232
96
 
233
- inline qdb_ts_range_t time_range() const
97
+ template < //
98
+ qdb::concepts::writer_push_strategy PushStrategy, //
99
+ qdb::concepts::sleep_strategy SleepStrategy> //
100
+ void push_fast(detail::writer_data const & data, py::kwargs kwargs)
234
101
  {
235
- qdb_ts_range_t tr{_index.front(), _index.back()};
236
- // our range is end-exclusive, so let's move the pointer one nanosecond
237
- // *after* the last element in this batch.
238
- //
239
- // XXX(leon): this overflows if we're at exactly the last nanosecond of a second
240
- tr.end.tv_nsec++;
241
-
242
- return tr;
102
+ _logger.warn("writer.push_fast() is deprecated, please invoke writer.push() directly and "
103
+ "provide the push mode as a kwarg");
104
+
105
+ return push<PushStrategy, SleepStrategy>(
106
+ data, detail::batch_push_mode::set(kwargs, qdb_exp_batch_push_fast));
243
107
  }
244
108
 
245
- inline bool empty() const
109
+ template < //
110
+ qdb::concepts::writer_push_strategy PushStrategy, //
111
+ qdb::concepts::sleep_strategy SleepStrategy> //
112
+ void push_truncate(detail::writer_data const & data, py::kwargs kwargs)
246
113
  {
247
- return _index.empty();
114
+ _logger.warn("writer.push_fast() is deprecated, please invoke writer.push() directly and "
115
+ "provide the push mode as a kwarg");
116
+
117
+ return push<PushStrategy, SleepStrategy>(
118
+ data, detail::batch_push_mode::set(kwargs, qdb_exp_batch_push_truncate));
119
+
120
+ // qdb::object_tracker::scoped_capture capture{_object_tracker};
121
+ // auto idx = detail::staged_tables::index(data);
122
+
123
+ // // As we are actively removing data, let's add an additional check to ensure the user
124
+ // // doesn't accidentally truncate his whole database without inserting anything.
125
+ // if (data.empty()) [[unlikely]]
126
+ // {
127
+ // throw qdb::invalid_argument_exception{
128
+ // "Writer is empty: you did not provide any rows to push."};
129
+ // }
130
+
131
+ // qdb_ts_range_t tr;
132
+
133
+ // if (kwargs.contains("range"))
134
+ // {
135
+ // tr = convert::value<py::tuple, qdb_ts_range_t>(py::cast<py::tuple>(kwargs["range"]));
136
+ // }
137
+ // else
138
+ // {
139
+ // // TODO(leon): support multiple tables for push truncate
140
+ // if (idx.size() != 1) [[unlikely]]
141
+ // {
142
+ // throw qdb::invalid_argument_exception{"Writer push truncate only supports a single "
143
+ // "table unless an explicit range is provided:
144
+ // you " "provided more than one table without" "
145
+ // an explicit range."};
146
+ // }
147
+
148
+ // detail::staged_table const & staged_table = idx.first();
149
+ // tr = staged_table.time_range();
150
+ // }
151
+
152
+ // const qdb_exp_batch_options_t options = {
153
+ // .mode = qdb_exp_batch_push_truncate, //
154
+ // .push_flags = detail::batch_push_flags::from_kwargs(kwargs) //
155
+ // };
156
+
157
+ // _push_impl<PushStrategy, SleepStrategy>( //
158
+ // std::move(idx), //
159
+ // options, //
160
+ // kwargs, //
161
+ // &tr //
162
+ // ); //
248
163
  }
249
164
 
250
165
  private:
251
- private:
252
- qdb::logger _logger;
253
-
254
- std::string _table_name;
255
- std::vector<detail::column_info> _column_infos;
256
- std::vector<qdb_timespec_t> _index;
257
- std::vector<any_column> _columns;
258
-
259
- std::vector<qdb_exp_batch_push_column_t> _columns_data;
260
- };
261
-
262
- } // namespace detail
263
-
264
- class writer
265
- {
266
-
267
- using int64_column = detail::int64_column;
268
- using double_column = detail::double_column;
269
- using timestamp_column = detail::timestamp_column;
270
- using blob_column = detail::blob_column;
271
- using string_column = detail::string_column;
272
- using any_column = detail::any_column;
273
- using staged_tables_t = std::map<std::string, detail::staged_table>;
274
-
275
- public:
276
- /**
277
- * Convenience class that holds data that can be pushed to the writer. Makes it
278
- * easier for the end-user to provide the data in the correct format, in a single
279
- * function call, if they decide to use the low-level writer API themselves.
280
- */
281
- class data
166
+ template < //
167
+ concepts::writer_push_strategy PushStrategy, //
168
+ concepts::sleep_strategy SleepStrategy> //
169
+ void _push_impl( //
170
+ detail::staged_tables && idx, //
171
+ py::kwargs kwargs) //
282
172
  {
283
- friend class writer;
173
+ _handle->check_open();
284
174
 
285
- protected:
286
- struct value_type
287
- {
288
- qdb::table table;
289
- py::array index;
290
- py::list column_data;
291
- };
175
+ // Ensure some default variables that are set
176
+ kwargs = detail::batch_push_flags::ensure(kwargs);
292
177
 
293
- public:
294
- void append(qdb::table const & table, py::handle const & index, py::list const & column_data);
178
+ std::vector<qdb_ts_range_t> truncate_ranges{};
295
179
 
296
- inline bool empty() const noexcept
180
+ if (detail::batch_push_mode::from_kwargs(kwargs) == qdb_exp_batch_push_truncate)
181
+ [[unlikely]] // Unlikely because truncate isn't used much
297
182
  {
298
- return xs_.empty();
183
+ kwargs = detail::batch_truncate_ranges::ensure(kwargs, idx);
184
+ truncate_ranges = detail::batch_truncate_ranges::from_kwargs(kwargs);
299
185
  }
300
186
 
301
- inline value_type const & front() const noexcept
302
- {
303
- assert(empty() == false);
187
+ qdb_exp_batch_options_t options = detail::batch_options::from_kwargs(kwargs);
304
188
 
305
- return xs_.front();
189
+ if (idx.empty()) [[unlikely]]
190
+ {
191
+ throw qdb::invalid_argument_exception{"No data written to batch writer."};
306
192
  }
307
193
 
308
- inline value_type const & back() const noexcept
309
- {
310
- assert(empty() == false);
194
+ auto deduplicate_options = detail::deduplicate_options::from_kwargs(kwargs);
311
195
 
312
- return xs_.back();
313
- }
196
+ std::vector<qdb_exp_batch_push_table_t> batch;
197
+ batch.assign(idx.size(), qdb_exp_batch_push_table_t());
314
198
 
315
- std::vector<value_type> xs() const
199
+ qdb_ts_range_t * truncate_ranges_{nullptr};
200
+ if (truncate_ranges.empty() == false) [[unlikely]]
316
201
  {
317
- return xs_;
202
+ truncate_ranges_ = truncate_ranges.data();
318
203
  }
319
204
 
320
- private:
321
- std::vector<value_type> xs_;
322
- };
323
-
324
- public:
325
- writer(qdb::handle_ptr h)
326
- : _logger("quasardb.writer")
327
- , _handle{h}
328
- {}
329
-
330
- // prevent copy because of the table object, use a unique_ptr of the batch in cluster
331
- // to return the object
332
- writer(const writer &) = delete;
333
-
334
- ~writer()
335
- {}
205
+ int cur = 0;
336
206
 
337
- const std::vector<qdb_exp_batch_push_column_t> & prepare_columns();
207
+ for (auto pos = idx.begin(); pos != idx.end(); ++pos)
208
+ {
209
+ std::string const & table_name = pos->first;
210
+ detail::staged_table & staged_table = pos->second;
211
+ auto & batch_table = batch.at(cur++);
212
+
213
+ staged_table.prepare_batch( //
214
+ options.mode, //
215
+ deduplicate_options, //
216
+ truncate_ranges_, //
217
+ batch_table);
218
+
219
+ if (batch_table.data.column_count == 0) [[unlikely]]
220
+ {
221
+ throw qdb::invalid_argument_exception{
222
+ "Writer is empty: you did not provide any columns to push."};
223
+ }
224
+
225
+ _logger.debug("Pushing %d rows with %d columns in %s", batch_table.data.row_count,
226
+ batch_table.data.column_count, table_name);
227
+ }
338
228
 
339
- void push(writer::data const & data, py::kwargs args);
340
- void push_async(writer::data const & data, py::kwargs args);
341
- void push_fast(writer::data const & data, py::kwargs args);
342
- void push_truncate(writer::data const & data, py::kwargs args);
229
+ _do_push<PushStrategy, SleepStrategy>( //
230
+ options, //
231
+ batch, //
232
+ PushStrategy::from_kwargs(kwargs), //
233
+ detail::retry_options::from_kwargs(kwargs) //
234
+ ); //
235
+ }
343
236
 
344
- private:
345
- static inline detail::staged_table & _get_staged_table(
346
- qdb::table const & table, staged_tables_t & staged_tables)
237
+ template < //
238
+ concepts::writer_push_strategy PushStrategy, //
239
+ concepts::sleep_strategy SleepStrategy> //
240
+ void _do_push(qdb_exp_batch_options_t const & options,
241
+ std::vector<qdb_exp_batch_push_table_t> const & batch,
242
+ PushStrategy push_strategy,
243
+ detail::retry_options const & retry_options)
347
244
  {
348
- std::string table_name = table.get_name();
349
-
350
- auto pos = staged_tables.lower_bound(table_name);
245
+ qdb_error_t err{qdb_e_ok};
351
246
 
352
- // XXX(leon): can be optimized by using lower_bound and reusing the `pos` for insertion into
353
- // the correct place.
354
- if (pos == staged_tables.end() || pos->first != table_name) [[unlikely]]
355
247
  {
356
- // The table was not yet found
357
- pos = staged_tables.emplace_hint(pos, table_name, table);
358
- assert(pos->second.empty());
248
+ // Make sure to measure the time it takes to do the actual push.
249
+ // This is in its own scoped block so that we only actually measure
250
+ // the push time, not e.g. retry time.
251
+ qdb::metrics::scoped_capture capture{"qdb_batch_push"};
252
+
253
+ err = push_strategy( //
254
+ *_handle, //
255
+ &options, //
256
+ batch.data(), //
257
+ nullptr, //
258
+ batch.size()); //
359
259
  }
360
260
 
361
- assert(pos != staged_tables.end());
362
- assert(pos->first == table_name);
261
+ if (retry_options.should_retry(err))
262
+ [[unlikely]] // Unlikely, because err is most likely to be qdb_e_ok
263
+ {
264
+ if (err == qdb_e_async_pipe_full) [[likely]]
265
+ {
266
+ _logger.info("Async pipelines are currently full");
267
+ }
268
+ else
269
+ {
270
+ _logger.warn("A temporary error occurred");
271
+ }
272
+
273
+ std::chrono::milliseconds delay = retry_options.delay;
274
+ _logger.info("Sleeping for %d milliseconds", delay.count());
275
+
276
+ SleepStrategy::sleep(delay);
277
+
278
+ // Now try again -- easier way to go about this is to enter recursion. Note how
279
+ // we permutate the retry_options, which automatically adjusts the amount of retries
280
+ // left and the next sleep duration.
281
+ _logger.warn("Retrying push operation, retries left: %d", retry_options.retries_left);
282
+ return _do_push<PushStrategy, SleepStrategy>(
283
+ options, batch, push_strategy, retry_options.next());
284
+ }
363
285
 
364
- return pos->second;
286
+ qdb::qdb_throw_if_error(*_handle, err);
365
287
  }
366
288
 
367
- static staged_tables_t _stage_tables(writer::data const & data);
368
-
369
- void _push_impl(staged_tables_t & staged_tables,
370
- qdb_exp_batch_push_mode_t mode,
371
- detail::deduplicate_options deduplicate_options,
372
- qdb_ts_range_t * ranges = nullptr);
373
-
374
- detail::deduplicate_options _deduplicate_from_args(py::kwargs args);
375
-
376
289
  private:
377
290
  qdb::logger _logger;
378
291
  qdb::handle_ptr _handle;
@@ -391,6 +304,51 @@ public:
391
304
  // behavior
392
305
  using writer_ptr = std::unique_ptr<writer>;
393
306
 
394
- void register_writer(py::module_ & m);
307
+ template < //
308
+ qdb::concepts::writer_push_strategy PushStrategy, //
309
+ qdb::concepts::sleep_strategy SleepStrategy> //
310
+ static void register_writer(py::module_ & m)
311
+ {
312
+ using PS = PushStrategy;
313
+ using SS = SleepStrategy;
314
+
315
+ namespace py = pybind11;
316
+
317
+ // Writer data
318
+ auto writer_data_c = py::class_<qdb::detail::writer_data>{m, "WriterData"};
319
+ writer_data_c.def(py::init())
320
+ .def("append", &qdb::detail::writer_data::append, py::arg("table"), py::arg("index"),
321
+ py::arg("column_data"), "Append new data")
322
+ .def("empty", &qdb::detail::writer_data::empty, "Returns true if underlying data is empty");
323
+
324
+ // Different push modes, makes it easy to convert to<>from native types, as we accept the push
325
+ // mode as a kwarg.
326
+ py::enum_<qdb_exp_batch_push_mode_t>{m, "WriterPushMode", py::arithmetic(), "Push Mode"} //
327
+ .value("Transactional", qdb_exp_batch_push_transactional) //
328
+ .value("Fast", qdb_exp_batch_push_fast) //
329
+ .value("Truncate", qdb_exp_batch_push_truncate) //
330
+ .value("Async", qdb_exp_batch_push_fast); //
331
+
332
+ // And the actual pinned writer
333
+ auto writer_c = py::class_<qdb::writer>{m, "Writer"};
334
+
335
+ // basic interface
336
+ writer_c.def(py::init<qdb::handle_ptr>()); //
337
+
338
+ writer_c.def_readwrite("_legacy_state", &qdb::writer::legacy_state_);
339
+
340
+ // push functions
341
+ writer_c
342
+ .def("push", &qdb::writer::push<PS, SS>, "Regular batch push") //
343
+ .def("push_async", &qdb::writer::push_async<PS, SS>,
344
+ "Asynchronous batch push that buffers data inside the QuasarDB daemon") //
345
+ .def("push_fast", &qdb::writer::push_fast<PS, SS>,
346
+ "Fast, in-place batch push that is efficient when doing lots of small, incremental "
347
+ "pushes.") //
348
+ .def("push_truncate", &qdb::writer::push_truncate<PS, SS>,
349
+ "Before inserting data, truncates any existing data. This is useful when you want your "
350
+ "insertions to be idempotent, e.g. in "
351
+ "case of a retry.");
352
+ }
395
353
 
396
354
  } // namespace qdb
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quasardb
3
- Version: 3.14.2.dev1
3
+ Version: 3.14.2.dev3
4
4
  Summary: Python API for quasardb
5
5
  Home-page: https://www.quasardb.net/
6
6
  Author: quasardb SAS
@@ -14,22 +14,22 @@ Classifier: Intended Audience :: Information Technology
14
14
  Classifier: Intended Audience :: Other Audience
15
15
  Classifier: Intended Audience :: System Administrators
16
16
  Classifier: Intended Audience :: Telecommunications Industry
17
- Classifier: Programming Language :: Python :: 3.6
18
17
  Classifier: Programming Language :: Python :: 3.7
19
18
  Classifier: Programming Language :: Python :: 3.8
20
19
  Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
23
  Classifier: Topic :: Database
24
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
25
  Classifier: License :: OSI Approved :: BSD License
26
26
  License-File: LICENSE.md
27
27
  Requires-Dist: numpy
28
28
  Provides-Extra: pandas
29
- Requires-Dist: pandas ; extra == 'pandas'
29
+ Requires-Dist: pandas; extra == "pandas"
30
30
  Provides-Extra: tests
31
- Requires-Dist: pytest (>=6.2.5) ; extra == 'tests'
32
- Requires-Dist: pytest-runner (>=5.3.1) ; extra == 'tests'
33
- Requires-Dist: pytest-benchmark (==3.4.1) ; extra == 'tests'
34
- Requires-Dist: teamcity-messages (>=1.29) ; extra == 'tests'
31
+ Requires-Dist: pytest; extra == "tests"
32
+ Requires-Dist: pytest-runner; extra == "tests"
33
+ Requires-Dist: pytest-benchmark; extra == "tests"
34
+ Requires-Dist: teamcity-messages; extra == "tests"
35
35