trace_processor: Make GetColumnsForTable report errors
GetColumnsForTable now returns util::Status and so can report validation
errors rather than calling PERFETTO_FAIILURE.
Bug: 163136067
Change-Id: Icc01332aa7be56389c3a1250455ced2fa659121d
diff --git a/Android.bp b/Android.bp
index 37c42cf..8db7f44 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6842,6 +6842,7 @@
"src/trace_processor/sqlite/query_constraints_unittest.cc",
"src/trace_processor/sqlite/span_join_operator_table_unittest.cc",
"src/trace_processor/sqlite/sqlite3_str_split_unittest.cc",
+ "src/trace_processor/sqlite/sqlite_utils_unittest.cc",
],
}
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index ba2c13f..52352b7 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -61,6 +61,7 @@
"query_constraints_unittest.cc",
"span_join_operator_table_unittest.cc",
"sqlite3_str_split_unittest.cc",
+ "sqlite_utils_unittest.cc",
]
deps = [
":sqlite",
diff --git a/src/trace_processor/sqlite/span_join_operator_table.cc b/src/trace_processor/sqlite/span_join_operator_table.cc
index 1357ddf..c847a25 100644
--- a/src/trace_processor/sqlite/span_join_operator_table.cc
+++ b/src/trace_processor/sqlite/span_join_operator_table.cc
@@ -311,7 +311,11 @@
desc.name.c_str());
}
- auto cols = sqlite_utils::GetColumnsForTable(db_, desc.name);
+ std::vector<SqliteTable::Column> cols;
+ auto status = sqlite_utils::GetColumnsForTable(db_, desc.name, cols);
+ if (!status.ok()) {
+ return status;
+ }
uint32_t required_columns_found = 0;
uint32_t ts_idx = std::numeric_limits<uint32_t>::max();
diff --git a/src/trace_processor/sqlite/sqlite_utils.h b/src/trace_processor/sqlite/sqlite_utils.h
index 4896df7..cc5c459 100644
--- a/src/trace_processor/sqlite/sqlite_utils.h
+++ b/src/trace_processor/sqlite/sqlite_utils.h
@@ -26,6 +26,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_utils.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sqlite_table.h"
@@ -380,9 +381,11 @@
sqlite3_result_double(ctx, value);
}
-inline std::vector<SqliteTable::Column> GetColumnsForTable(
+inline util::Status GetColumnsForTable(
sqlite3* db,
- const std::string& raw_table_name) {
+ const std::string& raw_table_name,
+ std::vector<SqliteTable::Column>& columns) {
+ PERFETTO_DCHECK(columns.empty());
char sql[1024];
const char kRawSql[] = "SELECT name, type from pragma_table_info(\"%s\")";
@@ -394,21 +397,18 @@
sqlite3_stmt* raw_stmt = nullptr;
int err = sqlite3_prepare_v2(db, sql, n, &raw_stmt, nullptr);
if (err != SQLITE_OK) {
- PERFETTO_ELOG("Preparing database failed");
- return {};
+ return util::ErrStatus("Preparing database failed");
}
ScopedStmt stmt(raw_stmt);
PERFETTO_DCHECK(sqlite3_column_count(*stmt) == 2);
- std::vector<SqliteTable::Column> columns;
for (;;) {
err = sqlite3_step(raw_stmt);
if (err == SQLITE_DONE)
break;
if (err != SQLITE_ROW) {
- PERFETTO_ELOG("Querying schema of table %s failed",
- raw_table_name.c_str());
- return {};
+ return util::ErrStatus("Querying schema of table %s failed",
+ raw_table_name.c_str());
}
const char* name =
@@ -416,31 +416,31 @@
const char* raw_type =
reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 1));
if (!name || !raw_type || !*name) {
- PERFETTO_FATAL("Schema for %s has invalid column values",
- raw_table_name.c_str());
+ return util::ErrStatus("Schema for %s has invalid column values",
+ raw_table_name.c_str());
}
SqlValue::Type type;
- if (strcmp(raw_type, "STRING") == 0) {
+ if (base::CaseInsensitiveEqual(raw_type, "STRING")) {
type = SqlValue::Type::kString;
- } else if (strcmp(raw_type, "DOUBLE") == 0) {
+ } else if (base::CaseInsensitiveEqual(raw_type, "DOUBLE")) {
type = SqlValue::Type::kDouble;
- } else if (strcmp(raw_type, "BIG INT") == 0 ||
- strcmp(raw_type, "UNSIGNED INT") == 0 ||
- strcmp(raw_type, "INT") == 0 ||
- strcmp(raw_type, "BOOLEAN") == 0) {
+ } else if (base::CaseInsensitiveEqual(raw_type, "BIG INT") ||
+ base::CaseInsensitiveEqual(raw_type, "UNSIGNED INT") ||
+ base::CaseInsensitiveEqual(raw_type, "INT") ||
+ base::CaseInsensitiveEqual(raw_type, "BOOLEAN")) {
type = SqlValue::Type::kLong;
} else if (!*raw_type) {
PERFETTO_DLOG("Unknown column type for %s %s", raw_table_name.c_str(),
name);
type = SqlValue::Type::kNull;
} else {
- PERFETTO_FATAL("Unknown column type '%s' on table %s", raw_type,
- raw_table_name.c_str());
+ return util::ErrStatus("Unknown column type '%s' on table %s", raw_type,
+ raw_table_name.c_str());
}
columns.emplace_back(columns.size(), name, type);
}
- return columns;
+ return util::OkStatus();
}
template <typename T>
diff --git a/src/trace_processor/sqlite/sqlite_utils_unittest.cc b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
new file mode 100644
index 0000000..1d1de13
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+class GetColumnsForTableTest : public ::testing::Test {
+ public:
+ GetColumnsForTableTest() {
+ sqlite3* db = nullptr;
+ PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
+ PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
+ db_.reset(db);
+ }
+
+ void PrepareValidStatement(const std::string& sql) {
+ int size = static_cast<int>(sql.size());
+ sqlite3_stmt* stmt;
+ ASSERT_EQ(sqlite3_prepare_v2(*db_, sql.c_str(), size, &stmt, nullptr),
+ SQLITE_OK);
+ stmt_.reset(stmt);
+ }
+
+ void RunStatement(const std::string& sql) {
+ PrepareValidStatement(sql);
+ ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+ }
+
+ protected:
+ ScopedDb db_;
+ ScopedStmt stmt_;
+};
+
+TEST_F(GetColumnsForTableTest, ValidInput) {
+ RunStatement("CREATE TABLE foo (name STRING, ts INT, dur INT);");
+ std::vector<SqliteTable::Column> columns;
+ auto status = sqlite_utils::GetColumnsForTable(*db_, "foo", columns);
+ ASSERT_TRUE(status.ok());
+}
+
+TEST_F(GetColumnsForTableTest, UnknownType) {
+ // Currently GetColumnsForTable does not work with tables containing types it
+ // doesn't recognise. This just ensures that the query fails rather than
+ // crashing.
+ RunStatement("CREATE TABLE foo (name NUM, ts INT, dur INT);");
+ std::vector<SqliteTable::Column> columns;
+ auto status = sqlite_utils::GetColumnsForTable(*db_, "foo", columns);
+ ASSERT_FALSE(status.ok());
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto