1
2 /*
3 * Copyright (C) 2019 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "src/trace_processor/sqlite/db_sqlite_table.h"
19
20 #include <sqlite3.h>
21 #include <array>
22 #include <cstdint>
23
24 #include "perfetto/trace_processor/basic_types.h"
25 #include "src/trace_processor/db/table.h"
26 #include "test/gtest_and_gmock.h"
27
28 namespace perfetto::trace_processor {
29 namespace {
30
CreateSchema()31 Table::Schema CreateSchema() {
32 Table::Schema schema;
33 schema.columns.push_back({"id", SqlValue::Type::kLong, true /* is_id */,
34 true /* is_sorted */, false /* is_hidden */,
35 false /* is_set_id */});
36 schema.columns.push_back({"type", SqlValue::Type::kLong, false /* is_id */,
37 false /* is_sorted */, false /* is_hidden */,
38 false /* is_set_id */});
39 schema.columns.push_back({"test1", SqlValue::Type::kLong, false /* is_id */,
40 true /* is_sorted */, false /* is_hidden */,
41 false /* is_set_id */});
42 schema.columns.push_back({"test2", SqlValue::Type::kLong, false /* is_id */,
43 false /* is_sorted */, false /* is_hidden */,
44 false /* is_set_id */});
45 schema.columns.push_back({"test3", SqlValue::Type::kLong, false /* is_id */,
46 false /* is_sorted */, false /* is_hidden */,
47 false /* is_set_id */});
48 return schema;
49 }
50
CreateConstraint(int col,uint8_t op)51 sqlite3_index_info::sqlite3_index_constraint CreateConstraint(int col,
52 uint8_t op) {
53 return {col, op, true, 0};
54 }
55
CreateUsage()56 sqlite3_index_info::sqlite3_index_constraint_usage CreateUsage() {
57 return {1, true};
58 }
59
CreateCsIndexInfo(int cs_count,sqlite3_index_info::sqlite3_index_constraint * c,sqlite3_index_info::sqlite3_index_constraint_usage * u)60 sqlite3_index_info CreateCsIndexInfo(
61 int cs_count,
62 sqlite3_index_info::sqlite3_index_constraint* c,
63 sqlite3_index_info::sqlite3_index_constraint_usage* u) {
64 return {cs_count, c, 0, nullptr, u, 0, nullptr, false, 0, 0, 0, 0, 0};
65 }
66
CreateObIndexInfo(int ob_count,sqlite3_index_info::sqlite3_index_orderby * o)67 sqlite3_index_info CreateObIndexInfo(
68 int ob_count,
69 sqlite3_index_info::sqlite3_index_orderby* o) {
70 return {0, nullptr, ob_count, o, nullptr, 0, nullptr, false, 0, 0, 0, 0, 0};
71 }
72
TEST(DbSqliteModule,IdEqCheaperThanOtherEq)73 TEST(DbSqliteModule, IdEqCheaperThanOtherEq) {
74 auto schema = CreateSchema();
75 constexpr uint32_t kRowCount = 1234;
76
77 auto c = CreateConstraint(0, SQLITE_INDEX_CONSTRAINT_EQ);
78 auto u = CreateUsage();
79 auto info = CreateCsIndexInfo(1, &c, &u);
80
81 auto id_cost =
82 DbSqliteModule::EstimateCost(schema, kRowCount, &info, {0u}, {});
83
84 c.iColumn = 1;
85 auto a_cost =
86 DbSqliteModule::EstimateCost(schema, kRowCount, &info, {0u}, {});
87
88 ASSERT_LT(id_cost.cost, a_cost.cost);
89 ASSERT_LT(id_cost.rows, a_cost.rows);
90 }
91
TEST(DbSqliteModule,IdEqCheaperThatOtherConstraint)92 TEST(DbSqliteModule, IdEqCheaperThatOtherConstraint) {
93 auto schema = CreateSchema();
94 constexpr uint32_t kRowCount = 1234;
95
96 auto c = CreateConstraint(0, SQLITE_INDEX_CONSTRAINT_EQ);
97 auto u = CreateUsage();
98 auto info = CreateCsIndexInfo(1, &c, &u);
99
100 auto id_cost =
101 DbSqliteModule::EstimateCost(schema, kRowCount, &info, {0u}, {});
102
103 c.iColumn = 1;
104 c.op = SQLITE_INDEX_CONSTRAINT_LT;
105 auto a_cost =
106 DbSqliteModule::EstimateCost(schema, kRowCount, &info, {0u}, {});
107
108 ASSERT_LT(id_cost.cost, a_cost.cost);
109 ASSERT_LT(id_cost.rows, a_cost.rows);
110 }
111
TEST(DbSqliteModule,SingleEqCheaperThanMultipleConstraint)112 TEST(DbSqliteModule, SingleEqCheaperThanMultipleConstraint) {
113 auto schema = CreateSchema();
114 constexpr uint32_t kRowCount = 1234;
115
116 auto c = CreateConstraint(1, SQLITE_INDEX_CONSTRAINT_EQ);
117 auto u = CreateUsage();
118 auto info = CreateCsIndexInfo(1, &c, &u);
119
120 auto single_cost =
121 DbSqliteModule::EstimateCost(schema, kRowCount, &info, {0u}, {});
122
123 std::array c2{CreateConstraint(1, SQLITE_INDEX_CONSTRAINT_EQ),
124 CreateConstraint(2, SQLITE_INDEX_CONSTRAINT_EQ)};
125 std::array u2{CreateUsage(), CreateUsage()};
126 auto info2 = CreateCsIndexInfo(c2.size(), c2.data(), u2.data());
127
128 auto multi_cost =
129 DbSqliteModule::EstimateCost(schema, kRowCount, &info2, {0u, 1u}, {});
130
131 // The cost of the single filter should be cheaper (because of our special
132 // handling of single equality). But the number of rows should be greater.
133 ASSERT_LT(single_cost.cost, multi_cost.cost);
134 ASSERT_GT(single_cost.rows, multi_cost.rows);
135 }
136
TEST(DbSqliteModule,MultiSortedEqCheaperThanMultiUnsortedEq)137 TEST(DbSqliteModule, MultiSortedEqCheaperThanMultiUnsortedEq) {
138 auto schema = CreateSchema();
139 constexpr uint32_t kRowCount = 1234;
140
141 std::array c1{CreateConstraint(1, SQLITE_INDEX_CONSTRAINT_EQ),
142 CreateConstraint(2, SQLITE_INDEX_CONSTRAINT_EQ)};
143 std::array u1{CreateUsage(), CreateUsage()};
144 auto info1 = CreateCsIndexInfo(c1.size(), c1.data(), u1.data());
145
146 auto sorted_cost =
147 DbSqliteModule::EstimateCost(schema, kRowCount, &info1, {0u, 1u}, {});
148
149 std::array c2{CreateConstraint(3, SQLITE_INDEX_CONSTRAINT_EQ),
150 CreateConstraint(4, SQLITE_INDEX_CONSTRAINT_EQ)};
151 std::array u2{CreateUsage(), CreateUsage()};
152 auto info2 = CreateCsIndexInfo(c2.size(), c2.data(), u2.data());
153
154 auto unsorted_cost =
155 DbSqliteModule::EstimateCost(schema, kRowCount, &info2, {0u, 1u}, {});
156
157 // The number of rows should be the same but the cost of the sorted
158 // query should be less.
159 ASSERT_LT(sorted_cost.cost, unsorted_cost.cost);
160 ASSERT_EQ(sorted_cost.rows, unsorted_cost.rows);
161 }
162
TEST(DbSqliteModule,EmptyTableCosting)163 TEST(DbSqliteModule, EmptyTableCosting) {
164 auto schema = CreateSchema();
165 constexpr uint32_t kRowCount = 0;
166
167 std::array c1{CreateConstraint(0, SQLITE_INDEX_CONSTRAINT_EQ)};
168 std::array u1{CreateUsage()};
169 auto info1 = CreateCsIndexInfo(c1.size(), c1.data(), u1.data());
170
171 auto id_cost =
172 DbSqliteModule::EstimateCost(schema, kRowCount, &info1, {0u}, {});
173
174 std::array c2{CreateConstraint(0, SQLITE_INDEX_CONSTRAINT_EQ)};
175 std::array u2{CreateUsage()};
176 auto info2 = CreateCsIndexInfo(c2.size(), c2.data(), u2.data());
177
178 auto a_cost =
179 DbSqliteModule::EstimateCost(schema, kRowCount, &info2, {0u}, {});
180
181 ASSERT_DOUBLE_EQ(id_cost.cost, a_cost.cost);
182 ASSERT_EQ(id_cost.rows, a_cost.rows);
183 }
184
TEST(DbSqliteModule,OrderByOnSortedCheaper)185 TEST(DbSqliteModule, OrderByOnSortedCheaper) {
186 auto schema = CreateSchema();
187 constexpr uint32_t kRowCount = 1234;
188
189 sqlite3_index_info::sqlite3_index_orderby ob1{1u, false};
190 auto info1 = CreateObIndexInfo(1, &ob1);
191
192 auto a_cost =
193 DbSqliteModule::EstimateCost(schema, kRowCount, &info1, {}, {0u});
194
195 sqlite3_index_info::sqlite3_index_orderby ob2{2u, false};
196 auto info2 = CreateObIndexInfo(1, &ob2);
197
198 // On an ordered column, the constraint for sorting would get pruned so
199 // we would end up with an empty constraint set.
200 auto sorted_cost =
201 DbSqliteModule::EstimateCost(schema, kRowCount, &info2, {}, {});
202
203 ASSERT_LT(sorted_cost.cost, a_cost.cost);
204 ASSERT_EQ(sorted_cost.rows, a_cost.rows);
205 }
206
207 } // namespace
208 } // namespace perfetto::trace_processor
209