/*
* 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 <algorithm>
#include "perfetto/base/logging.h"
namespace perfetto {
namespace trace_processor {
namespace {
std::string TypeToString(Table::ColumnType type) {
switch (type) {
case Table::ColumnType::kString:
return "STRING";
case Table::ColumnType::kUint:
return "UNSIGNED INT";
case Table::ColumnType::kLong:
return "BIG INT";
case Table::ColumnType::kInt:
return "INT";
case Table::ColumnType::kDouble:
return "DOUBLE";
case Table::ColumnType::kUnknown:
PERFETTO_FATAL("Cannot map unknown column type");
}
PERFETTO_FATAL("Not reached"); // For gcc
}
} // namespace
// static
bool Table::debug = false;
Table::Table() = default;
Table::~Table() = default;
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;
}
int Table::Update(int, sqlite3_value**, sqlite3_int64*) {
return SQLITE_READONLY;
}
const QueryConstraints& Table::ParseConstraints(int idxNum,
const char* idxStr,
int argc) {
bool cache_hit = true;
if (idxNum != qc_hash_) {
qc_cache_ = QueryConstraints::FromString(idxStr);
qc_hash_ = idxNum;
cache_hit = false;
}
if (Table::debug) {
PERFETTO_LOG("[%s::ParseConstraints] constraints=%s argc=%d cache_hit=%d",
name_.c_str(), idxStr, argc, cache_hit);
}
return qc_cache_;
}
Table::Cursor::Cursor(Table* table) : table_(table) {
// This is required to prevent us from leaving this field uninitialised if
// we ever move construct the Cursor.
pVtab = table;
}
Table::Cursor::~Cursor() = default;
int Table::Cursor::RowId(sqlite3_int64*) {
return SQLITE_ERROR;
}
Table::Column::Column(size_t index,
std::string name,
ColumnType type,
bool hidden)
: index_(index), name_(name), type_(type), hidden_(hidden) {}
Table::Schema::Schema(std::vector<Column> columns,
std::vector<size_t> primary_keys)
: columns_(std::move(columns)), primary_keys_(std::move(primary_keys)) {
for (size_t i = 0; i < columns_.size(); i++) {
PERFETTO_CHECK(columns_[i].index() == i);
}
for (auto key : primary_keys_) {
PERFETTO_CHECK(key < columns_.size());
}
}
Table::Schema::Schema() = default;
Table::Schema::Schema(const Schema&) = default;
Table::Schema& Table::Schema::operator=(const Schema&) = default;
std::string Table::Schema::ToCreateTableStmt() const {
std::string stmt = "CREATE TABLE x(";
for (size_t i = 0; i < columns_.size(); ++i) {
const Column& col = columns_[i];
stmt += " " + col.name();
if (col.type() != ColumnType::kUnknown) {
stmt += " " + TypeToString(col.type());
} else if (std::find(primary_keys_.begin(), primary_keys_.end(), i) !=
primary_keys_.end()) {
PERFETTO_FATAL("Unknown type for primary key column %s",
col.name().c_str());
}
if (col.hidden()) {
stmt += " HIDDEN";
}
stmt += ",";
}
stmt += " PRIMARY KEY(";
for (size_t i = 0; i < primary_keys_.size(); i++) {
if (i != 0)
stmt += ", ";
stmt += columns_[primary_keys_[i]].name();
}
stmt += ")) WITHOUT ROWID;";
return stmt;
}
} // namespace trace_processor
} // namespace perfetto