trace_processor: handle null partition keys in span join

If there are any null partition keys, simply skip them all. This ensures
that we don't end up with 0 partition keys mistakenly in the span
output.

We cannot do any better than skip as NULL in SQL means "unknown" so we
cannot assume any partition value for these NULLs.

Bug: 120273757
Change-Id: I496c42f955d791965392e8be641d4622a3d6cc2e
diff --git a/src/trace_processor/span_join_operator_table.cc b/src/trace_processor/span_join_operator_table.cc
index 3299b49..c6b9de2 100644
--- a/src/trace_processor/span_join_operator_table.cc
+++ b/src/trace_processor/span_join_operator_table.cc
@@ -300,7 +300,14 @@
 
 int SpanJoinOperatorTable::Cursor::TableQueryState::StepAndCacheValues() {
   sqlite3_stmt* stmt = stmt_.get();
-  int res = sqlite3_step(stmt);
+
+  // Fastforward through any rows with null partition keys.
+  int res, row_type;
+  do {
+    res = sqlite3_step(stmt);
+    row_type = sqlite3_column_type(stmt, Column::kPartition);
+  } while (res == SQLITE_ROW && row_type == SQLITE_NULL);
+
   if (res == SQLITE_ROW) {
     int64_t ts = sqlite3_column_int64(stmt, Column::kTimestamp);
     int64_t dur = sqlite3_column_int64(stmt, Column::kDuration);
diff --git a/src/trace_processor/span_join_operator_table_unittest.cc b/src/trace_processor/span_join_operator_table_unittest.cc
index 9d326c1..3587e59 100644
--- a/src/trace_processor/span_join_operator_table_unittest.cc
+++ b/src/trace_processor/span_join_operator_table_unittest.cc
@@ -120,6 +120,70 @@
   ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
 }
 
+TEST_F(SpanJoinOperatorTableTest, NullPartitionKey) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts UNSIGNED BIG INT PRIMARY KEY, "
+      "dur UNSIGNED BIG INT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts UNSIGNED BIG INT PRIMARY KEY, "
+      "dur UNSIGNED BIG INT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED cpu, "
+      "s PARTITIONED cpu);");
+
+  RunStatement("INSERT INTO f VALUES(30, 20, NULL);");
+  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
+  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
+  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
+
+  RunStatement("INSERT INTO s VALUES(40, 10, NULL);");
+  RunStatement("INSERT INTO s VALUES(100, 5, 5);");
+  RunStatement("INSERT INTO s VALUES(105, 100, 5);");
+  RunStatement("INSERT INTO s VALUES(110, 50, 2);");
+  RunStatement("INSERT INTO s VALUES(160, 100, 2);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 120);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 60);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto