blob: a5d637c29fbd3acbd46842d7a403f83269ada854 [file]
/*
* 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 <sqlite3.h>
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/sqlite/scoped_db.h"
namespace perfetto::trace_processor::sqlite::utils {
namespace internal {
namespace {
std::string ToExpectedTypesString(ExpectedTypesSet expected_types) {
PERFETTO_CHECK(expected_types.any());
std::vector<std::string> type_names;
for (size_t i = 0; i < expected_types.size(); ++i) {
if (expected_types[i]) {
type_names.emplace_back(
SqliteTypeToFriendlyString(static_cast<SqlValue::Type>(i)));
}
}
std::string result;
if (type_names.size() > 1) {
result = "any of ";
}
result += base::Join(type_names, ", ");
return result;
}
inline SqlValue::Type SqliteTypeToSqlValueType(int sqlite_type) {
switch (sqlite_type) {
case SQLITE_NULL:
return SqlValue::Type::kNull;
case SQLITE_BLOB:
return SqlValue::Type::kBytes;
case SQLITE_INTEGER:
return SqlValue::Type::kLong;
case SQLITE_FLOAT:
return SqlValue::Type::kDouble;
case SQLITE_TEXT:
return SqlValue::Type::kString;
}
PERFETTO_FATAL("Unknown SQLite type %d", sqlite_type);
}
} // 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 value;
}
} // namespace internal
std::vector<SqliteColumn> GetColumns(sqlite3* db, const std::string& name) {
sqlite3_stmt* raw_stmt = nullptr;
if (sqlite3_prepare_v2(db,
"SELECT name, type, hidden FROM pragma_table_xinfo(?)",
-1, &raw_stmt, nullptr) != SQLITE_OK) {
return {};
}
ScopedStmt stmt(raw_stmt);
sqlite3_bind_text(*stmt, 1, name.c_str(), static_cast<int>(name.size()),
kSqliteStatic);
std::vector<SqliteColumn> columns;
int rc;
while ((rc = sqlite3_step(*stmt)) == SQLITE_ROW) {
const auto* column =
reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 0));
const auto* type =
reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 1));
// 1 is a hidden column; 2 and 3 are generated ones, which `*` includes.
columns.push_back({column ? column : "", type ? type : "",
sqlite3_column_int(*stmt, 2) == 1});
}
return rc == SQLITE_DONE ? columns : std::vector<SqliteColumn>();
}
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<std::string> ExtractStringArg(const char* function_name,
const char* arg_name,
sqlite3_value* sql_value) {
SqlValue value = SqliteValueToSqlValue(sql_value);
if (value.type != SqlValue::kString) {
return base::ErrStatus(
"%s(%s): value has type %s which does not match the expected type %s",
function_name, arg_name, SqliteTypeToFriendlyString(value.type),
SqliteTypeToFriendlyString(SqlValue::kString));
}
const char* result = value.AsString();
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 =
internal::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();
}
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 perfetto::trace_processor::sqlite::utils