quasardb 3.13.6.post1__cp311-cp311-win32.whl → 3.13.7__cp311-cp311-win32.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 (86) hide show
  1. quasardb/CMakeFiles/generate.stamp.depend +1 -1
  2. quasardb/CMakeLists.txt +43 -22
  3. quasardb/INSTALL.vcxproj +11 -11
  4. quasardb/INSTALL.vcxproj.filters +2 -2
  5. quasardb/__init__.py +1 -1
  6. quasardb/add_boost_test.cmake +43 -0
  7. quasardb/batch_column.hpp +1 -1
  8. quasardb/batch_inserter.hpp +1 -1
  9. quasardb/blob.hpp +1 -1
  10. quasardb/cluster.hpp +42 -17
  11. quasardb/cmake_install.cmake +4 -4
  12. quasardb/concepts.hpp +30 -16
  13. quasardb/continuous.hpp +1 -1
  14. quasardb/convert/array.hpp +61 -7
  15. quasardb/convert/point.hpp +16 -11
  16. quasardb/convert/range.hpp +19 -25
  17. quasardb/convert/unicode.hpp +457 -149
  18. quasardb/convert/value.hpp +87 -18
  19. quasardb/convert.hpp +1 -1
  20. quasardb/date/ALL_BUILD.vcxproj +21 -21
  21. quasardb/date/ALL_BUILD.vcxproj.filters +1 -1
  22. quasardb/date/CMakeFiles/generate.stamp.depend +1 -1
  23. quasardb/date/INSTALL.vcxproj +11 -11
  24. quasardb/date/INSTALL.vcxproj.filters +2 -2
  25. quasardb/date/cmake_install.cmake +6 -6
  26. quasardb/date/date.sln +31 -31
  27. quasardb/date/dateTargets.cmake +2 -2
  28. quasardb/detail/qdb_resource.hpp +6 -6
  29. quasardb/detail/ts_column.hpp +1 -1
  30. quasardb/direct_blob.hpp +1 -1
  31. quasardb/direct_handle.hpp +1 -1
  32. quasardb/direct_integer.hpp +1 -1
  33. quasardb/{version.cpp → double.hpp} +46 -28
  34. quasardb/entry.hpp +1 -1
  35. quasardb/error.hpp +14 -10
  36. quasardb/handle.hpp +1 -1
  37. quasardb/integer.hpp +1 -1
  38. quasardb/logger.hpp +1 -1
  39. quasardb/masked_array.hpp +1 -1
  40. quasardb/module.cpp +73 -0
  41. quasardb/module.hpp +24 -0
  42. quasardb/node.hpp +16 -11
  43. quasardb/numpy/__init__.py +76 -7
  44. quasardb/numpy.hpp +1 -1
  45. quasardb/options.hpp +31 -9
  46. quasardb/pandas/__init__.py +1 -1
  47. quasardb/perf.hpp +6 -6
  48. quasardb/pinned_writer.hpp +1 -1
  49. quasardb/pybind11/ALL_BUILD.vcxproj +25 -25
  50. quasardb/pybind11/ALL_BUILD.vcxproj.filters +1 -1
  51. quasardb/pybind11/CMakeFiles/generate.stamp.depend +4 -4
  52. quasardb/pybind11/INSTALL.vcxproj +11 -11
  53. quasardb/pybind11/INSTALL.vcxproj.filters +2 -2
  54. quasardb/pybind11/cmake_install.cmake +1 -1
  55. quasardb/pybind11/pybind11.sln +31 -31
  56. quasardb/pytypes.hpp +2 -2
  57. quasardb/qdb_api.dll +0 -0
  58. quasardb/quasardb.cp311-win32.pyd +0 -0
  59. quasardb/query.cpp +3 -3
  60. quasardb/query.hpp +1 -1
  61. quasardb/range-v3/ALL_BUILD.vcxproj +25 -25
  62. quasardb/range-v3/ALL_BUILD.vcxproj.filters +1 -1
  63. quasardb/range-v3/CMakeFiles/generate.stamp.depend +6 -6
  64. quasardb/range-v3/INSTALL.vcxproj +11 -11
  65. quasardb/range-v3/INSTALL.vcxproj.filters +2 -2
  66. quasardb/range-v3/Range-v3.sln +39 -39
  67. quasardb/range-v3/cmake_install.cmake +6 -6
  68. quasardb/range-v3/range-v3-config.cmake +3 -3
  69. quasardb/range-v3/range.v3.headers.vcxproj +338 -338
  70. quasardb/range-v3/range.v3.headers.vcxproj.filters +315 -315
  71. quasardb/reader/ts_row.hpp +1 -1
  72. quasardb/reader/ts_value.hpp +1 -1
  73. quasardb/string.hpp +160 -0
  74. quasardb/table.hpp +1 -1
  75. quasardb/table_reader.hpp +1 -1
  76. quasardb/tag.hpp +1 -1
  77. quasardb/timestamp.hpp +97 -0
  78. quasardb/utils.hpp +1 -1
  79. {quasardb-3.13.6.post1.dist-info → quasardb-3.13.7.dist-info}/LICENSE.md +1 -1
  80. {quasardb-3.13.6.post1.dist-info → quasardb-3.13.7.dist-info}/METADATA +1 -1
  81. quasardb-3.13.7.dist-info/RECORD +114 -0
  82. {quasardb-3.13.6.post1.dist-info → quasardb-3.13.7.dist-info}/WHEEL +1 -1
  83. quasardb/qdb_client.cpp +0 -67
  84. quasardb/version.hpp +0 -43
  85. quasardb-3.13.6.post1.dist-info/RECORD +0 -111
  86. {quasardb-3.13.6.post1.dist-info → quasardb-3.13.7.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
30
30
  */
31
31
  #pragma once
32
32
 
33
- #include "../handle.hpp"
33
+ #include <qdb/client.h> // for qdb_handle_t
34
34
 
35
35
  namespace qdb
36
36
  {
@@ -49,14 +49,14 @@ public:
49
49
  /**
50
50
  * Default constructor, initializes nullpointer.
51
51
  */
52
- qdb_resource(qdb::handle_ptr h)
52
+ qdb_resource(qdb_handle_t h)
53
53
  : qdb_resource(h, nullptr)
54
54
  {}
55
55
 
56
56
  /**
57
57
  * Constructor and immediately initialize with pointer.
58
58
  */
59
- qdb_resource(qdb::handle_ptr h, ValueType * p)
59
+ qdb_resource(qdb_handle_t h, ValueType * p)
60
60
  : h_(h)
61
61
  , p_(p)
62
62
  {}
@@ -68,7 +68,7 @@ public:
68
68
  {
69
69
  if (p_ != nullptr)
70
70
  {
71
- qdb_release(*h_, p_);
71
+ qdb_release(h_, p_);
72
72
  }
73
73
 
74
74
  p_ = nullptr;
@@ -110,7 +110,7 @@ public:
110
110
  }
111
111
 
112
112
  private:
113
- qdb::handle_ptr h_;
113
+ qdb_handle_t h_;
114
114
  ValueType * p_;
115
115
  };
116
116
 
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/direct_blob.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -28,42 +28,60 @@
28
28
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
29
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
  */
31
- #include "version.hpp"
32
- #include <regex>
33
- #include <sstream>
34
- #include <stdexcept>
31
+ #pragma once
35
32
 
36
- static std::pair<int, int> get_version_pair(const char * version)
33
+ #include "entry.hpp"
34
+ #include <qdb/double.h>
35
+
36
+ namespace qdb
37
+ {
38
+
39
+ class double_entry : public expirable_entry
37
40
  {
38
- std::regex re("([0-9]+)\\.([0-9]+)\\..*");
39
- std::cmatch m;
40
- if (!std::regex_match(version, m, re))
41
+ public:
42
+ double_entry(handle_ptr h, std::string a) noexcept
43
+ : expirable_entry{h, a}
44
+ {}
45
+
46
+ public:
47
+ double get()
41
48
  {
42
- std::ostringstream sstr;
43
- sstr << "Got an invalid QuasarDB C API version string (" << version << ").";
44
- throw std::invalid_argument(sstr.str());
49
+ double result;
50
+ qdb::qdb_throw_if_error(*_handle, qdb_double_get(*_handle, _alias.c_str(), &result));
51
+ return result;
45
52
  }
46
- const auto major = std::stoi(m[1].str());
47
- const auto minor = std::stoi(m[2].str());
48
- return std::make_pair(major, minor);
49
- }
50
53
 
51
- namespace qdb
52
- {
54
+ void put(double val)
55
+ {
56
+ qdb::qdb_throw_if_error(*_handle, qdb_double_put(*_handle, _alias.c_str(), val, qdb_time_t{0}));
57
+ }
53
58
 
54
- const char * const qdb_c_api_version = QDB_PY_VERSION;
59
+ void update(double val)
60
+ {
61
+ qdb::qdb_throw_if_error(
62
+ *_handle, qdb_double_update(*_handle, _alias.c_str(), val, qdb_time_t{0}));
63
+ }
55
64
 
56
- void check_qdb_c_api_version(const char * candidate)
57
- {
58
- const auto ver_c = get_version_pair(candidate);
59
- const auto ver_ref = get_version_pair(qdb_c_api_version);
60
- if (ver_c != ver_ref)
65
+ double add(double val)
61
66
  {
62
- std::ostringstream sstr;
63
- sstr << "QuasarDB C API version mismatch. Expected " << ver_ref.first << '.' << ver_ref.second
64
- << " but got " << ver_c.first << '.' << ver_c.second << " instead.";
65
- throw std::runtime_error(sstr.str());
67
+ double result;
68
+ qdb::qdb_throw_if_error(*_handle, qdb_double_add(*_handle, _alias.c_str(), val, &result));
69
+ return result;
66
70
  }
71
+ };
72
+
73
+ template <typename Module>
74
+ static inline void register_double(Module & m)
75
+ {
76
+ namespace py = pybind11;
77
+
78
+ py::class_<qdb::double_entry, qdb::expirable_entry>(m, "Double") //
79
+ .def(py::init<qdb::handle_ptr, std::string>()) //
80
+ .def("get", &qdb::double_entry::get) //
81
+ .def("put", &qdb::double_entry::put, py::arg("double")) //
82
+ .def("update", &qdb::double_entry::update, py::arg("double")) //
83
+ .def("add", &qdb::double_entry::add, py::arg("addend")) //
84
+ ; //
67
85
  }
68
86
 
69
87
  } // namespace qdb
quasardb/entry.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/error.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@
35
35
  #include <qdb/client.h>
36
36
  #include <qdb/error.h>
37
37
  #include <qdb/query.h>
38
+ #include "detail/qdb_resource.hpp"
38
39
  #include <pybind11/pybind11.h>
39
40
  #include <iostream>
40
41
  #include <utility>
@@ -209,7 +210,7 @@ inline void qdb_throw_if_error(
209
210
 
210
211
  if ((qdb_e_ok != err) && (qdb_e_ok_created != err)) [[unlikely]]
211
212
  {
212
- qdb_string_t msg_;
213
+ detail::qdb_resource<qdb_string_t> msg_{h, nullptr};
213
214
  qdb_error_t err_;
214
215
  qdb_get_last_error(h, &err_, &msg_);
215
216
 
@@ -220,38 +221,41 @@ inline void qdb_throw_if_error(
220
221
  throw qdb::exception{err, qdb_error(err)};
221
222
  }
222
223
  assert(err_ == err);
224
+ assert(msg_ != nullptr);
223
225
 
224
226
  pre_throw();
225
227
 
228
+ std::string msg_data_{msg_.get()->data};
229
+
226
230
  switch (err)
227
231
  {
228
232
 
229
233
  case qdb_e_invalid_query:
230
- throw qdb::invalid_query_exception{msg_.data};
234
+ throw qdb::invalid_query_exception{msg_data_};
231
235
 
232
236
  case qdb_e_alias_already_exists:
233
- throw qdb::alias_already_exists_exception{msg_.data};
237
+ throw qdb::alias_already_exists_exception{msg_data_};
234
238
 
235
239
  case qdb_e_alias_not_found:
236
- throw qdb::alias_not_found_exception{msg_.data};
240
+ throw qdb::alias_not_found_exception{msg_data_};
237
241
 
238
242
  case qdb_e_network_inbuf_too_small:
239
243
  throw qdb::input_buffer_too_small_exception{};
240
244
 
241
245
  case qdb_e_incompatible_type:
242
- throw qdb::incompatible_type_exception{msg_.data};
246
+ throw qdb::incompatible_type_exception{msg_data_};
243
247
 
244
248
  case qdb_e_not_implemented:
245
- throw qdb::not_implemented_exception{msg_.data};
249
+ throw qdb::not_implemented_exception{msg_data_};
246
250
 
247
251
  case qdb_e_internal_local:
248
- throw qdb::internal_local_exception{msg_.data};
252
+ throw qdb::internal_local_exception{msg_data_};
249
253
 
250
254
  case qdb_e_invalid_argument:
251
- throw qdb::invalid_argument_exception{msg_.data};
255
+ throw qdb::invalid_argument_exception{msg_data_};
252
256
 
253
257
  default:
254
- throw qdb::exception{err_, msg_.data};
258
+ throw qdb::exception{err_, msg_data_};
255
259
  };
256
260
  }
257
261
  }
quasardb/handle.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/integer.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/logger.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/masked_array.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/module.cpp ADDED
@@ -0,0 +1,73 @@
1
+ #include "module.hpp"
2
+ #include "cluster.hpp"
3
+ #include "node.hpp"
4
+ #include <functional>
5
+ #include <list>
6
+
7
+ namespace qdb
8
+ {
9
+ /**
10
+ * This approach is inspired from pybind11, where we keep a global static variable
11
+ * of initializers for components/modules that which to register with our global
12
+ * quasardb module.
13
+ *
14
+ * It has the downside of module initialization order essentially being random, which
15
+ * is fine for our use case. It allows decoupling of code in many places.
16
+ */
17
+ std::list<std::function<void(py::module_ &)>> & initializers()
18
+ {
19
+ static std::list<std::function<void(py::module_ &)>> inits;
20
+ return inits;
21
+ }
22
+
23
+ submodule_initializer::submodule_initializer(initialize_fn init)
24
+ {
25
+ initializers().emplace_back(init);
26
+ }
27
+
28
+ submodule_initializer::submodule_initializer(const char * submodule_name, initialize_fn init)
29
+ {
30
+ initializers().emplace_back([=](py::module_ & parent) { init(parent); });
31
+ }
32
+
33
+ }; // namespace qdb
34
+
35
+ PYBIND11_MODULE(quasardb, m)
36
+ {
37
+ m.doc() = "QuasarDB Official Python API";
38
+ m.def("version", &qdb_version, "Return version number");
39
+ m.def("build", &qdb_build, "Return build number");
40
+ m.attr("never_expires") = std::chrono::system_clock::time_point{};
41
+
42
+ qdb::register_errors(m);
43
+ qdb::register_cluster(m);
44
+ qdb::register_node(m);
45
+ qdb::register_options(m);
46
+ qdb::register_perf(m);
47
+ qdb::register_entry(m);
48
+ qdb::register_double(m);
49
+ qdb::register_integer(m);
50
+ qdb::register_blob(m);
51
+ qdb::register_string(m);
52
+ qdb::register_timestamp(m);
53
+ qdb::register_direct_blob(m);
54
+ qdb::register_direct_integer(m);
55
+ qdb::register_tag(m);
56
+ qdb::register_query(m);
57
+ qdb::register_continuous(m);
58
+ qdb::register_table(m);
59
+ qdb::register_batch_column(m);
60
+ qdb::register_batch_inserter(m);
61
+ qdb::register_pinned_writer(m);
62
+ qdb::register_table_reader(m);
63
+ qdb::register_masked_array(m);
64
+
65
+ qdb::detail::register_ts_column(m);
66
+ qdb::reader::register_ts_value(m);
67
+ qdb::reader::register_ts_row(m);
68
+
69
+ for (const auto & initializer : qdb::initializers())
70
+ {
71
+ initializer(m);
72
+ }
73
+ }
quasardb/module.hpp ADDED
@@ -0,0 +1,24 @@
1
+ #pragma once
2
+
3
+ #include <pybind11/pybind11.h>
4
+
5
+ namespace qdb
6
+ {
7
+
8
+ namespace py = pybind11;
9
+
10
+ class submodule_initializer
11
+ {
12
+ using initialize_fn = void (*)(py::module_ &);
13
+
14
+ public:
15
+ explicit submodule_initializer(initialize_fn init);
16
+ submodule_initializer(const char * submodule_name, initialize_fn init);
17
+ };
18
+
19
+ }; // namespace qdb
20
+
21
+ #define QDB_REGISTER_MODULE(name, variable) \
22
+ void qdb_submodule_##name(py::module_ &); \
23
+ submodule_initializer name(#name, qdb_submodule_##name); \
24
+ void qdb_submodule_##name(py::module_ &(variable))
quasardb/node.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -47,14 +47,17 @@ public:
47
47
  * Construct a direct node connection directly, possibly with security credentials.
48
48
  */
49
49
  node(const std::string & node_uri,
50
- const std::string & user_name = {},
51
- const std::string & user_private_key = {},
52
- const std::string & cluster_public_key = {})
50
+ const std::string & user_name = {},
51
+ const std::string & user_private_key = {},
52
+ const std::string & cluster_public_key = {},
53
+ const std::string & user_security_file = {},
54
+ const std::string & cluster_public_key_file = {})
53
55
  : _uri{node_uri}
54
56
  , _handle{make_handle_ptr()}
55
57
  , _direct_handle{make_direct_handle_ptr()}
56
58
  {
57
- qdb::options{_handle}.apply_credentials(user_name, user_private_key, cluster_public_key);
59
+ qdb::options{_handle}.apply_credentials(user_name, user_private_key, cluster_public_key,
60
+ user_security_file, cluster_public_key_file);
58
61
  _direct_handle->connect(_handle, node_uri);
59
62
  }
60
63
 
@@ -104,12 +107,14 @@ static inline void register_node(Module & m)
104
107
  namespace py = pybind11;
105
108
 
106
109
  py::class_<qdb::node>(m, "Node")
107
- .def(py::init<std::string const &, std::string const &, std::string const &,
108
- std::string const &>(),
109
- py::arg("uri"), //
110
- py::arg("user_name") = std::string{}, //
111
- py::arg("user_private_key") = std::string{}, //
112
- py::arg("cluster_public_key") = std::string{}) //
110
+ .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{}) //
113
118
  .def("prefix_get", &qdb::node::prefix_get)
114
119
  .def("blob", &qdb::node::blob)
115
120
  .def("integer", &qdb::node::integer);
@@ -81,6 +81,19 @@ class IncompatibleDtypeErrors(TypeError):
81
81
  def msg(self):
82
82
  return "\n".join(x.msg() for x in self.xs)
83
83
 
84
+ class InvalidDataCardinalityError(ValueError):
85
+ """
86
+ Raised when the provided data arrays doesn't match the table's columns.
87
+ """
88
+ def __init__(self, data, cinfos):
89
+ self.data = data
90
+ self.cinfos = cinfos
91
+ super().__init__(self.msg())
92
+
93
+ def msg(self):
94
+ return "Provided data array length '{}' exceeds amount of table columns '{}', unable to map data to columns".format(len(self.data), len(self.cinfos))
95
+
96
+
84
97
  # Based on QuasarDB column types, which dtype do we accept?
85
98
  # First entry will always be the 'preferred' dtype, other ones
86
99
  # those that we can natively convert in native code.
@@ -259,6 +272,42 @@ def _coerce_deduplicate(deduplicate, deduplication_mode, columns):
259
272
 
260
273
  return deduplicate
261
274
 
275
+ def _clean_nulls(xs, dtype):
276
+ """
277
+ Numpy's masked arrays have a downside that in case they're not able to convert a (masked!) value to
278
+ the desired dtype, they raise an error. So, for example, if I have a masked array of objects that
279
+ look like this
280
+
281
+ xs: [1.234 <pd.NA> 5.678]
282
+ mask: [1 0 1]
283
+
284
+ even though pd.NA is not "visible", because it cannot be converted to a float(), the operation will
285
+ fail!
286
+
287
+ This function fixes this by replacing the null values with an acceptable value that can always be
288
+ converted to the desired dtype.
289
+ """
290
+
291
+ assert ma.isMA(xs)
292
+
293
+ if xs.dtype is not np.dtype('object'):
294
+ return xs
295
+
296
+ fill_value = None
297
+ if dtype == np.float64 or dtype == np.float32 or dtype == np.float16:
298
+ fill_value = float('nan')
299
+ elif dtype == np.int64 or dtype == np.int32 or dtype == np.int16:
300
+ fill_value = -1
301
+ elif dtype == np.dtype('datetime64[ns]'):
302
+ fill_value = np.datetime64('nat')
303
+
304
+ mask = xs.mask
305
+ xs_ = xs.filled(fill_value)
306
+
307
+ return ma.array(xs_, mask=mask)
308
+
309
+
310
+
262
311
  def _coerce_data(data, dtype):
263
312
  """
264
313
  Coerces each numpy array of `data` to the dtype present in `dtype`.
@@ -271,25 +320,34 @@ def _coerce_data(data, dtype):
271
320
  data_ = data[i]
272
321
 
273
322
  if dtype_ is not None and dtypes_equal(data_.dtype, dtype_) == False:
323
+ data_ = _clean_nulls(data_, dtype_)
324
+
325
+ assert ma.isMA(data_)
326
+
274
327
  logger.debug("data for column with offset %d was provided in dtype '%s', but need '%s': converting data...", i, data_.dtype, dtype_)
275
328
 
276
- logger.debug("type of data[%d] after: %s", i, type(data[i]))
277
- logger.debug("size of data[%d] after: %s", i, ma.size(data[i]))
278
- logger.debug("data of data[%d] after: %s", i, data[i])
329
+ logger.debug("dtype of data[%d] before: %s", i, data_.dtype)
330
+ logger.debug("type of data[%d] after: %s", i, type(data_))
331
+ logger.debug("size of data[%d] after: %s", i, ma.size(data_))
332
+ logger.debug("data of data[%d] after: %s", i, data_)
279
333
 
280
334
  try:
281
335
  data[i] = data_.astype(dtype_)
282
336
  except TypeError as err:
283
337
  # One 'bug' is that, if everything is masked, the underlying data type can be
284
338
  # pretty much anything.
285
- if not _is_all_masked(data_):
339
+ if _is_all_masked(data_):
340
+ logger.debug("array completely empty, re-initializing to empty array of '%s'", dtype_)
341
+ data[i] = ma.masked_all(ma.size(data_),
342
+ dtype=dtype_)
343
+
344
+ # Another 'bug' is that when the input data is objects, we may have null-like values (like pd.NA)
345
+ # that cannot easily be converted to, say, float.
346
+ else:
286
347
  logger.error("An error occured while coercing input data type from dtype '%s' to dtype '%s': ", data_.dtype, dtype_)
287
348
  logger.exception(err)
288
349
  raise err
289
350
 
290
- logger.debug("array completely empty, re-initializing to empty array of '%s'", dtype_)
291
- data[i] = ma.masked_all(ma.size(data_),
292
- dtype=dtype_)
293
351
  assert data[i].dtype.kind == dtype_.kind
294
352
 
295
353
  logger.debug("type of data[%d] after: %s", i, type(data[i]))
@@ -321,6 +379,13 @@ def _ensure_list(xs, cinfos):
321
379
  if isinstance(xs, list):
322
380
  return xs
323
381
 
382
+ if isinstance(xs, np.ndarray):
383
+ ret = []
384
+ for x in xs:
385
+ ret.append(x)
386
+
387
+ return ret
388
+
324
389
  # As we only accept list-likes or dicts as input data, it *must* be a dict at this
325
390
  # point
326
391
  assert isinstance(xs, dict)
@@ -612,6 +677,10 @@ def write_arrays(
612
677
  dtype = _add_desired_dtypes(dtype, cinfos)
613
678
 
614
679
  data = _ensure_list(data, cinfos)
680
+
681
+ if len(data) != len(cinfos):
682
+ raise InvalidDataCardinalityError(data, cinfos)
683
+
615
684
  data = ensure_ma(data, dtype=dtype)
616
685
  data = _coerce_data(data, dtype)
617
686
 
quasardb/numpy.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
quasardb/options.hpp CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * Official Python API
4
4
  *
5
- * Copyright (c) 2009-2022, quasardb SAS. All rights reserved.
5
+ * Copyright (c) 2009-2023, quasardb SAS. All rights reserved.
6
6
  * All rights reserved.
7
7
  *
8
8
  * Redistribution and use in source and binary forms, with or without
@@ -50,21 +50,35 @@ public:
50
50
  */
51
51
  void apply_credentials(const std::string & user_name,
52
52
  const std::string & user_private_key,
53
- const std::string & cluster_public_key)
53
+ const std::string & cluster_public_key,
54
+ const std::string & user_security_file,
55
+ const std::string & cluster_public_key_file)
54
56
  {
55
- // must specify everything or nothing
56
- if (user_name.empty() != user_private_key.empty())
57
- throw qdb::exception{qdb_e_invalid_argument,
58
- "Either all security settings must be provided, or none at all"};
59
- if (user_name.empty() != cluster_public_key.empty())
57
+ // must specify keys or files or nothing
58
+ auto empty_keys = user_name.empty() && user_private_key.empty() && cluster_public_key.empty();
59
+ auto empty_files = user_security_file.empty() && cluster_public_key_file.empty();
60
+
61
+ if(!empty_keys && !empty_files)
60
62
  throw qdb::exception{qdb_e_invalid_argument,
61
- "Either all security settings must be provided, or none at all"};
63
+ "Either key or file security settings must be provided, or none at all"};
62
64
 
63
- if (!user_name.empty())
65
+ if (!empty_keys)
64
66
  {
67
+ if (user_name.empty() || user_private_key.empty() || cluster_public_key.empty())
68
+ throw qdb::exception{qdb_e_invalid_argument,
69
+ "Either all keys security settings must be provided, or none at all"};
70
+
65
71
  set_user_credentials(user_name, user_private_key);
66
72
  set_cluster_public_key(cluster_public_key);
67
73
  }
74
+ else if (!empty_files)
75
+ {
76
+ if (user_security_file.empty() || cluster_public_key_file.empty())
77
+ throw qdb::exception{qdb_e_invalid_argument,
78
+ "Either all files security settings must be provided, or none at all"};
79
+
80
+ set_file_credential(user_security_file, cluster_public_key_file);
81
+ }
68
82
  };
69
83
 
70
84
  void set_timeout(std::chrono::milliseconds ms)
@@ -123,6 +137,14 @@ public:
123
137
  *_handle, qdb_option_set_user_credentials(*_handle, user.c_str(), private_key.c_str()));
124
138
  }
125
139
 
140
+ void set_file_credential(
141
+ const std::string & user_security_file, const std::string & cluster_public_key_file)
142
+ {
143
+ qdb::qdb_throw_if_error(
144
+ *_handle, qdb_option_load_security_files(
145
+ *_handle, cluster_public_key_file.c_str(), user_security_file.c_str()));
146
+ }
147
+
126
148
  void set_client_max_in_buf_size(size_t max_size)
127
149
  {
128
150
  qdb::qdb_throw_if_error(*_handle, qdb_option_set_client_max_in_buf_size(*_handle, max_size));