|  | /* | 
|  | * Copyright (C) 2022 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 <bitset> | 
|  | #include <sstream> | 
|  | #include "perfetto/base/status.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace trace_processor { | 
|  | namespace sqlite_utils { | 
|  | namespace internal { | 
|  | namespace { | 
|  | std::string ToExpectedTypesString(ExpectedTypesSet expected_types) { | 
|  | PERFETTO_CHECK(expected_types.any()); | 
|  | std::stringstream ss; | 
|  | if (expected_types.count() > 1) { | 
|  | ss << "any of "; | 
|  | } | 
|  |  | 
|  | bool add_separator = false; | 
|  | for (size_t i = 0; i < expected_types.size(); ++i) { | 
|  | if (expected_types[i]) { | 
|  | ss << (add_separator ? ", " : "") | 
|  | << SqliteTypeToFriendlyString(static_cast<SqlValue::Type>(i)); | 
|  | add_separator = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ss.str(); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | base::Status InvalidArgumentTypeError(const char* argument_name, | 
|  | size_t arg_index, | 
|  | SqlValue::Type actual_type, | 
|  | ExpectedTypesSet expected_types) { | 
|  | return ToInvalidArgumentError( | 
|  | argument_name, arg_index, | 
|  | base::ErrStatus("does not have expected type. Expected %s but found %s", | 
|  | ToExpectedTypesString(expected_types).c_str(), | 
|  | SqliteTypeToFriendlyString(actual_type))); | 
|  | } | 
|  |  | 
|  | base::StatusOr<SqlValue> ExtractArgument(size_t argc, | 
|  | sqlite3_value** argv, | 
|  | const char* argument_name, | 
|  | size_t arg_index, | 
|  | ExpectedTypesSet expected_types) { | 
|  | if (arg_index >= argc) { | 
|  | return MissingArgumentError(argument_name); | 
|  | } | 
|  |  | 
|  | SqlValue value = sqlite_utils::SqliteValueToSqlValue(argv[arg_index]); | 
|  |  | 
|  | if (!expected_types.test(value.type)) { | 
|  | return InvalidArgumentTypeError(argument_name, arg_index, value.type, | 
|  | expected_types); | 
|  | } | 
|  |  | 
|  | return std::move(value); | 
|  | } | 
|  | }  // namespace internal | 
|  |  | 
|  | std::wstring SqliteValueToWString(sqlite3_value* value) { | 
|  | PERFETTO_CHECK(sqlite3_value_type(value) == SQLITE_TEXT); | 
|  | int len = sqlite3_value_bytes16(value); | 
|  | PERFETTO_CHECK(len >= 0); | 
|  | size_t count = static_cast<size_t>(len) / sizeof(wchar_t); | 
|  | return std::wstring( | 
|  | reinterpret_cast<const wchar_t*>(sqlite3_value_text16(value)), count); | 
|  | } | 
|  |  | 
|  | base::Status GetColumnsForTable(sqlite3* db, | 
|  | 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\")"; | 
|  |  | 
|  | // Support names which are table valued functions with arguments. | 
|  | std::string table_name = raw_table_name.substr(0, raw_table_name.find('(')); | 
|  | size_t n = base::SprintfTrunc(sql, sizeof(sql), kRawSql, table_name.c_str()); | 
|  | PERFETTO_DCHECK(n > 0); | 
|  |  | 
|  | sqlite3_stmt* raw_stmt = nullptr; | 
|  | int err = | 
|  | sqlite3_prepare_v2(db, sql, static_cast<int>(n), &raw_stmt, nullptr); | 
|  | if (err != SQLITE_OK) { | 
|  | return base::ErrStatus("Preparing database failed"); | 
|  | } | 
|  | ScopedStmt stmt(raw_stmt); | 
|  | PERFETTO_DCHECK(sqlite3_column_count(*stmt) == 2); | 
|  |  | 
|  | for (;;) { | 
|  | err = sqlite3_step(raw_stmt); | 
|  | if (err == SQLITE_DONE) | 
|  | break; | 
|  | if (err != SQLITE_ROW) { | 
|  | return base::ErrStatus("Querying schema of table %s failed", | 
|  | raw_table_name.c_str()); | 
|  | } | 
|  |  | 
|  | const char* name = | 
|  | reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 0)); | 
|  | const char* raw_type = | 
|  | reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 1)); | 
|  | if (!name || !raw_type || !*name) { | 
|  | return base::ErrStatus("Schema for %s has invalid column values", | 
|  | raw_table_name.c_str()); | 
|  | } | 
|  |  | 
|  | SqlValue::Type type; | 
|  | if (base::CaseInsensitiveEqual(raw_type, "STRING") || | 
|  | base::CaseInsensitiveEqual(raw_type, "TEXT")) { | 
|  | type = SqlValue::Type::kString; | 
|  | } else if (base::CaseInsensitiveEqual(raw_type, "DOUBLE")) { | 
|  | type = SqlValue::Type::kDouble; | 
|  | } else if (base::CaseInsensitiveEqual(raw_type, "BIG INT") || | 
|  | base::CaseInsensitiveEqual(raw_type, "BIGINT") || | 
|  | base::CaseInsensitiveEqual(raw_type, "UNSIGNED INT") || | 
|  | base::CaseInsensitiveEqual(raw_type, "INT") || | 
|  | base::CaseInsensitiveEqual(raw_type, "BOOLEAN") || | 
|  | base::CaseInsensitiveEqual(raw_type, "INTEGER")) { | 
|  | 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 { | 
|  | return base::ErrStatus("Unknown column type '%s' on table %s", raw_type, | 
|  | raw_table_name.c_str()); | 
|  | } | 
|  | columns.emplace_back(columns.size(), name, type); | 
|  | } | 
|  |  | 
|  | // Catch mis-spelt table names. | 
|  | // | 
|  | // A SELECT on pragma_table_info() returns no rows if the | 
|  | // table that was queried is not present. | 
|  | if (columns.empty()) { | 
|  | return base::ErrStatus("Unknown table or view name '%s'", | 
|  | raw_table_name.c_str()); | 
|  | } | 
|  |  | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | const char* SqliteTypeToFriendlyString(SqlValue::Type type) { | 
|  | switch (type) { | 
|  | case SqlValue::Type::kNull: | 
|  | return "NULL"; | 
|  | case SqlValue::Type::kLong: | 
|  | return "BOOL/INT/UINT/LONG"; | 
|  | case SqlValue::Type::kDouble: | 
|  | return "FLOAT/DOUBLE"; | 
|  | case SqlValue::Type::kString: | 
|  | return "STRING"; | 
|  | case SqlValue::Type::kBytes: | 
|  | return "BYTES/PROTO"; | 
|  | } | 
|  | PERFETTO_FATAL("For GCC"); | 
|  | } | 
|  |  | 
|  | base::Status CheckArgCount(const char* function_name, | 
|  | size_t argc, | 
|  | size_t expected_argc) { | 
|  | if (argc == expected_argc) { | 
|  | return base::OkStatus(); | 
|  | } | 
|  | return base::ErrStatus("%s: expected %zu arguments, got %zu", function_name, | 
|  | expected_argc, argc); | 
|  | } | 
|  |  | 
|  | base::StatusOr<int64_t> ExtractIntArg(const char* function_name, | 
|  | const char* arg_name, | 
|  | sqlite3_value* sql_value) { | 
|  | SqlValue value = SqliteValueToSqlValue(sql_value); | 
|  | std::optional<int64_t> result; | 
|  |  | 
|  | base::Status status = ExtractFromSqlValue(value, result); | 
|  | if (!status.ok()) { | 
|  | return base::ErrStatus("%s(%s): %s", function_name, arg_name, | 
|  | status.message().c_str()); | 
|  | } | 
|  | PERFETTO_CHECK(result); | 
|  | return *result; | 
|  | } | 
|  |  | 
|  | base::StatusOr<double> ExtractDoubleArg(const char* function_name, | 
|  | const char* arg_name, | 
|  | sqlite3_value* sql_value) { | 
|  | SqlValue value = SqliteValueToSqlValue(sql_value); | 
|  | std::optional<double> result; | 
|  |  | 
|  | base::Status status = ExtractFromSqlValue(value, result); | 
|  | if (!status.ok()) { | 
|  | return base::ErrStatus("%s(%s): %s", function_name, arg_name, | 
|  | status.message().c_str()); | 
|  | } | 
|  | PERFETTO_CHECK(result); | 
|  | return *result; | 
|  | } | 
|  |  | 
|  | base::StatusOr<std::string> ExtractStringArg(const char* function_name, | 
|  | const char* arg_name, | 
|  | sqlite3_value* sql_value) { | 
|  | SqlValue value = SqliteValueToSqlValue(sql_value); | 
|  | std::optional<const char*> result; | 
|  |  | 
|  | base::Status status = ExtractFromSqlValue(value, result); | 
|  | if (!status.ok()) { | 
|  | return base::ErrStatus("%s(%s): %s", function_name, arg_name, | 
|  | status.message().c_str()); | 
|  | } | 
|  | PERFETTO_CHECK(result); | 
|  | return std::string(*result); | 
|  | } | 
|  |  | 
|  | base::Status TypeCheckSqliteValue(sqlite3_value* value, | 
|  | SqlValue::Type expected_type) { | 
|  | return TypeCheckSqliteValue(value, expected_type, | 
|  | SqliteTypeToFriendlyString(expected_type)); | 
|  | } | 
|  |  | 
|  | base::Status TypeCheckSqliteValue(sqlite3_value* value, | 
|  | SqlValue::Type expected_type, | 
|  | const char* expected_type_str) { | 
|  | SqlValue::Type actual_type = | 
|  | sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(value)); | 
|  | if (actual_type != SqlValue::Type::kNull && actual_type != expected_type) { | 
|  | return base::ErrStatus( | 
|  | "does not have expected type: expected %s, actual %s", | 
|  | expected_type_str, SqliteTypeToFriendlyString(actual_type)); | 
|  | } | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | base::Status ExtractFromSqlValueInt(const SqlValue& value, | 
|  | std::optional<T>& out) { | 
|  | if (value.is_null()) { | 
|  | out = std::nullopt; | 
|  | return base::OkStatus(); | 
|  | } | 
|  | if (value.type != SqlValue::kLong) { | 
|  | return base::ErrStatus( | 
|  | "value has type %s which does not match the expected type %s", | 
|  | SqliteTypeToFriendlyString(value.type), | 
|  | SqliteTypeToFriendlyString(SqlValue::kLong)); | 
|  | } | 
|  |  | 
|  | int64_t res = value.AsLong(); | 
|  | if (res > std::numeric_limits<T>::max() || | 
|  | res < std::numeric_limits<T>::min()) { | 
|  | return base::ErrStatus("value %ld does not fit inside the range [%ld, %ld]", | 
|  | static_cast<long>(res), | 
|  | static_cast<long>(std::numeric_limits<T>::min()), | 
|  | static_cast<long>(std::numeric_limits<T>::max())); | 
|  | } | 
|  | out = static_cast<T>(res); | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | base::Status ExtractFromSqlValue(const SqlValue& value, | 
|  | std::optional<int64_t>& out) { | 
|  | return ExtractFromSqlValueInt(value, out); | 
|  | } | 
|  | base::Status ExtractFromSqlValue(const SqlValue& value, | 
|  | std::optional<int32_t>& out) { | 
|  | return ExtractFromSqlValueInt(value, out); | 
|  | } | 
|  | base::Status ExtractFromSqlValue(const SqlValue& value, | 
|  | std::optional<uint32_t>& out) { | 
|  | return ExtractFromSqlValueInt(value, out); | 
|  | } | 
|  | base::Status ExtractFromSqlValue(const SqlValue& value, | 
|  | std::optional<double>& out) { | 
|  | if (value.is_null()) { | 
|  | out = std::nullopt; | 
|  | return base::OkStatus(); | 
|  | } | 
|  | if (value.type != SqlValue::kDouble) { | 
|  | return base::ErrStatus( | 
|  | "value has type %s which does not match the expected type %s", | 
|  | SqliteTypeToFriendlyString(value.type), | 
|  | SqliteTypeToFriendlyString(SqlValue::kDouble)); | 
|  | } | 
|  | out = value.AsDouble(); | 
|  | return base::OkStatus(); | 
|  | } | 
|  | base::Status ExtractFromSqlValue(const SqlValue& value, | 
|  | std::optional<const char*>& out) { | 
|  | if (value.is_null()) { | 
|  | out = std::nullopt; | 
|  | return base::OkStatus(); | 
|  | } | 
|  | if (value.type != SqlValue::kString) { | 
|  | return base::ErrStatus( | 
|  | "value has type %s which does not match the expected type %s", | 
|  | SqliteTypeToFriendlyString(value.type), | 
|  | SqliteTypeToFriendlyString(SqlValue::kString)); | 
|  | } | 
|  | out = value.AsString(); | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | base::Status MissingArgumentError(const char* argument_name) { | 
|  | return base::ErrStatus("argument missing: %s", argument_name); | 
|  | } | 
|  |  | 
|  | base::Status ToInvalidArgumentError(const char* argument_name, | 
|  | size_t arg_index, | 
|  | const base::Status error) { | 
|  | return base::ErrStatus("argument %s at pos %zu: %s", argument_name, | 
|  | arg_index + 1, error.message().c_str()); | 
|  | } | 
|  |  | 
|  | }  // namespace sqlite_utils | 
|  | }  // namespace trace_processor | 
|  | }  // namespace perfetto |