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