• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2019 The Android Open Source Project
2 //
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 #ifndef IORAP_SRC_DB_MODELS_H_
16 #define IORAP_SRC_DB_MODELS_H_
17 
18 #include "clean_up.h"
19 #include "file_models.h"
20 
21 #include <android-base/logging.h>
22 #include <utils/String8.h>
23 
24 #include <filesystem>
25 #include <iostream>
26 #include <optional>
27 #include <ostream>
28 #include <string>
29 #include <sstream>
30 #include <type_traits>
31 #include <vector>
32 
33 #include <sqlite3.h>
34 
35 namespace iorap::db {
36 
37 const constexpr int kDbVersion = 2;
38 
39 struct SqliteDbDeleter {
operatorSqliteDbDeleter40   void operator()(sqlite3* db) {
41     if (db != nullptr) {
42       LOG(VERBOSE) << "sqlite3_close for: " << db;
43       sqlite3_close(db);
44     }
45   }
46 };
47 
48 class DbHandle {
49  public:
50   // Take over ownership of sqlite3 db.
DbHandle(sqlite3 * db)51   explicit DbHandle(sqlite3* db)
52     : db_{std::shared_ptr<sqlite3>{db, SqliteDbDeleter{}}},
53       mutex_{std::make_shared<std::mutex>()} {
54   }
55 
get()56   sqlite3* get() {
57     return db_.get();
58   }
59 
60   operator sqlite3*() {
61     return db_.get();
62   }
63 
mutex()64   std::mutex& mutex() {
65     return *mutex_.get();
66   }
67 
68  private:
69   std::shared_ptr<sqlite3> db_;
70   std::shared_ptr<std::mutex> mutex_;
71 };
72 
73 class ScopedLockDb {
74  public:
ScopedLockDb(std::mutex & mutex)75   ScopedLockDb(std::mutex& mutex) : mutex(mutex) {
76     mutex.lock();
77   }
78 
ScopedLockDb(DbHandle & handle)79   ScopedLockDb(DbHandle& handle) : ScopedLockDb(handle.mutex()) {
80   }
81 
~ScopedLockDb()82   ~ScopedLockDb() {
83     mutex.unlock();
84   }
85  private:
86   std::mutex& mutex;
87 };
88 
89 class DbStatement {
90  public:
91   template <typename ... Args>
Prepare(DbHandle db,const std::string & sql,Args &&...args)92   static DbStatement Prepare(DbHandle db, const std::string& sql, Args&&... args) {
93     return Prepare(db, sql.c_str(), std::forward<Args>(args)...);
94   }
95 
96   template <typename ... Args>
Prepare(DbHandle db,const char * sql,Args &&...args)97   static DbStatement Prepare(DbHandle db, const char* sql, Args&&... args) {
98     DCHECK(db.get() != nullptr);
99     DCHECK(sql != nullptr);
100 
101     // LOG(VERBOSE) << "Prepare DB=" << db.get();
102 
103     sqlite3_stmt* stmt = nullptr;
104     int rc = sqlite3_prepare_v2(db.get(), sql, -1, /*out*/&stmt, nullptr);
105 
106     DbStatement db_stmt{db, stmt};
107     DCHECK(db_stmt.CheckOk(rc)) << sql;
108     db_stmt.BindAll(std::forward<Args>(args)...);
109 
110     return db_stmt;
111   }
112 
DbStatement(DbHandle db,sqlite3_stmt * stmt)113   DbStatement(DbHandle db, sqlite3_stmt* stmt) : db_(db), stmt_(stmt) {
114   }
115 
get()116   sqlite3_stmt* get() {
117     return stmt_;
118   }
119 
db()120   DbHandle db() {
121     return db_;
122   }
123 
124   // Successive BindAll calls *do not* start back at the 0th bind position.
125   template <typename T, typename ... Args>
BindAll(T && arg,Args &&...args)126   void BindAll(T&& arg, Args&&... args) {
127     Bind(std::forward<T>(arg));
128     BindAll(std::forward<Args>(args)...);
129   }
130 
BindAll()131   void BindAll() {}
132 
133   template <typename T>
Bind(const std::optional<T> & value)134   void Bind(const std::optional<T>& value) {
135     if (value) {
136       Bind(*value);
137     } else {
138       BindNull();
139     }
140   }
141 
Bind(bool value)142   void Bind(bool value) {
143     CheckOk(sqlite3_bind_int(stmt_, bind_counter_++, value));
144   }
145 
Bind(int value)146   void Bind(int value) {
147     CheckOk(sqlite3_bind_int(stmt_, bind_counter_++, value));
148   }
149 
Bind(uint64_t value)150   void Bind(uint64_t value) {
151     CheckOk(sqlite3_bind_int64(stmt_, bind_counter_++, static_cast<int64_t>(value)));
152   }
153 
Bind(const char * value)154   void Bind(const char* value) {
155     if (value != nullptr) {
156       //sqlite3_bind_text(stmt_, /*index*/bind_counter_++, value, -1, SQLITE_STATIC);
157       CheckOk(sqlite3_bind_text(stmt_, /*index*/bind_counter_++, value, -1, SQLITE_TRANSIENT));
158     } else {
159       BindNull();
160     }
161   }
162 
Bind(const std::string & value)163   void Bind(const std::string& value) {
164     Bind(value.c_str());
165   }
166 
167   template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
Bind(E value)168   void Bind(E value) {
169     Bind(static_cast<std::underlying_type_t<E>>(value));
170   }
171 
BindNull()172   void BindNull() {
173     CheckOk(sqlite3_bind_null(stmt_, bind_counter_++));
174   }
175 
Step()176   int Step() {
177     ++step_counter_;
178     return sqlite3_step(stmt_);
179   }
180 
Step(int expected)181   bool Step(int expected) {
182     int rc = Step();
183     if (rc != expected) {
184       LOG(ERROR) << "SQLite error: " << sqlite3_errmsg(db_.get());
185       return false;
186     }
187     return true;
188   }
189 
190   // Successive ColumnAll calls start at the 0th column again.
191   template <typename T, typename ... Args>
ColumnAll(T & first,Args &...rest)192   void ColumnAll(T& first, Args&... rest) {
193     Column(first);
194     ColumnAll(rest...);
195     // Reset column counter back to 0
196     column_counter_ = 0;
197   }
198 
ColumnAll()199   void ColumnAll() {}
200 
201   template <typename T>
Column(std::optional<T> & value)202   void Column(std::optional<T>& value) {
203     T tmp;
204     Column(/*out*/tmp);
205 
206     if (!tmp) {  // disambiguate 0 and NULL
207       const unsigned char* text = sqlite3_column_text(stmt_, column_counter_ - 1);
208       if (text == nullptr) {
209         value = std::nullopt;
210         return;
211       }
212     }
213     value = std::move(tmp);
214   }
215 
216   template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
Column(E & value)217   void Column(E& value) {
218     std::underlying_type_t<E> tmp;
219     Column(/*out*/tmp);
220     value = static_cast<E>(tmp);
221   }
222 
Column(bool & value)223   void Column(bool& value) {
224     value = sqlite3_column_int(stmt_, column_counter_++);
225   }
226 
Column(int & value)227   void Column(int& value) {
228     value = sqlite3_column_int(stmt_, column_counter_++);
229   }
230 
Column(uint64_t & value)231   void Column(uint64_t& value) {
232     value = static_cast<uint64_t>(sqlite3_column_int64(stmt_, column_counter_++));
233   }
234 
Column(std::string & value)235   void Column(std::string& value) {
236     const unsigned char* text = sqlite3_column_text(stmt_, column_counter_++);
237 
238     DCHECK(text != nullptr) << "Column should be marked NOT NULL, otherwise use optional<string>";
239     if (text == nullptr) {
240       LOG(ERROR) << "Got NULL back for column " << column_counter_-1
241                  << "; is this column marked NOT NULL?";
242       value = "(((null)))";  // Don't segfault, keep going.
243       return;
244     }
245 
246     value = std::string{reinterpret_cast<const char*>(text)};
247   }
248 
ExpandedSql()249   const char* ExpandedSql() {
250     char* p = sqlite3_expanded_sql(stmt_);
251     if (p == nullptr) {
252       return "(nullptr)";
253     }
254     return p;
255   }
256 
Sql()257   const char* Sql() {
258     const char* p = sqlite3_sql(stmt_);
259     if (p == nullptr) {
260       return "(nullptr)";
261     }
262     return p;
263   }
264 
265 
DbStatement(DbStatement && other)266   DbStatement(DbStatement&& other)
267     : db_{other.db_}, stmt_{other.stmt_}, bind_counter_{other.bind_counter_},
268       step_counter_{other.step_counter_} {
269     other.db_ = DbHandle{nullptr};
270     other.stmt_ = nullptr;
271   }
272 
~DbStatement()273   ~DbStatement() {
274     if (stmt_ != nullptr) {
275       DCHECK_GT(step_counter_, 0) << " forgot to call Step()?";
276       sqlite3_finalize(stmt_);
277     }
278   }
279 
280  private:
281   bool CheckOk(int rc, int expected = SQLITE_OK) {
282     if (rc != expected) {
283       LOG(ERROR) << "Got error for SQL query: '" << Sql() << "'"
284                  << ", expanded: '" << ExpandedSql() << "'";
285       LOG(ERROR) << "Failed SQLite api call (" << rc << "): " << sqlite3_errstr(rc);
286     }
287     return rc == expected;
288   }
289 
290   DbHandle db_;
291   sqlite3_stmt* stmt_;
292   int bind_counter_ = 1;
293   int step_counter_ = 0;
294   int column_counter_ = 0;
295 };
296 
297 class DbQueryBuilder {
298  public:
299   // Returns the row ID that was inserted last.
300   template <typename... Args>
Insert(DbHandle db,const std::string & sql,Args &&...args)301   static std::optional<int> Insert(DbHandle db, const std::string& sql, Args&&... args) {
302     ScopedLockDb lock{db};
303 
304     sqlite3_int64 last_rowid = sqlite3_last_insert_rowid(db.get());
305     DbStatement stmt = DbStatement::Prepare(db, sql, std::forward<Args>(args)...);
306 
307     if (!stmt.Step(SQLITE_DONE)) {
308       return std::nullopt;
309     }
310 
311     last_rowid = sqlite3_last_insert_rowid(db.get());
312     DCHECK_GT(last_rowid, 0);
313 
314     return static_cast<int>(last_rowid);
315   }
316 
317   template <typename... Args>
Delete(DbHandle db,const std::string & sql,Args &&...args)318   static bool Delete(DbHandle db, const std::string& sql, Args&&... args) {
319     return ExecuteOnce(db, sql, std::forward<Args>(args)...);
320   }
321 
322   template <typename... Args>
Update(DbHandle db,const std::string & sql,Args &&...args)323   static bool Update(DbHandle db, const std::string& sql, Args&&... args) {
324     return ExecuteOnce(db, sql, std::forward<Args>(args)...);
325   }
326 
327   template <typename... Args>
ExecuteOnce(DbHandle db,const std::string & sql,Args &&...args)328   static bool ExecuteOnce(DbHandle db, const std::string& sql, Args&&... args) {
329     ScopedLockDb lock{db};
330 
331     DbStatement stmt = DbStatement::Prepare(db, sql, std::forward<Args>(args)...);
332 
333     if (!stmt.Step(SQLITE_DONE)) {
334       return false;
335     }
336 
337     return true;
338   }
339 
340   template <typename... Args>
SelectOnce(DbStatement & stmt,Args &...args)341   static bool SelectOnce(DbStatement& stmt, Args&... args) {
342     int rc = stmt.Step();
343     switch (rc) {
344       case SQLITE_ROW:
345         stmt.ColumnAll(/*out*/args...);
346         return true;
347       case SQLITE_DONE:
348         return false;
349       default:
350         LOG(ERROR) << "Failed to step (" << rc << "): " << sqlite3_errmsg(stmt.db());
351         return false;
352     }
353   }
354 };
355 
356 class Model {
357  public:
db()358   DbHandle db() const {
359     return db_;
360   }
361 
Model(DbHandle db)362   Model(DbHandle db) : db_{db} {
363   }
364 
365  private:
366   DbHandle db_;
367 };
368 
369 class SchemaModel : public Model {
370  public:
GetOrCreate(std::string location)371   static SchemaModel GetOrCreate(std::string location) {
372     int rc = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, /*data*/nullptr);
373 
374     if (rc != SQLITE_OK) {
375       LOG(FATAL) << "Failed to configure logging";
376     }
377 
378     sqlite3* db = nullptr;
379     bool is_deprecated = false;
380     if (location != ":memory:") {
381       // Try to open DB if it already exists.
382       rc = sqlite3_open_v2(location.c_str(), /*out*/&db, SQLITE_OPEN_READWRITE, /*vfs*/nullptr);
383 
384       if (rc == SQLITE_OK) {
385         LOG(INFO) << "Opened existing database at '" << location << "'";
386         SchemaModel schema{DbHandle{db}, location};
387         if (schema.Version() == kDbVersion) {
388           return schema;
389         } else {
390           LOG(DEBUG) << "The version is old, reinit the db."
391                      << " old version is "
392                      << schema.Version()
393                      << " and new version is "
394                      << kDbVersion;
395           CleanUpFilesForDb(schema.db());
396           is_deprecated = true;
397        }
398       }
399     }
400 
401     if (is_deprecated) {
402       // Remove the db and recreate it.
403       // TODO: migrate to a newer version without deleting the old one.
404       std::filesystem::remove(location.c_str());
405     }
406 
407     // Create a new DB if one didn't exist already.
408     rc = sqlite3_open(location.c_str(), /*out*/&db);
409 
410     if (rc != SQLITE_OK) {
411       LOG(FATAL) << "Failed to open DB: " << sqlite3_errmsg(db);
412     }
413 
414     SchemaModel schema{DbHandle{db}, location};
415     schema.Reinitialize();
416     // TODO: migrate versions upwards when we rev the schema version
417 
418     int old_version = schema.Version();
419     LOG(VERBOSE) << "Loaded schema version: " << old_version;
420 
421     return schema;
422   }
423 
MarkSingleton()424   void MarkSingleton() {
425     s_singleton_ = db();
426   }
427 
GetSingleton()428   static DbHandle GetSingleton() {
429     DCHECK(s_singleton_.has_value());
430     return *s_singleton_;
431   }
432 
Reinitialize()433   void Reinitialize() {
434     const char* sql_to_initialize = R"SQLC0D3(
435         DROP TABLE IF EXISTS schema_versions;
436         DROP TABLE IF EXISTS packages;
437         DROP TABLE IF EXISTS activities;
438         DROP TABLE IF EXISTS app_launch_histories;
439         DROP TABLE IF EXISTS raw_traces;
440         DROP TABLE IF EXISTS prefetch_files;
441 )SQLC0D3";
442     char* err_msg = nullptr;
443     int rc = sqlite3_exec(db().get(),
444                           sql_to_initialize,
445                           /*callback*/nullptr,
446                           /*arg*/0,
447                           /*out*/&err_msg);
448     if (rc != SQLITE_OK) {
449       LOG(FATAL) << "Failed to drop tables: " << err_msg ? err_msg : "nullptr";
450     }
451 
452     CreateSchema();
453     LOG(INFO) << "Reinitialized database at '" << location_ << "'";
454   }
455 
Version()456   int Version() {
457     std::string query = "SELECT MAX(version) FROM schema_versions;";
458     DbStatement stmt = DbStatement::Prepare(db(), query);
459 
460     int return_value = 0;
461     if (!DbQueryBuilder::SelectOnce(stmt, /*out*/return_value)) {
462       LOG(ERROR) << "Failed to query schema version";
463       return -1;
464     }
465 
466     return return_value;
467   }
468 
469  protected:
SchemaModel(DbHandle db,std::string location)470   SchemaModel(DbHandle db, std::string location) : Model{db}, location_(location) {
471   }
472 
473  private:
474   static std::optional<DbHandle> s_singleton_;
475 
CreateSchema()476   void CreateSchema() {
477     const char* sql_to_initialize = R"SQLC0D3(
478         CREATE TABLE schema_versions(
479             version INTEGER NOT NULL
480         );
481 
482         CREATE TABLE packages(
483             id INTEGER NOT NULL,
484             name TEXT NOT NULL,
485             version INTEGER NOT NULL,
486 
487             PRIMARY KEY(id)
488         );
489 
490         CREATE TABLE activities(
491             id INTEGER NOT NULL,
492             name TEXT NOT NULL,
493             package_id INTEGER NOT NULL,
494 
495             PRIMARY KEY(id),
496             FOREIGN KEY (package_id) REFERENCES packages (id) ON DELETE CASCADE
497         );
498 
499         CREATE TABLE app_launch_histories(
500             id INTEGER NOT NULL PRIMARY KEY,
501             activity_id INTEGER NOT NULL,
502             -- 1:Cold, 2:Warm, 3:Hot
503             temperature INTEGER CHECK (temperature IN (1, 2, 3)) NOT NULL,
504             trace_enabled INTEGER CHECK(trace_enabled in (TRUE, FALSE)) NOT NULL,
505             readahead_enabled INTEGER CHECK(trace_enabled in (TRUE, FALSE)) NOT NULL,
506             -- absolute timestamp since epoch
507             intent_started_ns INTEGER CHECK(intent_started_ns IS NULL or intent_started_ns >= 0),
508             -- absolute timestamp since epoch
509             total_time_ns INTEGER CHECK(total_time_ns IS NULL or total_time_ns >= 0),
510             -- absolute timestamp since epoch
511             report_fully_drawn_ns INTEGER CHECK(report_fully_drawn_ns IS NULL or report_fully_drawn_ns >= 0),
512 
513             FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
514         );
515 
516         CREATE TABLE raw_traces(
517             id INTEGER NOT NULL PRIMARY KEY,
518             history_id INTEGER NOT NULL,
519             file_path TEXT NOT NULL,
520 
521             FOREIGN KEY (history_id) REFERENCES app_launch_histories (id) ON DELETE CASCADE
522         );
523 
524         CREATE TABLE prefetch_files(
525           id INTEGER NOT NULL PRIMARY KEY,
526           activity_id INTEGER NOT NULL,
527           file_path TEXT NOT NULL,
528 
529           FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
530         );
531 )SQLC0D3";
532 
533     char* err_msg = nullptr;
534     int rc = sqlite3_exec(db().get(),
535                           sql_to_initialize,
536                           /*callback*/nullptr,
537                           /*arg*/0,
538                           /*out*/&err_msg);
539 
540     if (rc != SQLITE_OK) {
541       LOG(FATAL) << "Failed to create tables: " << err_msg ? err_msg : "nullptr";
542     }
543 
544     const char* sql_to_insert_schema_version = R"SQLC0D3(
545       INSERT INTO schema_versions VALUES(%d)
546       )SQLC0D3";
547     rc = sqlite3_exec(db().get(),
548                       android::String8::format(sql_to_insert_schema_version,
549                                                kDbVersion),
550                       /*callback*/nullptr,
551                       /*arg*/0,
552                       /*out*/&err_msg);
553 
554     if (rc != SQLITE_OK) {
555       LOG(FATAL) << "Failed to insert the schema version: "
556                  << err_msg ? err_msg : "nullptr";
557     }
558   }
559 
ErrorLogCallback(void * pArg,int iErrCode,const char * zMsg)560   static void ErrorLogCallback(void *pArg, int iErrCode, const char *zMsg) {
561     LOG(ERROR) << "SQLite error (" << iErrCode << "): " << zMsg;
562   }
563 
564   std::string location_;
565 };
566 
567 class PackageModel : public Model {
568  protected:
PackageModel(DbHandle db)569   PackageModel(DbHandle db) : Model{db} {
570   }
571 
572  public:
SelectById(DbHandle db,int id)573   static std::optional<PackageModel> SelectById(DbHandle db, int id) {
574     ScopedLockDb lock{db};
575     int original_id = id;
576 
577     std::string query = "SELECT * FROM packages WHERE id = ?1 LIMIT 1;";
578     DbStatement stmt = DbStatement::Prepare(db, query, id);
579 
580     PackageModel p{db};
581     if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
582       return std::nullopt;
583     }
584 
585     return p;
586   }
587 
SelectByName(DbHandle db,const char * name)588   static std::vector<PackageModel> SelectByName(DbHandle db, const char* name) {
589     ScopedLockDb lock{db};
590 
591     std::string query = "SELECT * FROM packages WHERE name = ?1;";
592     DbStatement stmt = DbStatement::Prepare(db, query, name);
593 
594     std::vector<PackageModel> packages;
595 
596     PackageModel p{db};
597     while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
598       packages.push_back(p);
599     }
600 
601     return packages;
602   }
603 
SelectByNameAndVersion(DbHandle db,const char * name,int version)604   static std::optional<PackageModel> SelectByNameAndVersion(DbHandle db,
605                                                             const char* name,
606                                                             int version) {
607     ScopedLockDb lock{db};
608 
609     std::string query =
610         "SELECT * FROM packages WHERE name = ?1 AND version = ?2 LIMIT 1;";
611     DbStatement stmt = DbStatement::Prepare(db, query, name, version);
612 
613     PackageModel p{db};
614     if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
615       return std::nullopt;
616     }
617 
618     return p;
619   }
620 
SelectAll(DbHandle db)621   static std::vector<PackageModel> SelectAll(DbHandle db) {
622     ScopedLockDb lock{db};
623 
624     std::string query = "SELECT * FROM packages;";
625     DbStatement stmt = DbStatement::Prepare(db, query);
626 
627     std::vector<PackageModel> packages;
628     PackageModel p{db};
629     while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
630       packages.push_back(p);
631     }
632 
633     return packages;
634   }
635 
Insert(DbHandle db,std::string name,int version)636   static std::optional<PackageModel> Insert(DbHandle db,
637                                             std::string name,
638                                             int version) {
639     const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);";
640 
641     std::optional<int> inserted_row_id =
642         DbQueryBuilder::Insert(db, sql, name, version);
643     if (!inserted_row_id) {
644       return std::nullopt;
645     }
646 
647     PackageModel p{db};
648     p.name = name;
649     p.version = version;
650     p.id = *inserted_row_id;
651 
652     return p;
653   }
654 
Delete()655   bool Delete() {
656     const char* sql = "DELETE FROM packages WHERE id = ?";
657 
658     return DbQueryBuilder::Delete(db(), sql, id);
659   }
660 
661   int id;
662   std::string name;
663   int version;
664 };
665 
666 inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
667   os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
668   os << "version=";
669   os << p.version;
670   os << "}";
671   return os;
672 }
673 
674 class ActivityModel : public Model {
675  protected:
ActivityModel(DbHandle db)676   ActivityModel(DbHandle db) : Model{db} {
677   }
678 
679  public:
SelectById(DbHandle db,int id)680   static std::optional<ActivityModel> SelectById(DbHandle db, int id) {
681     ScopedLockDb lock{db};
682     int original_id = id;
683 
684     std::string query = "SELECT * FROM activities WHERE id = ? LIMIT 1;";
685     DbStatement stmt = DbStatement::Prepare(db, query, id);
686 
687     ActivityModel p{db};
688     if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
689       return std::nullopt;
690     }
691 
692     return p;
693   }
694 
SelectByNameAndPackageId(DbHandle db,const char * name,int package_id)695   static std::optional<ActivityModel> SelectByNameAndPackageId(DbHandle db,
696                                                                const char* name,
697                                                                int package_id) {
698     ScopedLockDb lock{db};
699 
700     std::string query = "SELECT * FROM activities WHERE name = ? AND package_id = ? LIMIT 1;";
701     DbStatement stmt = DbStatement::Prepare(db, query, name, package_id);
702 
703     ActivityModel p{db};
704     if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
705       return std::nullopt;
706     }
707 
708     return p;
709   }
710 
SelectByPackageId(DbHandle db,int package_id)711   static std::vector<ActivityModel> SelectByPackageId(DbHandle db,
712                                                       int package_id) {
713     ScopedLockDb lock{db};
714 
715     std::string query = "SELECT * FROM activities WHERE package_id = ?;";
716     DbStatement stmt = DbStatement::Prepare(db, query, package_id);
717 
718     std::vector<ActivityModel> activities;
719     ActivityModel p{db};
720     while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
721       activities.push_back(p);
722     }
723 
724     return activities;
725   }
726 
Insert(DbHandle db,std::string name,int package_id)727   static std::optional<ActivityModel> Insert(DbHandle db,
728                                              std::string name,
729                                              int package_id) {
730     const char* sql = "INSERT INTO activities (name, package_id) VALUES (?1, ?2);";
731 
732     std::optional<int> inserted_row_id =
733         DbQueryBuilder::Insert(db, sql, name, package_id);
734     if (!inserted_row_id) {
735       return std::nullopt;
736     }
737 
738     ActivityModel p{db};
739     p.id = *inserted_row_id;
740     p.name = name;
741     p.package_id = package_id;
742 
743     return p;
744   }
745 
746   // Try to select by package_name+activity_name, otherwise insert into both tables.
747   // Package version is ignored for selects.
SelectOrInsert(DbHandle db,std::string package_name,int package_version,std::string activity_name)748   static std::optional<ActivityModel> SelectOrInsert(
749       DbHandle db,
750       std::string package_name,
751       int package_version,
752       std::string activity_name) {
753     std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db,
754                                                                                package_name.c_str(),
755                                                                                package_version);
756     if (!package) {
757       package = PackageModel::Insert(db, package_name, package_version);
758       DCHECK(package.has_value());
759     }
760 
761     std::optional<ActivityModel> activity =
762         ActivityModel::SelectByNameAndPackageId(db,
763                                                 activity_name.c_str(),
764                                                 package->id);
765     if (!activity) {
766       activity = Insert(db, activity_name, package->id);
767       // XX: should we really return an optional here? This feels like it should never fail.
768     }
769 
770     return activity;
771   }
772 
773   int id;
774   std::string name;
775   int package_id;  // PackageModel::id
776 };
777 
778 inline std::ostream& operator<<(std::ostream& os, const ActivityModel& p) {
779   os << "ActivityModel{id=" << p.id << ",name=" << p.name << ",";
780   os << "package_id=" << p.package_id << "}";
781   return os;
782 }
783 
784 class AppLaunchHistoryModel : public Model {
785  protected:
AppLaunchHistoryModel(DbHandle db)786   AppLaunchHistoryModel(DbHandle db) : Model{db} {
787   }
788 
789  public:
790   enum class Temperature : int32_t {
791     kUninitialized = -1,  // Note: Not a valid SQL value.
792     kCold = 1,
793     kWarm = 2,
794     kHot = 3,
795   };
796 
SelectById(DbHandle db,int id)797   static std::optional<AppLaunchHistoryModel> SelectById(DbHandle db, int id) {
798     ScopedLockDb lock{db};
799     int original_id = id;
800 
801     std::string query = "SELECT * FROM app_launch_histories WHERE id = ? LIMIT 1;";
802     DbStatement stmt = DbStatement::Prepare(db, query, id);
803 
804     AppLaunchHistoryModel p{db};
805     if (!DbQueryBuilder::SelectOnce(stmt,
806                                     p.id,
807                                     p.activity_id,
808                                     p.temperature,
809                                     p.trace_enabled,
810                                     p.readahead_enabled,
811                                     p.intent_started_ns,
812                                     p.total_time_ns,
813                                     p.report_fully_drawn_ns)) {
814       return std::nullopt;
815     }
816 
817     return p;
818   }
819 
820   // Selects the activity history for an activity id.
821   // The requirements are:
822   // * Should be cold run.
823   // * Pefetto trace is enabled.
824   // * intent_start_ns is *NOT* null.
SelectActivityHistoryForCompile(DbHandle db,int activity_id)825   static std::vector<AppLaunchHistoryModel> SelectActivityHistoryForCompile(
826       DbHandle db,
827       int activity_id) {
828     ScopedLockDb lock{db};
829     std::string query = "SELECT * FROM app_launch_histories "
830                         "WHERE activity_id = ?1 AND"
831                         "  temperature = 1 AND"
832                         "  trace_enabled = TRUE AND"
833                         "  intent_started_ns IS NOT NULL;";
834     DbStatement stmt = DbStatement::Prepare(db, query, activity_id);
835     std::vector<AppLaunchHistoryModel> result;
836 
837     AppLaunchHistoryModel p{db};
838     while (DbQueryBuilder::SelectOnce(stmt,
839                                       p.id,
840                                       p.activity_id,
841                                       p.temperature,
842                                       p.trace_enabled,
843                                       p.readahead_enabled,
844                                       p.intent_started_ns,
845                                       p.total_time_ns,
846                                       p.report_fully_drawn_ns)) {
847       result.push_back(p);
848     }
849     return result;
850   }
851 
Insert(DbHandle db,int activity_id,AppLaunchHistoryModel::Temperature temperature,bool trace_enabled,bool readahead_enabled,std::optional<uint64_t> intent_started_ns,std::optional<uint64_t> total_time_ns,std::optional<uint64_t> report_fully_drawn_ns)852   static std::optional<AppLaunchHistoryModel> Insert(DbHandle db,
853                                                      int activity_id,
854                                                      AppLaunchHistoryModel::Temperature temperature,
855                                                      bool trace_enabled,
856                                                      bool readahead_enabled,
857                                                      std::optional<uint64_t> intent_started_ns,
858                                                      std::optional<uint64_t> total_time_ns,
859                                                      std::optional<uint64_t> report_fully_drawn_ns)
860   {
861     const char* sql = "INSERT INTO app_launch_histories (activity_id, temperature, trace_enabled, "
862                                                         "readahead_enabled, intent_started_ns, "
863                                                         "total_time_ns, report_fully_drawn_ns) "
864                       "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);";
865 
866     std::optional<int> inserted_row_id =
867         DbQueryBuilder::Insert(db,
868                                sql,
869                                activity_id,
870                                temperature,
871                                trace_enabled,
872                                readahead_enabled,
873                                intent_started_ns,
874                                total_time_ns,
875                                report_fully_drawn_ns);
876     if (!inserted_row_id) {
877       return std::nullopt;
878     }
879 
880     AppLaunchHistoryModel p{db};
881     p.id = *inserted_row_id;
882     p.activity_id = activity_id;
883     p.temperature = temperature;
884     p.trace_enabled = trace_enabled;
885     p.readahead_enabled = readahead_enabled;
886     p.intent_started_ns = intent_started_ns;
887     p.total_time_ns = total_time_ns;
888     p.report_fully_drawn_ns = report_fully_drawn_ns;
889 
890     return p;
891   }
892 
UpdateReportFullyDrawn(DbHandle db,int history_id,uint64_t report_fully_drawn_ns)893   static bool UpdateReportFullyDrawn(DbHandle db,
894                                      int history_id,
895                                      uint64_t report_fully_drawn_ns)
896   {
897     const char* sql = "UPDATE app_launch_histories "
898                       "SET report_fully_drawn_ns = ?1 "
899                       "WHERE id = ?2;";
900 
901     bool result = DbQueryBuilder::Update(db, sql, report_fully_drawn_ns, history_id);
902 
903     if (!result) {
904       LOG(ERROR)<< "Failed to update history_id:"<< history_id
905                 << ", report_fully_drawn_ns: " << report_fully_drawn_ns;
906     }
907     return result;
908   }
909 
910   int id;
911   int activity_id;  // ActivityModel::id
912   Temperature temperature = Temperature::kUninitialized;
913   bool trace_enabled;
914   bool readahead_enabled;
915   std::optional<uint64_t> intent_started_ns;
916   std::optional<uint64_t> total_time_ns;
917   std::optional<uint64_t> report_fully_drawn_ns;
918 };
919 
920 inline std::ostream& operator<<(std::ostream& os, const AppLaunchHistoryModel& p) {
921   os << "AppLaunchHistoryModel{id=" << p.id << ","
922      << "activity_id=" << p.activity_id << ","
923      << "temperature=" << static_cast<int>(p.temperature) << ","
924      << "trace_enabled=" << p.trace_enabled << ","
925      << "readahead_enabled=" << p.readahead_enabled << ","
926      << "intent_started_ns=";
927   if (p.intent_started_ns) {
928     os << *p.intent_started_ns;
929   } else {
930     os << "(nullopt)";
931   }
932   os << ",";
933   os << "total_time_ns=";
934   if (p.total_time_ns) {
935     os << *p.total_time_ns;
936   } else {
937     os << "(nullopt)";
938   }
939   os << ",";
940   os << "report_fully_drawn_ns=";
941   if (p.report_fully_drawn_ns) {
942     os << *p.report_fully_drawn_ns;
943   } else {
944     os << "(nullopt)";
945   }
946   os << "}";
947   return os;
948 }
949 
950 class RawTraceModel : public Model {
951  protected:
RawTraceModel(DbHandle db)952   RawTraceModel(DbHandle db) : Model{db} {
953   }
954 
955  public:
956 
957   // Return raw_traces, sorted ascending by the id.
SelectByVersionedComponentName(DbHandle db,VersionedComponentName vcn)958   static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db,
959                                                                    VersionedComponentName vcn) {
960     ScopedLockDb lock{db};
961 
962     const char* sql =
963       "SELECT raw_traces.id, raw_traces.history_id, raw_traces.file_path "
964       "FROM raw_traces "
965       "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id "
966       "INNER JOIN activities ON activities.id = app_launch_histories.activity_id "
967       "INNER JOIN packages ON packages.id = activities.package_id "
968       "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"
969       "ORDER BY raw_traces.id ASC";
970 
971     DbStatement stmt = DbStatement::Prepare(db,
972                                             sql,
973                                             vcn.GetPackage(),
974                                             vcn.GetActivity(),
975                                             vcn.GetVersion());
976 
977     std::vector<RawTraceModel> results;
978 
979     RawTraceModel p{db};
980     while (DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
981       results.push_back(p);
982     }
983 
984     return results;
985   }
986 
SelectByHistoryId(DbHandle db,int history_id)987   static std::optional<RawTraceModel> SelectByHistoryId(DbHandle db, int history_id) {
988     ScopedLockDb lock{db};
989 
990     const char* sql =
991       "SELECT id, history_id, file_path "
992       "FROM raw_traces "
993       "WHERE history_id = ?1 "
994       "LIMIT 1;";
995 
996     DbStatement stmt = DbStatement::Prepare(db, sql, history_id);
997 
998     RawTraceModel p{db};
999     if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
1000       return std::nullopt;
1001     }
1002 
1003     return p;
1004   }
1005 
Insert(DbHandle db,int history_id,std::string file_path)1006   static std::optional<RawTraceModel> Insert(DbHandle db,
1007                                              int history_id,
1008                                              std::string file_path) {
1009     const char* sql = "INSERT INTO raw_traces (history_id, file_path) VALUES (?1, ?2);";
1010 
1011     std::optional<int> inserted_row_id =
1012         DbQueryBuilder::Insert(db, sql, history_id, file_path);
1013     if (!inserted_row_id) {
1014       return std::nullopt;
1015     }
1016 
1017     RawTraceModel p{db};
1018     p.id = *inserted_row_id;
1019     p.history_id = history_id;
1020     p.file_path = file_path;
1021 
1022     return p;
1023   }
1024 
Delete()1025   bool Delete() {
1026     const char* sql = "DELETE FROM raw_traces WHERE id = ?";
1027 
1028     return DbQueryBuilder::Delete(db(), sql, id);
1029   }
1030 
1031   int id;
1032   int history_id;
1033   std::string file_path;
1034 };
1035 
1036 inline std::ostream& operator<<(std::ostream& os, const RawTraceModel& p) {
1037   os << "RawTraceModel{id=" << p.id << ",history_id=" << p.history_id << ",";
1038   os << "file_path=" << p.file_path << "}";
1039   return os;
1040 }
1041 
1042 class PrefetchFileModel : public Model {
1043  protected:
PrefetchFileModel(DbHandle db)1044   PrefetchFileModel(DbHandle db) : Model{db} {
1045   }
1046 
1047  public:
SelectByVersionedComponentName(DbHandle db,VersionedComponentName vcn)1048   static std::optional<PrefetchFileModel> SelectByVersionedComponentName(
1049       DbHandle db,
1050       VersionedComponentName vcn) {
1051     ScopedLockDb lock{db};
1052 
1053     const char* sql =
1054       "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path "
1055       "FROM prefetch_files "
1056       "INNER JOIN activities ON activities.id = prefetch_files.activity_id "
1057       "INNER JOIN packages ON packages.id = activities.package_id "
1058       "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?";
1059 
1060     DbStatement stmt = DbStatement::Prepare(db,
1061                                             sql,
1062                                             vcn.GetPackage(),
1063                                             vcn.GetActivity(),
1064                                             vcn.GetVersion());
1065 
1066     PrefetchFileModel p{db};
1067 
1068     if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) {
1069       return std::nullopt;
1070     }
1071 
1072     return p;
1073   }
1074 
SelectAll(DbHandle db)1075   static std::vector<PrefetchFileModel> SelectAll(DbHandle db) {
1076     ScopedLockDb lock{db};
1077 
1078     std::string query =
1079       "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path "
1080       "FROM prefetch_files";
1081     DbStatement stmt = DbStatement::Prepare(db, query);
1082 
1083     std::vector<PrefetchFileModel> prefetch_files;
1084     PrefetchFileModel p{db};
1085     while (DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) {
1086       prefetch_files.push_back(p);
1087     }
1088 
1089     return prefetch_files;
1090   }
1091 
Insert(DbHandle db,int activity_id,std::string file_path)1092   static std::optional<PrefetchFileModel> Insert(DbHandle db,
1093                                                  int activity_id,
1094                                                  std::string file_path) {
1095     const char* sql = "INSERT INTO prefetch_files (activity_id, file_path) VALUES (?1, ?2);";
1096 
1097     std::optional<int> inserted_row_id =
1098         DbQueryBuilder::Insert(db, sql, activity_id, file_path);
1099     if (!inserted_row_id) {
1100       return std::nullopt;
1101     }
1102 
1103     PrefetchFileModel p{db};
1104     p.id = *inserted_row_id;
1105     p.activity_id = activity_id;
1106     p.file_path = file_path;
1107 
1108     return p;
1109   }
1110 
Delete()1111   bool Delete() {
1112     const char* sql = "DELETE FROM prefetch_files WHERE id = ?";
1113 
1114     return DbQueryBuilder::Delete(db(), sql, id);
1115   }
1116 
1117   int id;
1118   int activity_id;
1119   std::string file_path;
1120 };
1121 
1122 inline std::ostream& operator<<(std::ostream& os, const PrefetchFileModel& p) {
1123   os << "PrefetchFileModel{id=" << p.id << ",activity_id=" << p.activity_id << ",";
1124   os << "file_path=" << p.file_path << "}";
1125   return os;
1126 }
1127 
1128 }  // namespace iorap::db
1129 
1130 #endif  // IORAP_SRC_DB_MODELS_H_
1131