1 /*
2  * Copyright (C) 2018 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/perfetto_sql/intrinsics/operators/window_operator.h"
18 
19 #include <sqlite3.h>
20 #include <cstdint>
21 #include <memory>
22 
23 #include "perfetto/base/logging.h"
24 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
25 #include "src/trace_processor/sqlite/module_lifecycle_manager.h"
26 #include "src/trace_processor/sqlite/sqlite_utils.h"
27 
28 namespace perfetto::trace_processor {
29 
30 namespace {
31 constexpr char kSchema[] = R"(
32     CREATE TABLE x(
33       rowid BIGINT HIDDEN,
34       quantum BIGINT HIDDEN,
35       window_start BIGINT HIDDEN,
36       window_dur BIGINT HIDDEN,
37       ts BIGINT,
38       dur BIGINT,
39       quantum_ts BIGINT,
40       PRIMARY KEY(rowid)
41     ) WITHOUT ROWID
42   )";
43 }
44 
45 enum Column {
46   kRowId = 0,
47   kQuantum = 1,
48   kWindowStart = 2,
49   kWindowDur = 3,
50   kTs = 4,
51   kDuration = 5,
52   kQuantumTs = 6
53 };
54 
Create(sqlite3 * db,void * raw_ctx,int argc,const char * const * argv,sqlite3_vtab ** vtab,char **)55 int WindowOperatorModule::Create(sqlite3* db,
56                                  void* raw_ctx,
57                                  int argc,
58                                  const char* const* argv,
59                                  sqlite3_vtab** vtab,
60                                  char**) {
61   PERFETTO_CHECK(argc == 3);
62   if (int ret = sqlite3_declare_vtab(db, kSchema); ret != SQLITE_OK) {
63     return ret;
64   }
65   auto* ctx = GetContext(raw_ctx);
66   std::unique_ptr<Vtab> res = std::make_unique<Vtab>();
67   res->state = ctx->manager.OnCreate(argv, std::make_unique<State>());
68   *vtab = res.release();
69   return SQLITE_OK;
70 }
71 
Destroy(sqlite3_vtab * vtab)72 int WindowOperatorModule::Destroy(sqlite3_vtab* vtab) {
73   std::unique_ptr<Vtab> tab(GetVtab(vtab));
74   sqlite::ModuleStateManager<WindowOperatorModule>::OnDestroy(tab->state);
75   return SQLITE_OK;
76 }
77 
Connect(sqlite3 * db,void * raw_ctx,int argc,const char * const * argv,sqlite3_vtab ** vtab,char **)78 int WindowOperatorModule::Connect(sqlite3* db,
79                                   void* raw_ctx,
80                                   int argc,
81                                   const char* const* argv,
82                                   sqlite3_vtab** vtab,
83                                   char**) {
84   PERFETTO_CHECK(argc == 3);
85   if (int ret = sqlite3_declare_vtab(db, kSchema); ret != SQLITE_OK) {
86     return ret;
87   }
88   auto* ctx = GetContext(raw_ctx);
89   std::unique_ptr<Vtab> res = std::make_unique<Vtab>();
90   res->state = ctx->manager.OnConnect(argv);
91   *vtab = res.release();
92   return SQLITE_OK;
93 }
94 
Disconnect(sqlite3_vtab * vtab)95 int WindowOperatorModule::Disconnect(sqlite3_vtab* vtab) {
96   std::unique_ptr<Vtab> tab(GetVtab(vtab));
97   sqlite::ModuleStateManager<WindowOperatorModule>::OnDisconnect(tab->state);
98   return SQLITE_OK;
99 }
100 
BestIndex(sqlite3_vtab *,sqlite3_index_info * info)101 int WindowOperatorModule::BestIndex(sqlite3_vtab*, sqlite3_index_info* info) {
102   info->orderByConsumed = info->nOrderBy == 1 &&
103                           info->aOrderBy[0].iColumn == Column::kTs &&
104                           !info->aOrderBy[0].desc;
105 
106   // Set return first if there is a equals constraint on the row id asking to
107   // return the first row.
108   bool is_row_id_constraint = info->nConstraint == 1 &&
109                               info->aConstraint[0].iColumn == Column::kRowId &&
110                               info->aConstraint[0].usable &&
111                               sqlite::utils::IsOpEq(info->aConstraint[0].op);
112   if (is_row_id_constraint) {
113     info->idxNum = 1;
114     info->aConstraintUsage[0].argvIndex = 1;
115   } else {
116     info->idxNum = 0;
117   }
118   return SQLITE_OK;
119 }
120 
Open(sqlite3_vtab *,sqlite3_vtab_cursor ** cursor)121 int WindowOperatorModule::Open(sqlite3_vtab*, sqlite3_vtab_cursor** cursor) {
122   std::unique_ptr<Cursor> c = std::make_unique<Cursor>();
123   *cursor = c.release();
124   return SQLITE_OK;
125 }
126 
Close(sqlite3_vtab_cursor * cursor)127 int WindowOperatorModule::Close(sqlite3_vtab_cursor* cursor) {
128   delete GetCursor(cursor);
129   return SQLITE_OK;
130 }
131 
Filter(sqlite3_vtab_cursor * cursor,int is_row_id_constraint,const char *,int argc,sqlite3_value ** argv)132 int WindowOperatorModule::Filter(sqlite3_vtab_cursor* cursor,
133                                  int is_row_id_constraint,
134                                  const char*,
135                                  int argc,
136                                  sqlite3_value** argv) {
137   auto* t = GetVtab(cursor->pVtab);
138   auto* c = GetCursor(cursor);
139   auto* s =
140       sqlite::ModuleStateManager<WindowOperatorModule>::GetState(t->state);
141 
142   c->window_end = s->window_start + s->window_dur;
143   c->step_size = s->quantum == 0 ? s->window_dur : s->quantum;
144   c->current_ts = s->window_start;
145 
146   if (is_row_id_constraint) {
147     PERFETTO_CHECK(argc == 1);
148     c->filter_type = sqlite3_value_int(argv[0]) == 0 ? FilterType::kReturnFirst
149                                                      : FilterType::kReturnAll;
150   } else {
151     c->filter_type = FilterType::kReturnAll;
152   }
153   return SQLITE_OK;
154 }
155 
Next(sqlite3_vtab_cursor * cursor)156 int WindowOperatorModule::Next(sqlite3_vtab_cursor* cursor) {
157   auto* c = GetCursor(cursor);
158   switch (c->filter_type) {
159     case FilterType::kReturnFirst:
160       c->current_ts = c->window_end;
161       break;
162     case FilterType::kReturnAll:
163       c->current_ts += c->step_size;
164       c->quantum_ts++;
165       break;
166   }
167   c->row_id++;
168   return SQLITE_OK;
169 }
170 
Eof(sqlite3_vtab_cursor * cursor)171 int WindowOperatorModule::Eof(sqlite3_vtab_cursor* cursor) {
172   auto* c = GetCursor(cursor);
173   return c->current_ts >= c->window_end;
174 }
175 
Column(sqlite3_vtab_cursor * cursor,sqlite3_context * ctx,int N)176 int WindowOperatorModule::Column(sqlite3_vtab_cursor* cursor,
177                                  sqlite3_context* ctx,
178                                  int N) {
179   auto* t = GetVtab(cursor->pVtab);
180   auto* c = GetCursor(cursor);
181   auto* s =
182       sqlite::ModuleStateManager<WindowOperatorModule>::GetState(t->state);
183   switch (N) {
184     case Column::kQuantum: {
185       sqlite::result::Long(ctx, static_cast<sqlite_int64>(s->quantum));
186       break;
187     }
188     case Column::kWindowStart: {
189       sqlite::result::Long(ctx, static_cast<sqlite_int64>(s->window_start));
190       break;
191     }
192     case Column::kWindowDur: {
193       sqlite::result::Long(ctx, static_cast<int>(s->window_dur));
194       break;
195     }
196     case Column::kTs: {
197       sqlite::result::Long(ctx, static_cast<sqlite_int64>(c->current_ts));
198       break;
199     }
200     case Column::kDuration: {
201       sqlite::result::Long(ctx, static_cast<sqlite_int64>(c->step_size));
202       break;
203     }
204     case Column::kQuantumTs: {
205       sqlite::result::Long(ctx, static_cast<sqlite_int64>(c->quantum_ts));
206       break;
207     }
208     case Column::kRowId: {
209       sqlite::result::Long(ctx, static_cast<sqlite_int64>(c->row_id));
210       break;
211     }
212     default: {
213       PERFETTO_FATAL("Unknown column %d", N);
214       break;
215     }
216   }
217   return SQLITE_OK;
218 }
219 
Rowid(sqlite3_vtab_cursor *,sqlite_int64 *)220 int WindowOperatorModule::Rowid(sqlite3_vtab_cursor*, sqlite_int64*) {
221   return SQLITE_ERROR;
222 }
223 
Update(sqlite3_vtab * tab,int argc,sqlite3_value ** argv,sqlite_int64 *)224 int WindowOperatorModule::Update(sqlite3_vtab* tab,
225                                  int argc,
226                                  sqlite3_value** argv,
227                                  sqlite_int64*) {
228   auto* t = GetVtab(tab);
229   auto* s =
230       sqlite::ModuleStateManager<WindowOperatorModule>::GetState(t->state);
231 
232   // We only support updates to ts and dur. Disallow deletes (argc == 1) and
233   // inserts (argv[0] == null).
234   if (argc < 2 || sqlite3_value_type(argv[0]) == SQLITE_NULL) {
235     return sqlite::utils::SetError(
236         tab, "Invalid number/value of arguments when updating window table");
237   }
238 
239   int64_t new_quantum = sqlite3_value_int64(argv[3]);
240   int64_t new_start = sqlite3_value_int64(argv[4]);
241   int64_t new_dur = sqlite3_value_int64(argv[5]);
242   if (new_dur == 0) {
243     return sqlite::utils::SetError(
244         tab, "Cannot set duration of window table to zero.");
245   }
246 
247   s->quantum = new_quantum;
248   s->window_start = new_start;
249   s->window_dur = new_dur;
250 
251   return SQLITE_OK;
252 }
253 
254 }  // namespace perfetto::trace_processor
255