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
15 changes: 13 additions & 2 deletions cpp/src/cwrapper/tsfile_cwrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1118,8 +1118,13 @@ int duplicate_ideviceid_to_device_fields(storage::IDeviceID* id,
for (int i = 0; i < n; i++) {
const std::string* ps =
(static_cast<size_t>(i) < segs.size()) ? segs[i] : nullptr;
const char* lit = (ps != nullptr) ? ps->c_str() : "null";
seg_arr[i] = strdup(lit);
// 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]);
Expand Down Expand Up @@ -1627,6 +1632,12 @@ TagFilterHandle tsfile_tag_filter_create(TsFileReader reader,
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:
*err_code = common::E_INVALID_ARG;
return nullptr;
Expand Down
5 changes: 4 additions & 1 deletion cpp/src/cwrapper/tsfile_cwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,8 @@ typedef enum {
TAG_FILTER_GTEQ = 5,
TAG_FILTER_REGEXP = 6,
TAG_FILTER_NOT_REGEXP = 7,
TAG_FILTER_IS_NULL = 8,
TAG_FILTER_IS_NOT_NULL = 9,
} TagFilterOp;

/**
Expand All @@ -884,7 +886,8 @@ typedef enum {
* index).
* @param table_name [in] Table name whose schema defines the TAG columns.
* @param column_name [in] Name of the TAG column to filter on.
* @param value [in] Comparison value (string).
* @param value [in] Comparison value (string). Ignored for
* TAG_FILTER_IS_NULL / TAG_FILTER_IS_NOT_NULL (may be NULL).
* @param op [in] Comparison operator (TagFilterOp).
* @param err_code [out] Error code. E_OK(0) on success.
* @return TagFilterHandle on success; NULL on failure.
Expand Down
82 changes: 61 additions & 21 deletions cpp/src/reader/filter/tag_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ TagEq::TagEq(int col_idx, std::string tag_value)
: TagFilter(col_idx, std::move(tag_value)) {}

bool TagEq::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
return *segments[col_idx_] == value_;
}

Expand All @@ -53,7 +54,8 @@ TagNeq::TagNeq(int col_idx, std::string tag_value)
: TagFilter(col_idx, std::move(tag_value)) {}

bool TagNeq::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
return *segments[col_idx_] != value_;
}

Expand All @@ -62,7 +64,8 @@ TagLt::TagLt(int col_idx, std::string tag_value)
: TagFilter(col_idx, std::move(tag_value)) {}

bool TagLt::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
return *segments[col_idx_] < value_;
}

Expand All @@ -71,7 +74,8 @@ TagLteq::TagLteq(int col_idx, std::string tag_value)
: TagFilter(col_idx, std::move(tag_value)) {}

bool TagLteq::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
return *segments[col_idx_] <= value_;
}

Expand All @@ -80,7 +84,8 @@ TagGt::TagGt(int col_idx, std::string tag_value)
: TagFilter(col_idx, std::move(tag_value)) {}

bool TagGt::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
return *segments[col_idx_] > value_;
}

Expand All @@ -89,7 +94,8 @@ TagGteq::TagGteq(int col_idx, std::string tag_value)
: TagFilter(col_idx, std::move(tag_value)) {}

bool TagGteq::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
return *segments[col_idx_] >= value_;
}

Expand All @@ -105,7 +111,9 @@ TagRegExp::TagRegExp(int col_idx, std::string tag_value)
}

bool TagRegExp::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size() || !is_valid_pattern_) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr ||
!is_valid_pattern_)
return false;
try {
return std::regex_search(*segments[col_idx_], pattern_);
} catch (const std::regex_error&) {
Expand All @@ -125,14 +133,32 @@ TagNotRegExp::TagNotRegExp(int col_idx, std::string tag_value)
}

bool TagNotRegExp::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size() || !is_valid_pattern_) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr ||
!is_valid_pattern_)
return false;
try {
return !std::regex_search(*segments[col_idx_], pattern_);
} catch (const std::regex_error&) {
return true;
}
}

// TagIsNull implementation
TagIsNull::TagIsNull(int col_idx) : TagFilter(col_idx, "") {}

bool TagIsNull::satisfyRow(std::vector<std::string*> segments) const {
// A tag is null when its segment is an explicit null pointer or when the
// device id omits the (trailing) segment entirely.
return col_idx_ >= segments.size() || segments[col_idx_] == nullptr;
}

// TagIsNotNull implementation
TagIsNotNull::TagIsNotNull(int col_idx) : TagFilter(col_idx, "") {}

bool TagIsNotNull::satisfyRow(std::vector<std::string*> segments) const {
return col_idx_ < segments.size() && segments[col_idx_] != nullptr;
}

// TagBetween implementation
TagBetween::TagBetween(int col_idx, std::string lower_value,
std::string upper_value)
Expand All @@ -141,7 +167,8 @@ TagBetween::TagBetween(int col_idx, std::string lower_value,
}

bool TagBetween::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
const std::string& segment_value = *segments[col_idx_];
return segment_value >= value_ && segment_value <= value2_;
}
Expand All @@ -154,7 +181,8 @@ TagNotBetween::TagNotBetween(int col_idx, std::string lower_value,
}

bool TagNotBetween::satisfyRow(std::vector<std::string*> segments) const {
if (col_idx_ >= segments.size()) return false;
if (col_idx_ >= segments.size() || segments[col_idx_] == nullptr)
return false;
const std::string& segment_value = *segments[col_idx_];
return segment_value < value_ || segment_value > value2_;
}
Expand Down Expand Up @@ -200,72 +228,84 @@ TagFilterBuilder::TagFilterBuilder(TableSchema* schema)

Filter* TagFilterBuilder::eq(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagEq(idx, value);
}

Filter* TagFilterBuilder::neq(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagNeq(idx, value);
}

Filter* TagFilterBuilder::lt(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagLt(idx, value);
}

Filter* TagFilterBuilder::lteq(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagLteq(idx, value);
}

Filter* TagFilterBuilder::gt(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagGt(idx, value);
}

Filter* TagFilterBuilder::gteq(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagGteq(idx, value);
}

Filter* TagFilterBuilder::reg_exp(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagRegExp(idx, value);
}

Filter* TagFilterBuilder::not_reg_exp(const std::string& columnName,
const std::string& value) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagNotRegExp(idx, value);
}

Filter* TagFilterBuilder::is_null(const std::string& columnName) {
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagIsNull(idx);
}

Filter* TagFilterBuilder::is_not_null(const std::string& columnName) {
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagIsNotNull(idx);
}

Filter* TagFilterBuilder::between_and(const std::string& columnName,
const std::string& lower,
const std::string& upper) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagBetween(idx, lower, upper);
}

Filter* TagFilterBuilder::not_between_and(const std::string& columnName,
const std::string& lower,
const std::string& upper) {
auto idx = get_id_column_index(columnName);
auto idx = get_tag_column_index(columnName);
if (idx < 0) return nullptr;
return new TagNotBetween(idx, lower, upper);
}
Expand All @@ -284,7 +324,7 @@ Filter* TagFilterBuilder::not_filter(Filter* filter) {
return new TagNot(dynamic_cast<TagFilter*>(filter));
}

int TagFilterBuilder::get_id_column_index(const std::string& columnName) {
int TagFilterBuilder::get_tag_column_index(const std::string& columnName) {
int idColumnOrder = table_schema_->find_id_column_order(columnName);
if (idColumnOrder == -1) {
return -1;
Expand Down
19 changes: 18 additions & 1 deletion cpp/src/reader/filter/tag_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ class TagNotRegExp : public TagFilter {
bool satisfyRow(std::vector<std::string*> segments) const override;
};

// IS NULL: tag column has no value for this device. An absent trailing
// segment (col_idx_ beyond the device's segment count) is also treated as null.
class TagIsNull : public TagFilter {
public:
explicit TagIsNull(int col_idx);
bool satisfyRow(std::vector<std::string*> segments) const override;
};

// IS NOT NULL: tag column has a concrete value for this device.
class TagIsNotNull : public TagFilter {
public:
explicit TagIsNotNull(int col_idx);
bool satisfyRow(std::vector<std::string*> segments) const override;
};

// Range query [value_, value2_]
class TagBetween : public TagFilter {
public:
Expand Down Expand Up @@ -171,6 +186,8 @@ class TagFilterBuilder {
Filter* reg_exp(const std::string& columnName, const std::string& value);
Filter* not_reg_exp(const std::string& columnName,
const std::string& value);
Filter* is_null(const std::string& columnName);
Filter* is_not_null(const std::string& columnName);
Filter* between_and(const std::string& columnName, const std::string& lower,
const std::string& upper);
Filter* not_between_and(const std::string& columnName,
Expand All @@ -182,7 +199,7 @@ class TagFilterBuilder {
static Filter* not_filter(Filter* filter);

private:
int get_id_column_index(const std::string& columnName);
int get_tag_column_index(const std::string& columnName);
};

} // namespace storage
Expand Down
Loading
Loading