| /* |
| * 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/storage_table.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| StorageTable::StorageTable() = default; |
| StorageTable::~StorageTable() = default; |
| |
| util::Status StorageTable::Init(int, const char* const*, Schema* schema) { |
| schema_ = CreateStorageSchema(); |
| *schema = schema_.ToTableSchema(); |
| return util::OkStatus(); |
| } |
| |
| std::unique_ptr<SqliteTable::Cursor> StorageTable::CreateCursor() { |
| return std::unique_ptr<Cursor>(new Cursor(this)); |
| } |
| |
| std::unique_ptr<RowIterator> StorageTable::CreateBestRowIterator( |
| const QueryConstraints& qc, |
| sqlite3_value** argv) { |
| const auto& cs = qc.constraints(); |
| auto obs = RemoveRedundantOrderBy(cs, qc.order_by()); |
| |
| // Figure out whether the data is already ordered and which order we should |
| // traverse the data. |
| bool is_ordered, is_desc = false; |
| std::tie(is_ordered, is_desc) = IsOrdered(obs); |
| |
| // Create the range iterator and if we are sorted, just return it. |
| auto index = CreateRangeIterator(cs, argv); |
| if (!index.error().empty()) { |
| SetErrorMessage(sqlite3_mprintf(index.error().c_str())); |
| return nullptr; |
| } |
| |
| if (is_ordered) |
| return index.ToRowIterator(is_desc); |
| |
| // Otherwise, create the sorted vector of indices and create the vector |
| // iterator. |
| return std::unique_ptr<VectorRowIterator>( |
| new VectorRowIterator(CreateSortedIndexVector(std::move(index), obs))); |
| } |
| |
| FilteredRowIndex StorageTable::CreateRangeIterator( |
| const std::vector<QueryConstraints::Constraint>& cs, |
| sqlite3_value** argv) { |
| // Try and bound the search space to the smallest possible index region and |
| // store any leftover constraints to filter using bitvector. |
| uint32_t min_idx = 0; |
| uint32_t max_idx = RowCount(); |
| std::vector<size_t> bitvector_cs; |
| for (size_t i = 0; i < cs.size(); i++) { |
| const auto& c = cs[i]; |
| size_t column = static_cast<size_t>(c.column); |
| auto bounds = schema_.GetColumn(column).BoundFilter(c.op, argv[i]); |
| |
| min_idx = std::max(min_idx, bounds.min_idx); |
| max_idx = std::min(max_idx, bounds.max_idx); |
| |
| // If the lower bound is higher than the upper bound, return a zero-sized |
| // range iterator. |
| if (min_idx >= max_idx) |
| return FilteredRowIndex(min_idx, min_idx); |
| |
| if (!bounds.consumed) |
| bitvector_cs.emplace_back(i); |
| } |
| |
| // Create an filter index and allow each of the columns filter on it. |
| FilteredRowIndex index(min_idx, max_idx); |
| for (const auto& c_idx : bitvector_cs) { |
| const auto& c = cs[c_idx]; |
| auto* value = argv[c_idx]; |
| |
| const auto& schema_col = schema_.GetColumn(static_cast<size_t>(c.column)); |
| schema_col.Filter(c.op, value, &index); |
| |
| if (!index.error().empty()) |
| break; |
| } |
| return index; |
| } |
| |
| std::pair<bool, bool> StorageTable::IsOrdered( |
| const std::vector<QueryConstraints::OrderBy>& obs) { |
| if (obs.size() == 0) |
| return std::make_pair(true, false); |
| |
| if (obs.size() != 1) |
| return std::make_pair(false, false); |
| |
| const auto& ob = obs[0]; |
| auto col = static_cast<size_t>(ob.iColumn); |
| return std::make_pair(schema_.GetColumn(col).HasOrdering(), ob.desc); |
| } |
| |
| std::vector<QueryConstraints::OrderBy> StorageTable::RemoveRedundantOrderBy( |
| const std::vector<QueryConstraints::Constraint>& cs, |
| const std::vector<QueryConstraints::OrderBy>& obs) { |
| std::vector<QueryConstraints::OrderBy> filtered; |
| std::set<int> equality_cols; |
| for (const auto& c : cs) { |
| if (sqlite_utils::IsOpEq(c.op)) |
| equality_cols.emplace(c.column); |
| } |
| for (const auto& o : obs) { |
| if (equality_cols.count(o.iColumn) > 0) |
| continue; |
| filtered.emplace_back(o); |
| } |
| return filtered; |
| } |
| |
| std::vector<uint32_t> StorageTable::CreateSortedIndexVector( |
| FilteredRowIndex index, |
| const std::vector<QueryConstraints::OrderBy>& obs) { |
| PERFETTO_DCHECK(obs.size() > 0); |
| |
| // Retrieve the index created above from the index. |
| std::vector<uint32_t> sorted_rows = index.ToRowVector(); |
| |
| std::vector<StorageColumn::Comparator> comparators; |
| for (const auto& ob : obs) { |
| auto col = static_cast<size_t>(ob.iColumn); |
| comparators.emplace_back(schema_.GetColumn(col).Sort(ob)); |
| } |
| |
| auto comparator = [&comparators](uint32_t f, uint32_t s) { |
| for (const auto& comp : comparators) { |
| int c = comp(f, s); |
| if (c != 0) |
| return c < 0; |
| } |
| return false; |
| }; |
| std::sort(sorted_rows.begin(), sorted_rows.end(), comparator); |
| |
| return sorted_rows; |
| } |
| |
| bool StorageTable::HasEqConstraint(const QueryConstraints& qc, |
| const std::string& col_name) { |
| size_t c_idx = schema().ColumnIndexFromName(col_name); |
| auto fn = [c_idx](const QueryConstraints::Constraint& c) { |
| return c.column == static_cast<int>(c_idx) && sqlite_utils::IsOpEq(c.op); |
| }; |
| const auto& cs = qc.constraints(); |
| return std::find_if(cs.begin(), cs.end(), fn) != cs.end(); |
| } |
| |
| StorageTable::Cursor::Cursor(StorageTable* table) |
| : SqliteTable::Cursor(table), table_(table) {} |
| |
| int StorageTable::Cursor::Filter(const QueryConstraints& qc, |
| sqlite3_value** argv, |
| FilterHistory) { |
| iterator_ = table_->CreateBestRowIterator(qc, argv); |
| if (!iterator_) |
| return SQLITE_ERROR; |
| columns_ = table_->schema_.mutable_columns(); |
| return SQLITE_OK; |
| } |
| |
| int StorageTable::Cursor::Next() { |
| iterator_->NextRow(); |
| return SQLITE_OK; |
| } |
| |
| int StorageTable::Cursor::Eof() { |
| return iterator_->IsEnd(); |
| } |
| |
| int StorageTable::Cursor::Column(sqlite3_context* context, int raw_col) { |
| size_t column = static_cast<size_t>(raw_col); |
| (*columns_)[column]->ReportResult(context, iterator_->Row()); |
| return SQLITE_OK; |
| } |
| |
| } // namespace trace_processor |
| } // namespace perfetto |