1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/db/table.h"
18
19 namespace perfetto {
20 namespace trace_processor {
21
22 Table::Table() = default;
23 Table::~Table() = default;
24
Table(StringPool * pool,const Table * parent)25 Table::Table(StringPool* pool, const Table* parent) : string_pool_(pool) {
26 if (!parent)
27 return;
28
29 // If this table has a parent, then copy over all the columns pointing to
30 // empty RowMaps.
31 for (uint32_t i = 0; i < parent->row_maps_.size(); ++i)
32 row_maps_.emplace_back();
33 for (const Column& col : parent->columns_)
34 columns_.emplace_back(col, this, columns_.size(), col.row_map_idx_);
35 }
36
operator =(Table && other)37 Table& Table::operator=(Table&& other) noexcept {
38 row_count_ = other.row_count_;
39 string_pool_ = other.string_pool_;
40
41 row_maps_ = std::move(other.row_maps_);
42 columns_ = std::move(other.columns_);
43 for (Column& col : columns_) {
44 col.table_ = this;
45 }
46 return *this;
47 }
48
Copy() const49 Table Table::Copy() const {
50 Table table = CopyExceptRowMaps();
51 for (const RowMap& rm : row_maps_) {
52 table.row_maps_.emplace_back(rm.Copy());
53 }
54 return table;
55 }
56
CopyExceptRowMaps() const57 Table Table::CopyExceptRowMaps() const {
58 Table table(string_pool_, nullptr);
59 table.row_count_ = row_count_;
60 for (const Column& col : columns_) {
61 table.columns_.emplace_back(col, &table, col.index_in_table(),
62 col.row_map_idx_);
63 }
64 return table;
65 }
66
Sort(const std::vector<Order> & od) const67 Table Table::Sort(const std::vector<Order>& od) const {
68 if (od.empty())
69 return Copy();
70
71 // Build an index vector with all the indices for the first |size_| rows.
72 std::vector<uint32_t> idx(row_count_);
73 std::iota(idx.begin(), idx.end(), 0);
74
75 // As our data is columnar, it's always more efficient to sort one column
76 // at a time rather than try and sort lexiographically all at once.
77 // To preserve correctness, we need to stably sort the index vector once
78 // for each order by in *reverse* order. Reverse order is important as it
79 // preserves the lexiographical property.
80 //
81 // For example, suppose we have the following:
82 // Table {
83 // Column x;
84 // Column y
85 // Column z;
86 // }
87 //
88 // Then, to sort "y asc, x desc", we could do one of two things:
89 // 1) sort the index vector all at once and on each index, we compare
90 // y then z. This is slow as the data is columnar and we need to
91 // repeatedly branch inside each column.
92 // 2) we can stably sort first on x desc and then sort on y asc. This will
93 // first put all the x in the correct order such that when we sort on
94 // y asc, we will have the correct order of x where y is the same (since
95 // the sort is stable).
96 //
97 // TODO(lalitm): it is possible that we could sort the last constraint (i.e.
98 // the first constraint in the below loop) in a non-stable way. However, this
99 // is more subtle than it appears as we would then need special handling where
100 // there are order bys on a column which is already sorted (e.g. ts, id).
101 // Investigate whether the performance gains from this are worthwhile. This
102 // also needs changes to the constraint modification logic in DbSqliteTable
103 // which currently eliminates constraints on sorted columns.
104 for (auto it = od.rbegin(); it != od.rend(); ++it) {
105 columns_[it->col_idx].StableSort(it->desc, &idx);
106 }
107
108 // Return a copy of this table with the RowMaps using the computed ordered
109 // RowMap.
110 Table table = CopyExceptRowMaps();
111 RowMap rm(std::move(idx));
112 for (const RowMap& map : row_maps_) {
113 table.row_maps_.emplace_back(map.SelectRows(rm));
114 PERFETTO_DCHECK(table.row_maps_.back().size() == table.row_count());
115 }
116
117 // Remove the sorted flag from all the columns.
118 for (auto& col : table.columns_) {
119 col.flags_ &= ~Column::Flag::kSorted;
120 }
121
122 // For the first order by, make the column flag itself as sorted.
123 table.columns_[od.front().col_idx].flags_ |= Column::Flag::kSorted;
124
125 return table;
126 }
127
LookupJoin(JoinKey left,const Table & other,JoinKey right)128 Table Table::LookupJoin(JoinKey left, const Table& other, JoinKey right) {
129 // The join table will have the same size and RowMaps as the left (this)
130 // table because the left column is indexing the right table.
131 Table table(string_pool_, nullptr);
132 table.row_count_ = row_count_;
133 for (const RowMap& rm : row_maps_) {
134 table.row_maps_.emplace_back(rm.Copy());
135 }
136
137 for (const Column& col : columns_) {
138 // We skip id columns as they are misleading on join tables.
139 if (col.IsId())
140 continue;
141 table.columns_.emplace_back(col, &table, table.columns_.size(),
142 col.row_map_idx_);
143 }
144
145 const Column& left_col = columns_[left.col_idx];
146 const Column& right_col = other.columns_[right.col_idx];
147
148 // For each index in the left column, retrieve the index of the row inside
149 // the RowMap of the right column. By getting the index of the row rather
150 // than the row number itself, we can call |Apply| on the other RowMaps
151 // in the right table.
152 std::vector<uint32_t> indices(row_count_);
153 for (uint32_t i = 0; i < row_count_; ++i) {
154 SqlValue val = left_col.Get(i);
155 PERFETTO_CHECK(val.type != SqlValue::Type::kNull);
156 indices[i] = right_col.IndexOf(val).value();
157 }
158
159 // Apply the computed RowMap to each of the right RowMaps, adding it to the
160 // join table as we go.
161 RowMap rm(std::move(indices));
162 for (const RowMap& ot : other.row_maps_) {
163 table.row_maps_.emplace_back(ot.SelectRows(rm));
164 }
165
166 uint32_t left_row_maps_size = static_cast<uint32_t>(row_maps_.size());
167 for (const Column& col : other.columns_) {
168 // We skip id columns as they are misleading on join tables.
169 if (col.IsId())
170 continue;
171
172 // Ensure that we offset the RowMap index by the number of RowMaps in the
173 // left table.
174 table.columns_.emplace_back(col, &table, table.columns_.size(),
175 col.row_map_idx_ + left_row_maps_size);
176 }
177 return table;
178 }
179
180 } // namespace trace_processor
181 } // namespace perfetto
182