Skip to content

Commit da58a20

Browse files
authored
fix(discover): Fix has:measurements.* queries (#78627)
Fixes SENTRY-3DB9 We're seeing Snuba errors for comparisons of a numeric value to a string. e.g., ```sql `measurements.frames_frozen_rate` != '' ``` Measurements are numeric, so this errors out. This comparison is created for queries like `has:measurements.frozen_frame_rate`. That measurement is "synthetic", it's a function that divides two other measurements. It returns a numeric value, so comparison to string fails. The query builder's current logic doesn't account for measurement _functions_ (i.e., plain measurements like `measurements.lcp` still work), and creates the comparison incorrectly. The fix is to check for measurements, and return early with a basic check for null.
1 parent 5f5d2ed commit da58a20

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

src/sentry/search/events/builder/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,11 @@ def default_filter_converter(
13091309
if search_filter.operator in ("=", "!=") and search_filter.value.value == "":
13101310
if is_tag or is_attr or is_context or name in self.config.non_nullable_keys:
13111311
return Condition(lhs, Op(search_filter.operator), value)
1312+
elif is_measurement(name):
1313+
# Measurements can be a `Column` (e.g., `"lcp"`) or a `Function` (e.g., `"frames_frozen_rate"`). In either cause, since they are nullable, return a simple null check
1314+
return Condition(
1315+
Function("isNull", [lhs]), Op.EQ, 1 if search_filter.operator == "=" else 0
1316+
)
13121317
elif isinstance(lhs, Column):
13131318
# If not a tag, we can just check that the column is null.
13141319
return Condition(Function("isNull", [lhs]), Op(search_filter.operator), 1)

tests/sentry/search/events/builder/test_discover.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,51 @@ def test_count_if_with_tags(self):
475475
],
476476
)
477477

478+
def test_not_empty_measurement(self):
479+
query = DiscoverQueryBuilder(
480+
Dataset.Discover,
481+
self.params,
482+
query="has:measurements.lcp",
483+
)
484+
485+
lcp = Column("measurements[lcp]")
486+
487+
self.assertCountEqual(
488+
query.where,
489+
[
490+
Condition(Function("isNull", [lcp]), Op.EQ, 0),
491+
*self.default_conditions,
492+
],
493+
)
494+
495+
def test_not_empty_function_measurement(self):
496+
query = DiscoverQueryBuilder(
497+
Dataset.Discover,
498+
self.params,
499+
query="has:measurements.frames_frozen_rate",
500+
)
501+
502+
frames_total = Column("measurements[frames_total]")
503+
frames_frozen = Column("measurements[frames_frozen]")
504+
505+
frames_frozen_rate = Function(
506+
"if",
507+
[
508+
Function("greater", [frames_total, 0]),
509+
Function("divide", [frames_frozen, frames_total]),
510+
None,
511+
],
512+
alias="measurements.frames_frozen_rate",
513+
)
514+
515+
self.assertCountEqual(
516+
query.where,
517+
[
518+
Condition(Function("isNull", [frames_frozen_rate]), Op.EQ, 0),
519+
*self.default_conditions,
520+
],
521+
)
522+
478523
def test_array_join(self):
479524
query = DiscoverQueryBuilder(
480525
Dataset.Discover,

0 commit comments

Comments
 (0)