| /* |
| * 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 a |
| * |
| * 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 <gtest/gtest.h> |
| |
| #include <random> |
| |
| #include "common/record.h" |
| #include "common/schema.h" |
| #include "common/tablet.h" |
| #include "file/tsfile_io_writer.h" |
| #include "file/write_file.h" |
| #include "reader/tsfile_reader.h" |
| #include "writer/chunk_writer.h" |
| #include "writer/tsfile_table_writer.h" |
| using namespace storage; |
| using namespace common; |
| |
| class TsFileWriterTableTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| libtsfile_init(); |
| file_name_ = std::string("tsfile_writer_table_test_") + |
| generate_random_string(10) + std::string(".tsfile"); |
| remove(file_name_.c_str()); |
| int flags = O_WRONLY | O_CREAT | O_TRUNC; |
| #ifdef _WIN32 |
| flags |= O_BINARY; |
| #endif |
| mode_t mode = 0666; |
| write_file_.create(file_name_, flags, mode); |
| } |
| void TearDown() override { /*remove(file_name_.c_str());*/ } |
| std::string file_name_; |
| WriteFile write_file_; |
| |
| public: |
| static std::string generate_random_string(int length) { |
| std::random_device rd; |
| std::mt19937 gen(rd()); |
| std::uniform_int_distribution<> dis(0, 61); |
| |
| const std::string chars = |
| "0123456789" |
| "abcdefghijklmnopqrstuvwxyz" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| |
| std::string random_string; |
| |
| for (int i = 0; i < length; ++i) { |
| random_string += chars[dis(gen)]; |
| } |
| |
| return random_string; |
| } |
| |
| static TableSchema* gen_table_schema(int table_num, int id_col_num = 5, |
| int field_col_num = 5) { |
| std::vector<MeasurementSchema*> measurement_schemas; |
| std::vector<ColumnCategory> column_categories; |
| int id_schema_num = id_col_num; |
| int measurement_schema_num = field_col_num; |
| for (int i = 0; i < id_schema_num; i++) { |
| measurement_schemas.emplace_back(new MeasurementSchema( |
| "id" + std::to_string(i), TSDataType::STRING, TSEncoding::PLAIN, |
| CompressionType::UNCOMPRESSED)); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| } |
| for (int i = 0; i < measurement_schema_num; i++) { |
| measurement_schemas.emplace_back(new MeasurementSchema( |
| "s" + std::to_string(i), TSDataType::INT64, TSEncoding::PLAIN, |
| CompressionType::UNCOMPRESSED)); |
| column_categories.emplace_back(ColumnCategory::FIELD); |
| } |
| return new TableSchema("testTable" + std::to_string(table_num), |
| measurement_schemas, column_categories); |
| } |
| |
| static storage::Tablet gen_tablet(TableSchema* table_schema, int offset, |
| int device_num, |
| int num_timestamp_per_device = 10) { |
| storage::Tablet tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types(), |
| device_num * num_timestamp_per_device); |
| static int timestamp = 0; |
| for (int i = 0; i < device_num; i++) { |
| PageArena pa; |
| pa.init(512, MOD_DEFAULT); |
| std::string device_str = std::string("device_id_") + std::to_string(i); |
| String literal_str(device_str, pa); |
| for (int l = 0; l < num_timestamp_per_device; l++) { |
| int row_index = i * num_timestamp_per_device + l; |
| tablet.add_timestamp(row_index, timestamp++); |
| auto column_schemas = table_schema->get_measurement_schemas(); |
| for (const auto& column_schema : column_schemas) { |
| switch (column_schema->data_type_) { |
| case TSDataType::INT64: |
| tablet.add_value(row_index, |
| column_schema->measurement_name_, |
| static_cast<int64_t>(i)); |
| break; |
| case TSDataType::STRING: |
| tablet.add_value(row_index, |
| column_schema->measurement_name_, |
| literal_str); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| return tablet; |
| } |
| }; |
| |
| TEST_F(TsFileWriterTableTest, WriteTableTest) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| auto tablet = gen_tablet(table_schema, 0, 1); |
| ASSERT_EQ(tsfile_table_writer_->write_table(tablet), common::E_OK); |
| ASSERT_EQ(tsfile_table_writer_->flush(), common::E_OK); |
| ASSERT_EQ(tsfile_table_writer_->close(), common::E_OK); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WithoutTagAndMultiPage) { |
| // std::vector<MeasurementSchema*> measurement_schemas; |
| // std::vector<ColumnCategory> column_categories; |
| // measurement_schemas.resize(1); |
| // measurement_schemas[0] = new MeasurementSchema("value", DOUBLE); |
| // column_categories.emplace_back(ColumnCategory::FIELD); |
| // TableSchema* table_schema = |
| // new TableSchema("test_table", measurement_schemas, column_categories); |
| // auto tsfile_table_writer = |
| // std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| // |
| // int cur_line = 0; |
| // for (int j = 0; j < 100 * 10 * 3; j++) { |
| // Tablet tablet = Tablet(table_schema->get_measurement_names(), |
| // table_schema->get_data_types(), 1000100); |
| // tablet.set_table_name("test_table"); |
| // for (int i = 0; i < 1000100; i++) { |
| // tablet.add_timestamp(i, static_cast<int64_t>(cur_line++)); |
| // tablet.add_value(i, "value", i * 1.1); |
| // } |
| // tsfile_table_writer->write_table(tablet); |
| // std::cout<<"tablet id"<< j << std::endl; |
| // tsfile_table_writer->flush(); |
| // } |
| // |
| // tsfile_table_writer->flush(); |
| // tsfile_table_writer->close(); |
| |
| // TsFileReader reader = TsFileReader(); |
| // reader.open("/Users/colin/dev/tsfile/cpp/timebench_0.tsfile"); |
| // ResultSet* ret = nullptr; |
| // auto start = std::chrono::high_resolution_clock::now(); |
| // int ret_value = reader.query("timebench", {"value"}, 0, 1+1024, ret); |
| // ASSERT_EQ(common::E_OK, ret_value); |
| // auto* table_result_set = (TableResultSet*)ret; |
| // bool has_next = false; |
| // int cur_line = 0; |
| // while (IS_SUCC(table_result_set->next(has_next)) && has_next) { |
| // cur_line++; |
| // std::cout<<table_result_set->get_value<int64_t>(1); |
| // } |
| // // 记录结束时间点 |
| // auto end = std::chrono::high_resolution_clock::now(); |
| // |
| // // 计算耗时(微秒) |
| // auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); |
| // |
| // // 输出结果 |
| // std::cout << "耗时: " << duration.count() * 1.0 /1000/1000 << " 秒" << std::endl; |
| // ASSERT_EQ(cur_line, 1026); |
| // table_result_set->close(); |
| // reader.destroy_query_data_set(table_result_set); |
| // |
| // reader.close(); |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriteDisorderTest) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| |
| int device_num = 1; |
| int num_timestamp_per_device = 10; |
| int offset = 0; |
| storage::Tablet tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types(), |
| device_num * num_timestamp_per_device); |
| |
| char* literal = new char[std::strlen("device_id") + 1]; |
| std::strcpy(literal, "device_id"); |
| String literal_str(literal, std::strlen("device_id")); |
| for (int i = 0; i < device_num; i++) { |
| for (int l = 0; l < num_timestamp_per_device; l++) { |
| int row_index = i * num_timestamp_per_device + l; |
| // disordered timestamp. |
| tablet.add_timestamp(row_index, l > num_timestamp_per_device / 2 |
| ? l - num_timestamp_per_device |
| : offset + l); |
| auto column_schemas = table_schema->get_measurement_schemas(); |
| for (const auto& column_schema : column_schemas) { |
| switch (column_schema->data_type_) { |
| case TSDataType::INT64: |
| tablet.add_value(row_index, |
| column_schema->measurement_name_, |
| static_cast<int64_t>(i)); |
| break; |
| case TSDataType::STRING: |
| tablet.add_value(row_index, |
| column_schema->measurement_name_, |
| literal_str); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| delete[] literal; |
| |
| ASSERT_EQ(tsfile_table_writer_->write_table(tablet), |
| common::E_OUT_OF_ORDER); |
| ASSERT_EQ(tsfile_table_writer_->flush(), common::E_OK); |
| ASSERT_EQ(tsfile_table_writer_->close(), common::E_OK); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriteTableTestMultiFlush) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = std::make_shared<TsFileTableWriter>( |
| &write_file_, table_schema, 2 * 1024); |
| for (int i = 0; i < 100; i++) { |
| auto tablet = gen_tablet(table_schema, i * 10000, 1, 10000); |
| ASSERT_EQ(tsfile_table_writer_->write_table(tablet), common::E_OK); |
| } |
| ASSERT_EQ(tsfile_table_writer_->flush(), common::E_OK); |
| ASSERT_EQ(tsfile_table_writer_->close(), common::E_OK); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriteNonExistColumnTest) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| |
| auto measurment_schemas = table_schema->get_measurement_schemas(); |
| auto column_categories = table_schema->get_column_categories(); |
| measurment_schemas.emplace_back( |
| std::make_shared<MeasurementSchema>("non_exist", TSDataType::INT64)); |
| column_categories.emplace_back(ColumnCategory::FIELD); |
| std::vector<ColumnSchema> column_schemas; |
| for (size_t i = 0; i < measurment_schemas.size(); ++i) { |
| column_schemas.emplace_back(measurment_schemas[i]->measurement_name_, |
| measurment_schemas[i]->data_type_, |
| measurment_schemas[i]->compression_type_, |
| measurment_schemas[i]->encoding_, |
| column_categories[i]); |
| } |
| auto write_table_schema = |
| TableSchema(table_schema->get_table_name(), column_schemas); |
| |
| auto tablet = gen_tablet(&write_table_schema, 0, 1); |
| ASSERT_EQ(tsfile_table_writer_->write_table(tablet), |
| common::E_COLUMN_NOT_EXIST); |
| tsfile_table_writer_->close(); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriteNonExistTableTest) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| auto tablet = gen_tablet(table_schema, 0, 1); |
| tablet.set_table_name("non_exist"); |
| ASSERT_EQ(tsfile_table_writer_->write_table(tablet), |
| common::E_TABLE_NOT_EXIST); |
| tsfile_table_writer_->close(); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriterWithMemoryThreshold) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = std::make_shared<TsFileTableWriter>( |
| &write_file_, table_schema, 256 * 1024 * 1024); |
| ASSERT_EQ(common::g_config_value_.chunk_group_size_threshold_, |
| 256 * 1024 * 1024); |
| tsfile_table_writer_->close(); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, EmptyTagWrite) { |
| std::vector<MeasurementSchema*> measurement_schemas; |
| std::vector<ColumnCategory> column_categories; |
| measurement_schemas.resize(3); |
| measurement_schemas[0] = new MeasurementSchema("device1", STRING); |
| measurement_schemas[1] = new MeasurementSchema("device2", STRING); |
| measurement_schemas[2] = new MeasurementSchema("value", DOUBLE); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| column_categories.emplace_back(ColumnCategory::FIELD); |
| TableSchema* table_schema = |
| new TableSchema("test_table", measurement_schemas, column_categories); |
| auto tsfile_table_writer = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| Tablet tablet = Tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types()); |
| tablet.set_table_name("test_table"); |
| for (int i = 0; i < 100; i++) { |
| tablet.add_timestamp(i, static_cast<int64_t>(i)); |
| tablet.add_value(i, "device1", |
| std::string("device" + std::to_string(i)).c_str()); |
| tablet.add_value(i, "device2", ""); |
| tablet.add_value(i, "value", i * 1.1); |
| } |
| tsfile_table_writer->write_table(tablet); |
| tsfile_table_writer->flush(); |
| tsfile_table_writer->close(); |
| |
| TsFileReader reader = TsFileReader(); |
| reader.open(write_file_.get_file_path()); |
| ResultSet* ret = nullptr; |
| int ret_value = |
| reader.query("test_table", {"device1", "device2", "value"}, 0, 50, ret); |
| ASSERT_EQ(common::E_OK, ret_value); |
| |
| ASSERT_EQ(ret_value, 0); |
| auto* table_result_set = (TableResultSet*)ret; |
| bool has_next = false; |
| int cur_line = 0; |
| while (IS_SUCC(table_result_set->next(has_next)) && has_next) { |
| cur_line++; |
| int64_t timestamp = table_result_set->get_value<int64_t>("time"); |
| ASSERT_EQ(table_result_set->get_value<common::String*>("device1") |
| ->to_std_string(), |
| "device" + std::to_string(timestamp)); |
| ASSERT_EQ(table_result_set->get_value<double>("value"), |
| timestamp * 1.1); |
| } |
| ASSERT_EQ(cur_line, 51); |
| table_result_set->close(); |
| reader.destroy_query_data_set(table_result_set); |
| |
| reader.close(); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WritehDataTypeMisMatch) { |
| auto table_schema = gen_table_schema(0); |
| auto tsfile_table_writer_ = std::make_shared<TsFileTableWriter>( |
| &write_file_, table_schema, 256 * 1024 * 1024); |
| int device_num = 3; |
| int num_timestamp_per_device = 10; |
| int offset = 0; |
| auto datatypes = table_schema->get_data_types(); |
| |
| datatypes[6] = TSDataType::INT32; |
| storage::Tablet tablet(table_schema->get_measurement_names(), datatypes, |
| device_num * num_timestamp_per_device); |
| |
| char* literal = new char[std::strlen("device_id") + 1]; |
| std::strcpy(literal, "device_id"); |
| String literal_str(literal, std::strlen("device_id")); |
| for (int i = 0; i < device_num; i++) { |
| for (int l = 0; l < num_timestamp_per_device; l++) { |
| int row_index = i * num_timestamp_per_device + l; |
| tablet.add_timestamp(row_index, offset + l); |
| auto column_schemas = table_schema->get_measurement_schemas(); |
| for (int idx = 0; idx < column_schemas.size(); idx++) { |
| switch (datatypes[idx]) { |
| case TSDataType::INT64: |
| tablet.add_value(row_index, |
| column_schemas[idx]->measurement_name_, |
| static_cast<int64_t>(i)); |
| break; |
| case TSDataType::INT32: |
| tablet.add_value(row_index, |
| column_schemas[idx]->measurement_name_, |
| static_cast<int32_t>(i)); |
| break; |
| case TSDataType::STRING: |
| tablet.add_value(row_index, |
| column_schemas[idx]->measurement_name_, |
| literal_str); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| delete[] literal; |
| delete table_schema; |
| |
| ASSERT_EQ(E_TYPE_NOT_MATCH, tsfile_table_writer_->write_table(tablet)); |
| tsfile_table_writer_->close(); |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriteAndReadSimple) { |
| std::vector<MeasurementSchema*> measurement_schemas; |
| std::vector<ColumnCategory> column_categories; |
| measurement_schemas.resize(2); |
| measurement_schemas[0] = new MeasurementSchema("device", STRING); |
| measurement_schemas[1] = new MeasurementSchema("value", DOUBLE); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| column_categories.emplace_back(ColumnCategory::FIELD); |
| TableSchema* table_schema = |
| new TableSchema("test_table", measurement_schemas, column_categories); |
| auto tsfile_table_writer = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| Tablet tablet = Tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types()); |
| tablet.set_table_name("test_table"); |
| for (int i = 0; i < 100; i++) { |
| tablet.add_timestamp(i, static_cast<int64_t>(i)); |
| tablet.add_value(i, "device", |
| std::string("device" + std::to_string(i)).c_str()); |
| tablet.add_value(i, "value", i * 1.1); |
| } |
| tsfile_table_writer->write_table(tablet); |
| tsfile_table_writer->flush(); |
| tsfile_table_writer->close(); |
| |
| TsFileReader reader = TsFileReader(); |
| reader.open(write_file_.get_file_path()); |
| ResultSet* ret = nullptr; |
| std::vector<std::string> column_names = {"device", "VALUE"}; |
| int ret_value = reader.query("test_table", column_names, 0, 50, ret); |
| ASSERT_EQ(common::E_OK, ret_value); |
| |
| ASSERT_EQ(ret_value, 0); |
| auto* table_result_set = (TableResultSet*)ret; |
| auto metadata = ret->get_metadata(); |
| ASSERT_EQ(metadata->get_column_name(column_names.size() + 1), "VALUE"); |
| bool has_next = false; |
| int cur_line = 0; |
| while (IS_SUCC(table_result_set->next(has_next)) && has_next) { |
| cur_line++; |
| int64_t timestamp = table_result_set->get_value<int64_t>("time"); |
| ASSERT_EQ(table_result_set->get_value<common::String*>("device") |
| ->to_std_string(), |
| "device" + std::to_string(timestamp)); |
| ASSERT_EQ(table_result_set->get_value<double>("VaLue"), |
| timestamp * 1.1); |
| } |
| ASSERT_EQ(cur_line, 51); |
| table_result_set->close(); |
| reader.destroy_query_data_set(table_result_set); |
| |
| reader.close(); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, DuplicateColumnName) { |
| std::vector<MeasurementSchema*> measurement_schemas; |
| std::vector<ColumnCategory> column_categories; |
| measurement_schemas.resize(3); |
| measurement_schemas[0] = new MeasurementSchema("device", STRING); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| measurement_schemas[1] = new MeasurementSchema("Device", STRING); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| measurement_schemas[2] = new MeasurementSchema("value", DOUBLE); |
| column_categories.emplace_back(ColumnCategory::FIELD); |
| TableSchema* table_schema = |
| new TableSchema("test_table", measurement_schemas, column_categories); |
| auto tsfile_table_writer = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| Tablet tablet = Tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types()); |
| tablet.set_table_name("test_table"); |
| ASSERT_EQ(E_INVALID_ARG, tablet.add_timestamp(0, 10)); |
| ASSERT_EQ(E_INVALID_ARG, tablet.add_value(1, 1, 10)); |
| ASSERT_EQ(E_INVALID_ARG, tablet.add_value(1, "test", 10)); |
| std::vector<MeasurementSchema> measurement_schemas2; |
| for (int i = 0; i < 2; i++) { |
| measurement_schemas2.push_back(*measurement_schemas[i]); |
| } |
| Tablet tablet1 = Tablet( |
| "test_table", |
| std::make_shared<std::vector<MeasurementSchema>>(measurement_schemas2)); |
| tablet1.set_table_name("test_table"); |
| ASSERT_EQ(E_INVALID_ARG, tablet1.add_timestamp(0, 10)); |
| ASSERT_EQ(E_INVALID_ARG, tablet1.add_value(1, 1, 10)); |
| ASSERT_EQ(E_INVALID_ARG, tablet1.add_value(1, "test", 10)); |
| |
| ASSERT_EQ(E_INVALID_ARG, tsfile_table_writer->write_table(tablet)); |
| ASSERT_EQ(E_INVALID_ARG, tsfile_table_writer->register_table( |
| std::make_shared<TableSchema>(*table_schema))); |
| delete table_schema; |
| } |
| |
| TEST_F(TsFileWriterTableTest, WriteWithNullAndEmptyTag) { |
| std::vector<MeasurementSchema*> measurement_schemas; |
| std::vector<ColumnCategory> column_categories; |
| for (int i = 0; i < 3; i++) { |
| measurement_schemas.emplace_back(new MeasurementSchema( |
| "id" + std::to_string(i), TSDataType::STRING)); |
| column_categories.emplace_back(ColumnCategory::TAG); |
| } |
| measurement_schemas.emplace_back(new MeasurementSchema("value", DOUBLE)); |
| column_categories.emplace_back(ColumnCategory::FIELD); |
| auto table_schema = |
| new TableSchema("testTable", measurement_schemas, column_categories); |
| auto tsfile_table_writer = |
| std::make_shared<TsFileTableWriter>(&write_file_, table_schema); |
| int time = 0; |
| Tablet tablet = Tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types(), 10); |
| |
| for (int i = 0; i < 10; i++) { |
| tablet.add_timestamp(i, static_cast<int64_t>(time++)); |
| tablet.add_value(i, 0, "tag1"); |
| tablet.add_value(i, 1, "tag2"); |
| tablet.add_value(i, 2, "tag3"); |
| tablet.add_value(i, 3, 100.0f); |
| } |
| |
| tsfile_table_writer->write_table(tablet); |
| Tablet tablet2 = Tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types(), 10); |
| |
| for (int i = 0; i < 10; i++) { |
| tablet2.add_timestamp(i, static_cast<int64_t>(time++)); |
| tablet2.add_value(i, 0, i % 2 == 0 ? "" : "tag4"); |
| tablet2.add_value(i, 1, i % 2 == 1 ? "" : "tag5"); |
| tablet2.add_value(i, 2, i % 3 == 0 ? "" : "tag6"); |
| tablet2.add_value(i, 3, 101.0f); |
| } |
| tsfile_table_writer->write_table(tablet2); |
| |
| Tablet tablet3 = Tablet(table_schema->get_measurement_names(), |
| table_schema->get_data_types(), 10); |
| for (int i = 0; i < 10; i++) { |
| tablet3.add_timestamp(i, static_cast<int64_t>(time++)); |
| tablet3.add_value(i, 0, "tag7"); |
| if (i % 2 == 0) { |
| tablet3.add_value(i, 1, "tag8\0ta"); |
| } else { |
| tablet3.add_value(i, 2, "tag9"); |
| } |
| tablet3.add_value(i, 3, 102.0f); |
| } |
| |
| tsfile_table_writer->write_table(tablet3); |
| tsfile_table_writer->flush(); |
| tsfile_table_writer->close(); |
| |
| delete table_schema; |
| |
| auto reader = TsFileReader(); |
| reader.open(write_file_.get_file_path()); |
| ResultSet* ret = nullptr; |
| int ret_value = |
| reader.query("testTable", {"id0", "id1", "id2", "value"}, 0, 50, ret); |
| ASSERT_EQ(common::E_OK, ret_value); |
| |
| auto table_result_set = (TableResultSet*)ret; |
| bool has_next = false; |
| int cur_line = 0; |
| auto schema = table_result_set->get_metadata(); |
| while (IS_SUCC(table_result_set->next(has_next)) && has_next) { |
| int64_t timestamp = table_result_set->get_value<int64_t>(1); |
| switch (timestamp) { |
| case 0: { |
| // All tag fields have valid values. |
| ASSERT_EQ(common::String(std::string("tag1")), |
| *table_result_set->get_value<common::String*>(2)); |
| ASSERT_EQ(common::String(std::string("tag2")), |
| *table_result_set->get_value<common::String*>(3)); |
| ASSERT_EQ(common::String(std::string("tag3")), |
| *table_result_set->get_value<common::String*>(4)); |
| ASSERT_EQ(100.0f, table_result_set->get_value<double>(5)); |
| break; |
| } |
| case 10: { |
| // The first and last tag fields are empty strings. |
| ASSERT_EQ(common::String(std::string("")), |
| *table_result_set->get_value<common::String*>(2)); |
| ASSERT_EQ(common::String(std::string("tag5")), |
| *table_result_set->get_value<common::String*>(3)); |
| ASSERT_EQ(common::String(std::string("")), |
| *table_result_set->get_value<common::String*>(4)); |
| ASSERT_EQ(101.0f, table_result_set->get_value<double>(5)); |
| break; |
| } |
| case 11: { |
| // The middle tag field is an empty string. |
| ASSERT_EQ(common::String(std::string("tag4")), |
| *table_result_set->get_value<common::String*>(2)); |
| ASSERT_EQ(common::String(std::string("")), |
| *table_result_set->get_value<common::String*>(3)); |
| ASSERT_EQ(common::String(std::string("tag6")), |
| *table_result_set->get_value<common::String*>(4)); |
| ASSERT_EQ(101.0f, table_result_set->get_value<double>(5)); |
| break; |
| } |
| case 20: { |
| // The last tag field is null. |
| ASSERT_EQ(common::String(std::string("tag7")), |
| *table_result_set->get_value<common::String*>(2)); |
| ASSERT_EQ(common::String(std::string("tag8\0ta")), |
| *table_result_set->get_value<common::String*>(3)); |
| ASSERT_TRUE(table_result_set->is_null(4)); |
| ASSERT_EQ(102.0f, table_result_set->get_value<double>(5)); |
| break; |
| } |
| case 21: { |
| // The middle tag field is null. |
| ASSERT_EQ(common::String(std::string("tag7")), |
| *table_result_set->get_value<common::String*>(2)); |
| ASSERT_EQ(common::String(std::string("tag9")), |
| *table_result_set->get_value<common::String*>(4)); |
| ASSERT_TRUE(table_result_set->is_null(3)); |
| ASSERT_EQ(102.0f, table_result_set->get_value<double>(5)); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| reader.destroy_query_data_set(table_result_set); |
| ASSERT_EQ(reader.close(), common::E_OK); |
| } |
| |
| TEST_F(TsFileWriterTableTest, MultiDeviceMultiFields) { |
| common::config_set_max_degree_of_index_node(5); |
| auto table_schema = gen_table_schema(0, 1, 100); |
| auto tsfile_table_writer_ = std::make_shared<TsFileTableWriter>( |
| &write_file_, table_schema); |
| int num_row_per_device = 10; |
| auto tablet = gen_tablet(table_schema, 0, 100, num_row_per_device); |
| ASSERT_EQ(tsfile_table_writer_->write_table(tablet), common::E_OK); |
| ASSERT_EQ(tsfile_table_writer_->flush(), common::E_OK); |
| ASSERT_EQ(tsfile_table_writer_->close(), common::E_OK); |
| |
| storage::TsFileReader reader; |
| int ret = reader.open(file_name_); |
| ASSERT_EQ(ret, common::E_OK); |
| |
| ResultSet* tmp_result_set = nullptr; |
| ret = reader.query(table_schema->get_table_name(), |
| table_schema->get_measurement_names(), 0, |
| INT32_MAX, tmp_result_set); |
| auto* table_result_set = (TableResultSet*)tmp_result_set; |
| bool has_next = false; |
| int64_t row_num = 0; |
| auto result_set_meta = table_result_set->get_metadata(); |
| ASSERT_EQ(result_set_meta->get_column_count(), |
| table_schema->get_columns_num() + 1); // +1: time column |
| while (IS_SUCC(table_result_set->next(has_next)) && has_next) { |
| auto column_schemas = table_schema->get_measurement_schemas(); |
| std::string tag_col_val; // "device_id_[num]" |
| std::string tag_col_val_prefix = "device_id_"; |
| for (const auto& column_schema : column_schemas) { |
| switch (column_schema->data_type_) { |
| case TSDataType::INT64: |
| if (!table_result_set->is_null( |
| column_schema->measurement_name_)) { |
| std::string num = tag_col_val.substr( |
| tag_col_val_prefix.length(), |
| tag_col_val.length() - tag_col_val_prefix.length()); |
| EXPECT_EQ(table_result_set->get_value<int64_t>( |
| column_schema->measurement_name_), |
| std::stoi(num)); |
| } |
| break; |
| case TSDataType::STRING: |
| tag_col_val = table_result_set->get_value<common::String*>( |
| column_schema->measurement_name_)->to_std_string(); |
| default: |
| break; |
| } |
| } |
| row_num++; |
| } |
| ASSERT_EQ(row_num, tablet.get_cur_row_size()); |
| reader.destroy_query_data_set(table_result_set); |
| ASSERT_EQ(reader.close(), common::E_OK); |
| delete table_schema; |
| } |