trace_processor: add support for filtering on nullable types

This CL adds support for filtering using IS NULL and IS NOT NULL which
was previously causing CHECKs.

Change-Id: I6533c3f24a48cd1d52f78500ba4d2ec283eee8d1
diff --git a/src/trace_processor/args_table.cc b/src/trace_processor/args_table.cc
index 2a0f9d1..cd2756d 100644
--- a/src/trace_processor/args_table.cc
+++ b/src/trace_processor/args_table.cc
@@ -141,31 +141,40 @@
                                     FilteredRowIndex* index) const {
   switch (type_) {
     case VarardicType::kInt: {
-      auto binary_op = sqlite_utils::GetPredicateForOp<int64_t>(op);
+      auto binary_op = sqlite_utils::GetOptionalPredicateForOp<int64_t>(op);
       int64_t extracted = sqlite_utils::ExtractSqliteValue<int64_t>(value);
       index->FilterRows([this, &binary_op, extracted](uint32_t row) {
         const auto& arg = storage_->args().arg_values()[row];
-        return arg.type == type_ && binary_op(arg.int_value, extracted);
+        if (arg.type == type_) {
+          return binary_op(arg.int_value, extracted);
+        }
+        return binary_op(base::nullopt, extracted);
       });
       break;
     }
     case VarardicType::kReal: {
-      auto binary_op = sqlite_utils::GetPredicateForOp<double>(op);
+      auto binary_op = sqlite_utils::GetOptionalPredicateForOp<double>(op);
       double extracted = sqlite_utils::ExtractSqliteValue<double>(value);
       index->FilterRows([this, &binary_op, extracted](uint32_t row) {
         const auto& arg = storage_->args().arg_values()[row];
-        return arg.type == type_ && binary_op(arg.real_value, extracted);
+        if (arg.type == type_) {
+          return binary_op(arg.real_value, extracted);
+        }
+        return binary_op(base::nullopt, extracted);
       });
       break;
     }
     case VarardicType::kString: {
-      auto binary_op = sqlite_utils::GetPredicateForOp<std::string>(op);
+      auto binary_op = sqlite_utils::GetOptionalPredicateForOp<std::string>(op);
       const auto* extracted =
           reinterpret_cast<const char*>(sqlite3_value_text(value));
       index->FilterRows([this, &binary_op, extracted](uint32_t row) {
         const auto& arg = storage_->args().arg_values()[row];
         const auto& str = storage_->GetString(arg.string_value);
-        return arg.type == type_ && binary_op(str, extracted);
+        if (arg.type == type_) {
+          return binary_op(str, extracted);
+        }
+        return binary_op(base::nullopt, extracted);
       });
       break;
     }
diff --git a/src/trace_processor/counters_table.cc b/src/trace_processor/counters_table.cc
index 8edeaca..9bb8d6d 100644
--- a/src/trace_processor/counters_table.cc
+++ b/src/trace_processor/counters_table.cc
@@ -114,7 +114,7 @@
 void CountersTable::RefColumn::Filter(int op,
                                       sqlite3_value* value,
                                       FilteredRowIndex* index) const {
-  auto binary_op = sqlite_utils::GetPredicateForOp<int64_t>(op);
+  auto binary_op = sqlite_utils::GetOptionalPredicateForOp<int64_t>(op);
   int64_t extracted = sqlite_utils::ExtractSqliteValue<int64_t>(value);
   index->FilterRows([this, &binary_op, extracted](uint32_t row) {
     auto ref = storage_->counters().refs()[row];
@@ -123,7 +123,7 @@
       auto upid = storage_->GetThread(static_cast<uint32_t>(ref)).upid;
       // Trying to filter null with any operation we currently handle
       // should return false.
-      return upid.has_value() && binary_op(upid.value(), extracted);
+      return binary_op(upid, extracted);
     }
     return binary_op(ref, extracted);
   });
diff --git a/src/trace_processor/sqlite_utils.h b/src/trace_processor/sqlite_utils.h
index fe0f0d4..285b6e5 100644
--- a/src/trace_processor/sqlite_utils.h
+++ b/src/trace_processor/sqlite_utils.h
@@ -25,6 +25,7 @@
 #include <string>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/base/optional.h"
 #include "src/trace_processor/scoped_db.h"
 #include "src/trace_processor/table.h"
 
@@ -77,6 +78,7 @@
 std::function<bool(T, T)> GetPredicateForOp(int op) {
   switch (op) {
     case SQLITE_INDEX_CONSTRAINT_EQ:
+    case SQLITE_INDEX_CONSTRAINT_IS:
       return std::equal_to<T>();
     case SQLITE_INDEX_CONSTRAINT_GE:
       return std::greater_equal<T>();
@@ -87,12 +89,28 @@
     case SQLITE_INDEX_CONSTRAINT_LT:
       return std::less<T>();
     case SQLITE_INDEX_CONSTRAINT_NE:
+    case SQLITE_INDEX_CONSTRAINT_ISNOT:
       return std::not_equal_to<T>();
     default:
       PERFETTO_CHECK(false);
   }
 }
 
+template <class T>
+std::function<bool(base::Optional<T>, T)> GetOptionalPredicateForOp(int op) {
+  switch (op) {
+    case SQLITE_INDEX_CONSTRAINT_ISNULL:
+      return [](base::Optional<T> f, T) { return !f.has_value(); };
+    case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
+      return [](base::Optional<T> f, T) { return f.has_value(); };
+    default:
+      auto fn = GetPredicateForOp<T>(op);
+      return [fn](base::Optional<T> f, T s) {
+        return f.has_value() && fn(f.value(), s);
+      };
+  }
+}
+
 template <typename T>
 T ExtractSqliteValue(sqlite3_value* value);