tp: improve API of RuntimeTable
Phrasing as a Builder is a much more accurate way to do implement this
and prevents using Table APIs accidentally when the table is an
inconsistent state.
Change-Id: I60414c65d3ae62ca58f0bdfab3373ff304719c3c
diff --git a/src/base/test/status_matchers.h b/src/base/test/status_matchers.h
index e16214b..a6cddc2 100644
--- a/src/base/test/status_matchers.h
+++ b/src/base/test/status_matchers.h
@@ -36,13 +36,22 @@
return !arg.ok();
}
-// Macros for testing the results of functions that return absl::Status or
-// absl::StatusOr<T> (for any type T).
+// Macros for testing the results of functions that return base::Status or
+// base::StatusOr<T> (for any type T).
#define EXPECT_OK(expression) \
EXPECT_THAT(expression, ::perfetto::base::gtest_matchers::IsOk())
#define ASSERT_OK(expression) \
ASSERT_THAT(expression, ::perfetto::base::gtest_matchers::IsOk())
+// Macros for testing the results of function returning base::StatusOr<T>.
+#define PERFETTO_TEST_STATUS_MATCHER_CONCAT(x, y) x##y
+#define ASSERT_OK_AND_ASSIGN(lhs, rhs) \
+ PERFETTO_TEST_STATUS_MATCHER_CONCAT(auto status_or, __LINE__) = rhs; \
+ ASSERT_OK( \
+ PERFETTO_TEST_STATUS_MATCHER_CONCAT(status_or, __LINE__).status()); \
+ lhs = std::move( \
+ PERFETTO_TEST_STATUS_MATCHER_CONCAT(status_or, __LINE__).value())
+
} // namespace gtest_matchers
// Add a |PrintTo| function to allow easily determining what the cause of the
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index aca3a21..d870996 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -68,6 +68,8 @@
"../../../gn:gtest_and_gmock",
"../../../include/perfetto/trace_processor:basic_types",
"../../base",
+ "../../base:test_support",
+ "../containers",
"../tables",
"column",
"column:fake_storage",
diff --git a/src/trace_processor/db/runtime_table.cc b/src/trace_processor/db/runtime_table.cc
index a37d22a..6ccc942 100644
--- a/src/trace_processor/db/runtime_table.cc
+++ b/src/trace_processor/db/runtime_table.cc
@@ -28,11 +28,11 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/db/column.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
namespace {
template <typename T, typename U>
@@ -56,17 +56,17 @@
} // namespace
-RuntimeTable::RuntimeTable(StringPool* pool, std::vector<std::string> col_names)
- : Table(pool),
- col_names_(std::move(col_names)),
- storage_(col_names_.size()) {
- for (uint32_t i = 0; i < col_names_.size(); i++)
- storage_[i] = std::make_unique<VariantStorage>();
-}
-
RuntimeTable::~RuntimeTable() = default;
-base::Status RuntimeTable::AddNull(uint32_t idx) {
+RuntimeTable::Builder::Builder(StringPool* pool,
+ std::vector<std::string> col_names)
+ : string_pool_(pool), col_names_(std::move(col_names)) {
+ for (uint32_t i = 0; i < col_names_.size(); i++) {
+ storage_.emplace_back(std::make_unique<VariantStorage>());
+ }
+}
+
+base::Status RuntimeTable::Builder::AddNull(uint32_t idx) {
auto* col = storage_[idx].get();
PERFETTO_DCHECK(IsStorageNotIntNorDouble(*col));
if (auto* leading_nulls = std::get_if<uint32_t>(col)) {
@@ -83,7 +83,7 @@
return base::OkStatus();
}
-base::Status RuntimeTable::AddInteger(uint32_t idx, int64_t res) {
+base::Status RuntimeTable::Builder::AddInteger(uint32_t idx, int64_t res) {
auto* col = storage_[idx].get();
PERFETTO_DCHECK(IsStorageNotIntNorDouble(*col));
if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) {
@@ -107,7 +107,7 @@
return base::OkStatus();
}
-base::Status RuntimeTable::AddFloat(uint32_t idx, double res) {
+base::Status RuntimeTable::Builder::AddFloat(uint32_t idx, double res) {
auto* col = storage_[idx].get();
PERFETTO_DCHECK(IsStorageNotIntNorDouble(*col));
if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) {
@@ -139,7 +139,7 @@
return base::OkStatus();
}
-base::Status RuntimeTable::AddText(uint32_t idx, const char* ptr) {
+base::Status RuntimeTable::Builder::AddText(uint32_t idx, const char* ptr) {
auto* col = storage_[idx].get();
PERFETTO_DCHECK(IsStorageNotIntNorDouble(*col));
if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) {
@@ -154,10 +154,16 @@
return base::OkStatus();
}
-base::Status RuntimeTable::AddColumnsAndOverlays(uint32_t rows) {
- overlays_.emplace_back(rows);
- for (uint32_t i = 0; i < col_names_.size(); ++i) {
- auto* col = storage_[i].get();
+base::StatusOr<std::unique_ptr<RuntimeTable>> RuntimeTable::Builder::Build(
+ uint32_t rows) && {
+ auto table = std::make_unique<RuntimeTable>();
+ table->row_count_ = rows;
+ table->string_pool_ = string_pool_;
+ table->overlays_.emplace_back(rows);
+ table->storage_ = std::move(storage_);
+ table->col_names_ = std::move(col_names_);
+ for (uint32_t i = 0; i < table->col_names_.size(); ++i) {
+ auto* col = table->storage_[i].get();
PERFETTO_DCHECK(IsStorageNotIntNorDouble(*col));
if (auto* leading_nulls = std::get_if<uint32_t>(col)) {
PERFETTO_CHECK(*leading_nulls == rows);
@@ -174,19 +180,18 @@
uint32_t flags = is_sorted ? ColumnLegacy::Flag::kNonNull |
ColumnLegacy::Flag::kSorted
: ColumnLegacy::Flag::kNonNull;
- columns_.push_back(ColumnLegacy(col_names_[i].c_str(), non_null_ints,
- flags, this, i, 0));
+ table->columns_.emplace_back(table->col_names_[i].c_str(),
+ non_null_ints, flags, table.get(), i, 0);
} else {
- columns_.push_back(ColumnLegacy(col_names_[i].c_str(), ints,
- ColumnLegacy::Flag::kNoFlag, this, i,
- 0));
+ table->columns_.emplace_back(table->col_names_[i].c_str(), ints,
+ ColumnLegacy::Flag::kNoFlag, table.get(),
+ i, 0);
}
-
} else if (auto* strings = std::get_if<StringStorage>(col)) {
PERFETTO_CHECK(strings->size() == rows);
- columns_.push_back(ColumnLegacy(col_names_[i].c_str(), strings,
- ColumnLegacy::Flag::kNonNull, this, i,
- 0));
+ table->columns_.emplace_back(table->col_names_[i].c_str(), strings,
+ ColumnLegacy::Flag::kNonNull, table.get(), i,
+ 0);
} else if (auto* doubles = std::get_if<NullDoubleStorage>(col)) {
PERFETTO_CHECK(doubles->size() == rows);
// Check if the column is nullable.
@@ -199,31 +204,29 @@
uint32_t flags = is_sorted ? ColumnLegacy::Flag::kNonNull |
ColumnLegacy::Flag::kSorted
: ColumnLegacy::Flag::kNonNull;
-
- columns_.push_back(ColumnLegacy(col_names_[i].c_str(), non_null_doubles,
- flags, this, i, 0));
+ table->columns_.emplace_back(table->col_names_[i].c_str(),
+ non_null_doubles, flags, table.get(), i,
+ 0);
} else {
- columns_.push_back(ColumnLegacy(col_names_[i].c_str(), doubles,
- ColumnLegacy::Flag::kNoFlag, this, i,
- 0));
+ table->columns_.emplace_back(table->col_names_[i].c_str(), doubles,
+ ColumnLegacy::Flag::kNoFlag, table.get(),
+ i, 0);
}
} else {
PERFETTO_FATAL("Unexpected column type");
}
}
- columns_.push_back(ColumnLegacy::IdColumn(
- this, static_cast<uint32_t>(col_names_.size()), 0, "_auto_id",
+ table->columns_.push_back(ColumnLegacy::IdColumn(
+ table.get(), static_cast<uint32_t>(table->columns_.size()), 0, "_auto_id",
ColumnLegacy::kIdFlags | ColumnLegacy::Flag::kHidden));
- row_count_ = rows;
- schema_.columns.reserve(columns_.size());
- for (const auto& col : columns_) {
- schema_.columns.emplace_back(Schema::Column{col.name(), col.type(),
- col.IsId(), col.IsSorted(),
- col.IsHidden(), col.IsSetId()});
+ table->schema_.columns.reserve(table->columns_.size());
+ for (const auto& col : table->columns_) {
+ table->schema_.columns.emplace_back(
+ Schema::Column{col.name(), col.type(), col.IsId(), col.IsSorted(),
+ col.IsHidden(), col.IsSetId()});
}
- return base::OkStatus();
+ return {std::move(table)};
}
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/db/runtime_table.h b/src/trace_processor/db/runtime_table.h
index 8678e68..f61b0dd 100644
--- a/src/trace_processor/db/runtime_table.h
+++ b/src/trace_processor/db/runtime_table.h
@@ -25,6 +25,7 @@
#include <vector>
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/db/column_storage.h"
#include "src/trace_processor/db/table.h"
@@ -46,20 +47,25 @@
StringStorage,
DoubleStorage,
NullDoubleStorage>;
+ class Builder {
+ public:
+ Builder(StringPool* pool, std::vector<std::string> col_names);
- RuntimeTable(StringPool* pool, std::vector<std::string> col_names);
+ base::Status AddNull(uint32_t idx);
+ base::Status AddInteger(uint32_t idx, int64_t res);
+ base::Status AddFloat(uint32_t idx, double res);
+ base::Status AddText(uint32_t idx, const char* ptr);
+
+ base::StatusOr<std::unique_ptr<RuntimeTable>> Build(uint32_t rows) &&;
+
+ private:
+ StringPool* string_pool_ = nullptr;
+ std::vector<std::string> col_names_;
+ std::vector<std::unique_ptr<VariantStorage>> storage_;
+ };
+
~RuntimeTable() override;
- base::Status AddNull(uint32_t idx);
-
- base::Status AddInteger(uint32_t idx, int64_t res);
-
- base::Status AddFloat(uint32_t idx, double res);
-
- base::Status AddText(uint32_t idx, const char* ptr);
-
- base::Status AddColumnsAndOverlays(uint32_t rows);
-
const Table::Schema& schema() const { return schema_; }
private:
diff --git a/src/trace_processor/db/runtime_table_unittest.cc b/src/trace_processor/db/runtime_table_unittest.cc
index 85ca8ec..d5629ca 100644
--- a/src/trace_processor/db/runtime_table_unittest.cc
+++ b/src/trace_processor/db/runtime_table_unittest.cc
@@ -16,45 +16,51 @@
#include "src/trace_processor/db/runtime_table.h"
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/base/test/status_matchers.h"
+#include "src/trace_processor/containers/string_pool.h"
#include "test/gtest_and_gmock.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
namespace {
+using base::gtest_matchers::IsOk;
+using testing::Not;
class RuntimeTableTest : public ::testing::Test {
protected:
StringPool pool_;
std::vector<std::string> names_{{"foo"}};
- RuntimeTable table_{&pool_, names_};
+ RuntimeTable::Builder builder_{&pool_, names_};
};
TEST_F(RuntimeTableTest, DoubleThenIntValid) {
- ASSERT_TRUE(table_.AddFloat(0, 1024.3).ok());
- ASSERT_TRUE(table_.AddInteger(0, 1ll << 53).ok());
- ASSERT_TRUE(table_.AddColumnsAndOverlays(2).ok());
+ ASSERT_OK(builder_.AddFloat(0, 1024.3));
+ ASSERT_OK(builder_.AddInteger(0, 1ll << 53));
+ ASSERT_OK_AND_ASSIGN(auto table, std::move(builder_).Build(2));
- const auto& col = table_.columns()[0];
+ const auto& col = table->columns()[0];
ASSERT_EQ(col.Get(0).AsDouble(), 1024.3);
ASSERT_EQ(col.Get(1).AsDouble(), static_cast<double>(1ll << 53));
}
TEST_F(RuntimeTableTest, DoubleThenIntInvalid) {
- ASSERT_TRUE(table_.AddFloat(0, 1024.0).ok());
- ASSERT_FALSE(table_.AddInteger(0, (1ll << 53) + 1).ok());
- ASSERT_FALSE(table_.AddInteger(0, -(1ll << 53) - 1).ok());
+ ASSERT_OK(builder_.AddFloat(0, 1024.0));
+ ASSERT_THAT(builder_.AddInteger(0, (1ll << 53) + 1), Not(IsOk()));
+ ASSERT_THAT(builder_.AddInteger(0, -(1ll << 53) - 1), Not(IsOk()));
}
TEST_F(RuntimeTableTest, IntThenDouble) {
- ASSERT_TRUE(table_.AddInteger(0, 1024).ok());
- ASSERT_TRUE(table_.AddFloat(0, 1.3).ok());
- ASSERT_TRUE(table_.AddColumnsAndOverlays(2).ok());
+ ASSERT_TRUE(builder_.AddInteger(0, 1024).ok());
+ ASSERT_TRUE(builder_.AddFloat(0, 1.3).ok());
+ ASSERT_OK_AND_ASSIGN(auto table, std::move(builder_).Build(2));
- const auto& col = table_.columns()[0];
+ const auto& col = table->columns()[0];
ASSERT_EQ(col.Get(0).AsDouble(), 1024.0);
ASSERT_EQ(col.Get(1).AsDouble(), 1.3);
}
} // namespace
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index 086b42c..0ccc219 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -17,6 +17,8 @@
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
#include <cctype>
+#include <cstddef>
+#include <cstdint>
#include <memory>
#include <optional>
#include <string>
@@ -29,6 +31,7 @@
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/db/runtime_table.h"
#include "src/trace_processor/db/table.h"
#include "src/trace_processor/perfetto_sql/engine/created_function.h"
#include "src/trace_processor/perfetto_sql/engine/function_util.h"
@@ -409,7 +412,7 @@
"CREATE PERFETTO TABLE"));
size_t column_count = column_names.size();
- auto table = std::make_unique<RuntimeTable>(pool_, std::move(column_names));
+ RuntimeTable::Builder builder(pool_, std::move(column_names));
uint32_t rows = 0;
int res;
for (res = sqlite3_step(stmt.sqlite_stmt()); res == SQLITE_ROW;
@@ -418,18 +421,18 @@
int int_i = static_cast<int>(i);
switch (sqlite3_column_type(stmt.sqlite_stmt(), int_i)) {
case SQLITE_NULL:
- RETURN_IF_ERROR(table->AddNull(i));
+ RETURN_IF_ERROR(builder.AddNull(i));
break;
case SQLITE_INTEGER:
- RETURN_IF_ERROR(table->AddInteger(
+ RETURN_IF_ERROR(builder.AddInteger(
i, sqlite3_column_int64(stmt.sqlite_stmt(), int_i)));
break;
case SQLITE_FLOAT:
- RETURN_IF_ERROR(table->AddFloat(
+ RETURN_IF_ERROR(builder.AddFloat(
i, sqlite3_column_double(stmt.sqlite_stmt(), int_i)));
break;
case SQLITE_TEXT: {
- RETURN_IF_ERROR(table->AddText(
+ RETURN_IF_ERROR(builder.AddText(
i, reinterpret_cast<const char*>(
sqlite3_column_text(stmt.sqlite_stmt(), int_i))));
break;
@@ -448,7 +451,7 @@
create_table.name.c_str(),
sqlite3_errmsg(engine_->db()));
}
- RETURN_IF_ERROR(table->AddColumnsAndOverlays(rows));
+ ASSIGN_OR_RETURN(auto table, std::move(builder).Build(rows));
if (runtime_tables_.Find(create_table.name)) {
if (!create_table.replace) {
diff --git a/src/trace_processor/util/status_macros.h b/src/trace_processor/util/status_macros.h
index d7acb98..c902bac 100644
--- a/src/trace_processor/util/status_macros.h
+++ b/src/trace_processor/util/status_macros.h
@@ -38,6 +38,6 @@
PERFETTO_INTERNAL_MACRO_CONCAT(auto status_or, __LINE__) = rhs; \
RETURN_IF_ERROR( \
PERFETTO_INTERNAL_MACRO_CONCAT(status_or, __LINE__).status()); \
- lhs = std::move(rhs.value())
+ lhs = std::move(PERFETTO_INTERNAL_MACRO_CONCAT(status_or, __LINE__).value())
#endif // SRC_TRACE_PROCESSOR_UTIL_STATUS_MACROS_H_