diff --git a/quasardb/detail/ts_column.hpp b/quasardb/detail/ts_column.hpp index a3fd9849..8f7cf913 100644 --- a/quasardb/detail/ts_column.hpp +++ b/quasardb/detail/ts_column.hpp @@ -32,8 +32,13 @@ #include "../error.hpp" #include +#include #include +#include +#include #include +#include +#include namespace qdb { @@ -41,6 +46,8 @@ namespace qdb namespace detail { +inline constexpr const char * timestamp_column_name = "$timestamp"; + inline std::string type_to_string(qdb_ts_column_type_t t) { switch (t) @@ -144,11 +151,71 @@ static inline std::vector convert_columns( return res; } + +static inline bool is_timestamp_column(column_info const & column) +{ + return column.name == timestamp_column_name; +} + +static inline void validate_timestamp_column(column_info const & column, std::size_t idx) +{ + if (column.type != qdb_ts_column_timestamp) [[unlikely]] + { + throw qdb::invalid_argument_exception{"column '" + column.name + "' must have type timestamp"}; + } + + if (column.symtable.empty() == false) [[unlikely]] + { + throw qdb::invalid_argument_exception{ + "column '" + column.name + "' cannot define a symbol table"}; + } + + if (idx != 0) [[unlikely]] + { + throw qdb::invalid_argument_exception{ + "column '$timestamp' must be the first column in the table schema"}; + } +} + +static inline bool find_timestamp_column(const std::vector & columns) +{ + for (std::size_t idx = 0; idx < columns.size(); ++idx) + { + auto const & column = columns[idx]; + if (!is_timestamp_column(column)) continue; + + validate_timestamp_column(column, idx); + return true; + } + return false; +} + +static inline std::vector convert_create_columns_ex( + const std::vector & columns) +{ + std::vector res; + res.reserve(columns.size() + 1); + + bool has_timestamp_column = find_timestamp_column(columns); + if (has_timestamp_column == false) + { + res.push_back(qdb_ts_column_info_ex_t{ + timestamp_column_name, + qdb_ts_column_timestamp, + nullptr, + }); + } + + std::transform(columns.cbegin(), columns.cend(), std::back_inserter(res), + [](const column_info & ci) -> qdb_ts_column_info_ex_t { return ci; }); + + return res; +} + static inline std::vector convert_columns_ex( const std::vector & columns) { std::vector res(columns.size()); - std::transform(columns.cbegin(), columns.cend(), res.begin(), [](const column_info & ci) -> qdb_ts_column_info_ex_t { return ci; }); @@ -159,7 +226,6 @@ static inline std::vector convert_columns( const qdb_ts_column_info_ex_t * columns, size_t count) { std::vector res(count); - std::transform(columns, columns + count, res.begin(), [](const qdb_ts_column_info_ex_t & ci) { return column_info{ci}; }); diff --git a/quasardb/table.hpp b/quasardb/table.hpp index b8ff0b71..5e3541be 100644 --- a/quasardb/table.hpp +++ b/quasardb/table.hpp @@ -75,7 +75,7 @@ class table : public entry ttl_ = ttl.count(); } - const auto c_columns = detail::convert_columns_ex(columns); + const auto c_columns = detail::convert_create_columns_ex(columns); qdb::qdb_throw_if_error(*_handle, qdb_ts_create_ex(*_handle, _alias.c_str(), shard_size.count(), c_columns.data(), c_columns.size(), ttl_)); } @@ -325,14 +325,14 @@ static inline void register_table(Module & m) py::class_{m, "Table", "Table representation"} .def(py::init([](py::args, py::kwargs) { - throw qdb::direct_instantiation_exception{"conn.table(...)"}; - return nullptr; + throw qdb::direct_instantiation_exception{"conn.table(...)"}; + return nullptr; })) .def("__repr__", &qdb::table::repr) - .def("create", &qdb::table::create, py::arg("columns"), + .def("create", &qdb::table::create, // + py::arg("columns"), // py::arg("shard_size") = std::chrono::hours{24}, - py::arg("ttl") = std::chrono::milliseconds::zero() - ) + py::arg("ttl") = std::chrono::milliseconds::zero()) .def("retrieve_metadata", &qdb::table::retrieve_metadata) .def("column_index_by_id", &qdb::table::column_index_by_id) .def("column_type_by_id", &qdb::table::column_type_by_id) @@ -345,12 +345,11 @@ static inline void register_table(Module & m) .def("get_ttl", &qdb::table::get_ttl) .def("get_shard_size", &qdb::table::get_shard_size) - .def("reader", &qdb::table::reader, - py::kw_only(), - py::arg("column_names") = std::vector{}, - py::arg("batch_size") = std::size_t{0}, - py::arg("ranges") = std::vector{} - ) + .def("reader", &qdb::table::reader, // + py::kw_only(), // + py::arg("column_names") = std::vector{}, // + py::arg("batch_size") = std::size_t{0}, // + py::arg("ranges") = std::vector{}) .def("subscribe", &qdb::table::subscribe) .def("erase_ranges", &qdb::table::erase_ranges) @@ -360,26 +359,21 @@ static inline void register_table(Module & m) .def("int64_insert", &qdb::table::int64_insert) .def("timestamp_insert", &qdb::table::timestamp_insert) - .def("blob_get_ranges", &qdb::table::blob_get_ranges, - py::arg("column"), - py::arg("ranges") = py::none{} - ) - .def("string_get_ranges", &qdb::table::string_get_ranges, - py::arg("column"), - py::arg("ranges") = py::none{} - ) - .def("double_get_ranges", &qdb::table::double_get_ranges, - py::arg("column"), - py::arg("ranges") = py::none{} - ) - .def("int64_get_ranges", &qdb::table::int64_get_ranges, - py::arg("column"), - py::arg("ranges") = py::none{} - ) - .def("timestamp_get_ranges", &qdb::table::timestamp_get_ranges, - py::arg("column"), - py::arg("ranges") = py::none{} - ); + .def("blob_get_ranges", &qdb::table::blob_get_ranges, // + py::arg("column"), // + py::arg("ranges") = py::none{}) + .def("string_get_ranges", &qdb::table::string_get_ranges, // + py::arg("column"), // + py::arg("ranges") = py::none{}) + .def("double_get_ranges", &qdb::table::double_get_ranges, // + py::arg("column"), // + py::arg("ranges") = py::none{}) + .def("int64_get_ranges", &qdb::table::int64_get_ranges, // + py::arg("column"), // + py::arg("ranges") = py::none{}) + .def("timestamp_get_ranges", &qdb::table::timestamp_get_ranges, // + py::arg("column"), // + py::arg("ranges") = py::none{}); } } // namespace qdb diff --git a/tests/test_query.py b/tests/test_query.py index 23c51c0b..9593adde 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -415,5 +415,5 @@ def test_returns_inserted_multi_data_with_star_select( def test_create_table(qdbd_connection, entry_name): - query = 'create table "{}" (col int64)'.format(entry_name) + query = 'create table "{}" ($timestamp timestamp, col int64)'.format(entry_name) _ = qdbd_connection.query(query)