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: 2 additions & 4 deletions cpp/src/cwrapper/tsfile_cwrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -737,8 +737,7 @@ ResultSet tsfile_reader_query_tree(TsFileReader reader, char** paths,
return nullptr;
}
*err_code = common::E_INVALID_ARG;
if (reader == nullptr || paths == nullptr || path_num == 0 ||
end_time < start_time) {
if (reader == nullptr || paths == nullptr || path_num == 0) {
return nullptr;
}
try {
Expand Down Expand Up @@ -827,8 +826,7 @@ ResultSet tsfile_reader_query_table(TsFileReader reader, const char* table_name,
}
*err_code = common::E_INVALID_ARG;
if (reader == nullptr || table_name == nullptr || column_names == nullptr ||
column_names_len == 0 || end_time < start_time || offset < 0 ||
batch_size < 0) {
column_names_len == 0 || offset < 0 || batch_size < 0) {
return nullptr;
}
try {
Expand Down
17 changes: 13 additions & 4 deletions cpp/src/cwrapper/tsfile_cwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -740,13 +740,19 @@ PreparedSeriesHandle tsfile_reader_prepare_series_with_time_owner(
*/
void tsfile_prepared_series_free(PreparedSeriesHandle prepared);

/** Query a prepared series without traversing the TsFile footer index. */
/**
* Query a prepared series without traversing the TsFile footer index.
* An inverted time range succeeds with an empty result set.
*/
ResultSet tsfile_reader_query_prepared(TsFileReader reader,
PreparedSeriesHandle prepared,
Timestamp start_time, Timestamp end_time,
int offset, int limit, ERRNO* err_code);

/** Query multiple aligned prepared value columns sharing one time axis. */
/**
* Query multiple aligned prepared value columns sharing one time axis.
* An inverted time range succeeds with an empty result set.
*/
ResultSet tsfile_reader_query_prepared_multi(
TsFileReader reader, const PreparedSeriesHandle* prepared,
uint32_t prepared_count, Timestamp start_time, Timestamp end_time,
Expand All @@ -760,7 +766,8 @@ ResultSet tsfile_reader_query_prepared_multi(
* @param columns [in] Array of column names to fetch.
* @param column_num [in] Number of columns in array.
* @param start_time [in] Start timestamp.
* @param end_time [in] End timestamp. Must ≥ start_time.
* @param end_time [in] Inclusive end timestamp. If it precedes start_time,
* the query succeeds with an empty result set.
* @return ResultSet Query results handle. Must be freed with
* free_tsfile_result_set().
*/
Expand All @@ -769,6 +776,7 @@ ResultSet tsfile_query_table(TsFileReader reader, const char* table_name,
Timestamp start_time, Timestamp end_time,
ERRNO* err_code);

/** Query tree-model columns; an inverted time range yields no rows. */
ResultSet tsfile_query_table_on_tree(TsFileReader reader, char** columns,
uint32_t column_num, Timestamp start_time,
Timestamp end_time, ERRNO* err_code);
Expand All @@ -780,7 +788,8 @@ ResultSet tsfile_query_table_on_tree(TsFileReader reader, char** columns,
* @param paths [in] Array of full paths such as root.device.measurement.
* @param path_num [in] Number of paths; must be greater than zero.
* @param start_time [in] Inclusive start timestamp.
* @param end_time [in] Inclusive end timestamp.
* @param end_time [in] Inclusive end timestamp. If it precedes start_time,
* the query succeeds with an empty result set.
* @param err_code [out] Error code; must not be NULL.
* @return ResultSet handle on success, or NULL on failure.
*/
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/reader/tsfile_executor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ int TsFileExecutor::execute_prepared(
ResultSet*& ret_qds) {
ASSERT(is_inited_);
ret_qds = nullptr;
if (prepared == nullptr || start_time > end_time || offset < 0) {
if (prepared == nullptr || offset < 0) {
return E_INVALID_ARG;
}
auto tsblock_reader = std::unique_ptr<PreparedSeriesTsBlockReader>(
Expand All @@ -153,7 +153,7 @@ int TsFileExecutor::execute_prepared_multi(
ResultSet*& ret_qds) {
ASSERT(is_inited_);
ret_qds = nullptr;
if (prepared.empty() || start_time > end_time || offset < 0) {
if (prepared.empty() || offset < 0) {
return E_INVALID_ARG;
}

Expand Down
2 changes: 1 addition & 1 deletion cpp/src/reader/tsfile_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ int TsFileReader::query(const std::string& table_name,
schema_it->second == nullptr) {
return E_TABLE_NOT_EXIST;
}
if (end_time < start_time || offset < 0) {
if (offset < 0) {
return E_INVALID_ARG;
}
if (limit < 0) {
Expand Down
14 changes: 13 additions & 1 deletion cpp/test/reader/prepared_series_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ TEST_F(PreparedSeriesBatchTest,

ResultSet* empty = nullptr;
ASSERT_EQ(common::E_OK,
reader.query_prepared(prepared, 100000, 200000, 0, -1, empty));
reader.query_prepared(prepared, 200000, 100000, 0, -1, empty));
auto* empty_table = dynamic_cast<TableResultSet*>(empty);
ASSERT_NE(nullptr, empty_table);
block = nullptr;
Expand Down Expand Up @@ -369,6 +369,18 @@ TEST_F(PreparedSeriesBatchTest,
}
EXPECT_EQ(10U, row);
reader.destroy_query_data_set(result);

ResultSet* empty_multi = nullptr;
ASSERT_EQ(common::E_OK,
reader.query_prepared_multi({prepared_value2, prepared_value}, 9,
0, 0, -1, empty_multi));
auto* empty_multi_table = dynamic_cast<TableResultSet*>(empty_multi);
ASSERT_NE(nullptr, empty_multi_table);
block = nullptr;
EXPECT_EQ(common::E_NO_MORE_DATA,
empty_multi_table->get_next_tsblock(block));
EXPECT_EQ(nullptr, block);
reader.destroy_query_data_set(empty_multi);
EXPECT_EQ(common::E_OK, reader.close());
}

Expand Down
25 changes: 25 additions & 0 deletions cpp/test/reader/table_view/tsfile_reader_table_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,31 @@ TEST_F(TsFileTableReaderTest, TableModelQueryWithTimeFilter) {
test_table_model_query(10, 1, 2);
}

TEST_F(TsFileTableReaderTest, InvertedTimeRangeReturnsEmptyResult) {
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, 10);
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;
ASSERT_EQ(reader.open(file_name_), common::E_OK);
ResultSet* result = nullptr;
ASSERT_EQ(
reader.query(table_schema->get_table_name(),
table_schema->get_measurement_names(), 10, 0, result),
common::E_OK);
auto* table_result = static_cast<TableResultSet*>(result);
bool has_next = false;
ASSERT_EQ(table_result->next(has_next), common::E_OK);
EXPECT_FALSE(has_next);
reader.destroy_query_data_set(table_result);
ASSERT_EQ(reader.close(), common::E_OK);
delete table_schema;
}

TEST_F(TsFileTableReaderTest, TableModelResultMetadata) {
auto table_schema = gen_table_schema(0);
auto tsfile_table_writer_ =
Expand Down
10 changes: 10 additions & 0 deletions go/tsfile/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ func TestTableRoundTripAndNull(t *testing.T) {
if got, err := page.Float64(2); err != nil || got != 1.5 {
t.Fatalf("page value = %v, %v", got, err)
}

inverted, err := reader.Query("metrics", []string{"value"},
WithTimeRange(2, 1))
if err != nil {
t.Fatal(err)
}
defer inverted.Close()
if ok, err := inverted.Next(); err != nil || ok {
t.Fatalf("inverted range Next = %v, %v", ok, err)
}
}

func TestNewWriterTruncatesExistingFile(t *testing.T) {
Expand Down
5 changes: 2 additions & 3 deletions go/tsfile/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@ type queryOptions struct {
// QueryOption customizes one table query.
type QueryOption func(*queryOptions) error

// WithTimeRange limits the query to the inclusive [start, end] range.
// When start is greater than end, the range is empty and the query returns no rows.
func WithTimeRange(start, end int64) QueryOption {
return func(options *queryOptions) error {
if end < start {
return fmt.Errorf("%w: end must not precede start", ErrInvalidArgument)
}
options.start, options.end = start, end
return nil
}
Expand Down
13 changes: 6 additions & 7 deletions go/tsfile/table_api_contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,17 @@ func TestQueryOptionDefaultsAndComposition(t *testing.T) {
}

func TestQueryOptionValidation(t *testing.T) {
cases := []QueryOption{
nil,
WithTimeRange(2, 1),
WithOffset(-1),
WithTagFilter(nil),
}
cases := []QueryOption{nil, WithOffset(-1), WithTagFilter(nil)}
for _, option := range cases {
if _, err := buildQueryOptions(option); !errors.Is(err, ErrInvalidArgument) {
t.Fatalf("option error = %v, want ErrInvalidArgument", err)
}
}
options, err := buildQueryOptions(WithBatchSize(-1))
options, err := buildQueryOptions(WithTimeRange(2, 1))
if err != nil || options.start != 2 || options.end != 1 {
t.Fatalf("inverted time range should be preserved: %+v, %v", options, err)
}
options, err = buildQueryOptions(WithBatchSize(-1))
if err != nil || options.batchSize != 0 {
t.Fatalf("negative batch size should select row mode: %+v, %v", options, err)
}
Expand Down
Loading