// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef NET_EXTRAS_SQLITE_SQLITE_PERSISTENT_STORE_BACKEND_BASE_H_ #define NET_EXTRAS_SQLITE_SQLITE_PERSISTENT_STORE_BACKEND_BASE_H_ #include #include #include "base/files/file_path.h" #include "base/functional/callback.h" #include "base/functional/callback_forward.h" #include "base/memory/ref_counted.h" #include "base/thread_annotations.h" #include "sql/meta_table.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace base { class Location; class SequencedTaskRunner; } // namespace base namespace sql { class Database; class Statement; } // namespace sql namespace net { // This class handles the initialization and closing of a SQLite database. It // is designed to be shared between a client thread and a background task // runner. // // Subclasses will want to have: // - methods to load the data from the database, which should call // InitializeDatabase() from the background thread to ensure the database has // been initialized before attempting to load data, // - overridden DoMigrateDatabaseSchema() and CreateDatabaseSchema(), // which will be called in the course of initializing the database, // - optionally overridden DoInitializeDatabase() which performs any other // initialization tasks, // - a way to keep track of pending operations in order to commit them // by invoking Commit() on the background thread, e.g. when a certain batch // size is reached or a certain amount of time has passed, // - overridden DoCommit() to actually handle the logic of committing // pending operations to the database, // - optionally overridden Record*() to record the appropriate metrics. class SQLitePersistentStoreBackendBase : public base::RefCountedThreadSafe { public: SQLitePersistentStoreBackendBase(const SQLitePersistentStoreBackendBase&) = delete; SQLitePersistentStoreBackendBase& operator=( const SQLitePersistentStoreBackendBase&) = delete; // Posts a task to flush pending operations to the database in the background. // |callback| is run in the foreground when it is done. void Flush(base::OnceClosure callback); // Commit any pending operations and close the database. This must be called // before the object is destroyed. void Close(); // Set the callback that will be run at the beginning of Commit. void SetBeforeCommitCallback(base::RepeatingClosure callback); protected: friend class base::RefCountedThreadSafe; // |current_version_number| and |compatible_version_number| must be greater // than 0, as per //sql/meta_table.h. |background_task_runner| should be // non-null. If |enable_exclusive_access| is true then the sqlite3 database // will be opened with exclusive flag. SQLitePersistentStoreBackendBase( const base::FilePath& path, std::string histogram_tag, const int current_version_number, const int compatible_version_number, scoped_refptr background_task_runner, scoped_refptr client_task_runner, bool enable_exclusive_access); virtual ~SQLitePersistentStoreBackendBase(); // Initialize the database. Should be called on background thread. Call this // from a subclass' Load method(s) to ensure the database is initialized // before loading data from it. bool InitializeDatabase(); // Record metrics on various errors/events that may occur during // initialization. virtual void RecordOpenDBProblem() {} virtual void RecordDBMigrationProblem() {} // Embedder-specific database upgrade statements. Returns the version number // that the database ends up at, or returns nullopt on error. This is called // during MigrateDatabaseSchema() which is called during InitializeDatabase(), // and returning |absl::nullopt| will cause the initialization process to fail // and stop. virtual absl::optional DoMigrateDatabaseSchema() = 0; // Initializes the desired table(s) of the database, e.g. by creating them or // checking that they already exist. Returns whether the tables exist. // |db()| should not be null when this is called. This is called during // InitializeDatabase(), and returning false will cause the initialization // process to fail and stop. virtual bool CreateDatabaseSchema() = 0; // Embedder-specific database initialization tasks. Returns whether they were // successful. |db()| should not be null when this is called. // This is called during InitializeDatabase(), and returning false will cause // the initialization process to fail and stop. The default implementation // just returns true. virtual bool DoInitializeDatabase(); // Raze and reset the metatable and database, e.g. if errors are encountered // in initialization. void Reset(); // Commit pending operations to the database. First runs // |before_commit_callback_|. Should be called on the background thread. void Commit(); // Embedder-specific logic to commit pending operations. (This base class has // no notion of pending operations or what to do with them.) virtual void DoCommit() = 0; // Post a task to the background task runner. void PostBackgroundTask(const base::Location& origin, base::OnceClosure task); // Post a task to the client task runner. void PostClientTask(const base::Location& origin, base::OnceClosure task); sql::Database* db() { return db_.get(); } sql::MetaTable* meta_table() { return &meta_table_; } base::SequencedTaskRunner* background_task_runner() { return background_task_runner_.get(); } base::SequencedTaskRunner* client_task_runner() { return client_task_runner_.get(); } private: // Ensures that the database is at the current version, upgrading if // necessary. Returns whether it was successful. bool MigrateDatabaseSchema(); // Flushes (commits pending operations) on the background runner, and invokes // |callback| on the client thread when done. void FlushAndNotifyInBackground(base::OnceClosure callback); // Close the database on the background runner. void DoCloseInBackground(); // Error-handling callback. On errors, the error number (and statement, if // available) will be passed to the callback. // Sets |corruption_detected_| and posts a task to the background runner to // kill the database. void DatabaseErrorCallback(int error, sql::Statement* stmt); // Kills the database in the case of a catastropic error. void KillDatabase(); // The file path where the database is stored. const base::FilePath path_; std::unique_ptr db_; sql::MetaTable meta_table_; // The identifying prefix for metrics. const std::string histogram_tag_; // Whether the database has been initialized. bool initialized_ = false; // Whether the KillDatabase callback has been scheduled. bool corruption_detected_ = false; // Current version number of the database. Must be greater than 0. const int current_version_number_; // The lowest version of the code that the database can be read by. Must be // greater than 0. const int compatible_version_number_; const scoped_refptr background_task_runner_; const scoped_refptr client_task_runner_; // If true, then sqlite will be requested to open the file with exclusive // access. const bool enable_exclusive_access_; // Callback to be run before Commit. base::RepeatingClosure before_commit_callback_ GUARDED_BY(before_commit_callback_lock_); // Guards |before_commit_callback_|. base::Lock before_commit_callback_lock_; }; } // namespace net #endif // NET_EXTRAS_SQLITE_SQLITE_PERSISTENT_STORE_BACKEND_BASE_H_