Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions cpp/README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ sudo apt-get install -y cmake make g++ clang-format libuuid-dev
bash build.sh
```

`build.sh` 默认只编译,不执行安装。如果需要安装到 CMake 的安装前缀目录,显式传入 `install` 参数:

```bash
bash build.sh install
```

如果你安装了 Maven 工具,也可以运行:

```bash
Expand Down
52 changes: 44 additions & 8 deletions cpp/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
build_type=Release
build_test=0
build_bench=0
do_install=0
use_cpp11=1
enable_cov=0
debug_se=0
Expand All @@ -39,10 +40,37 @@ get_key_value() {
echo "${1#*=}"
}

usage()
{
cat <<EOF
Usage: bash build.sh [install] [options]

Commands:
install Run make install after a successful build.

Options:
-t=<type>, -t <type> Build type: Debug, Release, RelWithDebInfo, MinSizeRel.
-a=<ON|OFF> Enable or disable AddressSanitizer.
-c=<ON|OFF> Enable or disable code coverage.
--enable-antlr4=<ON|OFF>
--disable-antlr4
--enable-snappy=<ON|OFF>
--disable-snappy
--enable-lz4=<ON|OFF>
--disable-lz4
--enable-lzokay=<ON|OFF>
--disable-lzokay
--enable-zlib=<ON|OFF>
--disable-zlib
-h, --help Show this help message.
EOF
}

function print_config()
{
echo "build_type=$build_type"
echo "build_test=$build_test"
echo "do_install=$do_install"
echo "use_cpp11=$use_cpp11"
echo "enable_cov=$enable_cov"
echo "enable_asan=$enable_asan"
Expand All @@ -68,6 +96,8 @@ parse_options()
do_clean=1;;
run_cov)
run_cov_only=1;;
install | --install)
do_install=1;;
-t=*)
build_type=$(get_key_value "$1");;
-t)
Expand Down Expand Up @@ -103,18 +133,19 @@ parse_options()
enable_lzokay=OFF;;
--disable-zlib)
enable_zlib=OFF;;
#-h | --help)
# usage
# exit 0;;
#*)
# echo "Unknown option '$1'"
# exit 1;;
-h | --help)
usage
exit 0;;
*)
echo "Unknown option '$1'"
usage
exit 1;;
esac
shift
done
}

parse_options $*
parse_options "$@"
print_config

if [[ ${run_cov_only} -eq 1 ]]
Expand Down Expand Up @@ -171,4 +202,9 @@ cmake ../../ \
-DENABLE_ZLIB=$enable_zlib

VERBOSE=1 make
VERBOSE=1 make install
if [ ${do_install} -eq 1 ]
then
VERBOSE=1 make install
else
echo "Skip install. Pass 'install' to run 'make install'."
fi
28 changes: 28 additions & 0 deletions cpp/test/tools/cli_args_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,34 @@ TEST(ParseArgsTest, LimitOffsetAndTimeRange) {
EXPECT_EQ(p.end, 200);
}

TEST(ParseArgsTest, TagFilterParsed) {
auto p = tsfile_cli::parse_args(
{"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");
}

TEST(ParseArgsTest, TagBetweenParsed) {
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");
}

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

TEST(ParseArgsTest, UnknownFlagIsError) {
auto p = tsfile_cli::parse_args({"ls", "--bogus", "data.tsfile"});
EXPECT_FALSE(p.error.empty());
Expand Down
43 changes: 43 additions & 0 deletions cpp/test/tools/cli_test_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,49 @@ inline std::string write_table_fixture() {
return out_path;
}

inline std::string write_tag_filter_fixture() {
storage::libtsfile_init();
std::string out_path =
unique_temp_path("tsfile_cli_tag_filter_fixture", ".tsfile");
std::string table_name = "t1";

storage::WriteFile file;
int flags = O_WRONLY | O_CREAT | O_TRUNC;
#ifdef _WIN32
flags |= O_BINARY;
#endif
file.create(out_path, flags, 0666);

auto* schema = new storage::TableSchema(
table_name,
{
common::ColumnSchema("id1", common::STRING, common::UNCOMPRESSED,
common::PLAIN, common::ColumnCategory::TAG),
common::ColumnSchema("s1", common::INT64, common::UNCOMPRESSED,
common::PLAIN, common::ColumnCategory::FIELD),
});

auto* writer = new storage::TsFileTableWriter(&file, schema);
storage::Tablet tablet(
table_name, {"id1", "s1"}, {common::STRING, common::INT64},
{common::ColumnCategory::TAG, common::ColumnCategory::FIELD}, 10);

const char* tags[] = {"dev_a", "dev_b", "dev_b", "dev_c"};
for (int row = 0; row < 4; ++row) {
tablet.add_timestamp(row, static_cast<int64_t>(row));
tablet.add_value(row, "id1", tags[row]);
tablet.add_value(row, "s1", static_cast<int64_t>((row + 1) * 10));
}

writer->write_table(tablet);
writer->flush();
writer->close();

delete writer;
delete schema;
return out_path;
}

} // namespace tsfile_cli_test

#endif // TSFILE_CLI_TEST_UTIL_H
119 changes: 119 additions & 0 deletions cpp/test/tools/command_e2e_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ struct Fixture {
~Fixture() { std::remove(path.c_str()); }
};

struct TagFilterFixture {
std::string path = tsfile_cli_test::write_tag_filter_fixture();
~TagFilterFixture() { std::remove(path.c_str()); }
};

size_t count_lines(const std::string& s) {
size_t n = 0;
for (char c : s) {
Expand Down Expand Up @@ -134,6 +139,28 @@ TEST(CliE2E, CatReturnsAllRows) {
EXPECT_NE(out.str().find("time\ts1\n"), std::string::npos);
}

TEST(CliE2E, CatPushesDownOffsetAndLimit) {
Fixture f;
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli(
{"cat", "-m", "s1", "--offset", "2", "-n", "2", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 0);
EXPECT_EQ(out.str(), "time\ts1\n2\t20\n3\t30\n");
}

TEST(CliE2E, HeadPushesDownOffsetAndLimit) {
Fixture f;
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli(
{"head", "-m", "s1", "--offset", "1", "-n", "3", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 0);
EXPECT_EQ(out.str(), "time\ts1\n1\t10\n2\t20\n3\t30\n");
}

TEST(CliE2E, CatWithTimeRange) {
Fixture f;
std::ostringstream out;
Expand All @@ -145,6 +172,65 @@ TEST(CliE2E, CatWithTimeRange) {
EXPECT_EQ(out.str(), "time\ts1\n2\t20\n3\t30\n");
}

TEST(CliE2E, CatAppliesOffsetAfterTimeRange) {
Fixture f;
std::ostringstream out;
std::ostringstream err;
int code =
tsfile_cli::run_cli({"cat", "-m", "s1", "--start", "1", "--end", "4",
"--offset", "1", "-n", "2", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 0);
EXPECT_EQ(out.str(), "time\ts1\n2\t20\n3\t30\n");
}

TEST(CliE2E, CatFiltersRowsByTagEq) {
TagFilterFixture f;
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli({"cat", "-m", "s1", "--tag-filter", "id1",
"eq", "dev_b", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 0) << err.str();
EXPECT_EQ(out.str(), "time\ts1\n1\t20\n2\t30\n");
}

TEST(CliE2E, HeadFiltersRowsByTagBetween) {
TagFilterFixture f;
std::ostringstream out;
std::ostringstream err;
int code =
tsfile_cli::run_cli({"head", "-m", "s1", "--tag-between", "id1",
"dev_b", "dev_c", "-n", "10", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 0) << err.str();
EXPECT_EQ(out.str(), "time\ts1\n1\t20\n2\t30\n3\t40\n");
}

TEST(CliE2E, SampleFiltersRowsByTagEq) {
TagFilterFixture f;
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli(
{"sample", "-m", "s1", "--tag-filter", "id1", "eq", "dev_b", "-n", "10",
"--seed", "1", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 0) << err.str();
EXPECT_EQ(out.str(), "time\ts1\n1\t20\n2\t30\n");
}

TEST(CliE2E, TagFilterRejectsFieldColumn) {
TagFilterFixture f;
std::ostringstream out;
std::ostringstream err;
int code = tsfile_cli::run_cli({"cat", "-m", "s1", "--tag-filter", "s1",
"eq", "20", "-f", "tsv", f.path},
out, err);
EXPECT_EQ(code, 1);
EXPECT_NE(err.str().find("invalid tag filter column"), std::string::npos)
<< err.str();
}

TEST(CliE2E, CatJsonIsNdjson) {
Fixture f;
std::ostringstream out;
Expand Down Expand Up @@ -181,6 +267,39 @@ TEST(CliE2E, CountReportsSeriesCountsAndTotal) {
EXPECT_NE(out.str().find("total\t\t"), std::string::npos);
}

TEST(CliE2E, MetadataTableFilterIsCaseInsensitive) {
Fixture f;

std::ostringstream schema_out;
std::ostringstream schema_err;
EXPECT_EQ(
tsfile_cli::run_cli({"schema", "-t", "TABLE1", "-f", "tsv", f.path},
schema_out, schema_err),
0);
EXPECT_NE(schema_out.str().find("table1\ts1\tINT64"), std::string::npos)
<< schema_out.str();

std::ostringstream count_out;
std::ostringstream count_err;
EXPECT_EQ(
tsfile_cli::run_cli({"count", "-t", "TABLE1", "-f", "tsv", f.path},
count_out, count_err),
0);
EXPECT_NE(count_out.str().find("table1.id1_field_1.id2_field_2\ts1\t5"),
std::string::npos)
<< count_out.str();

std::ostringstream stats_out;
std::ostringstream stats_err;
EXPECT_EQ(
tsfile_cli::run_cli({"stats", "-t", "TABLE1", "-f", "tsv", f.path},
stats_out, stats_err),
0);
EXPECT_NE(stats_out.str().find("table1.id1_field_1.id2_field_2\ts1\t5"),
std::string::npos)
<< stats_out.str();
}

TEST(CliE2E, SampleIsReproducibleWithSeed) {
Fixture f;
std::ostringstream out1;
Expand Down
10 changes: 7 additions & 3 deletions cpp/tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Choose any one of the following.
```bash
bash build.sh -t=Debug # -> cpp/build/Debug/bin/tsfile-cli
bash build.sh # Release (default) -> cpp/build/Release/bin/tsfile-cli
bash build.sh install # Release build, then run make install
```

**2. Maven (builds the whole C++ module).** From the repository root:
Expand Down Expand Up @@ -77,9 +78,10 @@ Verify the binary:
```

The executable links the `tsfile` shared library built alongside it. To run it from
anywhere, either run it in place by its full path, or use CMake's install step
(`cmake --install .` / `make install`), which installs the binary to `<prefix>/bin` and
`libtsfile` to `<prefix>/lib`.
anywhere, either run it in place by its full path, or explicitly install it with
`bash build.sh install`, `cmake --install .`, or `make install`. The install step places
the binary under `<prefix>/bin` and `libtsfile` under `<prefix>/lib`. The build script
does not install by default.

## Usage

Expand Down Expand Up @@ -117,6 +119,7 @@ Shared options:
| `-n, --limit N` / `--offset N` | Max rows / rows to skip (`head`, `cat`; `--offset` not valid for `sample`) |
| `--start <ms>` / `--end <ms>` | Inclusive epoch-millisecond time range (`head`, `cat`, `sample`) |
| `--seed N` | Reproducible sampling seed (`sample` only) |
| `--tag-filter C OP V` / `--tag-between C L U` / `--tag-not-between C L U` | Table TAG predicate for `head`, `cat`, `sample`; `OP` is `eq`, `neq`, `lt`, `lteq`, `gt`, `gteq`, `regexp`, or `not-regexp` |
| `--no-header` | Omit the header row |
| `--model tree\|table` | Force the model (otherwise auto-detected) |

Expand All @@ -130,6 +133,7 @@ BIN=cpp/build/Debug/bin/tsfile-cli
$BIN ls -f tsv data.tsfile # list tables / devices
$BIN meta data.tsfile # quick file overview
$BIN count -t table1 -f tsv data.tsfile # row counts, no page scan
$BIN cat -t table1 --tag-filter device eq dev_1 -m temp -f tsv data.tsfile
$BIN cat -m temp,humidity --start 1700000000000 -f csv data.tsfile | head
$BIN sample -m temp -n 20 --seed 42 -f json data.tsfile | jq .
```
Expand Down
Loading
Loading