blob: acca3e62af1c3afa0ffc62d7057fe980ce2ca897 [file] [log] [blame]
/*
* 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.
*/
#ifndef SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
#include <sqlite3.h>
#include <memory>
#include "src/trace_processor/sqlite/sqlite_utils.h"
namespace perfetto {
namespace trace_processor {
// Prototype for a C++ function which can be registered with SQLite.
//
// Usage
//
// Define a subclass of this struct as follows:
// struct YourFunction : public SqlFunction {
// // Optional if you want a custom context object (i.e. an object
// // passed in at registration time which will be passed to Run on
// // every invocation)
// struct YourContext { /* define context fields here */ };
//
// static base::Status Run(/* see parameters below */) {
// /* function body here */
// }
//
// static base::Status Cleanup(/* see parameters below */) {
// /* function body here */
// }
// }
//
// Then, register this function with SQLite using RegisterFunction (see below);
// you'll likely want to do this in TraceProcessorImpl:
// RegisterFunction<YourFunction>(/* see arguments below */)
struct SqlFunction {
// The type of the context object which will be passed to the function.
// Can be redefined in any sub-classes to override the context.
using Context = void;
// Indicates whether this function is "void" (i.e. doesn't actually want
// to return a value). While the function will still return null in SQL
// (because SQLite does not actually allow null functions), for accounting
// purposes, this null will be ignored when verifying whether this statement
// has any output.
// Can be redefined in any sub-classes to override it.
// If this is set to true, subclasses must not modify |out| or |destructors|.
static constexpr bool kVoidReturn = false;
// Struct which holds destructors for strings/bytes returned from the
// function. Passed as an argument to |Run| to allow implementations to
// override the destructors.
struct Destructors {
sqlite3_destructor_type string_destructor = sqlite_utils::kSqliteTransient;
sqlite3_destructor_type bytes_destructor = sqlite_utils::kSqliteTransient;
};
// The function which will be exectued with the arguments from SQL.
//
// Implementations MUST define this function themselves; this function is
// declared but *not* defined so linker errors will be thrown if not defined.
//
// |ctx|: the context object passed at registration time.
// |argc|: number of arguments.
// |argv|: arguments to the function.
// |out|: the return value of the function.
// |destructors|: destructors for string/bytes return values.
static base::Status Run(Context* ctx,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors);
// Executed after the result from |Run| is reported to SQLite.
// Allows implementations to verify post-conditions without needing to worry
// about overwriting return types.
//
// Implementations do not need to define this function; a default no-op
// implementation will be used in this case.
static base::Status VerifyPostConditions(Context*);
// Executed after the result from |Run| is reported to SQLite.
// Allows any pending state to be cleaned up post-copy of results by SQLite:
// this function will be called even if |Run| or |PostRun| returned errors.
//
// Implementations do not need to define this function; a default no-op
// implementation will be used in this case.
static void Cleanup(Context*);
};
// Registers a C++ function to be runnable from SQL.
// The format of the function is given by the |SqlFunction|; see the
// documentaion above.
//
// |db|: sqlite3 database object
// |name|: name of the function in SQL
// |argc|: number of arguments for this function, -1 if variable
// |ctx|: context object for the function (see SqlFunction::Run above);
// this object *must* outlive the function so should likely be
// either static or scoped to the lifetime of TraceProcessor.
// |determistic|: whether this function has deterministic output given the
// same set of arguments.
template <typename Function>
base::Status RegisterSqlFunction(sqlite3* db,
const char* name,
int argc,
typename Function::Context* ctx,
bool deterministic = true);
// Same as above except allows a unique_ptr to be passed for the context; this
// allows for SQLite to manage the lifetime of this pointer instead of the
// essentially static requirement of the context pointer above.
template <typename Function>
base::Status RegisterSqlFunction(
sqlite3* db,
const char* name,
int argc,
std::unique_ptr<typename Function::Context> ctx,
bool deterministic = true);
} // namespace trace_processor
} // namespace perfetto
// The rest of this file is just implementation details which we need
// in the header file because it is templated code. We separate it out
// like this to keep the API people actually care about easy to read.
namespace perfetto {
namespace trace_processor {
namespace sqlite_internal {
// RAII type to call Function::Cleanup when destroyed.
template <typename Function>
struct ScopedCleanup {
typename Function::Context* ctx;
~ScopedCleanup() { Function::Cleanup(ctx); }
};
template <typename Function>
void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
using Context = typename Function::Context;
Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
ScopedCleanup<Function> scoped_cleanup{ud};
SqlValue value{};
SqlFunction::Destructors destructors{};
base::Status status =
Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
if (!status.ok()) {
sqlite3_result_error(ctx, status.c_message(), -1);
return;
}
if (Function::kVoidReturn) {
if (!value.is_null()) {
sqlite3_result_error(ctx, "void SQL function returned value", -1);
return;
}
// If the function doesn't want to return anything, set the "VOID"
// pointer type to a non-null value. Note that because of the weird
// way |sqlite3_value_pointer| works, we need to set some value even
// if we don't actually read it - just set it to a pointer to an empty
// string for this reason.
static char kVoidValue[] = "";
sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
} else {
sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
destructors.bytes_destructor);
}
status = Function::VerifyPostConditions(ud);
if (!status.ok()) {
sqlite3_result_error(ctx, status.c_message(), -1);
return;
}
}
} // namespace sqlite_internal
template <typename Function>
base::Status RegisterSqlFunction(sqlite3* db,
const char* name,
int argc,
typename Function::Context* ctx,
bool deterministic) {
int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
int ret = sqlite3_create_function_v2(
db, name, static_cast<int>(argc), flags, ctx,
sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
return base::ErrStatus("Unable to register function with name %s", name);
}
return base::OkStatus();
}
template <typename Function>
base::Status RegisterSqlFunction(
sqlite3* db,
const char* name,
int argc,
std::unique_ptr<typename Function::Context> user_data,
bool deterministic) {
int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
int ret = sqlite3_create_function_v2(
db, name, static_cast<int>(argc), flags, user_data.release(),
sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
[](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
if (ret != SQLITE_OK) {
return base::ErrStatus("Unable to register function with name %s", name);
}
return base::OkStatus();
}
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_