1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "table_base.h"
17
18 #include <cctype>
19 #include <cstring>
20
21 #include "log.h"
22
23 namespace SysTuning {
24 namespace TraceStreamer {
25 namespace {
26 struct TableContext {
27 TabTemplate tmplate;
28 TraceDataCache* dataCache;
29 sqlite3_module module;
30 std::string tableName;
31 };
32 } // namespace
33
~TableBase()34 TableBase::~TableBase()
35 {
36 dataCache_ = nullptr;
37 cursor_ = nullptr;
38 }
39
TableRegister(sqlite3 & db,TraceDataCache * cache,const std::string & tableName,TabTemplate tmplate)40 void TableBase::TableRegister(sqlite3& db, TraceDataCache* cache, const std::string& tableName, TabTemplate tmplate)
41 {
42 std::unique_ptr<TableContext> context(std::make_unique<TableContext>());
43 context->dataCache = cache;
44 context->tmplate = tmplate;
45 context->tableName = tableName;
46 sqlite3_module& module = context->module;
47 module = {0};
48
49 auto createFn = [](sqlite3* xdb, void* pAux, int32_t argc, const char* const* argv, sqlite3_vtab** ppVTab,
50 char** pzErr) {
51 Unused(argc);
52 Unused(argv);
53 Unused(pzErr);
54 auto xdesc = static_cast<const TableContext*>(pAux);
55 auto table = xdesc->tmplate(xdesc->dataCache);
56 table->name_ = xdesc->tableName;
57 if (table->name_ == "process" || table->name_ == "thread") {
58 table->wdataCache_ = xdesc->dataCache;
59 }
60
61 table->Init(argc, argv);
62 std::string createStmt = table->CreateTableSql();
63 TS_LOGD("xCreate table %s, statement: %s", table->name_.c_str(), createStmt.c_str());
64 int32_t ret = sqlite3_declare_vtab(xdb, createStmt.c_str());
65 if (ret != SQLITE_OK) {
66 if ((table->name_ == "span_join") || (table->name_ == "_span_join")) {
67 return ret;
68 }
69 TS_LOGE("sqlite3_declare_vtab %s faild: %s", table->name_.c_str(), createStmt.c_str());
70 return ret;
71 }
72 *ppVTab = table.release();
73 return SQLITE_OK;
74 };
75
76 auto destroyFn = [](sqlite3_vtab* t) {
77 TS_LOGD("xDestroy table %s", static_cast<TableBase*>(t)->name_.c_str());
78 delete static_cast<TableBase*>(t);
79 return SQLITE_OK;
80 };
81 module.xCreate = createFn;
82 module.xConnect = createFn;
83 module.xDisconnect = destroyFn;
84 module.xDestroy = destroyFn;
85
86 SetModuleCallbacks(module, tableName);
87 sqlite3_create_module_v2(&db, tableName.c_str(), &module, context.release(),
88 [](void* arg) { delete static_cast<TableContext*>(arg); });
89 }
90
SetModuleCallbacks(sqlite3_module & module,const std::string & tableName)91 void TableBase::SetModuleCallbacks(sqlite3_module& module, const std::string& tableName)
92 {
93 module.xOpen = [](sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor) {
94 TS_LOGD("xOpen: %s", static_cast<TableBase*>(pVTab)->name_.c_str());
95 return static_cast<TableBase*>(pVTab)->Open(ppCursor);
96 };
97
98 module.xClose = [](sqlite3_vtab_cursor* vc) {
99 TS_LOGD("xClose: %s", static_cast<Cursor*>(vc)->table_->name_.c_str());
100 delete static_cast<Cursor*>(vc);
101 return SQLITE_OK;
102 };
103
104 module.xBestIndex = [](sqlite3_vtab* pVTab, sqlite3_index_info* idxInfo) {
105 TS_LOGD("xBestIndex: %s %d", static_cast<TableBase*>(pVTab)->name_.c_str(), idxInfo->nConstraint);
106 return static_cast<TableBase*>(pVTab)->BestIndex(idxInfo);
107 };
108
109 module.xFilter = [](sqlite3_vtab_cursor* vc, int32_t idxNum, const char* idxStr, int32_t argc,
110 sqlite3_value** argv) {
111 auto* c = static_cast<Cursor*>(vc);
112 c->Reset();
113 TS_LOGD("xFilter %s: [%d]%s", static_cast<Cursor*>(vc)->table_->name_.c_str(), idxNum, idxStr);
114 if (c->table_->cacheIdxNum_ != idxNum) {
115 c->table_->cacheConstraint_.Clear();
116 c->table_->cacheConstraint_.FromString(idxStr);
117 c->table_->cacheIdxNum_ = idxNum;
118 }
119 return c->Filter(c->table_->cacheConstraint_, argv);
120 };
121 module.xNext = [](sqlite3_vtab_cursor* vc) { return static_cast<TableBase::Cursor*>(vc)->Next(); };
122 module.xEof = [](sqlite3_vtab_cursor* vc) { return static_cast<TableBase::Cursor*>(vc)->Eof(); };
123 module.xColumn = [](sqlite3_vtab_cursor* vc, sqlite3_context* ctx, int32_t col) {
124 static_cast<TableBase::Cursor*>(vc)->context_ = ctx;
125 return static_cast<TableBase::Cursor*>(vc)->Column(col);
126 };
127 if (tableName == "process" || tableName == "thread") {
128 module.xUpdate = [](sqlite3_vtab* pVTab, int32_t argc, sqlite3_value** argv, sqlite3_int64* pRowid) {
129 TS_LOGD("xUpdate: %s", static_cast<TableBase*>(pVTab)->name_.c_str());
130 return static_cast<TableBase*>(pVTab)->Update(argc, argv, pRowid);
131 };
132 }
133 }
134
CreateTableSql() const135 std::string TableBase::CreateTableSql() const
136 {
137 std::string stmt = "CREATE TABLE x(";
138 for (const auto& col : tableColumn_) {
139 stmt += " " + col.name_ + " " + col.type_;
140 stmt += ",";
141 }
142 stmt += " PRIMARY KEY(";
143 for (size_t i = 0; i < tablePriKey_.size(); i++) {
144 if (i != 0)
145 stmt += ", ";
146 stmt += tablePriKey_.at(i);
147 }
148 stmt += ")) WITHOUT ROWID;";
149 return stmt;
150 }
Next()151 int32_t TableBase::Cursor::Next()
152 {
153 indexMap_->Next();
154 return SQLITE_OK;
155 }
156
Eof()157 int32_t TableBase::Cursor::Eof()
158 {
159 return dataCache_->Cancel() || indexMap_->Eof();
160 }
CurrentRow() const161 uint32_t TableBase::Cursor::CurrentRow() const
162 {
163 return indexMap_->CurrentRow();
164 }
FilterEnd()165 void TableBase::Cursor::FilterEnd()
166 {
167 indexMap_->Sort();
168 }
BestIndex(sqlite3_index_info * idxInfo)169 int32_t TableBase::BestIndex(sqlite3_index_info* idxInfo)
170 {
171 FilterConstraints filterConstraints;
172 for (int32_t i = 0; i < idxInfo->nConstraint; i++) {
173 const auto& constraint = idxInfo->aConstraint[i];
174 if (constraint.usable) {
175 filterConstraints.AddConstraint(i, constraint.iColumn, constraint.op);
176 }
177 }
178 for (int32_t i = 0; i < idxInfo->nOrderBy; i++) {
179 filterConstraints.AddOrderBy(idxInfo->aOrderBy[i].iColumn, idxInfo->aOrderBy[i].desc);
180 }
181
182 EstimatedIndexInfo estimate = {idxInfo->estimatedRows, idxInfo->estimatedCost, false};
183 EstimateFilterCost(filterConstraints, estimate);
184
185 idxInfo->orderByConsumed = estimate.isOrdered;
186 idxInfo->estimatedCost = estimate.estimatedCost;
187 idxInfo->estimatedRows = estimate.estimatedRows;
188
189 auto cs = filterConstraints.GetConstraints();
190 for (size_t i = 0; i < cs.size(); i++) {
191 auto& c = cs[i];
192 idxInfo->aConstraintUsage[c.idxInaConstraint].argvIndex = static_cast<int32_t>(i + 1);
193 idxInfo->aConstraintUsage[c.idxInaConstraint].omit = c.isSupport;
194 }
195
196 std::string str;
197 filterConstraints.ToString(str);
198 char* pIdxStr = static_cast<char*>(sqlite3_malloc(str.size() + 1));
199 std::copy(str.begin(), str.end(), pIdxStr);
200 pIdxStr[str.size()] = '\0';
201 idxInfo->idxStr = pIdxStr;
202 idxInfo->needToFreeIdxStr = true;
203 idxInfo->idxNum = ++bestIndexNum_;
204
205 TS_LOGD("%s BestIndex return: %d: %s", name_.c_str(), idxInfo->idxNum, str.c_str());
206 TS_LOGD("%s, aConstraintUsage[%d]", idxInfo->idxStr, idxInfo->nConstraint);
207 for (int32_t i = 0; i < idxInfo->nConstraint; i++) {
208 TS_LOGD("col: %d op: %d, argvindex: %d omit: %d", idxInfo->aConstraint[i].iColumn, idxInfo->aConstraint[i].op,
209 idxInfo->aConstraintUsage[i].argvIndex, idxInfo->aConstraintUsage[i].omit);
210 }
211 TS_LOGD("estimated: %lld cost:%.3f", idxInfo->estimatedRows, idxInfo->estimatedCost);
212
213 return SQLITE_OK;
214 }
215
Open(sqlite3_vtab_cursor ** ppCursor)216 int32_t TableBase::Open(sqlite3_vtab_cursor** ppCursor)
217 {
218 *ppCursor = static_cast<sqlite3_vtab_cursor*>(CreateCursor().release());
219 return SQLITE_OK;
220 }
221
Cursor(const TraceDataCache * dataCache,TableBase * table,uint32_t rowCount)222 TableBase::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table, uint32_t rowCount)
223 : context_(nullptr),
224 table_(table),
225 dataCache_(dataCache),
226 indexMap_(std::make_unique<IndexMap>(0, rowCount)),
227 rowCount_(rowCount)
228 {
229 }
230
CanFilterId(const char op,size_t & rowCount)231 bool TableBase::CanFilterId(const char op, size_t& rowCount)
232 {
233 switch (op) {
234 case SQLITE_INDEX_CONSTRAINT_EQ:
235 rowCount = 1;
236 break;
237 case SQLITE_INDEX_CONSTRAINT_GT:
238 case SQLITE_INDEX_CONSTRAINT_GE:
239 case SQLITE_INDEX_CONSTRAINT_LE:
240 case SQLITE_INDEX_CONSTRAINT_LT:
241 // assume filter out a half of rows
242 rowCount = (rowCount >> 1);
243 break;
244 default:
245 return false;
246 }
247 return true;
248 }
249
~Cursor()250 TableBase::Cursor::~Cursor()
251 {
252 context_ = nullptr;
253 dataCache_ = nullptr;
254 }
FilterTS(unsigned char op,sqlite3_value * argv,const std::deque<InternalTime> & times)255 void TableBase::Cursor::FilterTS(unsigned char op, sqlite3_value* argv, const std::deque<InternalTime>& times)
256 {
257 auto v = static_cast<uint64_t>(sqlite3_value_int64(argv));
258 auto getValue = [](const uint64_t& row) { return row; };
259 switch (op) {
260 case SQLITE_INDEX_CONSTRAINT_EQ:
261 indexMap_->IntersectabcEqual(times, v, getValue);
262 break;
263 case SQLITE_INDEX_CONSTRAINT_GT:
264 v++;
265 case SQLITE_INDEX_CONSTRAINT_GE: {
266 indexMap_->IntersectGreaterEqual(times, v, getValue);
267 break;
268 }
269 case SQLITE_INDEX_CONSTRAINT_LE:
270 v++;
271 case SQLITE_INDEX_CONSTRAINT_LT: {
272 indexMap_->IntersectLessEqual(times, v, getValue);
273 break;
274 }
275 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: {
276 indexMap_->RemoveNullElements(times, v);
277 break;
278 }
279 default:
280 break;
281 } // end of switch (op)
282 }
283
RowId(sqlite3_int64 * id)284 int32_t TableBase::Cursor::RowId(sqlite3_int64* id)
285 {
286 if (dataCache_->Cancel() || indexMap_->Eof()) {
287 return SQLITE_ERROR;
288 }
289 *id = static_cast<sqlite3_int64>(indexMap_->CurrentRow());
290 return SQLITE_OK;
291 }
FilterId(unsigned char op,sqlite3_value * argv)292 void TableBase::Cursor::FilterId(unsigned char op, sqlite3_value* argv)
293 {
294 auto type = sqlite3_value_type(argv);
295 if (type != SQLITE_INTEGER) {
296 // other type consider it NULL
297 indexMap_->Intersect(0, 0);
298 return;
299 }
300 if (indexMap_->HasData()) {
301 indexMap_->CovertToIndexMap();
302 }
303
304 auto v = static_cast<TableRowId>(sqlite3_value_int64(argv));
305 switch (op) {
306 case SQLITE_INDEX_CONSTRAINT_EQ:
307 indexMap_->Intersect(v, v + 1);
308 break;
309 case SQLITE_INDEX_CONSTRAINT_GE:
310 indexMap_->Intersect(v, rowCount_);
311 break;
312 case SQLITE_INDEX_CONSTRAINT_GT:
313 v++;
314 indexMap_->Intersect(v, rowCount_);
315 break;
316 case SQLITE_INDEX_CONSTRAINT_LE:
317 v++;
318 indexMap_->Intersect(0, v);
319 break;
320 case SQLITE_INDEX_CONSTRAINT_LT:
321 indexMap_->Intersect(0, v);
322 break;
323 default:
324 // can't filter, all rows
325 break;
326 }
327 }
328
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)329 void TableBase::EstimateFilterCost(FilterConstraints& fc, EstimatedIndexInfo& ei)
330 {
331 constexpr double filterBaseCost = 1000.0; // set-up and tear-down
332 constexpr double indexCost = 2.0;
333 ei.estimatedCost = filterBaseCost;
334 auto rowCount = GetSize();
335 if (rowCount > -1) {
336 if (rowCount == 0 || rowCount == 1) {
337 ei.estimatedRows = rowCount;
338 ei.estimatedCost += indexCost * rowCount;
339 return;
340 }
341 double filterCost = 0.0;
342 auto constraints = fc.GetConstraints();
343 if (constraints.empty()) { // scan all rows
344 filterCost = rowCount;
345 } else {
346 for (int32_t i = 0; i < static_cast<int32_t>(constraints.size()); i++) {
347 if (rowCount <= 1) {
348 // only one row or nothing, needn't filter by constraint
349 filterCost += rowCount;
350 break;
351 }
352 FilterByConstraint(fc, filterCost, rowCount, i);
353 }
354 }
355
356 ei.estimatedCost += filterCost;
357 ei.estimatedRows = rowCount;
358 ei.estimatedCost += rowCount * indexCost;
359 ei.isOrdered = true;
360
361 GetOrbyes(fc, ei);
362 } else {
363 return;
364 }
365 }
366
367 } // namespace TraceStreamer
368 } // namespace SysTuning
369