|  | /* | 
|  | * 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 |