blob: 940d8b10bd2025cc6d5d1e49c85e45068c3f1594 [file] [log] [blame]
/*
* Copyright (C) 2019 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/db/table.h"
namespace perfetto {
namespace trace_processor {
Table::Table() = default;
Table::~Table() = default;
Table::Table(StringPool* pool, const Table* parent) : string_pool_(pool) {
if (!parent)
return;
// If this table has a parent, then copy over all the columns pointing to
// empty RowMaps.
for (uint32_t i = 0; i < parent->row_maps_.size(); ++i)
row_maps_.emplace_back();
for (const Column& col : parent->columns_)
columns_.emplace_back(col, this, columns_.size(), col.row_map_idx_);
}
Table& Table::operator=(Table&& other) noexcept {
row_count_ = other.row_count_;
string_pool_ = other.string_pool_;
row_maps_ = std::move(other.row_maps_);
columns_ = std::move(other.columns_);
for (Column& col : columns_) {
col.table_ = this;
}
return *this;
}
Table Table::Copy() const {
Table table = CopyExceptRowMaps();
for (const RowMap& rm : row_maps_) {
table.row_maps_.emplace_back(rm.Copy());
}
return table;
}
Table Table::CopyExceptRowMaps() const {
Table table(string_pool_, nullptr);
table.row_count_ = row_count_;
for (const Column& col : columns_) {
table.columns_.emplace_back(col, &table, col.index_in_table(),
col.row_map_idx_);
}
return table;
}
Table Table::Sort(const std::vector<Order>& od) const {
if (od.empty())
return Copy();
// Return a copy if there is a single constraint to sort the table
// by a column which is already sorted.
const auto& first_col = GetColumn(od.front().col_idx);
if (od.size() == 1 && first_col.IsSorted() && !od.front().desc)
return Copy();
// Build an index vector with all the indices for the first |size_| rows.
std::vector<uint32_t> idx(row_count_);
if (od.size() == 1 && first_col.IsSorted()) {
// We special case a single constraint in descending order as this
// happens any time the |max| function is used in SQLite. We can be
// more efficient as this column is already sorted so we simply need
// to reverse the order of this column.
PERFETTO_DCHECK(od.front().desc);
std::iota(idx.rbegin(), idx.rend(), 0);
} else {
// As our data is columnar, it's always more efficient to sort one column
// at a time rather than try and sort lexiographically all at once.
// To preserve correctness, we need to stably sort the index vector once
// for each order by in *reverse* order. Reverse order is important as it
// preserves the lexiographical property.
//
// For example, suppose we have the following:
// Table {
// Column x;
// Column y
// Column z;
// }
//
// Then, to sort "y asc, x desc", we could do one of two things:
// 1) sort the index vector all at once and on each index, we compare
// y then z. This is slow as the data is columnar and we need to
// repeatedly branch inside each column.
// 2) we can stably sort first on x desc and then sort on y asc. This will
// first put all the x in the correct order such that when we sort on
// y asc, we will have the correct order of x where y is the same (since
// the sort is stable).
//
// TODO(lalitm): it is possible that we could sort the last constraint (i.e.
// the first constraint in the below loop) in a non-stable way. However,
// this is more subtle than it appears as we would then need special
// handling where there are order bys on a column which is already sorted
// (e.g. ts, id). Investigate whether the performance gains from this are
// worthwhile. This also needs changes to the constraint modification logic
// in DbSqliteTable which currently eliminates constraints on sorted
// columns.
std::iota(idx.begin(), idx.end(), 0);
for (auto it = od.rbegin(); it != od.rend(); ++it) {
columns_[it->col_idx].StableSort(it->desc, &idx);
}
}
// Return a copy of this table with the RowMaps using the computed ordered
// RowMap.
Table table = CopyExceptRowMaps();
RowMap rm(std::move(idx));
for (const RowMap& map : row_maps_) {
table.row_maps_.emplace_back(map.SelectRows(rm));
PERFETTO_DCHECK(table.row_maps_.back().size() == table.row_count());
}
// Remove the sorted flag from all the columns.
for (auto& col : table.columns_) {
col.flags_ &= ~Column::Flag::kSorted;
}
// For the first order by, make the column flag itself as sorted but
// only if the sort was in ascending order.
if (!od.front().desc) {
table.columns_[od.front().col_idx].flags_ |= Column::Flag::kSorted;
}
return table;
}
Table Table::LookupJoin(JoinKey left, const Table& other, JoinKey right) {
// The join table will have the same size and RowMaps as the left (this)
// table because the left column is indexing the right table.
Table table(string_pool_, nullptr);
table.row_count_ = row_count_;
for (const RowMap& rm : row_maps_) {
table.row_maps_.emplace_back(rm.Copy());
}
for (const Column& col : columns_) {
// We skip id columns as they are misleading on join tables.
if (col.IsId())
continue;
table.columns_.emplace_back(col, &table, table.columns_.size(),
col.row_map_idx_);
}
const Column& left_col = columns_[left.col_idx];
const Column& right_col = other.columns_[right.col_idx];
// For each index in the left column, retrieve the index of the row inside
// the RowMap of the right column. By getting the index of the row rather
// than the row number itself, we can call |Apply| on the other RowMaps
// in the right table.
std::vector<uint32_t> indices(row_count_);
for (uint32_t i = 0; i < row_count_; ++i) {
SqlValue val = left_col.Get(i);
PERFETTO_CHECK(val.type != SqlValue::Type::kNull);
indices[i] = right_col.IndexOf(val).value();
}
// Apply the computed RowMap to each of the right RowMaps, adding it to the
// join table as we go.
RowMap rm(std::move(indices));
for (const RowMap& ot : other.row_maps_) {
table.row_maps_.emplace_back(ot.SelectRows(rm));
}
uint32_t left_row_maps_size = static_cast<uint32_t>(row_maps_.size());
for (const Column& col : other.columns_) {
// We skip id columns as they are misleading on join tables.
if (col.IsId())
continue;
// Ensure that we offset the RowMap index by the number of RowMaps in the
// left table.
table.columns_.emplace_back(col, &table, table.columns_.size(),
col.row_map_idx_ + left_row_maps_size);
}
return table;
}
} // namespace trace_processor
} // namespace perfetto