| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * License); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "cwrapper/tsfile_cwrapper.h" |
| |
| #include <fcntl.h> |
| #include <file/write_file.h> |
| #include <reader/qds_without_timegenerator.h> |
| #include <writer/tsfile_table_writer.h> |
| |
| #ifdef _WIN32 |
| #include <io.h> |
| #else |
| #include <unistd.h> |
| #endif |
| |
| #include <cstring> |
| #include <limits> |
| #include <memory> |
| #include <new> |
| #include <set> |
| #include <vector> |
| |
| #include "common/device_id.h" |
| #include "common/global.h" |
| #include "common/statistic.h" |
| #include "common/tablet.h" |
| #include "common/tsfile_common.h" |
| #include "reader/filter/tag_filter.h" |
| #include "reader/result_set.h" |
| #include "reader/table_result_set.h" |
| #include "reader/tsfile_reader.h" |
| #include "utils/injection.h" |
| #include "writer/tsfile_writer.h" |
| |
| // Forward declarations for arrow namespace functions (defined in arrow_c.cc) |
| namespace arrow { |
| int TsBlockToArrowStruct(common::TsBlock& tsblock, ArrowArray* out_array, |
| ArrowSchema* out_schema); |
| int ArrowStructToTablet(const char* table_name, const ArrowArray* in_array, |
| const ArrowSchema* in_schema, |
| const storage::TableSchema* reg_schema, |
| storage::Tablet** out_tablet, int time_col_index); |
| } // namespace arrow |
| |
| namespace { |
| |
| bool is_supported_table_type(TSDataType type) { |
| switch (type) { |
| case TS_DATATYPE_BOOLEAN: |
| case TS_DATATYPE_INT32: |
| case TS_DATATYPE_INT64: |
| case TS_DATATYPE_FLOAT: |
| case TS_DATATYPE_DOUBLE: |
| case TS_DATATYPE_TEXT: |
| case TS_DATATYPE_TIMESTAMP: |
| case TS_DATATYPE_DATE: |
| case TS_DATATYPE_BLOB: |
| case TS_DATATYPE_STRING: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| ERRNO validate_table_schema(const TableSchema* schema) { |
| if (schema == nullptr || schema->table_name == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| if (schema->table_name[0] == '\0' || schema->column_num <= 0) { |
| return common::E_INVALID_SCHEMA; |
| } |
| if (schema->column_schemas == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| std::set<std::string> names; |
| for (int i = 0; i < schema->column_num; ++i) { |
| const ColumnSchema& column = schema->column_schemas[i]; |
| if (column.column_name == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| if (column.column_name[0] == '\0' || |
| !names.insert(storage::to_lower(column.column_name)).second || |
| !is_supported_table_type(column.data_type) || |
| (column.column_category != TAG && |
| column.column_category != FIELD) || |
| (column.column_category == TAG && |
| column.data_type != TS_DATATYPE_STRING)) { |
| return common::E_INVALID_SCHEMA; |
| } |
| } |
| return common::E_OK; |
| } |
| |
| } // namespace |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| void init_tsfile_config() { storage::libtsfile_init(); } |
| |
| uint8_t get_global_time_encoding() { |
| return common::get_global_time_encoding(); |
| } |
| |
| uint8_t get_global_time_compression() { |
| return common::get_global_time_compression(); |
| } |
| |
| uint8_t get_datatype_encoding(uint8_t data_type) { |
| return common::get_datatype_encoding(data_type); |
| } |
| |
| uint8_t get_global_compression() { return common::get_global_compression(); } |
| |
| int set_global_time_encoding(uint8_t encoding) { |
| return common::set_global_time_encoding(encoding); |
| } |
| |
| int set_global_time_compression(uint8_t compression) { |
| return common::set_global_time_compression(compression); |
| } |
| |
| int set_datatype_encoding(uint8_t data_type, uint8_t encoding) { |
| return common::set_datatype_encoding(data_type, encoding); |
| } |
| |
| int set_global_compression(uint8_t compression) { |
| return common::set_global_compression(compression); |
| } |
| |
| ERRNO tsfile_set_file_read_backend(int32_t backend) { |
| switch (backend) { |
| case TSFILE_READ_BACKEND_AUTO: |
| return common::set_file_read_backend(common::FileReadBackend::AUTO); |
| case TSFILE_READ_BACKEND_MMAP: |
| return common::set_file_read_backend(common::FileReadBackend::MMAP); |
| case TSFILE_READ_BACKEND_PREAD: |
| return common::set_file_read_backend( |
| common::FileReadBackend::PREAD); |
| default: |
| return common::E_INVALID_ARG; |
| } |
| } |
| |
| TsFileReadBackend tsfile_get_file_read_backend() { |
| return static_cast<TsFileReadBackend>(common::get_file_read_backend()); |
| } |
| |
| WriteFile write_file_new(const char* pathname, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| if (pathname == nullptr || pathname[0] == '\0') { |
| *err_code = common::E_INVALID_ARG; |
| return nullptr; |
| } |
| int ret; |
| init_tsfile_config(); |
| |
| int flags = O_RDWR | O_CREAT | O_TRUNC; |
| #ifdef _WIN32 |
| flags |= O_BINARY; |
| #endif |
| mode_t mode = 0666; |
| storage::WriteFile* file = new (std::nothrow) storage::WriteFile; |
| if (file == nullptr) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } |
| ret = file->create(pathname, flags, mode); |
| if (ret != common::E_OK) { |
| delete file; |
| *err_code = ret; |
| return nullptr; |
| } |
| *err_code = ret; |
| return file; |
| } |
| |
| TsFileWriter tsfile_writer_new(WriteFile file, TableSchema* schema, |
| ERRNO* err_code) { |
| // C API: every public entry must defend against null callers — a null |
| // schema or err_code would crash the host process the moment it's |
| // dereferenced. The tag-filter helpers already follow this pattern. |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| if (file == nullptr) { |
| *err_code = common::E_INVALID_ARG; |
| return nullptr; |
| } |
| *err_code = validate_table_schema(schema); |
| if (*err_code != common::E_OK) { |
| return nullptr; |
| } |
| |
| try { |
| init_tsfile_config(); |
| std::vector<common::ColumnSchema> column_schemas; |
| for (int i = 0; i < schema->column_num; i++) { |
| ColumnSchema cur_schema = schema->column_schemas[i]; |
| column_schemas.emplace_back( |
| cur_schema.column_name, |
| static_cast<common::TSDataType>(cur_schema.data_type), |
| static_cast<common::ColumnCategory>( |
| cur_schema.column_category)); |
| } |
| |
| std::unique_ptr<storage::TableSchema> table_schema( |
| new storage::TableSchema(schema->table_name, column_schemas)); |
| std::unique_ptr<storage::TsFileTableWriter> table_writer( |
| new storage::TsFileTableWriter( |
| static_cast<storage::WriteFile*>(file), table_schema.get())); |
| *err_code = table_writer->get_error(); |
| if (*err_code != common::E_OK) { |
| return nullptr; |
| } |
| return table_writer.release(); |
| } catch (const std::bad_alloc&) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } catch (...) { |
| *err_code = common::E_INVALID_SCHEMA; |
| return nullptr; |
| } |
| } |
| |
| TsFileWriter tsfile_writer_new_with_memory_threshold(WriteFile file, |
| TableSchema* schema, |
| uint64_t memory_threshold, |
| ERRNO* err_code) { |
| // See tsfile_writer_new() above for the null-guard rationale. |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| if (file == nullptr || memory_threshold == 0) { |
| *err_code = common::E_INVALID_ARG; |
| return nullptr; |
| } |
| *err_code = validate_table_schema(schema); |
| if (*err_code != common::E_OK) { |
| return nullptr; |
| } |
| try { |
| init_tsfile_config(); |
| std::vector<common::ColumnSchema> column_schemas; |
| for (int i = 0; i < schema->column_num; i++) { |
| ColumnSchema cur_schema = schema->column_schemas[i]; |
| column_schemas.emplace_back( |
| cur_schema.column_name, |
| static_cast<common::TSDataType>(cur_schema.data_type), |
| static_cast<common::ColumnCategory>( |
| cur_schema.column_category)); |
| } |
| |
| std::unique_ptr<storage::TableSchema> table_schema( |
| new storage::TableSchema(schema->table_name, column_schemas)); |
| std::unique_ptr<storage::TsFileTableWriter> table_writer( |
| new storage::TsFileTableWriter( |
| static_cast<storage::WriteFile*>(file), table_schema.get(), |
| memory_threshold)); |
| *err_code = table_writer->get_error(); |
| if (*err_code != common::E_OK) { |
| return nullptr; |
| } |
| return table_writer.release(); |
| } catch (const std::bad_alloc&) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } catch (...) { |
| *err_code = common::E_INVALID_SCHEMA; |
| return nullptr; |
| } |
| } |
| |
| TsFileReader tsfile_reader_new(const char* pathname, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| if (pathname == nullptr || pathname[0] == '\0') { |
| *err_code = common::E_INVALID_ARG; |
| return nullptr; |
| } |
| init_tsfile_config(); |
| auto reader = new (std::nothrow) storage::TsFileReader(); |
| if (reader == nullptr) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } |
| int ret = reader->open(pathname); |
| if (ret != common::E_OK) { |
| *err_code = ret; |
| delete reader; |
| return nullptr; |
| } |
| return reader; |
| } |
| |
| ERRNO tsfile_writer_close(TsFileWriter writer) { |
| if (writer == nullptr) { |
| return common::E_OK; |
| } |
| auto* w = static_cast<storage::TsFileTableWriter*>(writer); |
| int ret = w->close(); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| delete w; |
| return ret; |
| } |
| |
| ERRNO tsfile_writer_add_tsfile_property(TsFileWriter writer, const char* key, |
| uint32_t key_len, const uint8_t* value, |
| uint32_t value_len) { |
| if (writer == nullptr || key == nullptr || |
| (value == nullptr && value_len > 0)) { |
| return common::E_INVALID_ARG; |
| } |
| if (key_len > static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) || |
| value_len > |
| static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { |
| return common::E_OUT_OF_RANGE; |
| } |
| try { |
| auto* w = static_cast<storage::TsFileTableWriter*>(writer); |
| return w->add_tsfile_property(std::string(key, key_len), value, |
| value_len); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_writer_flush(TsFileWriter writer) { |
| if (writer == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| return static_cast<storage::TsFileTableWriter*>(writer)->flush(); |
| } |
| |
| ERRNO tsfile_reader_close(TsFileReader reader) { |
| auto* ts_reader = static_cast<storage::TsFileReader*>(reader); |
| delete ts_reader; |
| return common::E_OK; |
| } |
| |
| Tablet tablet_new(char** column_name_list, TSDataType* data_types, |
| uint32_t column_num, uint32_t max_rows, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (column_num == 0 || max_rows == 0 || column_name_list == nullptr || |
| data_types == nullptr || max_rows >= (1u << 30)) { |
| return nullptr; |
| } |
| try { |
| std::vector<std::string> measurement_list; |
| std::vector<common::TSDataType> data_type_list; |
| std::set<std::string> names; |
| for (uint32_t i = 0; i < column_num; i++) { |
| if (column_name_list[i] == nullptr || |
| column_name_list[i][0] == '\0') { |
| return nullptr; |
| } |
| if (!is_supported_table_type(data_types[i])) { |
| *err_code = common::E_TYPE_NOT_SUPPORTED; |
| return nullptr; |
| } |
| std::string name = storage::to_lower(column_name_list[i]); |
| if (!names.insert(name).second) { |
| *err_code = common::E_INVALID_SCHEMA; |
| return nullptr; |
| } |
| measurement_list.emplace_back(std::move(name)); |
| data_type_list.push_back( |
| static_cast<common::TSDataType>(*(data_types + i))); |
| } |
| auto* tablet = new (std::nothrow) |
| storage::Tablet(measurement_list, data_type_list, max_rows); |
| if (tablet == nullptr) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } |
| if (tablet->err_code_ != common::E_OK) { |
| *err_code = tablet->err_code_; |
| delete tablet; |
| return nullptr; |
| } |
| *err_code = common::E_OK; |
| return tablet; |
| } catch (const std::bad_alloc&) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } catch (...) { |
| *err_code = common::E_INVALID_SCHEMA; |
| return nullptr; |
| } |
| } |
| |
| uint32_t tablet_get_cur_row_size(Tablet tablet) { |
| return static_cast<storage::Tablet*>(tablet)->get_cur_row_size(); |
| } |
| |
| ERRNO tablet_add_timestamp(Tablet tablet, uint32_t row_index, |
| Timestamp timestamp) { |
| return static_cast<storage::Tablet*>(tablet)->add_timestamp(row_index, |
| timestamp); |
| } |
| |
| #define TABLET_ADD_VALUE_BY_NAME_DEF(type) \ |
| ERRNO tablet_add_value_by_name_##type(Tablet tablet, uint32_t row_index, \ |
| const char* column_name, \ |
| const type value) { \ |
| return static_cast<storage::Tablet*>(tablet)->add_value( \ |
| row_index, storage::to_lower(column_name), value); \ |
| } |
| TABLET_ADD_VALUE_BY_NAME_DEF(int32_t); |
| TABLET_ADD_VALUE_BY_NAME_DEF(int64_t); |
| TABLET_ADD_VALUE_BY_NAME_DEF(float); |
| TABLET_ADD_VALUE_BY_NAME_DEF(double); |
| TABLET_ADD_VALUE_BY_NAME_DEF(bool); |
| |
| ERRNO tablet_add_value_by_name_string_with_len(Tablet tablet, |
| uint32_t row_index, |
| const char* column_name, |
| const char* value, |
| int value_len) { |
| return static_cast<storage::Tablet*>(tablet)->add_value( |
| row_index, storage::to_lower(column_name), |
| common::String(value, value_len)); |
| } |
| |
| #define TABLE_ADD_VALUE_BY_INDEX_DEF(type) \ |
| ERRNO tablet_add_value_by_index_##type(Tablet tablet, uint32_t row_index, \ |
| uint32_t column_index, \ |
| const type value) { \ |
| return static_cast<storage::Tablet*>(tablet)->add_value( \ |
| row_index, column_index, value); \ |
| } |
| |
| ERRNO tablet_add_value_by_index_string_with_len(Tablet tablet, |
| uint32_t row_index, |
| uint32_t column_index, |
| const char* value, |
| int value_len) { |
| return static_cast<storage::Tablet*>(tablet)->add_value( |
| row_index, column_index, common::String(value, value_len)); |
| } |
| |
| TABLE_ADD_VALUE_BY_INDEX_DEF(int32_t); |
| TABLE_ADD_VALUE_BY_INDEX_DEF(int64_t); |
| TABLE_ADD_VALUE_BY_INDEX_DEF(float); |
| TABLE_ADD_VALUE_BY_INDEX_DEF(double); |
| TABLE_ADD_VALUE_BY_INDEX_DEF(bool); |
| |
| // TsRecord API |
| TsRecord _ts_record_new(const char* device_id, Timestamp timestamp, |
| int timeseries_num) { |
| auto* record = new storage::TsRecord(timestamp, device_id, timeseries_num); |
| return record; |
| } |
| |
| #define INSERT_DATA_INTO_TS_RECORD_BY_NAME_DEF(type) \ |
| ERRNO _insert_data_into_ts_record_by_name_##type( \ |
| TsRecord data, const char* measurement_name, type value) { \ |
| auto* record = (storage::TsRecord*)data; \ |
| storage::DataPoint point(measurement_name, value); \ |
| if (record->points_.size() + 1 > record->points_.capacity()) \ |
| return common::E_BUF_NOT_ENOUGH; \ |
| record->points_.push_back(point); \ |
| return common::E_OK; \ |
| } |
| |
| ERRNO _insert_data_into_ts_record_by_name_string_with_len( |
| TsRecord data, const char* measurement_name, const char* value, |
| const uint32_t value_len) { |
| auto* record = (storage::TsRecord*)data; |
| if (record->points_.size() + 1 > record->points_.capacity()) |
| return common::E_BUF_NOT_ENOUGH; |
| common::String str_value; |
| str_value.dup_from(value, value_len, record->pa); |
| record->add_point(measurement_name, str_value); |
| return common::E_OK; |
| } |
| |
| INSERT_DATA_INTO_TS_RECORD_BY_NAME_DEF(int32_t); |
| INSERT_DATA_INTO_TS_RECORD_BY_NAME_DEF(int64_t); |
| INSERT_DATA_INTO_TS_RECORD_BY_NAME_DEF(bool); |
| INSERT_DATA_INTO_TS_RECORD_BY_NAME_DEF(float); |
| INSERT_DATA_INTO_TS_RECORD_BY_NAME_DEF(double); |
| /* |
| TsFileWriter tsfile_writer_new_with_conf(const char *pathname, |
| const mode_t flag, ERRNO *err_code, |
| TsFileConf *conf) { |
| init_tsfile_config(); |
| auto *writer = new storage::TsFileWriter(); |
| const int ret = writer->open(pathname, O_CREAT | O_RDWR, flag); |
| if (ret != common::E_OK) { |
| delete writer; |
| *err_code = ret; |
| return nullptr; |
| } |
| return writer; |
| } |
| |
| */ |
| ERRNO tsfile_writer_write(TsFileWriter writer, Tablet tablet) { |
| if (writer == nullptr || tablet == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| auto* w = static_cast<storage::TsFileTableWriter*>(writer); |
| auto* tbl = static_cast<storage::Tablet*>(tablet); |
| return w->write_table(*tbl); |
| } |
| |
| ERRNO tsfile_writer_write_arrow(TsFileWriter writer, ArrowArray* array, |
| ArrowSchema* schema, int time_col_index) { |
| if (writer == nullptr || array == nullptr || schema == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| auto* w = static_cast<storage::TsFileTableWriter*>(writer); |
| std::shared_ptr<storage::TableSchema> registered_schema = |
| w->get_table_schema(); |
| if (!registered_schema) { |
| return common::E_INVALID_SCHEMA; |
| } |
| storage::Tablet* raw_tablet = nullptr; |
| int ret = arrow::ArrowStructToTablet(w->get_table_name().c_str(), array, |
| schema, registered_schema.get(), |
| &raw_tablet, time_col_index); |
| std::unique_ptr<storage::Tablet> tablet(raw_tablet); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| return w->write_table(*tablet); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } catch (...) { |
| return common::E_INVALID_ARG; |
| } |
| } |
| |
| // ERRNO tsfile_writer_flush_data(TsFileWriter writer) { |
| // auto *w = static_cast<storage::TsFileWriter *>(writer); |
| // return w->flush(); |
| // } |
| |
| // Query |
| |
| PreparedSeriesHandle tsfile_reader_prepare_series( |
| TsFileReader reader, const TsFilePreparedLocator* locator, bool trust_index, |
| ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (reader == nullptr || locator == nullptr) { |
| return nullptr; |
| } |
| storage::FileGeneration generation; |
| generation.mapped_index_identity = locator->mapped_index_identity; |
| generation.file_id = locator->file_id; |
| generation.file_size = locator->file_size; |
| generation.file_fingerprint = locator->file_fingerprint; |
| generation.trust_index = trust_index; |
| storage::PreparedLocator native_locator; |
| native_locator.locator_id = locator->locator_id; |
| native_locator.layout = locator->layout; |
| native_locator.flags = locator->flags; |
| native_locator.value_metadata_offset = locator->value_metadata_offset; |
| native_locator.value_metadata_length = locator->value_metadata_length; |
| native_locator.time_metadata_offset = locator->time_metadata_offset; |
| native_locator.time_metadata_length = locator->time_metadata_length; |
| std::shared_ptr<storage::PreparedSeries> prepared; |
| *err_code = static_cast<storage::TsFileReader*>(reader)->prepare_series( |
| generation, native_locator, prepared); |
| if (*err_code != common::E_OK) { |
| return nullptr; |
| } |
| auto* handle = new (std::nothrow) |
| std::shared_ptr<storage::PreparedSeries>(std::move(prepared)); |
| if (handle == nullptr) { |
| *err_code = common::E_OOM; |
| } |
| return handle; |
| } |
| |
| PreparedSeriesHandle tsfile_reader_prepare_series_with_time_owner( |
| TsFileReader reader, const TsFilePreparedLocator* locator, |
| PreparedSeriesHandle aligned_time_owner, bool trust_index, |
| ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (reader == nullptr || locator == nullptr || |
| aligned_time_owner == nullptr) { |
| return nullptr; |
| } |
| |
| storage::FileGeneration generation; |
| generation.mapped_index_identity = locator->mapped_index_identity; |
| generation.file_id = locator->file_id; |
| generation.file_size = locator->file_size; |
| generation.file_fingerprint = locator->file_fingerprint; |
| generation.trust_index = trust_index; |
| storage::PreparedLocator native_locator; |
| native_locator.locator_id = locator->locator_id; |
| native_locator.layout = locator->layout; |
| native_locator.flags = locator->flags; |
| native_locator.value_metadata_offset = locator->value_metadata_offset; |
| native_locator.value_metadata_length = locator->value_metadata_length; |
| native_locator.time_metadata_offset = locator->time_metadata_offset; |
| native_locator.time_metadata_length = locator->time_metadata_length; |
| |
| auto* owner = static_cast<std::shared_ptr<storage::PreparedSeries>*>( |
| aligned_time_owner); |
| std::shared_ptr<storage::PreparedSeries> prepared; |
| *err_code = static_cast<storage::TsFileReader*>(reader)->prepare_series( |
| generation, native_locator, *owner, prepared); |
| if (*err_code != common::E_OK) { |
| return nullptr; |
| } |
| auto* handle = new (std::nothrow) |
| std::shared_ptr<storage::PreparedSeries>(std::move(prepared)); |
| if (handle == nullptr) { |
| *err_code = common::E_OOM; |
| } |
| return handle; |
| } |
| |
| void tsfile_prepared_series_free(PreparedSeriesHandle prepared) { |
| delete static_cast<std::shared_ptr<storage::PreparedSeries>*>(prepared); |
| } |
| |
| ResultSet tsfile_reader_query_prepared(TsFileReader reader, |
| PreparedSeriesHandle prepared, |
| Timestamp start_time, Timestamp end_time, |
| int offset, int limit, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (reader == nullptr || prepared == nullptr) { |
| return nullptr; |
| } |
| auto* handle = |
| static_cast<std::shared_ptr<storage::PreparedSeries>*>(prepared); |
| storage::ResultSet* result = nullptr; |
| *err_code = static_cast<storage::TsFileReader*>(reader)->query_prepared( |
| *handle, start_time, end_time, offset, limit, result); |
| return result; |
| } |
| |
| ResultSet tsfile_reader_query_prepared_multi( |
| TsFileReader reader, const PreparedSeriesHandle* prepared, |
| uint32_t prepared_count, Timestamp start_time, Timestamp end_time, |
| int offset, int limit, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (reader == nullptr || prepared == nullptr || prepared_count == 0) { |
| return nullptr; |
| } |
| |
| std::vector<std::shared_ptr<storage::PreparedSeries>> native_prepared; |
| native_prepared.reserve(prepared_count); |
| for (uint32_t i = 0; i < prepared_count; i++) { |
| if (prepared[i] == nullptr) { |
| return nullptr; |
| } |
| auto* handle = |
| static_cast<std::shared_ptr<storage::PreparedSeries>*>(prepared[i]); |
| native_prepared.push_back(*handle); |
| } |
| |
| storage::ResultSet* result = nullptr; |
| *err_code = |
| static_cast<storage::TsFileReader*>(reader)->query_prepared_multi( |
| native_prepared, start_time, end_time, offset, limit, result); |
| return result; |
| } |
| |
| ResultSet tsfile_query_table(TsFileReader reader, const char* table_name, |
| char** columns, uint32_t column_num, |
| Timestamp start_time, Timestamp end_time, |
| ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::ResultSet* table_result_set = nullptr; |
| std::vector<std::string> column_names; |
| for (uint32_t i = 0; i < column_num; i++) { |
| column_names.emplace_back(columns[i]); |
| } |
| *err_code = r->query(table_name, column_names, start_time, end_time, |
| table_result_set); |
| return table_result_set; |
| } |
| |
| ResultSet tsfile_query_table_on_tree(TsFileReader reader, char** columns, |
| uint32_t column_num, Timestamp start_time, |
| Timestamp end_time, ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::ResultSet* table_result_set = nullptr; |
| std::vector<std::string> column_names; |
| for (uint32_t i = 0; i < column_num; i++) { |
| column_names.emplace_back(columns[i]); |
| } |
| *err_code = r->query_table_on_tree(column_names, start_time, end_time, |
| table_result_set); |
| return table_result_set; |
| } |
| |
| ResultSet tsfile_reader_query_tree(TsFileReader reader, char** paths, |
| uint32_t path_num, Timestamp start_time, |
| Timestamp end_time, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (reader == nullptr || paths == nullptr || path_num == 0) { |
| return nullptr; |
| } |
| try { |
| std::vector<std::string> selected_paths; |
| selected_paths.reserve(path_num); |
| for (uint32_t i = 0; i < path_num; i++) { |
| if (paths[i] == nullptr) { |
| return nullptr; |
| } |
| selected_paths.emplace_back(paths[i]); |
| } |
| storage::ResultSet* result_set = nullptr; |
| *err_code = static_cast<storage::TsFileReader*>(reader)->query( |
| selected_paths, start_time, end_time, result_set); |
| return result_set; |
| } catch (const std::bad_alloc&) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } |
| } |
| |
| ResultSet tsfile_reader_query_tree_by_row(TsFileReader reader, |
| char** device_ids, int device_ids_len, |
| char** measurement_names, |
| int measurement_names_len, int offset, |
| int limit, ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::ResultSet* result_set = nullptr; |
| |
| std::vector<std::string> path_list; |
| if (device_ids_len > 0 && measurement_names_len > 0) { |
| path_list.reserve(static_cast<size_t>(device_ids_len) * |
| static_cast<size_t>(measurement_names_len)); |
| } |
| |
| for (int i = 0; i < device_ids_len; i++) { |
| const char* device_id = device_ids[i]; |
| if (device_id == nullptr) { |
| continue; |
| } |
| for (int j = 0; j < measurement_names_len; j++) { |
| const char* measurement_name = measurement_names[j]; |
| if (measurement_name == nullptr) { |
| continue; |
| } |
| path_list.emplace_back(std::string(device_id) + "." + |
| std::string(measurement_name)); |
| } |
| } |
| |
| *err_code = r->queryByRow(path_list, offset, limit, result_set); |
| return result_set; |
| } |
| |
| ResultSet tsfile_reader_query_table_by_row( |
| TsFileReader reader, const char* table_name, char** column_names, |
| int column_names_len, int offset, int limit, TagFilterHandle tag_filter, |
| int batch_size, ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::ResultSet* result_set = nullptr; |
| |
| std::vector<std::string> columns; |
| if (column_names_len > 0) { |
| columns.reserve(static_cast<size_t>(column_names_len)); |
| } |
| for (int i = 0; i < column_names_len; i++) { |
| const char* name = column_names[i]; |
| columns.emplace_back(name == nullptr ? "" : std::string(name)); |
| } |
| |
| *err_code = r->queryByRow( |
| table_name == nullptr ? "" : table_name, columns, offset, limit, |
| result_set, static_cast<storage::Filter*>(tag_filter), batch_size); |
| return result_set; |
| } |
| |
| ResultSet tsfile_reader_query_table(TsFileReader reader, const char* table_name, |
| char** column_names, |
| uint32_t column_names_len, |
| Timestamp start_time, Timestamp end_time, |
| int offset, int limit, |
| TagFilterHandle tag_filter, int batch_size, |
| ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_INVALID_ARG; |
| if (reader == nullptr || table_name == nullptr || column_names == nullptr || |
| column_names_len == 0 || offset < 0 || batch_size < 0) { |
| return nullptr; |
| } |
| try { |
| std::vector<std::string> columns; |
| columns.reserve(column_names_len); |
| for (uint32_t i = 0; i < column_names_len; ++i) { |
| if (column_names[i] == nullptr) { |
| return nullptr; |
| } |
| columns.emplace_back(column_names[i]); |
| } |
| storage::ResultSet* result_set = nullptr; |
| const int normalized_limit = limit < 0 ? -1 : limit; |
| *err_code = static_cast<storage::TsFileReader*>(reader)->query( |
| table_name, columns, start_time, end_time, offset, normalized_limit, |
| result_set, static_cast<storage::Filter*>(tag_filter), batch_size); |
| return result_set; |
| } catch (const std::bad_alloc&) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } catch (...) { |
| *err_code = common::E_FILE_READ_ERR; |
| return nullptr; |
| } |
| } |
| |
| ResultSet tsfile_query_table_batch(TsFileReader reader, const char* table_name, |
| char** columns, uint32_t column_num, |
| Timestamp start_time, Timestamp end_time, |
| TagFilterHandle tag_filter, int batch_size, |
| ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::ResultSet* table_result_set = nullptr; |
| std::vector<std::string> column_names; |
| for (uint32_t i = 0; i < column_num; i++) { |
| column_names.emplace_back(columns[i]); |
| } |
| *err_code = r->query(table_name, column_names, start_time, end_time, |
| table_result_set, |
| static_cast<storage::Filter*>(tag_filter), batch_size); |
| return table_result_set; |
| } |
| |
| bool tsfile_result_set_next(ResultSet result_set, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return false; |
| } |
| if (result_set == nullptr) { |
| *err_code = common::E_INVALID_ARG; |
| return false; |
| } |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| bool has_next = true; |
| int ret = common::E_OK; |
| ret = r->next(has_next); |
| *err_code = ret; |
| if (ret != common::E_OK) { |
| return false; |
| } |
| return has_next; |
| } |
| |
| ERRNO tsfile_result_set_get_next_tsblock_as_arrow(ResultSet result_set, |
| ArrowArray* out_array, |
| ArrowSchema* out_schema) { |
| if (result_set == nullptr || out_array == nullptr || |
| out_schema == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| std::memset(out_array, 0, sizeof(*out_array)); |
| std::memset(out_schema, 0, sizeof(*out_schema)); |
| |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| auto* table_result_set = dynamic_cast<storage::TableResultSet*>(r); |
| if (table_result_set == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| |
| common::TsBlock* tsblock = nullptr; |
| int ret = table_result_set->get_next_tsblock(tsblock); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| |
| if (tsblock == nullptr) { |
| return common::E_NO_MORE_DATA; |
| } |
| |
| ret = arrow::TsBlockToArrowStruct(*tsblock, out_array, out_schema); |
| return ret; |
| } |
| |
| #define TSFILE_RESULT_SET_GET_VALUE_BY_NAME_DEF(type) \ |
| type tsfile_result_set_get_value_by_name_##type(ResultSet result_set, \ |
| const char* column_name) { \ |
| auto* r = static_cast<storage::ResultSet*>(result_set); \ |
| std::string column_name_(column_name); \ |
| return r->get_value<type>(column_name_); \ |
| } |
| |
| TSFILE_RESULT_SET_GET_VALUE_BY_NAME_DEF(bool); |
| TSFILE_RESULT_SET_GET_VALUE_BY_NAME_DEF(int32_t); |
| TSFILE_RESULT_SET_GET_VALUE_BY_NAME_DEF(int64_t); |
| TSFILE_RESULT_SET_GET_VALUE_BY_NAME_DEF(float); |
| TSFILE_RESULT_SET_GET_VALUE_BY_NAME_DEF(double); |
| char* tsfile_result_set_get_value_by_name_string(ResultSet result_set, |
| const char* column_name) { |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| std::string column_name_(column_name); |
| common::String* ret = r->get_value<common::String*>(column_name_); |
| if (ret == nullptr) { |
| return nullptr; |
| } |
| // Caller should free return's char* 's space. |
| char* dup = (char*)malloc(ret->len_ + 1); |
| if (dup) { |
| memcpy(dup, ret->buf_, ret->len_); |
| dup[ret->len_] = '\0'; |
| } |
| return dup; |
| } |
| |
| #define TSFILE_RESULT_SET_GET_VALUE_BY_INDEX_DEF(type) \ |
| type tsfile_result_set_get_value_by_index_##type(ResultSet result_set, \ |
| uint32_t column_index) { \ |
| auto* r = static_cast<storage::ResultSet*>(result_set); \ |
| return r->get_value<type>(column_index); \ |
| } |
| |
| TSFILE_RESULT_SET_GET_VALUE_BY_INDEX_DEF(int32_t); |
| TSFILE_RESULT_SET_GET_VALUE_BY_INDEX_DEF(int64_t); |
| TSFILE_RESULT_SET_GET_VALUE_BY_INDEX_DEF(float); |
| TSFILE_RESULT_SET_GET_VALUE_BY_INDEX_DEF(double); |
| TSFILE_RESULT_SET_GET_VALUE_BY_INDEX_DEF(bool); |
| |
| char* tsfile_result_set_get_value_by_index_string(ResultSet result_set, |
| uint32_t column_index) { |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| common::String* ret = r->get_value<common::String*>(column_index); |
| if (ret == nullptr) { |
| return nullptr; |
| } |
| // Caller should free return's char* 's space. |
| char* dup = (char*)malloc(ret->len_ + 1); |
| if (dup) { |
| memcpy(dup, ret->buf_, ret->len_); |
| dup[ret->len_] = '\0'; |
| } |
| return dup; |
| } |
| |
| ERRNO tsfile_result_set_get_value_by_index_binary(ResultSet result_set, |
| uint32_t column_index, |
| uint8_t** out_value, |
| uint32_t* out_length) { |
| if (result_set == nullptr || column_index == 0 || out_value == nullptr || |
| out_length == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| *out_value = nullptr; |
| *out_length = 0; |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| common::String* value = r->get_value<common::String*>(column_index); |
| if (value == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| if (value->len_ == 0) { |
| return common::E_OK; |
| } |
| uint8_t* copied = static_cast<uint8_t*>(malloc(value->len_)); |
| if (copied == nullptr) { |
| return common::E_OOM; |
| } |
| memcpy(copied, value->buf_, value->len_); |
| *out_value = copied; |
| *out_length = value->len_; |
| return common::E_OK; |
| } |
| |
| bool tsfile_result_set_is_null_by_name(ResultSet result_set, |
| const char* column_name) { |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| return r->is_null(column_name); |
| } |
| |
| bool tsfile_result_set_is_null_by_index(const ResultSet result_set, |
| const uint32_t column_index) { |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| return r->is_null(column_index); |
| } |
| |
| ResultSetMetaData tsfile_result_set_get_metadata(ResultSet result_set) { |
| auto* r = static_cast<storage::ResultSet*>(result_set); |
| if (result_set == NULL) { |
| return ResultSetMetaData(); |
| } |
| |
| ResultSetMetaData meta_data; |
| std::shared_ptr<storage::ResultSetMetadata> result_set_metadata = |
| r->get_metadata(); |
| meta_data.column_num = result_set_metadata->get_column_count(); |
| meta_data.column_names = |
| static_cast<char**>(malloc(meta_data.column_num * sizeof(char*))); |
| meta_data.data_types = static_cast<TSDataType*>( |
| malloc(meta_data.column_num * sizeof(TSDataType))); |
| for (int i = 0; i < meta_data.column_num; i++) { |
| meta_data.column_names[i] = |
| strdup(result_set_metadata->get_column_name(i + 1).c_str()); |
| meta_data.data_types[i] = static_cast<TSDataType>( |
| result_set_metadata->get_column_type(i + 1)); |
| } |
| return meta_data; |
| } |
| |
| char* tsfile_result_set_metadata_get_column_name(ResultSetMetaData result_set, |
| uint32_t column_index) { |
| if (column_index < 1 || column_index > (uint32_t)result_set.column_num) { |
| return nullptr; |
| } |
| return result_set.column_names[column_index - 1]; |
| } |
| |
| TSDataType tsfile_result_set_metadata_get_data_type( |
| ResultSetMetaData result_set, uint32_t column_index) { |
| if (column_index < 1 || |
| column_index > static_cast<uint32_t>(result_set.column_num)) { |
| return TS_DATATYPE_INVALID; |
| } |
| return result_set.data_types[column_index - 1]; |
| } |
| |
| int tsfile_result_set_metadata_get_column_num(ResultSetMetaData result_set) { |
| return result_set.column_num; |
| } |
| |
| TableSchema tsfile_reader_get_table_schema(TsFileReader reader, |
| const char* table_name) { |
| TableSchema ret_schema{}; |
| if (tsfile_reader_get_table_schema_checked(reader, table_name, |
| &ret_schema) != common::E_OK) { |
| return TableSchema{}; |
| } |
| return ret_schema; |
| } |
| |
| static ColumnSchema* allocate_table_schema_columns(size_t count) { |
| #ifdef ENABLE_TEST |
| using common::g_all_inject_points; |
| using common::InjectPoint; |
| DBUG_EXECUTE_IF("table_schema_column_alloc_fail", return nullptr;); |
| #endif |
| return static_cast<ColumnSchema*>(calloc(count, sizeof(ColumnSchema))); |
| } |
| |
| // MSVC /EHsc assumes C-linkage helpers do not let exceptions escape. |
| static ERRNO copy_table_schema(const std::shared_ptr<storage::TableSchema>& src, |
| TableSchema* out_schema) { |
| if (out_schema == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| *out_schema = TableSchema{}; |
| if (!src) { |
| return common::E_TABLE_NOT_EXIST; |
| } |
| try { |
| out_schema->table_name = strdup(src->get_table_name().c_str()); |
| if (out_schema->table_name == nullptr) { |
| return common::E_OOM; |
| } |
| const int column_num = src->get_columns_num(); |
| if (column_num == 0) { |
| return common::E_OK; |
| } |
| out_schema->column_schemas = |
| allocate_table_schema_columns(static_cast<size_t>(column_num)); |
| if (out_schema->column_schemas == nullptr) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_OOM; |
| } |
| out_schema->column_num = column_num; |
| #ifdef ENABLE_TEST |
| using common::g_all_inject_points; |
| using common::InjectPoint; |
| DBUG_EXECUTE_IF("table_schema_copy_oom", throw std::bad_alloc();); |
| DBUG_EXECUTE_IF("table_schema_copy_error", |
| throw common::E_FILE_READ_ERR;); |
| #endif |
| const auto& measurements = src->get_measurement_schemas(); |
| const auto& categories = src->get_column_categories(); |
| if (measurements.size() < static_cast<size_t>(out_schema->column_num) || |
| categories.size() < static_cast<size_t>(out_schema->column_num)) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_INVALID_SCHEMA; |
| } |
| for (int i = 0; i < out_schema->column_num; ++i) { |
| if (!measurements[i]) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_INVALID_SCHEMA; |
| } |
| out_schema->column_schemas[i].column_name = |
| strdup(measurements[i]->measurement_name_.c_str()); |
| if (out_schema->column_schemas[i].column_name == nullptr) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_OOM; |
| } |
| out_schema->column_schemas[i].data_type = |
| static_cast<TSDataType>(measurements[i]->data_type_); |
| out_schema->column_schemas[i].column_category = |
| static_cast<ColumnCategory>(categories[i]); |
| } |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_OOM; |
| } catch (...) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| static void free_table_schema_array(TableSchema* schemas, size_t size) { |
| if (schemas == nullptr) { |
| return; |
| } |
| for (size_t i = 0; i < size; ++i) { |
| free_table_schema(schemas[i]); |
| } |
| free(schemas); |
| } |
| |
| static void free_device_schema_array(DeviceSchema* schemas, size_t size) { |
| if (schemas == nullptr) { |
| return; |
| } |
| for (size_t i = 0; i < size; ++i) { |
| free_device_schema(schemas[i]); |
| } |
| free(schemas); |
| } |
| |
| ERRNO tsfile_reader_get_table_schema_checked(TsFileReader reader, |
| const char* table_name, |
| TableSchema* out_schema) { |
| if (out_schema == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| *out_schema = TableSchema{}; |
| if (reader == nullptr || table_name == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| std::shared_ptr<storage::TableSchema> schema; |
| const int ret = |
| static_cast<storage::TsFileReader*>(reader)->get_table_schema( |
| table_name, schema); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| return copy_table_schema(schema, out_schema); |
| } catch (const std::bad_alloc&) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_OOM; |
| } catch (...) { |
| free_table_schema(*out_schema); |
| *out_schema = TableSchema{}; |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| TableSchema* tsfile_reader_get_all_table_schemas(TsFileReader reader, |
| uint32_t* size) { |
| ERRNO error_code = common::E_OK; |
| return tsfile_reader_get_all_table_schemas_with_error(reader, size, |
| &error_code); |
| } |
| |
| TableSchema* tsfile_reader_get_all_table_schemas_with_error(TsFileReader reader, |
| uint32_t* size, |
| ERRNO* error_code) { |
| if (size != nullptr) { |
| *size = 0; |
| } |
| if (error_code == nullptr) { |
| return nullptr; |
| } |
| TableSchema* schemas = nullptr; |
| *error_code = |
| tsfile_reader_get_all_table_schemas_checked(reader, &schemas, size); |
| return *error_code == common::E_OK ? schemas : nullptr; |
| } |
| |
| ERRNO tsfile_reader_get_all_table_schemas_checked(TsFileReader reader, |
| TableSchema** out_schemas, |
| uint32_t* out_size) { |
| if (out_schemas != nullptr) { |
| *out_schemas = nullptr; |
| } |
| if (out_size != nullptr) { |
| *out_size = 0; |
| } |
| if (reader == nullptr || out_schemas == nullptr || out_size == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| TableSchema* copied = nullptr; |
| size_t initialized = 0; |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::vector<std::shared_ptr<storage::TableSchema>> schemas; |
| const ERRNO read_ret = r->get_all_table_schemas(schemas); |
| if (read_ret != common::E_OK) { |
| return read_ret; |
| } |
| if (schemas.empty()) { |
| return common::E_OK; |
| } |
| const size_t table_num = schemas.size(); |
| if (table_num > std::numeric_limits<uint32_t>::max()) { |
| return common::E_OVERFLOW; |
| } |
| copied = static_cast<TableSchema*>( |
| calloc(schemas.size(), sizeof(TableSchema))); |
| if (copied == nullptr) { |
| return common::E_OOM; |
| } |
| for (size_t i = 0; i < schemas.size(); ++i) { |
| initialized = i + 1; |
| ERRNO ret = copy_table_schema(schemas[i], &copied[i]); |
| if (ret != common::E_OK) { |
| free_table_schema_array(copied, initialized); |
| return ret; |
| } |
| } |
| *out_schemas = copied; |
| *out_size = static_cast<uint32_t>(schemas.size()); |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| free_table_schema_array(copied, initialized); |
| return common::E_OOM; |
| } catch (...) { |
| free_table_schema_array(copied, initialized); |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| ERRNO tsfile_reader_get_all_timeseries_schemas_checked( |
| TsFileReader reader, DeviceSchema** out_schemas, uint32_t* out_size) { |
| if (out_schemas != nullptr) { |
| *out_schemas = nullptr; |
| } |
| if (out_size != nullptr) { |
| *out_size = 0; |
| } |
| if (reader == nullptr || out_schemas == nullptr || out_size == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| DeviceSchema* device_schema = nullptr; |
| size_t initialized = 0; |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::vector<std::shared_ptr<storage::IDeviceID>> device_ids; |
| const ERRNO read_ret = r->get_all_devices(device_ids); |
| if (read_ret != common::E_OK) { |
| return read_ret; |
| } |
| if (device_ids.empty()) { |
| return common::E_OK; |
| } |
| const size_t device_count = device_ids.size(); |
| if (device_count > std::numeric_limits<uint32_t>::max()) { |
| return common::E_OVERFLOW; |
| } |
| device_schema = static_cast<DeviceSchema*>( |
| calloc(device_count, sizeof(DeviceSchema))); |
| if (device_schema == nullptr) { |
| return common::E_OOM; |
| } |
| |
| size_t device_index = 0; |
| for (const auto& device_id : device_ids) { |
| initialized = device_index + 1; |
| DeviceSchema& cur_schema = device_schema[device_index++]; |
| std::string device_name = |
| device_id == nullptr ? "" : device_id->get_device_name(); |
| cur_schema.device_name = strdup(device_name.c_str()); |
| if (cur_schema.device_name == nullptr) { |
| free_device_schema_array(device_schema, initialized); |
| return common::E_OOM; |
| } |
| |
| std::vector<storage::MeasurementSchema> schemas; |
| int ret = r->get_timeseries_schema(device_id, schemas); |
| if (ret != common::E_OK) { |
| free_device_schema_array(device_schema, initialized); |
| return ret; |
| } |
| if (schemas.empty()) { |
| continue; |
| } |
| if (schemas.size() > |
| static_cast<size_t>(std::numeric_limits<int>::max())) { |
| free_device_schema_array(device_schema, initialized); |
| return common::E_OVERFLOW; |
| } |
| |
| cur_schema.timeseries_schema = static_cast<TimeseriesSchema*>( |
| calloc(schemas.size(), sizeof(TimeseriesSchema))); |
| if (cur_schema.timeseries_schema == nullptr) { |
| free_device_schema_array(device_schema, initialized); |
| return common::E_OOM; |
| } |
| cur_schema.timeseries_num = static_cast<int>(schemas.size()); |
| for (size_t i = 0; i < schemas.size(); ++i) { |
| const auto& measurement_schema = schemas[i]; |
| cur_schema.timeseries_schema[i].timeseries_name = |
| strdup(measurement_schema.measurement_name_.c_str()); |
| if (cur_schema.timeseries_schema[i].timeseries_name == |
| nullptr) { |
| free_device_schema_array(device_schema, initialized); |
| return common::E_OOM; |
| } |
| cur_schema.timeseries_schema[i].data_type = |
| static_cast<TSDataType>(measurement_schema.data_type_); |
| cur_schema.timeseries_schema[i].encoding = |
| static_cast<TSEncoding>(measurement_schema.encoding_); |
| cur_schema.timeseries_schema[i].compression = |
| static_cast<CompressionType>( |
| measurement_schema.compression_type_); |
| } |
| } |
| *out_schemas = device_schema; |
| *out_size = static_cast<uint32_t>(device_count); |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| free_device_schema_array(device_schema, initialized); |
| return common::E_OOM; |
| } catch (...) { |
| free_device_schema_array(device_schema, initialized); |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| DeviceSchema* tsfile_reader_get_all_timeseries_schemas(TsFileReader reader, |
| uint32_t* size) { |
| DeviceSchema* schemas = nullptr; |
| const ERRNO ret = tsfile_reader_get_all_timeseries_schemas_checked( |
| reader, &schemas, size); |
| return ret == common::E_OK ? schemas : nullptr; |
| } |
| |
| void tsfile_device_id_free_contents(DeviceID* d) { |
| if (d == nullptr) { |
| return; |
| } |
| free(d->path); |
| d->path = nullptr; |
| free(d->table_name); |
| d->table_name = nullptr; |
| if (d->segments != nullptr) { |
| for (uint32_t k = 0; k < d->segment_count; k++) { |
| free(d->segments[k]); |
| } |
| free(d->segments); |
| d->segments = nullptr; |
| } |
| d->segment_count = 0; |
| } |
| |
| namespace { |
| |
| char* dup_common_string_to_cstr(const common::String& s) { |
| if (s.buf_ == nullptr || s.len_ == 0) { |
| return strdup(""); |
| } |
| char* p = static_cast<char*>(malloc(static_cast<size_t>(s.len_) + 1U)); |
| if (p == nullptr) { |
| return nullptr; |
| } |
| memcpy(p, s.buf_, static_cast<size_t>(s.len_)); |
| p[s.len_] = '\0'; |
| return p; |
| } |
| |
| static TSDataType cpp_stat_type_to_c(common::TSDataType t) { |
| return static_cast<TSDataType>(static_cast<uint8_t>(t)); |
| } |
| |
| void free_timeseries_statistic_heap(TimeseriesStatistic* s) { |
| if (s == nullptr) { |
| return; |
| } |
| TsFileStatisticBase* b = tsfile_statistic_base(s); |
| if (!b->has_statistic) { |
| return; |
| } |
| switch (b->type) { |
| case TS_DATATYPE_STRING: |
| free(s->u.string_s.str_min); |
| s->u.string_s.str_min = nullptr; |
| free(s->u.string_s.str_max); |
| s->u.string_s.str_max = nullptr; |
| free(s->u.string_s.str_first); |
| s->u.string_s.str_first = nullptr; |
| free(s->u.string_s.str_last); |
| s->u.string_s.str_last = nullptr; |
| break; |
| case TS_DATATYPE_TEXT: |
| free(s->u.text_s.str_first); |
| s->u.text_s.str_first = nullptr; |
| free(s->u.text_s.str_last); |
| s->u.text_s.str_last = nullptr; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void clear_timeseries_statistic(TimeseriesStatistic* s) { |
| memset(s, 0, sizeof(*s)); |
| tsfile_statistic_base(s)->type = TS_DATATYPE_INVALID; |
| } |
| |
| /** |
| * Fills @p out from C++ Statistic. On allocation failure returns E_OOM and |
| * clears/frees any partial string fields in @p out. |
| */ |
| int fill_timeseries_statistic(storage::Statistic* st, |
| TimeseriesStatistic* out) { |
| clear_timeseries_statistic(out); |
| if (st == nullptr) { |
| return common::E_OK; |
| } |
| const common::TSDataType t = st->get_type(); |
| switch (t) { |
| case common::BOOLEAN: { |
| auto* bs = static_cast<storage::BooleanStatistic*>(st); |
| TsFileBoolStatistic* p = &out->u.bool_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::BOOLEAN); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = static_cast<double>(bs->sum_value_); |
| p->first_bool = bs->first_value_; |
| p->last_bool = bs->last_value_; |
| break; |
| } |
| case common::INT32: { |
| auto* is = static_cast<storage::Int32Statistic*>(st); |
| TsFileIntStatistic* p = &out->u.int_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::INT32); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = static_cast<double>(is->sum_value_); |
| if (p->base.row_count > 0) { |
| p->min_int64 = static_cast<int64_t>(is->min_value_); |
| p->max_int64 = static_cast<int64_t>(is->max_value_); |
| p->first_int64 = static_cast<int64_t>(is->first_value_); |
| p->last_int64 = static_cast<int64_t>(is->last_value_); |
| } |
| break; |
| } |
| case common::DATE: { |
| auto* is = static_cast<storage::Int32Statistic*>(st); |
| TsFileIntStatistic* p = &out->u.int_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::DATE); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = static_cast<double>(is->sum_value_); |
| if (p->base.row_count > 0) { |
| p->min_int64 = static_cast<int64_t>(is->min_value_); |
| p->max_int64 = static_cast<int64_t>(is->max_value_); |
| p->first_int64 = static_cast<int64_t>(is->first_value_); |
| p->last_int64 = static_cast<int64_t>(is->last_value_); |
| } |
| break; |
| } |
| case common::INT64: { |
| auto* ls = static_cast<storage::Int64Statistic*>(st); |
| TsFileIntStatistic* p = &out->u.int_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::INT64); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = ls->sum_value_; |
| if (p->base.row_count > 0) { |
| p->min_int64 = ls->min_value_; |
| p->max_int64 = ls->max_value_; |
| p->first_int64 = ls->first_value_; |
| p->last_int64 = ls->last_value_; |
| } |
| break; |
| } |
| case common::TIMESTAMP: { |
| auto* ls = static_cast<storage::Int64Statistic*>(st); |
| TsFileIntStatistic* p = &out->u.int_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::TIMESTAMP); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = ls->sum_value_; |
| if (p->base.row_count > 0) { |
| p->min_int64 = ls->min_value_; |
| p->max_int64 = ls->max_value_; |
| p->first_int64 = ls->first_value_; |
| p->last_int64 = ls->last_value_; |
| } |
| break; |
| } |
| case common::FLOAT: { |
| auto* fs = static_cast<storage::FloatStatistic*>(st); |
| TsFileFloatStatistic* p = &out->u.float_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::FLOAT); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = static_cast<double>(fs->sum_value_); |
| if (p->base.row_count > 0) { |
| p->min_float64 = static_cast<double>(fs->min_value_); |
| p->max_float64 = static_cast<double>(fs->max_value_); |
| p->first_float64 = static_cast<double>(fs->first_value_); |
| p->last_float64 = static_cast<double>(fs->last_value_); |
| } |
| break; |
| } |
| case common::DOUBLE: { |
| auto* ds = static_cast<storage::DoubleStatistic*>(st); |
| TsFileFloatStatistic* p = &out->u.float_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::DOUBLE); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->sum = ds->sum_value_; |
| if (p->base.row_count > 0) { |
| p->min_float64 = ds->min_value_; |
| p->max_float64 = ds->max_value_; |
| p->first_float64 = ds->first_value_; |
| p->last_float64 = ds->last_value_; |
| } |
| break; |
| } |
| case common::STRING: { |
| auto* ss = static_cast<storage::StringStatistic*>(st); |
| TsFileStringStatistic* p = &out->u.string_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::STRING); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->str_min = dup_common_string_to_cstr(ss->min_value_); |
| if (p->str_min == nullptr) { |
| free_timeseries_statistic_heap(out); |
| clear_timeseries_statistic(out); |
| return common::E_OOM; |
| } |
| p->str_max = dup_common_string_to_cstr(ss->max_value_); |
| if (p->str_max == nullptr) { |
| free_timeseries_statistic_heap(out); |
| clear_timeseries_statistic(out); |
| return common::E_OOM; |
| } |
| p->str_first = dup_common_string_to_cstr(ss->first_value_); |
| if (p->str_first == nullptr) { |
| free_timeseries_statistic_heap(out); |
| clear_timeseries_statistic(out); |
| return common::E_OOM; |
| } |
| p->str_last = dup_common_string_to_cstr(ss->last_value_); |
| if (p->str_last == nullptr) { |
| free_timeseries_statistic_heap(out); |
| clear_timeseries_statistic(out); |
| return common::E_OOM; |
| } |
| break; |
| } |
| case common::TEXT: { |
| auto* ts = static_cast<storage::TextStatistic*>(st); |
| TsFileTextStatistic* p = &out->u.text_s; |
| p->base.has_statistic = true; |
| p->base.type = cpp_stat_type_to_c(common::TEXT); |
| p->base.row_count = st->get_count(); |
| p->base.start_time = st->start_time_; |
| p->base.end_time = st->get_end_time(); |
| p->str_first = dup_common_string_to_cstr(ts->first_value_); |
| if (p->str_first == nullptr) { |
| free_timeseries_statistic_heap(out); |
| clear_timeseries_statistic(out); |
| return common::E_OOM; |
| } |
| p->str_last = dup_common_string_to_cstr(ts->last_value_); |
| if (p->str_last == nullptr) { |
| free_timeseries_statistic_heap(out); |
| clear_timeseries_statistic(out); |
| return common::E_OOM; |
| } |
| break; |
| } |
| default: { |
| TsFileStatisticBase* b = tsfile_statistic_base(out); |
| b->has_statistic = true; |
| b->type = TS_DATATYPE_INVALID; |
| b->row_count = st->get_count(); |
| b->start_time = st->start_time_; |
| b->end_time = st->get_end_time(); |
| break; |
| } |
| } |
| return common::E_OK; |
| } |
| |
| int fill_timeline_statistic(storage::ITimeseriesIndex* idx, |
| TimeseriesStatistic* out) { |
| clear_timeseries_statistic(out); |
| if (idx == nullptr) { |
| return common::E_OK; |
| } |
| |
| auto* aligned_idx = dynamic_cast<storage::AlignedTimeseriesIndex*>(idx); |
| if (aligned_idx != nullptr && aligned_idx->time_ts_idx_ != nullptr && |
| aligned_idx->time_ts_idx_->get_statistic() != nullptr) { |
| auto* st = aligned_idx->time_ts_idx_->get_statistic(); |
| TsFileStatisticBase* b = tsfile_statistic_base(out); |
| b->has_statistic = true; |
| b->type = TS_DATATYPE_VECTOR; |
| b->row_count = st->get_count(); |
| b->start_time = st->start_time_; |
| b->end_time = st->get_end_time(); |
| return common::E_OK; |
| } |
| |
| if (idx->get_statistic() != nullptr && |
| idx->get_time_chunk_meta_list() == nullptr) { |
| auto* st = idx->get_statistic(); |
| TsFileStatisticBase* b = tsfile_statistic_base(out); |
| b->has_statistic = true; |
| b->type = TS_DATATYPE_VECTOR; |
| b->row_count = st->get_count(); |
| b->start_time = st->start_time_; |
| b->end_time = st->get_end_time(); |
| return common::E_OK; |
| } |
| |
| auto* list = idx->get_time_chunk_meta_list(); |
| if (list == nullptr) { |
| list = idx->get_chunk_meta_list(); |
| } |
| if (list == nullptr) { |
| return common::E_OK; |
| } |
| |
| int64_t row_count = 0; |
| int64_t start_time = 0; |
| int64_t end_time = 0; |
| bool has_statistic = false; |
| for (auto it = list->begin(); it != list->end(); it++) { |
| auto* chunk_meta = it.get(); |
| if (chunk_meta == nullptr || chunk_meta->statistic_ == nullptr || |
| chunk_meta->statistic_->count_ <= 0) { |
| continue; |
| } |
| if (!has_statistic) { |
| start_time = chunk_meta->statistic_->start_time_; |
| end_time = chunk_meta->statistic_->end_time_; |
| has_statistic = true; |
| } else { |
| start_time = |
| std::min(start_time, chunk_meta->statistic_->start_time_); |
| end_time = std::max(end_time, chunk_meta->statistic_->end_time_); |
| } |
| row_count += chunk_meta->statistic_->count_; |
| } |
| |
| if (!has_statistic) { |
| return common::E_OK; |
| } |
| |
| TsFileStatisticBase* b = tsfile_statistic_base(out); |
| b->has_statistic = true; |
| b->type = TS_DATATYPE_VECTOR; |
| b->row_count = row_count; |
| b->start_time = start_time; |
| b->end_time = end_time; |
| return common::E_OK; |
| } |
| |
| void free_device_timeseries_metadata_entries_partial( |
| DeviceTimeseriesMetadataEntry* entries, size_t filled_count) { |
| if (entries == nullptr) { |
| return; |
| } |
| for (size_t i = 0; i < filled_count; i++) { |
| tsfile_device_id_free_contents(&entries[i].device); |
| if (entries[i].timeseries != nullptr) { |
| for (uint32_t j = 0; j < entries[i].timeseries_count; j++) { |
| free_timeseries_statistic_heap( |
| &entries[i].timeseries[j].statistic); |
| free_timeseries_statistic_heap( |
| &entries[i].timeseries[j].timeline_statistic); |
| free(entries[i].timeseries[j].measurement_name); |
| } |
| free(entries[i].timeseries); |
| entries[i].timeseries = nullptr; |
| } |
| } |
| free(entries); |
| } |
| |
| /** |
| * Copies path, table name, and segment strings from IDeviceID into heap |
| * buffers. On failure, frees any partial allocations and returns an error code. |
| */ |
| int duplicate_ideviceid_to_device_fields(storage::IDeviceID* id, |
| char** out_path, char** out_table_name, |
| uint32_t* out_segment_count, |
| char*** out_segments) { |
| *out_path = nullptr; |
| *out_table_name = nullptr; |
| *out_segment_count = 0; |
| *out_segments = nullptr; |
| auto cleanup = [&]() { |
| free(*out_path); |
| free(*out_table_name); |
| *out_path = nullptr; |
| *out_table_name = nullptr; |
| }; |
| try { |
| if (id == nullptr) { |
| *out_path = strdup(""); |
| *out_table_name = strdup(""); |
| if (*out_path == nullptr || *out_table_name == nullptr) { |
| free(*out_path); |
| free(*out_table_name); |
| *out_path = nullptr; |
| *out_table_name = nullptr; |
| return common::E_OOM; |
| } |
| return common::E_OK; |
| } |
| const std::string dname = id->get_device_name(); |
| *out_path = strdup(dname.c_str()); |
| if (*out_path == nullptr) { |
| return common::E_OOM; |
| } |
| #ifdef ENABLE_TEST |
| using common::g_all_inject_points; |
| using common::InjectPoint; |
| DBUG_EXECUTE_IF("device_id_copy_oom", throw std::bad_alloc();); |
| DBUG_EXECUTE_IF("device_id_copy_error", throw common::E_FILE_READ_ERR;); |
| #endif |
| const std::string tname = id->get_table_name(); |
| *out_table_name = strdup(tname.c_str()); |
| if (*out_table_name == nullptr) { |
| free(*out_path); |
| *out_path = nullptr; |
| return common::E_OOM; |
| } |
| const int n = id->segment_num(); |
| if (n <= 0) { |
| return common::E_OK; |
| } |
| const auto& segs = id->get_segments(); |
| auto* seg_arr = |
| static_cast<char**>(malloc(sizeof(char*) * static_cast<size_t>(n))); |
| if (seg_arr == nullptr) { |
| free(*out_table_name); |
| *out_table_name = nullptr; |
| free(*out_path); |
| *out_path = nullptr; |
| return common::E_OOM; |
| } |
| memset(seg_arr, 0, sizeof(char*) * static_cast<size_t>(n)); |
| for (int i = 0; i < n; i++) { |
| const std::string* ps = |
| (static_cast<size_t>(i) < segs.size()) ? segs[i] : nullptr; |
| // A null tag segment is exposed as a NULL pointer so callers can |
| // distinguish a missing/null tag from the literal string "null". |
| if (ps == nullptr) { |
| seg_arr[i] = nullptr; |
| continue; |
| } |
| seg_arr[i] = strdup(ps->c_str()); |
| if (seg_arr[i] == nullptr) { |
| for (int j = 0; j < i; j++) { |
| free(seg_arr[j]); |
| } |
| free(seg_arr); |
| free(*out_table_name); |
| *out_table_name = nullptr; |
| free(*out_path); |
| *out_path = nullptr; |
| return common::E_OOM; |
| } |
| } |
| *out_segment_count = static_cast<uint32_t>(n); |
| *out_segments = seg_arr; |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| cleanup(); |
| return common::E_OOM; |
| } catch (...) { |
| cleanup(); |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| int fill_device_id_from_ideviceid(storage::IDeviceID* id, DeviceID* out) { |
| memset(out, 0, sizeof(*out)); |
| return duplicate_ideviceid_to_device_fields( |
| id, &out->path, &out->table_name, &out->segment_count, &out->segments); |
| } |
| |
| ERRNO populate_c_metadata_map_from_cpp( |
| storage::DeviceTimeseriesMetadataMap& cpp_map, |
| DeviceTimeseriesMetadataMap* out_map) { |
| try { |
| if (cpp_map.empty()) { |
| return common::E_OK; |
| } |
| const uint32_t dev_n = static_cast<uint32_t>(cpp_map.size()); |
| auto* entries = static_cast<DeviceTimeseriesMetadataEntry*>( |
| malloc(sizeof(DeviceTimeseriesMetadataEntry) * dev_n)); |
| if (entries == nullptr) { |
| return common::E_OOM; |
| } |
| memset(entries, 0, sizeof(DeviceTimeseriesMetadataEntry) * dev_n); |
| auto cleanup = [dev_n](DeviceTimeseriesMetadataEntry* data) { |
| free_device_timeseries_metadata_entries_partial(data, dev_n); |
| }; |
| std::unique_ptr<DeviceTimeseriesMetadataEntry, decltype(cleanup)> guard( |
| entries, cleanup); |
| size_t di = 0; |
| for (const auto& kv : cpp_map) { |
| DeviceTimeseriesMetadataEntry& e = entries[di]; |
| const int dup_rc = fill_device_id_from_ideviceid( |
| kv.first ? kv.first.get() : nullptr, &e.device); |
| if (dup_rc != common::E_OK) { |
| return dup_rc; |
| } |
| const auto& vec = kv.second; |
| uint32_t n_ts = 0; |
| for (const auto& idx_nz : vec) { |
| if (idx_nz != nullptr) { |
| n_ts++; |
| } |
| } |
| e.timeseries_count = n_ts; |
| if (e.timeseries_count == 0) { |
| e.timeseries = nullptr; |
| di++; |
| continue; |
| } |
| e.timeseries = static_cast<TimeseriesMetadata*>( |
| malloc(sizeof(TimeseriesMetadata) * e.timeseries_count)); |
| if (e.timeseries == nullptr) { |
| return common::E_OOM; |
| } |
| memset(e.timeseries, 0, |
| sizeof(TimeseriesMetadata) * e.timeseries_count); |
| uint32_t slot = 0; |
| for (const auto& idx : vec) { |
| if (idx == nullptr) { |
| continue; |
| } |
| TimeseriesMetadata& m = e.timeseries[slot]; |
| common::String mn = idx->get_measurement_name(); |
| m.measurement_name = strdup(mn.to_std_string().c_str()); |
| if (m.measurement_name == nullptr) { |
| return common::E_OOM; |
| } |
| #ifdef ENABLE_TEST |
| using common::g_all_inject_points; |
| using common::InjectPoint; |
| DBUG_EXECUTE_IF("timeseries_metadata_copy_oom", |
| throw std::bad_alloc();); |
| DBUG_EXECUTE_IF("timeseries_metadata_copy_error", |
| throw common::E_FILE_READ_ERR;); |
| #endif |
| auto* aligned_idx = |
| dynamic_cast<storage::AlignedTimeseriesIndex*>(idx.get()); |
| if (aligned_idx != nullptr && |
| aligned_idx->value_ts_idx_ != nullptr) { |
| m.data_type = static_cast<TSDataType>( |
| aligned_idx->value_ts_idx_->get_data_type()); |
| const storage::TimeseriesIndex* value_idx = |
| aligned_idx->value_ts_idx_; |
| const storage::TimeseriesIndex* time_idx = |
| aligned_idx->time_ts_idx_; |
| if (value_idx->get_metadata_offset() >= 0) { |
| m.value_metadata_offset = static_cast<uint64_t>( |
| value_idx->get_metadata_offset()); |
| m.value_metadata_length = |
| value_idx->get_metadata_length(); |
| } |
| if (time_idx != nullptr && |
| time_idx->get_metadata_offset() >= 0) { |
| m.time_metadata_offset = static_cast<uint64_t>( |
| time_idx->get_metadata_offset()); |
| m.time_metadata_length = |
| time_idx->get_metadata_length(); |
| } |
| m.layout = 1; |
| } else { |
| m.data_type = static_cast<TSDataType>(idx->get_data_type()); |
| const storage::TimeseriesIndex* value_idx = |
| dynamic_cast<const storage::TimeseriesIndex*>( |
| idx.get()); |
| if (value_idx != nullptr && |
| value_idx->get_metadata_offset() >= 0) { |
| m.value_metadata_offset = static_cast<uint64_t>( |
| value_idx->get_metadata_offset()); |
| m.value_metadata_length = |
| value_idx->get_metadata_length(); |
| } |
| } |
| storage::Statistic* st = idx->get_statistic(); |
| int32_t chunk_cnt = 0; |
| auto* cl = aligned_idx != nullptr |
| ? idx->get_value_chunk_meta_list() |
| : idx->get_chunk_meta_list(); |
| if (cl != nullptr) { |
| chunk_cnt = static_cast<int32_t>(cl->size()); |
| } |
| m.chunk_meta_count = chunk_cnt; |
| if (chunk_cnt >= 0 && m.value_metadata_length > 0) { |
| m.locator_flags |= 1; |
| } |
| if (aligned_idx != nullptr) { |
| auto* time_chunks = idx->get_time_chunk_meta_list(); |
| if (time_chunks != nullptr) { |
| m.time_chunk_meta_count = |
| static_cast<uint32_t>(time_chunks->size()); |
| } |
| if (m.time_metadata_length == 0 || |
| m.time_chunk_meta_count != |
| static_cast<uint32_t>(chunk_cnt)) { |
| m.locator_flags &= ~static_cast<uint16_t>(1); |
| } |
| } |
| const int st_rc = fill_timeseries_statistic(st, &m.statistic); |
| if (st_rc != common::E_OK) { |
| return st_rc; |
| } |
| const int timeline_st_rc = |
| fill_timeline_statistic(idx.get(), &m.timeline_statistic); |
| if (timeline_st_rc != common::E_OK) { |
| return timeline_st_rc; |
| } |
| slot++; |
| } |
| di++; |
| } |
| out_map->entries = guard.release(); |
| out_map->device_count = dev_n; |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } catch (...) { |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| } // namespace |
| |
| void tsfile_free_device_id_array(DeviceID* devices, uint32_t length) { |
| if (devices == nullptr) { |
| return; |
| } |
| for (uint32_t i = 0; i < length; i++) { |
| tsfile_device_id_free_contents(&devices[i]); |
| } |
| free(devices); |
| } |
| |
| ERRNO tsfile_reader_get_all_devices(TsFileReader reader, DeviceID** out_devices, |
| uint32_t* out_length) { |
| if (reader == nullptr || out_devices == nullptr || out_length == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| *out_devices = nullptr; |
| *out_length = 0; |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::vector<std::shared_ptr<storage::IDeviceID>> ids; |
| const int ret = r->get_all_devices(ids); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| if (ids.empty()) { |
| return common::E_OK; |
| } |
| auto* arr = static_cast<DeviceID*>(malloc(sizeof(DeviceID) * ids.size())); |
| if (arr == nullptr) { |
| return common::E_OOM; |
| } |
| memset(arr, 0, sizeof(DeviceID) * ids.size()); |
| for (size_t i = 0; i < ids.size(); i++) { |
| const int rc = fill_device_id_from_ideviceid(ids[i].get(), &arr[i]); |
| if (rc != common::E_OK) { |
| tsfile_free_device_id_array(arr, static_cast<uint32_t>(i)); |
| return rc; |
| } |
| } |
| *out_devices = arr; |
| *out_length = static_cast<uint32_t>(ids.size()); |
| return common::E_OK; |
| } |
| |
| ERRNO tsfile_reader_get_timeseries_metadata_all( |
| TsFileReader reader, DeviceTimeseriesMetadataMap* out_map) { |
| if (out_map != nullptr) { |
| out_map->entries = nullptr; |
| out_map->device_count = 0; |
| } |
| if (reader == nullptr || out_map == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::DeviceTimeseriesMetadataMap cpp_map; |
| const int ret = r->get_timeseries_metadata(cpp_map); |
| if (ret != common::E_OK) return ret; |
| return populate_c_metadata_map_from_cpp(cpp_map, out_map); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } catch (...) { |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| ERRNO tsfile_reader_get_timeseries_metadata_for_devices( |
| TsFileReader reader, const DeviceID* devices, uint32_t length, |
| DeviceTimeseriesMetadataMap* out_map) { |
| if (out_map != nullptr) { |
| out_map->entries = nullptr; |
| out_map->device_count = 0; |
| } |
| if (reader == nullptr || out_map == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| if (length == 0) { |
| return common::E_OK; |
| } |
| if (devices == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| for (uint32_t i = 0; i < length; i++) { |
| if (devices[i].segment_count > 0) { |
| if (devices[i].segments == nullptr || |
| devices[i].segments[0] == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| } else if (devices[i].path == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| } |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::vector<std::shared_ptr<storage::IDeviceID>> query_ids; |
| query_ids.reserve(length); |
| for (uint32_t i = 0; i < length; i++) { |
| const DeviceID& device = devices[i]; |
| if (device.segment_count == 0) { |
| query_ids.push_back( |
| std::make_shared<storage::StringArrayDeviceID>( |
| std::string(device.path))); |
| continue; |
| } |
| std::vector<std::string> values; |
| values.reserve(device.segment_count); |
| std::vector<std::string*> segments(device.segment_count, nullptr); |
| for (uint32_t j = 0; j < device.segment_count; ++j) { |
| if (device.segments[j] != nullptr) { |
| values.emplace_back(device.segments[j]); |
| segments[j] = &values.back(); |
| } |
| } |
| query_ids.push_back( |
| std::make_shared<storage::StringArrayDeviceID>(segments)); |
| } |
| storage::DeviceTimeseriesMetadataMap cpp_map; |
| const int ret = r->get_timeseries_metadata(query_ids, cpp_map); |
| if (ret != common::E_OK) return ret; |
| return populate_c_metadata_map_from_cpp(cpp_map, out_map); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } catch (...) { |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| void tsfile_free_device_timeseries_metadata_map( |
| DeviceTimeseriesMetadataMap* map) { |
| if (map == nullptr) { |
| return; |
| } |
| free_device_timeseries_metadata_entries_partial(map->entries, |
| map->device_count); |
| map->entries = nullptr; |
| map->device_count = 0; |
| } |
| |
| void tsfile_free_tsfile_properties(TsFileProperty* properties, |
| uint32_t length) { |
| if (properties == nullptr) { |
| return; |
| } |
| for (uint32_t i = 0; i < length; i++) { |
| free(properties[i].key); |
| properties[i].key = nullptr; |
| free(properties[i].value); |
| properties[i].value = nullptr; |
| properties[i].key_len = 0; |
| properties[i].value_len = 0; |
| properties[i].is_null = false; |
| } |
| free(properties); |
| } |
| |
| ERRNO tsfile_reader_get_tsfile_properties(TsFileReader reader, |
| TsFileProperty** out_properties, |
| uint32_t* out_length) { |
| if (out_properties == nullptr || out_length == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| *out_properties = nullptr; |
| *out_length = 0; |
| if (reader == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::TsFileProperties cpp_properties = r->get_tsfile_properties(); |
| if (cpp_properties.size() > |
| static_cast<size_t>(std::numeric_limits<uint32_t>::max()) || |
| cpp_properties.size() > |
| std::numeric_limits<size_t>::max() / sizeof(TsFileProperty)) { |
| return common::E_OUT_OF_RANGE; |
| } |
| if (cpp_properties.empty()) { |
| return common::E_OK; |
| } |
| |
| auto* properties = static_cast<TsFileProperty*>( |
| malloc(sizeof(TsFileProperty) * cpp_properties.size())); |
| if (properties == nullptr) { |
| return common::E_OOM; |
| } |
| memset(properties, 0, sizeof(TsFileProperty) * cpp_properties.size()); |
| |
| uint32_t property_index = 0; |
| for (const auto& cpp_property : cpp_properties) { |
| TsFileProperty& property = properties[property_index]; |
| if (cpp_property.first.size() > |
| static_cast<size_t>(std::numeric_limits<uint32_t>::max())) { |
| tsfile_free_tsfile_properties(properties, property_index); |
| return common::E_OUT_OF_RANGE; |
| } |
| property.key_len = static_cast<uint32_t>(cpp_property.first.size()); |
| property.key = static_cast<char*>( |
| malloc(static_cast<size_t>(property.key_len) + 1U)); |
| if (property.key == nullptr) { |
| tsfile_free_tsfile_properties(properties, property_index + 1); |
| return common::E_OOM; |
| } |
| if (property.key_len > 0) { |
| memcpy(property.key, cpp_property.first.data(), |
| property.key_len); |
| } |
| property.key[property.key_len] = '\0'; |
| |
| const storage::TsFilePropertyValue& cpp_value = cpp_property.second; |
| property.is_null = cpp_value.is_null; |
| if (!cpp_value.is_null) { |
| if (cpp_value.value.size() > |
| static_cast<size_t>(std::numeric_limits<uint32_t>::max())) { |
| tsfile_free_tsfile_properties(properties, |
| property_index + 1); |
| return common::E_OUT_OF_RANGE; |
| } |
| property.value_len = |
| static_cast<uint32_t>(cpp_value.value.size()); |
| if (property.value_len > 0) { |
| property.value = |
| static_cast<uint8_t*>(malloc(property.value_len)); |
| if (property.value == nullptr) { |
| tsfile_free_tsfile_properties(properties, |
| property_index + 1); |
| return common::E_OOM; |
| } |
| memcpy(property.value, cpp_value.value.data(), |
| property.value_len); |
| } |
| } |
| property_index++; |
| } |
| |
| *out_properties = properties; |
| *out_length = static_cast<uint32_t>(cpp_properties.size()); |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| // delete pointer |
| void _free_tsfile_ts_record(TsRecord* record) { |
| if (*record != nullptr) { |
| delete static_cast<storage::TsRecord*>(*record); |
| } |
| *record = nullptr; |
| } |
| |
| void free_tablet(Tablet* tablet) { |
| if (*tablet != nullptr) { |
| delete static_cast<storage::Tablet*>(*tablet); |
| } |
| *tablet = nullptr; |
| } |
| |
| void free_tsfile_result_set(ResultSet* result_set) { |
| if (*result_set != nullptr) { |
| delete static_cast<storage::ResultSet*>(*result_set); |
| } |
| *result_set = nullptr; |
| } |
| |
| void free_result_set_meta_data(ResultSetMetaData result_set_meta_data) { |
| for (int i = 0; i < result_set_meta_data.column_num; i++) { |
| free(result_set_meta_data.column_names[i]); |
| } |
| free(result_set_meta_data.column_names); |
| free(result_set_meta_data.data_types); |
| } |
| |
| void free_device_schema(DeviceSchema schema) { |
| free(schema.device_name); |
| for (int i = 0; i < schema.timeseries_num; i++) { |
| free_timeseries_schema(schema.timeseries_schema[i]); |
| } |
| free(schema.timeseries_schema); |
| } |
| void free_timeseries_schema(TimeseriesSchema schema) { |
| free(schema.timeseries_name); |
| } |
| void free_table_schema(TableSchema schema) { |
| free(schema.table_name); |
| for (int i = 0; i < schema.column_num; i++) { |
| free_column_schema(schema.column_schemas[i]); |
| } |
| if (schema.column_num > 0) { |
| free(schema.column_schemas); |
| } |
| } |
| void free_column_schema(ColumnSchema schema) { free(schema.column_name); } |
| |
| void free_write_file(WriteFile* write_file) { |
| auto f = static_cast<storage::WriteFile*>(*write_file); |
| delete f; |
| *write_file = nullptr; |
| } |
| |
| // For Python API |
| TsFileWriter _tsfile_writer_new(const char* pathname, uint64_t memory_threshold, |
| ERRNO* err_code) { |
| init_tsfile_config(); |
| auto writer = new storage::TsFileWriter(); |
| int flags = O_WRONLY | O_CREAT | O_TRUNC; |
| #ifdef _WIN32 |
| flags |= O_BINARY; |
| #endif |
| int ret = writer->open(pathname, flags, 0644); |
| common::g_config_value_.chunk_group_size_threshold_ = memory_threshold; |
| if (ret != common::E_OK) { |
| delete writer; |
| *err_code = ret; |
| return nullptr; |
| } |
| return writer; |
| } |
| |
| Tablet _tablet_new_with_target_name(const char* device_id, |
| char** column_name_list, |
| TSDataType* data_types, int column_num, |
| int max_rows) { |
| std::vector<std::string> measurement_list; |
| std::vector<common::TSDataType> data_type_list; |
| for (int i = 0; i < column_num; i++) { |
| measurement_list.emplace_back(column_name_list[i]); |
| data_type_list.push_back( |
| static_cast<common::TSDataType>(*(data_types + i))); |
| } |
| if (device_id != nullptr) { |
| return new storage::Tablet(device_id, &measurement_list, |
| &data_type_list, max_rows); |
| } else { |
| return new storage::Tablet(measurement_list, data_type_list, max_rows); |
| } |
| } |
| |
| ERRNO _tsfile_writer_register_table(TsFileWriter writer, TableSchema* schema) { |
| if (writer == nullptr || schema == nullptr || |
| schema->column_schemas == nullptr || schema->table_name == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| if (schema->column_num <= 0) { |
| return common::E_INVALID_SCHEMA; |
| } |
| std::vector<storage::MeasurementSchema*> measurement_schemas; |
| std::vector<common::ColumnCategory> column_categories; |
| measurement_schemas.resize(schema->column_num); |
| for (int i = 0; i < schema->column_num; i++) { |
| ColumnSchema* cur_schema = schema->column_schemas + i; |
| measurement_schemas[i] = new storage::MeasurementSchema( |
| cur_schema->column_name, |
| static_cast<common::TSDataType>(cur_schema->data_type)); |
| column_categories.push_back( |
| static_cast<common::ColumnCategory>(cur_schema->column_category)); |
| } |
| auto tsfile_writer = static_cast<storage::TsFileWriter*>(writer); |
| return tsfile_writer->register_table(std::make_shared<storage::TableSchema>( |
| schema->table_name, measurement_schemas, column_categories)); |
| } |
| |
| ERRNO _tsfile_writer_register_timeseries(TsFileWriter writer, |
| const char* device_id, |
| const TimeseriesSchema* schema) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| |
| int ret = w->register_timeseries( |
| device_id, |
| storage::MeasurementSchema( |
| schema->timeseries_name, |
| static_cast<common::TSDataType>(schema->data_type), |
| static_cast<common::TSEncoding>(schema->encoding), |
| static_cast<common::CompressionType>(schema->compression))); |
| return ret; |
| } |
| |
| ERRNO _tsfile_writer_register_device(TsFileWriter writer, |
| const device_schema* device_schema) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| for (int column_id = 0; column_id < device_schema->timeseries_num; |
| column_id++) { |
| TimeseriesSchema schema = device_schema->timeseries_schema[column_id]; |
| const ERRNO ret = w->register_timeseries( |
| device_schema->device_name, |
| storage::MeasurementSchema( |
| schema.timeseries_name, |
| static_cast<common::TSDataType>(schema.data_type), |
| static_cast<common::TSEncoding>(schema.encoding), |
| static_cast<common::CompressionType>(schema.compression))); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| } |
| return common::E_OK; |
| } |
| |
| ERRNO _tsfile_writer_write_tablet(TsFileWriter writer, Tablet tablet) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| const auto* tbl = static_cast<storage::Tablet*>(tablet); |
| return w->write_tablet(*tbl); |
| } |
| |
| ERRNO _tsfile_writer_write_table(TsFileWriter writer, Tablet tablet) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| auto* tbl = static_cast<storage::Tablet*>(tablet); |
| return w->write_table(*tbl); |
| } |
| |
| ERRNO _tsfile_writer_write_arrow_table(TsFileWriter writer, |
| const char* table_name, |
| ArrowArray* array, ArrowSchema* schema, |
| int time_col_index) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| std::shared_ptr<storage::TableSchema> reg_schema = |
| w->get_table_schema(table_name ? std::string(table_name) : ""); |
| storage::Tablet* tablet = nullptr; |
| int ret = arrow::ArrowStructToTablet( |
| table_name, array, schema, reg_schema.get(), &tablet, time_col_index); |
| if (ret != common::E_OK) return ret; |
| ret = w->write_table(*tablet); |
| delete tablet; |
| return ret; |
| } |
| |
| ERRNO _tsfile_writer_write_ts_record(TsFileWriter writer, TsRecord data) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| const storage::TsRecord* record = static_cast<storage::TsRecord*>(data); |
| const int ret = w->write_record(*record); |
| return ret; |
| } |
| |
| ERRNO _tsfile_writer_close(TsFileWriter writer) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| int ret = w->close(); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| delete w; |
| return ret; |
| } |
| |
| ERRNO _tsfile_writer_flush(TsFileWriter writer) { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| return w->flush(); |
| } |
| |
| ERRNO _tsfile_writer_add_tsfile_property(TsFileWriter writer, const char* key, |
| uint32_t key_len, const uint8_t* value, |
| uint32_t value_len) { |
| if (writer == nullptr || key == nullptr || |
| (value == nullptr && value_len > 0)) { |
| return common::E_INVALID_ARG; |
| } |
| if (key_len > static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) || |
| value_len > |
| static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { |
| return common::E_OUT_OF_RANGE; |
| } |
| try { |
| auto* w = static_cast<storage::TsFileWriter*>(writer); |
| return w->add_tsfile_property(std::string(key, key_len), value, |
| value_len); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| TsFileGenericWriter tsfile_generic_writer_new(const char* pathname, |
| uint64_t memory_threshold, |
| ERRNO* err_code) { |
| // Public C entry point: guard null arguments before the private delegate |
| // dereferences them, and keep std::bad_alloc from crossing the C ABI. |
| // See tsfile_writer_new() above for the null-guard rationale. |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| *err_code = common::E_OK; |
| if (pathname == nullptr) { |
| *err_code = common::E_INVALID_ARG; |
| return nullptr; |
| } |
| try { |
| return _tsfile_writer_new(pathname, memory_threshold, err_code); |
| } catch (const std::bad_alloc&) { |
| *err_code = common::E_OOM; |
| return nullptr; |
| } |
| } |
| |
| Tablet tablet_new_with_target_name(const char* target_name, |
| char** column_name_list, |
| TSDataType* data_types, int column_num, |
| int max_rows) { |
| // Public C entry point: reject arguments that would make the private |
| // delegate read out-of-bounds memory (negative column_num, null lists or |
| // null column names) before touching caller buffers. The native Tablet |
| // constructor asserts 0 < max_rows < (1 << 30); reject values outside |
| // that domain here instead of letting an assertion abort the host |
| // process. Returns NULL on invalid arguments or allocation failure. |
| if (column_num < 0 || max_rows <= 0 || max_rows >= (1 << 30)) { |
| return nullptr; |
| } |
| if (column_num > 0) { |
| if (column_name_list == nullptr || data_types == nullptr) { |
| return nullptr; |
| } |
| for (int i = 0; i < column_num; i++) { |
| if (column_name_list[i] == nullptr) { |
| return nullptr; |
| } |
| } |
| } |
| try { |
| return _tablet_new_with_target_name(target_name, column_name_list, |
| data_types, column_num, max_rows); |
| } catch (const std::bad_alloc&) { |
| return nullptr; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_register_table(TsFileGenericWriter writer, |
| TableSchema* schema) { |
| if (writer == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| return _tsfile_writer_register_table(writer, schema); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_register_timeseries( |
| TsFileGenericWriter writer, const char* device_id, |
| const TimeseriesSchema* schema) { |
| if (writer == nullptr || device_id == nullptr || schema == nullptr || |
| schema->timeseries_name == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| return _tsfile_writer_register_timeseries(writer, device_id, schema); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_register_device(TsFileGenericWriter writer, |
| const DeviceSchema* device_schema) { |
| if (writer == nullptr || device_schema == nullptr || |
| device_schema->device_name == nullptr || |
| device_schema->timeseries_num < 0) { |
| return common::E_INVALID_ARG; |
| } |
| if (device_schema->timeseries_num > 0 && |
| device_schema->timeseries_schema == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| for (int i = 0; i < device_schema->timeseries_num; i++) { |
| if (device_schema->timeseries_schema[i].timeseries_name == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| } |
| try { |
| return _tsfile_writer_register_device(writer, device_schema); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_write_tree_tablet(TsFileGenericWriter writer, |
| Tablet tablet) { |
| if (writer == nullptr || tablet == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| return _tsfile_writer_write_tablet(writer, tablet); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_write_table_tablet(TsFileGenericWriter writer, |
| Tablet tablet) { |
| if (writer == nullptr || tablet == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| return _tsfile_writer_write_table(writer, tablet); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_flush(TsFileGenericWriter writer) { |
| if (writer == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| return _tsfile_writer_flush(writer); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ERRNO tsfile_generic_writer_add_tsfile_property(TsFileGenericWriter writer, |
| const char* key, |
| uint32_t key_len, |
| const uint8_t* value, |
| uint32_t value_len) { |
| return _tsfile_writer_add_tsfile_property(writer, key, key_len, value, |
| value_len); |
| } |
| |
| ERRNO tsfile_generic_writer_close(TsFileGenericWriter writer) { |
| // Matches the public tsfile_writer_close(): a null handle is treated as |
| // already closed. |
| if (writer == nullptr) { |
| return common::E_OK; |
| } |
| try { |
| return _tsfile_writer_close(writer); |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } |
| } |
| |
| ResultSet _tsfile_reader_query_device(TsFileReader reader, |
| const char* device_name, |
| char** sensor_name, uint32_t sensor_num, |
| Timestamp start_time, Timestamp end_time, |
| ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::vector<std::string> selected_paths; |
| selected_paths.reserve(sensor_num); |
| for (uint32_t i = 0; i < sensor_num; i++) { |
| selected_paths.push_back(std::string(device_name) + "." + |
| std::string(sensor_name[i])); |
| } |
| storage::ResultSet* qds = nullptr; |
| *err_code = r->query(selected_paths, start_time, end_time, qds); |
| return qds; |
| } |
| |
| // ============== Tag Filter API Implementation ============== |
| |
| #define DEFINE_TAG_FILTER_FACTORY(name, op) \ |
| TagFilterHandle tsfile_tag_filter_##name( \ |
| TsFileReader reader, const char* table_name, const char* column_name, \ |
| const char* value) { \ |
| ERRNO error_code = common::E_OK; \ |
| return tsfile_tag_filter_create(reader, table_name, column_name, \ |
| value, TAG_FILTER_##op, &error_code); \ |
| } |
| |
| DEFINE_TAG_FILTER_FACTORY(eq, EQ) |
| DEFINE_TAG_FILTER_FACTORY(neq, NEQ) |
| DEFINE_TAG_FILTER_FACTORY(lt, LT) |
| DEFINE_TAG_FILTER_FACTORY(lteq, LTEQ) |
| DEFINE_TAG_FILTER_FACTORY(gt, GT) |
| DEFINE_TAG_FILTER_FACTORY(gteq, GTEQ) |
| |
| #undef DEFINE_TAG_FILTER_FACTORY |
| |
| ERRNO tsfile_tag_filter_create_checked(TsFileReader reader, |
| const char* table_name, |
| const char* column_name, |
| const char* value, TagFilterOp op, |
| TagFilterHandle* out_filter) { |
| if (out_filter != nullptr) { |
| *out_filter = nullptr; |
| } |
| if (reader == nullptr || table_name == nullptr || column_name == nullptr || |
| out_filter == nullptr || |
| (value == nullptr && op != TAG_FILTER_IS_NULL && |
| op != TAG_FILTER_IS_NOT_NULL)) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::shared_ptr<storage::TableSchema> schema; |
| const int ret = r->get_table_schema(table_name, schema); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| storage::TagFilterBuilder builder(schema.get()); |
| storage::Filter* filter = nullptr; |
| switch (op) { |
| case TAG_FILTER_EQ: |
| filter = builder.eq(column_name, value); |
| break; |
| case TAG_FILTER_NEQ: |
| filter = builder.neq(column_name, value); |
| break; |
| case TAG_FILTER_LT: |
| filter = builder.lt(column_name, value); |
| break; |
| case TAG_FILTER_LTEQ: |
| filter = builder.lteq(column_name, value); |
| break; |
| case TAG_FILTER_GT: |
| filter = builder.gt(column_name, value); |
| break; |
| case TAG_FILTER_GTEQ: |
| filter = builder.gteq(column_name, value); |
| break; |
| case TAG_FILTER_REGEXP: |
| filter = builder.reg_exp(column_name, value); |
| break; |
| case TAG_FILTER_NOT_REGEXP: |
| filter = builder.not_reg_exp(column_name, value); |
| break; |
| case TAG_FILTER_IS_NULL: |
| filter = builder.is_null(column_name); |
| break; |
| case TAG_FILTER_IS_NOT_NULL: |
| filter = builder.is_not_null(column_name); |
| break; |
| default: |
| return common::E_INVALID_ARG; |
| } |
| if (filter == nullptr) { |
| return common::E_COLUMN_NOT_EXIST; |
| } |
| *out_filter = static_cast<void*>(filter); |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } catch (...) { |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| TagFilterHandle tsfile_tag_filter_create(TsFileReader reader, |
| const char* table_name, |
| const char* column_name, |
| const char* value, TagFilterOp op, |
| ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| TagFilterHandle filter = nullptr; |
| *err_code = tsfile_tag_filter_create_checked( |
| reader, table_name, column_name, value, op, &filter); |
| if (*err_code == common::E_TABLE_NOT_EXIST) { |
| *err_code = common::E_INVALID_ARG; |
| } |
| return *err_code == common::E_OK ? filter : nullptr; |
| } |
| |
| ERRNO tsfile_tag_filter_between_checked(TsFileReader reader, |
| const char* table_name, |
| const char* column_name, |
| const char* lower, const char* upper, |
| bool is_not, |
| TagFilterHandle* out_filter) { |
| if (out_filter != nullptr) { |
| *out_filter = nullptr; |
| } |
| if (reader == nullptr || table_name == nullptr || column_name == nullptr || |
| lower == nullptr || upper == nullptr || out_filter == nullptr) { |
| return common::E_INVALID_ARG; |
| } |
| try { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| std::shared_ptr<storage::TableSchema> schema; |
| const int ret = r->get_table_schema(table_name, schema); |
| if (ret != common::E_OK) { |
| return ret; |
| } |
| storage::TagFilterBuilder builder(schema.get()); |
| storage::Filter* filter = |
| is_not ? builder.not_between_and(column_name, lower, upper) |
| : builder.between_and(column_name, lower, upper); |
| if (filter == nullptr) { |
| return common::E_COLUMN_NOT_EXIST; |
| } |
| *out_filter = static_cast<void*>(filter); |
| return common::E_OK; |
| } catch (const std::bad_alloc&) { |
| return common::E_OOM; |
| } catch (...) { |
| return common::E_FILE_READ_ERR; |
| } |
| } |
| |
| TagFilterHandle tsfile_tag_filter_between(TsFileReader reader, |
| const char* table_name, |
| const char* column_name, |
| const char* lower, const char* upper, |
| bool is_not, ERRNO* err_code) { |
| if (err_code == nullptr) { |
| return nullptr; |
| } |
| TagFilterHandle filter = nullptr; |
| *err_code = tsfile_tag_filter_between_checked( |
| reader, table_name, column_name, lower, upper, is_not, &filter); |
| if (*err_code == common::E_TABLE_NOT_EXIST) { |
| *err_code = common::E_INVALID_ARG; |
| } |
| return *err_code == common::E_OK ? filter : nullptr; |
| } |
| |
| TagFilterHandle tsfile_tag_filter_and(TagFilterHandle left, |
| TagFilterHandle right) { |
| if (!left || !right) return nullptr; |
| try { |
| return storage::TagFilterBuilder::and_filter( |
| static_cast<storage::Filter*>(left), |
| static_cast<storage::Filter*>(right)); |
| } catch (...) { |
| return nullptr; |
| } |
| } |
| |
| TagFilterHandle tsfile_tag_filter_or(TagFilterHandle left, |
| TagFilterHandle right) { |
| if (!left || !right) return nullptr; |
| try { |
| return storage::TagFilterBuilder::or_filter( |
| static_cast<storage::Filter*>(left), |
| static_cast<storage::Filter*>(right)); |
| } catch (...) { |
| return nullptr; |
| } |
| } |
| |
| TagFilterHandle tsfile_tag_filter_not(TagFilterHandle filter) { |
| if (!filter) return nullptr; |
| try { |
| return storage::TagFilterBuilder::not_filter( |
| static_cast<storage::Filter*>(filter)); |
| } catch (...) { |
| return nullptr; |
| } |
| } |
| |
| void tsfile_tag_filter_free(TagFilterHandle filter) { |
| if (filter) { |
| delete static_cast<storage::Filter*>(filter); |
| } |
| } |
| |
| ResultSet tsfile_query_table_with_tag_filter( |
| TsFileReader reader, const char* table_name, char** columns, |
| uint32_t column_num, Timestamp start_time, Timestamp end_time, |
| TagFilterHandle tag_filter, int batch_size, ERRNO* err_code) { |
| auto* r = static_cast<storage::TsFileReader*>(reader); |
| storage::ResultSet* table_result_set = nullptr; |
| std::vector<std::string> column_names; |
| for (uint32_t i = 0; i < column_num; i++) { |
| column_names.emplace_back(columns[i]); |
| } |
| *err_code = r->query(table_name, column_names, start_time, end_time, |
| table_result_set, |
| static_cast<storage::Filter*>(tag_filter), batch_size); |
| return table_result_set; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |