Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions cpp/src/file/read_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ void ReadFile::close() {
fd_ = -1;
}
file_size_ = -1;
file_version_ = 0;
}

int ReadFile::open(const std::string& file_path) {
Expand Down Expand Up @@ -153,6 +154,20 @@ int ReadFile::check_file_magic() {
0) {
ret = E_TSFILE_CORRUPTED;
}
if (IS_FAIL(ret)) {
return ret;
}

char version = 0;
if (RET_FAIL(read(MAGIC_STRING_TSFILE_LEN, &version, 1, read_len))) {
} else if (read_len != 1 || version != VERSION_NUM_BYTE) {
ret = E_TSFILE_CORRUPTED;
} else {
file_version_ = static_cast<unsigned char>(version);
}
if (IS_FAIL(ret)) {
return ret;
}

// file footer magic
memset(buf, 0, MAGIC_STRING_TSFILE_LEN);
Expand Down
4 changes: 3 additions & 1 deletion cpp/src/file/read_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ namespace storage {

class ReadFile {
public:
ReadFile() : file_path_(), fd_(-1), file_size_(-1) {}
ReadFile() : file_path_(), fd_(-1), file_size_(-1), file_version_(0) {}
~ReadFile() { destroy(); }
void destroy() { close(); }

int open(const std::string& file_path);
FORCE_INLINE bool is_opened() const { return fd_ > 0; }
FORCE_INLINE int64_t file_size() const { return file_size_; }
FORCE_INLINE const std::string& file_path() const { return file_path_; }
FORCE_INLINE unsigned char file_version() const { return file_version_; }

/** Return size and the Dataset Index v1 FNV fingerprint of size+mtime_ns.
*/
Expand All @@ -64,6 +65,7 @@ class ReadFile {
std::string file_path_;
int fd_;
int64_t file_size_;
unsigned char file_version_;
};

} // end namespace storage
Expand Down
6 changes: 3 additions & 3 deletions cpp/src/reader/filter/tag_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ bool TagRegExp::satisfyRow(std::vector<std::string*> segments) const {
!is_valid_pattern_)
return false;
try {
return std::regex_search(*segments[col_idx_], pattern_);
return std::regex_match(*segments[col_idx_], pattern_);
} catch (const std::regex_error&) {
return false;
}
Expand All @@ -137,7 +137,7 @@ bool TagNotRegExp::satisfyRow(std::vector<std::string*> segments) const {
!is_valid_pattern_)
return false;
try {
return !std::regex_search(*segments[col_idx_], pattern_);
return !std::regex_match(*segments[col_idx_], pattern_);
} catch (const std::regex_error&) {
return true;
}
Expand Down Expand Up @@ -332,4 +332,4 @@ int TagFilterBuilder::get_tag_column_index(const std::string& columnName) {
return idColumnOrder + 1;
}

} // namespace storage
} // namespace storage
4 changes: 4 additions & 0 deletions cpp/src/reader/tsfile_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ int TsFileReader::open(const std::string& file_path) {
return ret;
}

unsigned char TsFileReader::get_file_version() const {
return read_file_ == nullptr ? 0 : read_file_->file_version();
}

int TsFileReader::ensure_table_query_executor(int batch_size) {
if (table_query_executor_ != nullptr &&
table_query_executor_batch_size_ == batch_size) {
Expand Down
1 change: 1 addition & 0 deletions cpp/src/reader/tsfile_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class TsFileReader {
* @return Returns 0 on success, or a non-zero error code on failure.
*/
int close();
unsigned char get_file_version() const;
/**
* @brief query the tsfile by the query expression,Users can construct
* their own query expressions to query tsfile
Expand Down
2 changes: 2 additions & 0 deletions cpp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ if (VENDORED_GTEST_INCLUDE_DIRS)
endif ()
if (BUILD_TOOLS)
target_include_directories(TsFile_Test PRIVATE ${CMAKE_SOURCE_DIR}/tools)
target_compile_definitions(TsFile_Test PRIVATE
TSFILE_CPP_SOURCE_DIR="${CMAKE_SOURCE_DIR}")
endif ()
if (APPLE AND NOT MSVC)
target_compile_options(TsFile_Test PRIVATE -std=c++14)
Expand Down
8 changes: 6 additions & 2 deletions cpp/test/reader/filter/tag_filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,13 @@ TEST_F(TagFilterTest, TagRegExpEdgeCases) {
invalid_filter->satisfyRow(0, segments)); // handles gracefully
cleanupSegments(segments);

// Empty pattern matches everything
// Full-value matching means an empty pattern only matches an empty value.
auto empty_filter = builder_->reg_exp("name", "");
segments = createSegments("any", "25", "engineering", "active", "95");
EXPECT_FALSE(empty_filter->satisfyRow(0, segments));
cleanupSegments(segments);

segments = createSegments("", "25", "engineering", "active", "95");
EXPECT_TRUE(empty_filter->satisfyRow(0, segments));
cleanupSegments(segments);

Expand Down Expand Up @@ -497,4 +501,4 @@ TEST_F(TagFilterTest, TagIsNotNullFilter) {

delete filter;
delete trailing;
}
}
100 changes: 54 additions & 46 deletions cpp/test/tools/cli_args_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ TEST(RunCliTest, VersionFlagPrintsVersionAndReturnsOk) {
EXPECT_TRUE(err.str().empty());
}

TEST(RunCliTest, VersionMustAppearByItself) {
std::ostringstream out;
std::ostringstream err;
int code =
tsfile_cli::run_cli({"cat", "--version", "data.tsfile"}, out, err);
EXPECT_EQ(code, 1);
EXPECT_TRUE(out.str().empty());
EXPECT_NE(err.str().find("--version"), std::string::npos) << err.str();
}

TEST(RunCliTest, TopLevelHelpMustAppearByItself) {
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli({"--help", "cat"}, out, err);
EXPECT_EQ(code, 1);
EXPECT_TRUE(out.str().empty());
EXPECT_NE(err.str().find("--help"), std::string::npos) << err.str();
}

TEST(RunCliTest, CommandHelpMustAppearByItself) {
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli({"cat", "--help", "data.tsfile"}, out, err);
EXPECT_EQ(code, 1);
EXPECT_TRUE(out.str().empty());
EXPECT_NE(err.str().find("--help"), std::string::npos) << err.str();
}

TEST(RunCliTest, NoArgsPrintsUsageToErrAndReturnsUsageError) {
std::ostringstream out;
std::ostringstream err;
Expand All @@ -54,7 +82,7 @@ TEST(RunCliTest, LeadingOptionBeforeCommandIsClearError) {
std::ostringstream out;
std::ostringstream err;
int code =
tsfile_cli::run_cli({"-f", "json", "meta", "data.tsfile"}, out, err);
tsfile_cli::run_cli({"-f", "ndjson", "meta", "data.tsfile"}, out, err);
EXPECT_EQ(code, 1);
EXPECT_NE(err.str().find("command must come before options"),
std::string::npos)
Expand All @@ -69,14 +97,15 @@ TEST(ParseArgsTest, CommandAndFilePositional) {
}

TEST(ParseArgsTest, FormatFlagParsed) {
auto p = tsfile_cli::parse_args({"cat", "-f", "json", "data.tsfile"});
auto p = tsfile_cli::parse_args({"cat", "-f", "ndjson", "data.tsfile"});
EXPECT_TRUE(p.error.empty());
EXPECT_EQ(p.format, tsfile_cli::ParsedArgs::Format::kJson);
}

TEST(ParseArgsTest, MeasurementsSplitOnComma) {
auto p = tsfile_cli::parse_args({"cat", "-m", "s1,s2,s3", "data.tsfile"});
ASSERT_EQ(p.measurements.size(), 3u);
TEST(ParseArgsTest, MeasurementsRepeatOneNamePerOption) {
auto p =
tsfile_cli::parse_args({"cat", "-m", "s1", "-m", "s2", "data.tsfile"});
ASSERT_EQ(p.measurements.size(), 2u);
EXPECT_EQ(p.measurements[1], "s2");
}

Expand All @@ -97,27 +126,24 @@ TEST(ParseArgsTest, TagFilterParsed) {
{"cat", "--tag-filter", "id1", "eq", "dev_a", "data.tsfile"});
EXPECT_TRUE(p.error.empty());
EXPECT_TRUE(p.has_tag_filter);
EXPECT_EQ(p.tag_filter_op, tsfile_cli::ParsedArgs::TagFilterOp::kEq);
EXPECT_EQ(p.tag_filter_column, "id1");
EXPECT_EQ(p.tag_filter_value, "dev_a");
ASSERT_EQ(p.tag_filters.size(), 1u);
EXPECT_EQ(p.tag_filters[0].op, tsfile_cli::ParsedArgs::TagFilterOp::kEq);
EXPECT_EQ(p.tag_filters[0].column, "id1");
EXPECT_EQ(p.tag_filters[0].value, "dev_a");
}

TEST(ParseArgsTest, TagBetweenParsed) {
TEST(ParseArgsTest, TagBetweenIsNotSupported) {
auto p = tsfile_cli::parse_args(
{"cat", "--tag-between", "id1", "dev_a", "dev_c", "data.tsfile"});
EXPECT_TRUE(p.error.empty());
EXPECT_TRUE(p.has_tag_filter);
EXPECT_EQ(p.tag_filter_op, tsfile_cli::ParsedArgs::TagFilterOp::kBetween);
EXPECT_EQ(p.tag_filter_column, "id1");
EXPECT_EQ(p.tag_filter_value, "dev_a");
EXPECT_EQ(p.tag_filter_value2, "dev_c");
EXPECT_FALSE(p.error.empty());
}

TEST(ParseArgsTest, DuplicateTagFilterIsError) {
auto p = tsfile_cli::parse_args({"cat", "--tag-filter", "id1", "eq",
"dev_a", "--tag-between", "id1", "a", "z",
"dev_a", "--tag-filter", "id1", "neq", "z",
"data.tsfile"});
EXPECT_FALSE(p.error.empty());
EXPECT_TRUE(p.error.empty());
ASSERT_EQ(p.tag_filters.size(), 2u);
}

TEST(ParseArgsTest, UnknownFlagIsError) {
Expand All @@ -138,9 +164,9 @@ TEST(ParseArgsTest, MissingFileIsAllowedAtParseTime) {
}

TEST(ParseArgsTest, WriteFlagsParsed) {
auto p = tsfile_cli::parse_args({"write", "--table", "t1", "--columns",
"s1:INT64:field", "-o", "out.tsfile", "-v",
"--header-match", "in.csv"});
auto p = tsfile_cli::parse_args({"write", "--table", "t1", "--field", "s1",
"INT64", "-i", "in.csv", "-o",
"out.tsfile", "-v", "--header-match"});
EXPECT_TRUE(p.error.empty());
EXPECT_EQ(p.command, "write");
EXPECT_EQ(p.table, "t1");
Expand All @@ -149,36 +175,20 @@ TEST(ParseArgsTest, WriteFlagsParsed) {
EXPECT_TRUE(p.verbose);
EXPECT_TRUE(p.header_match);
EXPECT_EQ(p.file, "in.csv");
EXPECT_TRUE(p.input_set);
}

TEST(ParseArgsTest, OutputFlagNeedsValue) {
auto p = tsfile_cli::parse_args({"write", "-o"});
EXPECT_FALSE(p.error.empty());
}

TEST(ParseArgsTest, DashIsStdinPositional) {
auto p =
tsfile_cli::parse_args({"write", "--table", "t1", "--columns",
"s1:INT64:field", "-o", "out.tsfile", "-"});
TEST(ParseArgsTest, StdinFlagParsed) {
auto p = tsfile_cli::parse_args({"write", "--table", "t1", "--field", "s1",
"INT64", "--stdin", "-o", "out.tsfile"});
EXPECT_TRUE(p.error.empty());
EXPECT_EQ(p.file, "-");
}

TEST(ParseArgsTest, SeedFlagParsed) {
auto p = tsfile_cli::parse_args(
{"sample", "-m", "s1", "-n", "3", "--seed", "42", "data.tsfile"});
EXPECT_TRUE(p.error.empty());
EXPECT_EQ(p.command, "sample");
EXPECT_EQ(p.limit, 3);
EXPECT_TRUE(p.has_seed);
EXPECT_EQ(p.seed, 42);
}

TEST(ParseArgsTest, BadSeedValueIsError) {
auto p = tsfile_cli::parse_args(
{"sample", "--seed", "not_a_number", "data.tsfile"});
EXPECT_FALSE(p.error.empty());
EXPECT_NE(p.error.find("Invalid --seed"), std::string::npos);
EXPECT_TRUE(p.input_set);
}

TEST(RunCliTest, SelectIsNoLongerKnownCommand) {
Expand All @@ -195,16 +205,14 @@ TEST(RunCliTest, SeedOnCatIsUsageError) {
int code =
tsfile_cli::run_cli({"cat", "--seed", "7", "x.tsfile"}, out, err);
EXPECT_EQ(code, 1);
EXPECT_NE(err.str().find("--seed is only valid for sample"),
std::string::npos);
EXPECT_NE(err.str().find("--seed is not supported"), std::string::npos);
}

TEST(RunCliTest, OffsetOnSampleIsUsageError) {
TEST(RunCliTest, SampleIsUnknownCommand) {
std::ostringstream out;
std::ostringstream err;
int code =
tsfile_cli::run_cli({"sample", "--offset", "2", "x.tsfile"}, out, err);
EXPECT_EQ(code, 1);
EXPECT_NE(err.str().find("--offset is not valid for sample"),
std::string::npos);
EXPECT_NE(err.str().find("Unknown command"), std::string::npos);
}
Loading
Loading