blob: 75eac08ae910d7e75d757069cab1be532fddd8bf [file] [log] [blame]
/*
* Copyright (C) 2018 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/table.h"
#include <ctype.h>
#include <string.h>
#include "perfetto/base/logging.h"
namespace perfetto {
namespace trace_processor {
namespace {
struct TableDescriptor {
Table::Factory factory;
const TraceStorage* storage = nullptr;
std::string name;
sqlite3_module module = {};
};
Table* ToTable(sqlite3_vtab* vtab) {
return static_cast<Table*>(vtab);
}
Table::Cursor* ToCursor(sqlite3_vtab_cursor* cursor) {
return static_cast<Table::Cursor*>(cursor);
}
} // namespace
// static
bool Table::debug = false;
Table::Table() = default;
Table::~Table() = default;
void Table::RegisterInternal(sqlite3* db,
const TraceStorage* storage,
const std::string& table_name,
Factory factory) {
std::unique_ptr<TableDescriptor> desc(new TableDescriptor());
desc->storage = storage;
desc->factory = factory;
desc->name = table_name;
sqlite3_module* module = &desc->module;
memset(module, 0, sizeof(*module));
module->xConnect = [](sqlite3* xdb, void* arg, int argc,
const char* const* argv, sqlite3_vtab** tab, char**) {
const TableDescriptor* xdesc = static_cast<const TableDescriptor*>(arg);
auto table = xdesc->factory(xdb, xdesc->storage);
auto create_stmt = table->CreateTableStmt(argc, argv);
if (create_stmt.empty())
return SQLITE_ERROR;
int res = sqlite3_declare_vtab(xdb, create_stmt.c_str());
if (res != SQLITE_OK)
return res;
// Freed in xDisconnect().
table->name_ = xdesc->name;
*tab = table.release();
return SQLITE_OK;
};
module->xDisconnect = [](sqlite3_vtab* t) {
delete ToTable(t);
return SQLITE_OK;
};
module->xOpen = [](sqlite3_vtab* t, sqlite3_vtab_cursor** c) {
return ToTable(t)->OpenInternal(c);
};
module->xClose = [](sqlite3_vtab_cursor* c) {
delete ToCursor(c);
return SQLITE_OK;
};
module->xBestIndex = [](sqlite3_vtab* t, sqlite3_index_info* i) {
return ToTable(t)->BestIndexInternal(i);
};
module->xFilter = [](sqlite3_vtab_cursor* c, int i, const char* s, int a,
sqlite3_value** v) {
return ToCursor(c)->FilterInternal(i, s, a, v);
};
module->xNext = [](sqlite3_vtab_cursor* c) { return ToCursor(c)->Next(); };
module->xEof = [](sqlite3_vtab_cursor* c) { return ToCursor(c)->Eof(); };
module->xColumn = [](sqlite3_vtab_cursor* c, sqlite3_context* a, int b) {
return ToCursor(c)->Column(a, b);
};
module->xRowid = [](sqlite3_vtab_cursor*, sqlite_int64*) {
return SQLITE_ERROR;
};
module->xFindFunction =
[](sqlite3_vtab* t, int, const char* name,
void (**fn)(sqlite3_context*, int, sqlite3_value**),
void** args) { return ToTable(t)->FindFunction(name, fn, args); };
int res = sqlite3_create_module_v2(
db, table_name.c_str(), module, desc.release(),
[](void* arg) { delete static_cast<TableDescriptor*>(arg); });
PERFETTO_CHECK(res == SQLITE_OK);
}
int Table::OpenInternal(sqlite3_vtab_cursor** ppCursor) {
// Freed in xClose().
*ppCursor = static_cast<sqlite3_vtab_cursor*>(CreateCursor().release());
return SQLITE_OK;
}
int Table::BestIndexInternal(sqlite3_index_info* idx) {
QueryConstraints query_constraints;
for (int i = 0; i < idx->nOrderBy; i++) {
int column = idx->aOrderBy[i].iColumn;
bool desc = idx->aOrderBy[i].desc;
query_constraints.AddOrderBy(column, desc);
}
for (int i = 0; i < idx->nConstraint; i++) {
const auto& cs = idx->aConstraint[i];
if (!cs.usable)
continue;
query_constraints.AddConstraint(cs.iColumn, cs.op);
// argvIndex is 1-based so use the current size of the vector.
int argv_index = static_cast<int>(query_constraints.constraints().size());
idx->aConstraintUsage[i].argvIndex = argv_index;
}
BestIndexInfo info;
info.omit.resize(query_constraints.constraints().size());
int ret = BestIndex(query_constraints, &info);
if (Table::debug) {
PERFETTO_LOG(
"[%s::BestIndex] constraints=%s orderByConsumed=%d estimatedCost=%d",
name_.c_str(), query_constraints.ToNewSqlite3String().get(),
info.order_by_consumed, info.estimated_cost);
}
if (ret != SQLITE_OK)
return ret;
idx->orderByConsumed = info.order_by_consumed;
idx->estimatedCost = info.estimated_cost;
size_t j = 0;
for (int i = 0; i < idx->nConstraint; i++) {
const auto& cs = idx->aConstraint[i];
if (cs.usable)
idx->aConstraintUsage[i].omit = info.omit[j++];
}
if (!info.order_by_consumed)
query_constraints.ClearOrderBy();
idx->idxStr = query_constraints.ToNewSqlite3String().release();
idx->needToFreeIdxStr = true;
idx->idxNum = ++best_index_num_;
return SQLITE_OK;
}
int Table::FindFunction(const char*, FindFunctionFn, void**) {
return 0;
};
Table::Cursor::~Cursor() = default;
int Table::Cursor::FilterInternal(int idxNum,
const char* idxStr,
int argc,
sqlite3_value** argv) {
auto* table = ToTable(this->pVtab);
bool cache_hit = true;
if (idxNum != table->qc_hash_) {
table->qc_cache_ = QueryConstraints::FromString(idxStr);
table->qc_hash_ = idxNum;
cache_hit = false;
}
if (Table::debug) {
PERFETTO_LOG("[%s::Filter] constraints=%s argc=%d cache_hit=%d",
table->name_.c_str(), idxStr, argc, cache_hit);
}
PERFETTO_DCHECK(table->qc_cache_.constraints().size() ==
static_cast<size_t>(argc));
return Filter(table->qc_cache_, argv);
}
} // namespace trace_processor
} // namespace perfetto