quasardb 3.14.2.dev1__cp38-cp38-win_amd64.whl → 3.14.2.dev3__cp38-cp38-win_amd64.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.
- quasardb/CMakeLists.txt +19 -12
- quasardb/INSTALL.vcxproj +4 -0
- quasardb/__init__.py +33 -4
- quasardb/cluster.cpp +14 -1
- quasardb/cluster.hpp +114 -72
- quasardb/concepts.hpp +56 -12
- quasardb/continuous.cpp +84 -34
- quasardb/continuous.hpp +10 -7
- quasardb/convert/array.hpp +23 -6
- quasardb/convert/value.hpp +78 -7
- quasardb/date/ALL_BUILD.vcxproj +4 -4
- quasardb/date/CMakeFiles/Export/df49adab93b9e0c10c64f72458b31971/dateTargets.cmake +12 -12
- quasardb/date/CMakeFiles/generate.stamp.depend +4 -4
- quasardb/date/INSTALL.vcxproj +4 -0
- quasardb/date/dateConfigVersion.cmake +0 -5
- quasardb/date/dateTargets.cmake +3 -7
- quasardb/detail/invoke.hpp +0 -0
- quasardb/detail/retry.cpp +30 -0
- quasardb/detail/retry.hpp +147 -0
- quasardb/detail/sleep.hpp +53 -0
- quasardb/{writer.cpp → detail/writer.cpp} +68 -162
- quasardb/detail/writer.hpp +550 -0
- quasardb/error.hpp +76 -1
- quasardb/masked_array.hpp +9 -2
- quasardb/module.cpp +20 -4
- quasardb/node.hpp +17 -8
- quasardb/numpy/__init__.py +58 -10
- quasardb/object_tracker.hpp +2 -3
- quasardb/options.hpp +32 -3
- quasardb/pandas/__init__.py +59 -102
- quasardb/properties.cpp +41 -0
- quasardb/properties.hpp +85 -0
- quasardb/pybind11/ALL_BUILD.vcxproj +4 -4
- quasardb/pybind11/CMakeFiles/generate.stamp.depend +14 -14
- quasardb/pybind11/INSTALL.vcxproj +4 -0
- quasardb/qdb_api.dll +0 -0
- quasardb/quasardb.cp38-win_amd64.pyd +0 -0
- quasardb/range-v3/ALL_BUILD.vcxproj +4 -4
- quasardb/range-v3/CMakeFiles/Export/d94ef200eca10a819b5858b33e808f5b/range-v3-targets.cmake +12 -12
- quasardb/range-v3/CMakeFiles/generate.stamp.depend +11 -11
- quasardb/range-v3/INSTALL.vcxproj +4 -0
- quasardb/range-v3/cmake_install.cmake +36 -0
- quasardb/range-v3/range-v3-config-version.cmake +0 -5
- quasardb/range-v3/range-v3-config.cmake +3 -7
- quasardb/range-v3/range.v3.headers.vcxproj +4 -4
- quasardb/reader.cpp +282 -0
- quasardb/reader.hpp +256 -0
- quasardb/table.cpp +4 -36
- quasardb/table.hpp +69 -28
- quasardb/traits.hpp +23 -0
- quasardb/writer.hpp +245 -287
- {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/METADATA +7 -7
- {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/RECORD +56 -50
- {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/WHEEL +1 -1
- quasardb/reader/ts_row.hpp +0 -281
- quasardb/reader/ts_value.hpp +0 -245
- quasardb/table_reader.hpp +0 -220
- {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/LICENSE.md +0 -0
- {quasardb-3.14.2.dev1.dist-info → quasardb-3.14.2.dev3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
/*
|
|
2
|
+
*
|
|
3
|
+
* Official Python API
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2009-2021, quasardb SAS. All rights reserved.
|
|
6
|
+
* All rights reserved.
|
|
7
|
+
*
|
|
8
|
+
* Redistribution and use in source and binary forms, with or without
|
|
9
|
+
* modification, are permitted provided that the following conditions are met:
|
|
10
|
+
*
|
|
11
|
+
* * Redistributions of source code must retain the above copyright
|
|
12
|
+
* notice, this list of conditions and the following disclaimer.
|
|
13
|
+
* * Redistributions in binary form must reproduce the above copyright
|
|
14
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
15
|
+
* documentation and/or other materials provided with the distribution.
|
|
16
|
+
* * Neither the name of quasardb nor the names of its contributors may
|
|
17
|
+
* be used to endorse or promote products derived from this software
|
|
18
|
+
* without specific prior written permission.
|
|
19
|
+
*
|
|
20
|
+
* THIS SOFTWARE IS PROVIDED BY QUASARDB AND CONTRIBUTORS ``AS IS'' AND ANY
|
|
21
|
+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
22
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
24
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
25
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
26
|
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
27
|
+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
28
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
29
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
*/
|
|
31
|
+
#pragma once
|
|
32
|
+
|
|
33
|
+
#include "../concepts.hpp"
|
|
34
|
+
#include "../convert/value.hpp"
|
|
35
|
+
#include "../dispatch.hpp"
|
|
36
|
+
#include "../error.hpp"
|
|
37
|
+
#include "../logger.hpp"
|
|
38
|
+
#include "../table.hpp"
|
|
39
|
+
#include "retry.hpp"
|
|
40
|
+
#include <variant>
|
|
41
|
+
#include <vector>
|
|
42
|
+
|
|
43
|
+
namespace qdb::detail
|
|
44
|
+
{
|
|
45
|
+
|
|
46
|
+
using deduplicate = std::variant<std::vector<std::string>, bool>;
|
|
47
|
+
|
|
48
|
+
enum deduplication_mode_t
|
|
49
|
+
{
|
|
50
|
+
deduplication_mode_drop,
|
|
51
|
+
deduplication_mode_upsert
|
|
52
|
+
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
constexpr inline qdb_exp_batch_deduplication_mode_t to_qdb(enum detail::deduplication_mode_t mode)
|
|
56
|
+
{
|
|
57
|
+
switch (mode)
|
|
58
|
+
{
|
|
59
|
+
case deduplication_mode_drop:
|
|
60
|
+
return qdb_exp_batch_deduplication_mode_drop;
|
|
61
|
+
case deduplication_mode_upsert:
|
|
62
|
+
return qdb_exp_batch_deduplication_mode_upsert;
|
|
63
|
+
default:
|
|
64
|
+
return qdb_exp_batch_deduplication_mode_disabled;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
struct deduplicate_options
|
|
69
|
+
{
|
|
70
|
+
detail::deduplicate columns_;
|
|
71
|
+
deduplication_mode_t mode_;
|
|
72
|
+
|
|
73
|
+
deduplicate_options()
|
|
74
|
+
{
|
|
75
|
+
columns_ = false;
|
|
76
|
+
mode_ = deduplication_mode_drop;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
deduplicate_options(deduplication_mode_t mode, detail::deduplicate columns)
|
|
80
|
+
: columns_{columns}
|
|
81
|
+
, mode_{mode} {};
|
|
82
|
+
|
|
83
|
+
static detail::deduplicate_options from_kwargs(py::kwargs args);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
using int64_column = std::vector<qdb_int_t>;
|
|
87
|
+
using double_column = std::vector<double>;
|
|
88
|
+
using timestamp_column = std::vector<qdb_timespec_t>;
|
|
89
|
+
using blob_column = std::vector<qdb_blob_t>;
|
|
90
|
+
using string_column = std::vector<qdb_string_t>;
|
|
91
|
+
|
|
92
|
+
using any_column =
|
|
93
|
+
std::variant<int64_column, double_column, timestamp_column, blob_column, string_column>;
|
|
94
|
+
|
|
95
|
+
template <qdb_ts_column_type_t T>
|
|
96
|
+
struct column_of_type;
|
|
97
|
+
|
|
98
|
+
template <qdb_ts_column_type_t T>
|
|
99
|
+
struct make_column;
|
|
100
|
+
|
|
101
|
+
#define COLUMN_OF_TYPE_DECL(TYPE, COLUMN) \
|
|
102
|
+
template <> \
|
|
103
|
+
struct column_of_type<TYPE> \
|
|
104
|
+
{ \
|
|
105
|
+
using value_type = COLUMN; \
|
|
106
|
+
}; \
|
|
107
|
+
\
|
|
108
|
+
template <> \
|
|
109
|
+
struct make_column<TYPE> \
|
|
110
|
+
{ \
|
|
111
|
+
any_column inline operator()() \
|
|
112
|
+
{ \
|
|
113
|
+
return COLUMN{}; \
|
|
114
|
+
}; \
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
COLUMN_OF_TYPE_DECL(qdb_ts_column_int64, int64_column);
|
|
118
|
+
COLUMN_OF_TYPE_DECL(qdb_ts_column_double, double_column);
|
|
119
|
+
COLUMN_OF_TYPE_DECL(qdb_ts_column_timestamp, timestamp_column);
|
|
120
|
+
COLUMN_OF_TYPE_DECL(qdb_ts_column_blob, blob_column);
|
|
121
|
+
COLUMN_OF_TYPE_DECL(qdb_ts_column_string, string_column);
|
|
122
|
+
COLUMN_OF_TYPE_DECL(qdb_ts_column_symbol, string_column);
|
|
123
|
+
|
|
124
|
+
#undef COLUMN_OF_TYPE_DECL
|
|
125
|
+
|
|
126
|
+
template <qdb_ts_column_type_t T>
|
|
127
|
+
std::vector<typename traits::qdb_column<T>::value_type> & access_column(
|
|
128
|
+
std::vector<any_column> & columns, size_t index)
|
|
129
|
+
{
|
|
130
|
+
using column_type = typename column_of_type<T>::value_type;
|
|
131
|
+
try
|
|
132
|
+
{
|
|
133
|
+
return std::get<column_type>(columns[index]);
|
|
134
|
+
}
|
|
135
|
+
catch (std::bad_variant_access const & /*e*/)
|
|
136
|
+
{
|
|
137
|
+
throw qdb::incompatible_type_exception{};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
template <qdb_ts_column_type_t T>
|
|
142
|
+
std::size_t column_size(std::vector<any_column> & columns, size_t index)
|
|
143
|
+
{
|
|
144
|
+
return access_column<T>(columns, index).size();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
template <qdb_ts_column_type_t T>
|
|
148
|
+
struct clear_column
|
|
149
|
+
{
|
|
150
|
+
void operator()(any_column & xs)
|
|
151
|
+
{
|
|
152
|
+
using value_type = typename detail::column_of_type<T>::value_type;
|
|
153
|
+
std::get<value_type>(xs).clear();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
class staged_table
|
|
158
|
+
{
|
|
159
|
+
public:
|
|
160
|
+
staged_table(qdb::table const & table)
|
|
161
|
+
: _logger("quasardb.writer")
|
|
162
|
+
, _table_name(table.get_name())
|
|
163
|
+
{
|
|
164
|
+
_column_infos = table.list_columns();
|
|
165
|
+
|
|
166
|
+
std::transform(std::cbegin(_column_infos), std::cend(_column_infos),
|
|
167
|
+
std::back_inserter(_columns),
|
|
168
|
+
|
|
169
|
+
[](auto const & col) { return dispatch::by_column_type<detail::make_column>(col.type); });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
~staged_table()
|
|
173
|
+
{
|
|
174
|
+
clear();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
void set_index(py::array const & timestamps);
|
|
178
|
+
void set_blob_column(std::size_t index, const masked_array & xs);
|
|
179
|
+
void set_string_column(std::size_t index, const masked_array & xs);
|
|
180
|
+
void set_double_column(std::size_t index, masked_array_t<traits::float64_dtype> const & xs);
|
|
181
|
+
void set_int64_column(std::size_t index, masked_array_t<traits::int64_dtype> const & xs);
|
|
182
|
+
void set_timestamp_column(
|
|
183
|
+
std::size_t index, masked_array_t<traits::datetime64_ns_dtype> const & xs);
|
|
184
|
+
|
|
185
|
+
std::vector<qdb_exp_batch_push_column_t> const & prepare_columns();
|
|
186
|
+
|
|
187
|
+
void prepare_table_data(qdb_exp_batch_push_table_data_t & table_data);
|
|
188
|
+
|
|
189
|
+
void prepare_batch(qdb_exp_batch_push_mode_t mode,
|
|
190
|
+
detail::deduplicate_options const & deduplicate_options,
|
|
191
|
+
qdb_ts_range_t * ranges,
|
|
192
|
+
qdb_exp_batch_push_table_t & batch);
|
|
193
|
+
|
|
194
|
+
static inline void _set_deduplication_mode(
|
|
195
|
+
enum detail::deduplication_mode_t mode, bool columns, qdb_exp_batch_push_table_t & out)
|
|
196
|
+
{
|
|
197
|
+
// Set deduplication mode only when `columns` is true, in which we will deduplicate based on
|
|
198
|
+
// *all* columns.
|
|
199
|
+
out.deduplication_mode =
|
|
200
|
+
(columns == true ? detail::to_qdb(mode) : qdb_exp_batch_deduplication_mode_disabled);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
static inline void _set_deduplication_mode(enum detail::deduplication_mode_t mode,
|
|
204
|
+
std::vector<std::string> const & columns,
|
|
205
|
+
qdb_exp_batch_push_table_t & out)
|
|
206
|
+
{
|
|
207
|
+
// A specific set of columns to deduplicate has been provided, in which case
|
|
208
|
+
// we'll need to do a small transformation of the column names.
|
|
209
|
+
auto where_duplicate = std::make_unique<char const *[]>(columns.size());
|
|
210
|
+
|
|
211
|
+
std::transform(std::cbegin(columns), std::cend(columns), where_duplicate.get(),
|
|
212
|
+
[](std::string const & column) -> char const * { return column.c_str(); });
|
|
213
|
+
|
|
214
|
+
out.deduplication_mode = detail::to_qdb(mode);
|
|
215
|
+
out.where_duplicate = where_duplicate.release();
|
|
216
|
+
out.where_duplicate_count = columns.size();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
inline void clear()
|
|
220
|
+
{
|
|
221
|
+
_index.clear();
|
|
222
|
+
for (size_t index = 0; index < _columns.size(); ++index)
|
|
223
|
+
{
|
|
224
|
+
dispatch::by_column_type<detail::clear_column>(_column_infos[index].type, _columns[index]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
_table_name.clear();
|
|
228
|
+
_column_infos.clear();
|
|
229
|
+
_columns_data.clear();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
inline qdb_ts_range_t time_range() const
|
|
233
|
+
{
|
|
234
|
+
qdb_ts_range_t tr{_index.front(), _index.back()};
|
|
235
|
+
// our range is end-exclusive, so let's move the pointer one nanosecond
|
|
236
|
+
// *after* the last element in this batch.
|
|
237
|
+
//
|
|
238
|
+
// XXX(leon): this overflows if we're at exactly the last nanosecond of a second
|
|
239
|
+
tr.end.tv_nsec++;
|
|
240
|
+
|
|
241
|
+
return tr;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
inline bool empty() const
|
|
245
|
+
{
|
|
246
|
+
return _index.empty();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private:
|
|
250
|
+
private:
|
|
251
|
+
qdb::logger _logger;
|
|
252
|
+
|
|
253
|
+
std::string _table_name;
|
|
254
|
+
std::vector<detail::column_info> _column_infos;
|
|
255
|
+
std::vector<qdb_timespec_t> _index;
|
|
256
|
+
std::vector<any_column> _columns;
|
|
257
|
+
|
|
258
|
+
std::vector<qdb_exp_batch_push_column_t> _columns_data;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Convenience class that holds data that can be pushed to the writer. Makes it
|
|
263
|
+
* easier for the end-user to provide the data in the correct format, in a single
|
|
264
|
+
* function call, if they decide to use the low-level writer API themselves.
|
|
265
|
+
*/
|
|
266
|
+
class writer_data
|
|
267
|
+
{
|
|
268
|
+
public:
|
|
269
|
+
struct value_type
|
|
270
|
+
{
|
|
271
|
+
qdb::table table;
|
|
272
|
+
py::array index;
|
|
273
|
+
py::list column_data;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
public:
|
|
277
|
+
void append(qdb::table const & table, py::handle const & index, py::list const & column_data)
|
|
278
|
+
{
|
|
279
|
+
py::array index_ = numpy::array::ensure<traits::datetime64_ns_dtype>(index);
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Additional check that all the data is actually of the same length, and data has been
|
|
283
|
+
* provided for each and every column.
|
|
284
|
+
*/
|
|
285
|
+
if (column_data.size() != table.list_columns().size())
|
|
286
|
+
{
|
|
287
|
+
throw qdb::invalid_argument_exception{"data must be provided for every table column"};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
for (py::handle const & data : column_data)
|
|
291
|
+
{
|
|
292
|
+
qdb::masked_array data_ = data.cast<qdb::masked_array>();
|
|
293
|
+
if (data_.size() != static_cast<std::size_t>(index_.size()))
|
|
294
|
+
{
|
|
295
|
+
throw qdb::invalid_argument_exception{
|
|
296
|
+
"every data array should be exactly the same length as the index array"};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
xs_.push_back(value_type{table, index_, column_data});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
inline bool empty() const noexcept
|
|
304
|
+
{
|
|
305
|
+
return xs_.empty();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
inline value_type const & front() const noexcept
|
|
309
|
+
{
|
|
310
|
+
assert(empty() == false);
|
|
311
|
+
|
|
312
|
+
return xs_.front();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
inline value_type const & back() const noexcept
|
|
316
|
+
{
|
|
317
|
+
assert(empty() == false);
|
|
318
|
+
|
|
319
|
+
return xs_.back();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
std::vector<value_type> xs() const
|
|
323
|
+
{
|
|
324
|
+
return xs_;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private:
|
|
328
|
+
std::vector<value_type> xs_;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Wraps an index to staged tables. Provides functionality for indexing writer
|
|
333
|
+
* data into staged tables as well.
|
|
334
|
+
*/
|
|
335
|
+
class staged_tables
|
|
336
|
+
{
|
|
337
|
+
public:
|
|
338
|
+
using key_type = std::string;
|
|
339
|
+
using value_type = staged_table;
|
|
340
|
+
using container_type = std::map<key_type, staged_table>;
|
|
341
|
+
using iterator = container_type::iterator;
|
|
342
|
+
using const_iterator = container_type::const_iterator;
|
|
343
|
+
|
|
344
|
+
public:
|
|
345
|
+
/**
|
|
346
|
+
* Free function that takes indexes all writer data into a staged_table object.
|
|
347
|
+
*/
|
|
348
|
+
static staged_tables index(writer_data const & data);
|
|
349
|
+
|
|
350
|
+
public:
|
|
351
|
+
inline container_type::size_type size() const
|
|
352
|
+
{
|
|
353
|
+
return idx_.size();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
inline bool empty() const
|
|
357
|
+
{
|
|
358
|
+
return idx_.empty();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
inline iterator begin()
|
|
362
|
+
{
|
|
363
|
+
return idx_.begin();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
inline iterator end()
|
|
367
|
+
{
|
|
368
|
+
return idx_.end();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
inline const_iterator begin() const
|
|
372
|
+
{
|
|
373
|
+
return idx_.cbegin();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
inline const_iterator cend() const
|
|
377
|
+
{
|
|
378
|
+
return idx_.cend();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Returns the first staged table. Useful in scenarios where we are only working with a single
|
|
383
|
+
* table.
|
|
384
|
+
*/
|
|
385
|
+
inline value_type & first()
|
|
386
|
+
{
|
|
387
|
+
assert(size() == 1);
|
|
388
|
+
|
|
389
|
+
return idx_.begin()->second;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Returns the first staged table. Useful in scenarios where we are only working with a single
|
|
394
|
+
* table.
|
|
395
|
+
*/
|
|
396
|
+
inline value_type const & first() const
|
|
397
|
+
{
|
|
398
|
+
assert(size() == 1);
|
|
399
|
+
|
|
400
|
+
return idx_.cbegin()->second;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Retrieves table by table object, or creates a new empty entry.
|
|
405
|
+
*/
|
|
406
|
+
inline value_type & get_or_create(qdb::table const & table)
|
|
407
|
+
{
|
|
408
|
+
std::string const & table_name = table.get_name();
|
|
409
|
+
auto pos = idx_.lower_bound(table_name);
|
|
410
|
+
|
|
411
|
+
if (pos == idx_.end() || pos->first != table_name) [[unlikely]]
|
|
412
|
+
{
|
|
413
|
+
// The table was not yet found
|
|
414
|
+
pos = idx_.emplace_hint(pos, table_name, table);
|
|
415
|
+
assert(pos->second.empty());
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
assert(pos != idx_.end());
|
|
419
|
+
assert(pos->first == table_name);
|
|
420
|
+
|
|
421
|
+
return pos->second;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private:
|
|
425
|
+
container_type idx_;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
struct batch_push_flags
|
|
429
|
+
{
|
|
430
|
+
static constexpr char const * kw_write_through = "write_through";
|
|
431
|
+
static constexpr bool default_write_through = true;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Ensures all options are always explicitly set, according to the defaults above.
|
|
435
|
+
*/
|
|
436
|
+
static py::kwargs ensure(py::kwargs kwargs);
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Returns the push mode, throws error if push mode is not set.
|
|
440
|
+
*/
|
|
441
|
+
static qdb_uint_t from_kwargs(py::kwargs const & kwargs);
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
struct batch_push_mode
|
|
445
|
+
{
|
|
446
|
+
static constexpr char const * kw_push_mode = "push_mode";
|
|
447
|
+
static constexpr qdb_exp_batch_push_mode_t default_push_mode = qdb_exp_batch_push_transactional;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Ensures push mode is always set in the kwargs, and uses the `default_push_mode` if not found.
|
|
451
|
+
*/
|
|
452
|
+
static py::kwargs ensure(py::kwargs kwargs);
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Sets the push mode to a certain value.
|
|
456
|
+
*/
|
|
457
|
+
static py::kwargs set(py::kwargs kwargs, qdb_exp_batch_push_mode_t push_mode);
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Returns the push mode, throws error if push mode is not set.
|
|
461
|
+
*/
|
|
462
|
+
static qdb_exp_batch_push_mode_t from_kwargs(py::kwargs const & kwargs);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
struct batch_options
|
|
466
|
+
{
|
|
467
|
+
static qdb_exp_batch_options_t from_kwargs(py::kwargs const & kwargs);
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
struct batch_truncate_ranges
|
|
471
|
+
{
|
|
472
|
+
|
|
473
|
+
static constexpr char const * kw_range = "range";
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Ensures a range is set. If not set, derives it from the range of the provided
|
|
477
|
+
* tables.
|
|
478
|
+
*/
|
|
479
|
+
static py::kwargs ensure(py::kwargs kwargs, detail::staged_tables const & idx)
|
|
480
|
+
{
|
|
481
|
+
if (kwargs.contains(kw_range) == false)
|
|
482
|
+
{
|
|
483
|
+
if (idx.size() != 1) [[unlikely]]
|
|
484
|
+
{
|
|
485
|
+
throw qdb::invalid_argument_exception{"Writer push truncate only supports a single "
|
|
486
|
+
"table unless an explicit range is provided: "
|
|
487
|
+
"you provided more than one table without "
|
|
488
|
+
" an explicit range."};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
detail::staged_table const & staged_table = idx.first();
|
|
492
|
+
|
|
493
|
+
auto range_ = staged_table.time_range();
|
|
494
|
+
|
|
495
|
+
py::print("1 before");
|
|
496
|
+
py::tuple x = convert::value<qdb_ts_range_t, py::tuple>(range_);
|
|
497
|
+
py::print("2 middle");
|
|
498
|
+
kwargs[kw_range] = x;
|
|
499
|
+
py::print("3 after");
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return kwargs;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Returns the truncate ranges based on the kwargs.
|
|
507
|
+
*/
|
|
508
|
+
static std::vector<qdb_ts_range_t> from_kwargs(py::kwargs kwargs)
|
|
509
|
+
{
|
|
510
|
+
// This function *could* be invoked, but doesn't make sense to be invoked, when we're not
|
|
511
|
+
// doing a push truncate.
|
|
512
|
+
//
|
|
513
|
+
// Strictly speaking this assertion is not necessary, but it doesn't hurt to have it.
|
|
514
|
+
assert(qdb::detail::batch_push_mode::from_kwargs(kwargs) == qdb_exp_batch_push_truncate);
|
|
515
|
+
|
|
516
|
+
// We also always assume a range is provided, i.e. `ensure` is called beforehand.
|
|
517
|
+
assert(kwargs.contains(detail::batch_truncate_ranges::kw_range) == true);
|
|
518
|
+
std::vector<qdb_ts_range_t> ret{};
|
|
519
|
+
|
|
520
|
+
py::tuple range_ = py::cast<py::tuple>(kwargs[detail::batch_truncate_ranges::kw_range]);
|
|
521
|
+
ret.push_back(convert::value<py::tuple, qdb_ts_range_t>(range_));
|
|
522
|
+
|
|
523
|
+
return ret;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Default batch push strategy, just invokes the regular function and returns the result.
|
|
529
|
+
*/
|
|
530
|
+
struct default_writer_push_strategy
|
|
531
|
+
{
|
|
532
|
+
|
|
533
|
+
static default_writer_push_strategy from_kwargs(py::kwargs const & /* kwargs */)
|
|
534
|
+
{
|
|
535
|
+
return {};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
inline qdb_error_t operator()(qdb_handle_t handle,
|
|
539
|
+
qdb_exp_batch_options_t const * options,
|
|
540
|
+
qdb_exp_batch_push_table_t const * tables,
|
|
541
|
+
qdb_exp_batch_push_table_schema_t const ** table_schemas,
|
|
542
|
+
qdb_size_t table_count) const noexcept
|
|
543
|
+
{
|
|
544
|
+
return qdb_exp_batch_push_with_options(handle, options, tables, table_schemas, table_count);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
static_assert(concepts::writer_push_strategy<default_writer_push_strategy>);
|
|
549
|
+
|
|
550
|
+
} // namespace qdb::detail
|
quasardb/error.hpp
CHANGED
|
@@ -89,6 +89,31 @@ public:
|
|
|
89
89
|
{}
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
+
class out_of_bounds_exception : public exception
|
|
93
|
+
{
|
|
94
|
+
public:
|
|
95
|
+
out_of_bounds_exception() noexcept
|
|
96
|
+
: exception(qdb_e_out_of_bounds, std::string("The requested operation is out of bounds"))
|
|
97
|
+
{}
|
|
98
|
+
|
|
99
|
+
out_of_bounds_exception(std::string const & what) noexcept
|
|
100
|
+
: exception(qdb_e_out_of_bounds, what)
|
|
101
|
+
{}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
class uninitialized_exception : public exception
|
|
105
|
+
{
|
|
106
|
+
public:
|
|
107
|
+
uninitialized_exception() noexcept
|
|
108
|
+
: exception(qdb_e_uninitialized,
|
|
109
|
+
std::string("The requested operation is unavailable because the resource is uninitialized"))
|
|
110
|
+
{}
|
|
111
|
+
|
|
112
|
+
uninitialized_exception(std::string const & what) noexcept
|
|
113
|
+
: exception(qdb_e_uninitialized, what)
|
|
114
|
+
{}
|
|
115
|
+
};
|
|
116
|
+
|
|
92
117
|
class incompatible_type_exception : public exception
|
|
93
118
|
{
|
|
94
119
|
public:
|
|
@@ -173,6 +198,33 @@ public:
|
|
|
173
198
|
{}
|
|
174
199
|
};
|
|
175
200
|
|
|
201
|
+
class try_again_exception : public exception
|
|
202
|
+
{
|
|
203
|
+
public:
|
|
204
|
+
try_again_exception() noexcept
|
|
205
|
+
: exception(qdb_e_try_again,
|
|
206
|
+
std::string("The operation could not be completed at this time, please try again"))
|
|
207
|
+
{}
|
|
208
|
+
|
|
209
|
+
try_again_exception(std::string const & what) noexcept
|
|
210
|
+
: exception(qdb_e_try_again, what)
|
|
211
|
+
{}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
class async_pipeline_full_exception : public exception
|
|
215
|
+
{
|
|
216
|
+
public:
|
|
217
|
+
async_pipeline_full_exception() noexcept
|
|
218
|
+
: exception(qdb_e_async_pipe_full,
|
|
219
|
+
std::string("The async pipelines are currently full, please try again later or slow down "
|
|
220
|
+
"your ingestion speed."))
|
|
221
|
+
{}
|
|
222
|
+
|
|
223
|
+
async_pipeline_full_exception(std::string const & what) noexcept
|
|
224
|
+
: exception(qdb_e_async_pipe_full, what)
|
|
225
|
+
{}
|
|
226
|
+
};
|
|
227
|
+
|
|
176
228
|
class invalid_datetime_exception : public exception
|
|
177
229
|
{
|
|
178
230
|
public:
|
|
@@ -225,7 +277,7 @@ inline void qdb_throw_if_error(
|
|
|
225
277
|
qdb_error_t err_;
|
|
226
278
|
qdb_get_last_error(h, &err_, &msg_);
|
|
227
279
|
|
|
228
|
-
if (err_ != err)
|
|
280
|
+
if (err_ != err) [[unlikely]]
|
|
229
281
|
{
|
|
230
282
|
// Error context returned is not the same, which means this thread already made
|
|
231
283
|
// another call to the QDB API, or the QDB API itself
|
|
@@ -235,6 +287,16 @@ inline void qdb_throw_if_error(
|
|
|
235
287
|
case qdb_e_not_connected:
|
|
236
288
|
case qdb_e_invalid_handle:
|
|
237
289
|
throw qdb::invalid_handle_exception{};
|
|
290
|
+
|
|
291
|
+
case qdb_e_invalid_argument:
|
|
292
|
+
throw qdb::invalid_argument_exception{};
|
|
293
|
+
|
|
294
|
+
case qdb_e_try_again:
|
|
295
|
+
throw qdb::try_again_exception{};
|
|
296
|
+
|
|
297
|
+
case qdb_e_async_pipe_full:
|
|
298
|
+
throw qdb::async_pipeline_full_exception{};
|
|
299
|
+
|
|
238
300
|
default:
|
|
239
301
|
throw qdb::exception{err, qdb_error(err)};
|
|
240
302
|
}
|
|
@@ -252,6 +314,9 @@ inline void qdb_throw_if_error(
|
|
|
252
314
|
case qdb_e_invalid_query:
|
|
253
315
|
throw qdb::invalid_query_exception{msg_data_};
|
|
254
316
|
|
|
317
|
+
case qdb_e_out_of_bounds:
|
|
318
|
+
throw qdb::out_of_bounds_exception{msg_data_};
|
|
319
|
+
|
|
255
320
|
case qdb_e_not_connected:
|
|
256
321
|
case qdb_e_invalid_handle:
|
|
257
322
|
throw qdb::invalid_handle_exception{};
|
|
@@ -277,6 +342,12 @@ inline void qdb_throw_if_error(
|
|
|
277
342
|
case qdb_e_invalid_argument:
|
|
278
343
|
throw qdb::invalid_argument_exception{msg_data_};
|
|
279
344
|
|
|
345
|
+
case qdb_e_try_again:
|
|
346
|
+
throw qdb::try_again_exception{msg_data_};
|
|
347
|
+
|
|
348
|
+
case qdb_e_async_pipe_full:
|
|
349
|
+
throw qdb::async_pipeline_full_exception{msg_data_};
|
|
350
|
+
|
|
280
351
|
default:
|
|
281
352
|
throw qdb::exception{err_, msg_data_};
|
|
282
353
|
};
|
|
@@ -306,6 +377,7 @@ static inline void register_errors(Module & m)
|
|
|
306
377
|
py::register_exception<qdb::alias_already_exists_exception>(
|
|
307
378
|
m, "AliasAlreadyExistsError", base_class);
|
|
308
379
|
py::register_exception<qdb::alias_not_found_exception>(m, "AliasNotFoundError", base_class);
|
|
380
|
+
py::register_exception<qdb::uninitialized_exception>(m, "UninitializedError", base_class);
|
|
309
381
|
py::register_exception<qdb::invalid_datetime_exception>(m, "InvalidDatetimeError", base_class);
|
|
310
382
|
py::register_exception<qdb::incompatible_type_exception>(m, "IncompatibleTypeError", base_class);
|
|
311
383
|
py::register_exception<qdb::not_implemented_exception>(m, "NotImplementedError", base_class);
|
|
@@ -313,6 +385,9 @@ static inline void register_errors(Module & m)
|
|
|
313
385
|
py::register_exception<qdb::invalid_argument_exception>(m, "InvalidArgumentError", base_class);
|
|
314
386
|
py::register_exception<qdb::invalid_query_exception>(m, "InvalidQueryError", base_class);
|
|
315
387
|
py::register_exception<qdb::invalid_handle_exception>(m, "InvalidHandleError", base_class);
|
|
388
|
+
py::register_exception<qdb::try_again_exception>(m, "TryAgainError", base_class);
|
|
389
|
+
py::register_exception<qdb::async_pipeline_full_exception>(m, "AsyncPipelineFullError", base_class);
|
|
390
|
+
py::register_exception<qdb::out_of_bounds_exception>(m, "OutOfBoundsError", base_class);
|
|
316
391
|
}
|
|
317
392
|
|
|
318
393
|
} // namespace qdb
|
quasardb/masked_array.hpp
CHANGED
|
@@ -318,7 +318,11 @@ public:
|
|
|
318
318
|
~masked_array()
|
|
319
319
|
{}
|
|
320
320
|
|
|
321
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Cast this masked array to an actual numpy.ma.MaskedArray. This invokes
|
|
323
|
+
* a python function and can be slow.
|
|
324
|
+
*/
|
|
325
|
+
py::handle cast(py::return_value_policy policy) const
|
|
322
326
|
{
|
|
323
327
|
py::module numpy_ma = py::module::import("numpy.ma");
|
|
324
328
|
py::object init = numpy_ma.attr("masked_array");
|
|
@@ -410,6 +414,9 @@ public:
|
|
|
410
414
|
return arr_.dtype();
|
|
411
415
|
}
|
|
412
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Returns size of array. Includes "masked" items, i.e. those that are not visible.
|
|
419
|
+
*/
|
|
413
420
|
inline std::size_t size() const noexcept
|
|
414
421
|
{
|
|
415
422
|
assert(arr_.size() == mask_.size());
|
|
@@ -640,7 +647,7 @@ public:
|
|
|
640
647
|
/**
|
|
641
648
|
* C++->Python
|
|
642
649
|
*/
|
|
643
|
-
static py::
|
|
650
|
+
static py::object cast(type && src, return_value_policy policy, handle /* parent */)
|
|
644
651
|
{
|
|
645
652
|
return src.cast(policy);
|
|
646
653
|
}
|