xref: /aosp_15_r20/external/executorch/backends/apple/coreml/runtime/kvstore/database.hpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1 //
2 // database.hpp
3 //
4 // Copyright © 2024 Apple Inc. All rights reserved.
5 //
6 // Please refer to the license found in the LICENSE file in the root directory of the source tree.
7 
8 #pragma once
9 
10 #include <bitset>
11 #include <functional>
12 #include <memory>
13 #include <string>
14 #include <system_error>
15 
16 #include <sqlite3.h>
17 
18 #include <statement.hpp>
19 
20 namespace executorchcoreml {
21 namespace sqlite {
22 
23 /// The deleter for a sqlite database, closes the database at the time of deallocation.
24 struct DatabaseDeleter {
operator ()executorchcoreml::sqlite::DatabaseDeleter25     inline void operator()(sqlite3* handle) {
26         if (handle) {
27             sqlite3_close(handle);
28         }
29     }
30 };
31 
32 /// Database represents a sqlite database.
33 class Database {
34 public:
35     /// OpenOptions lists all the options that can be used to open a sqlite database.
36     ///
37     /// The caller is responsible for setting and passing a valid option when opening the database.
38     class OpenOptions {
39     public:
40         /// Corresponds to `SQLITE_OPEN_READONLY` flag, when set the database will be opened in read-only mode.
set_read_only_option(bool enable)41         inline void set_read_only_option(bool enable) noexcept {
42             flags_[0] = enable;
43         }
44 
45         /// Returns `true` if read-only option is enabled otherwise `false`.
is_read_only_option_enabled() const46         inline bool is_read_only_option_enabled() const noexcept {
47             return flags_[0];
48         }
49 
50         /// Corresponds to `SQLITE_OPEN_READWRITE` flag, when set the database will be opened in read and write mode.
set_read_write_option(bool enable)51         inline void set_read_write_option(bool enable) noexcept {
52             flags_[1] = enable;
53         }
54 
55         /// Returns `true` if read and write option is enabled otherwise `false`.
is_read_write_option_enabled() const56         inline bool is_read_write_option_enabled() const noexcept {
57             return flags_[1];
58         }
59 
60         /// Corresponds to `SQLITE_OPEN_CREATE` flag, when set the database will be created if it does not exist.
set_create_option(bool enable)61         inline void set_create_option(bool enable) noexcept {
62             flags_[2] = enable;
63         }
64 
65         /// Returns `true` if create option is enabled otherwise `false`.
is_create_option_enabled() const66         inline bool is_create_option_enabled() const noexcept {
67             return flags_[2];
68         }
69 
70         /// Corresponds to `SQLITE_OPEN_MEMORY` flag, when set the database will be opened as in-memory database.
set_memory_option(bool enable)71         inline void set_memory_option(bool enable) noexcept {
72             flags_[3] = enable;
73         }
74 
75         /// Returns `true` if memory option is enabled otherwise `false`.
is_memory_option_enabled() const76         inline bool is_memory_option_enabled() const noexcept {
77             return flags_[3];
78         }
79 
80         /// Corresponds to `SQLITE_OPEN_NOMUTEX` flag, when set the database connection will use the "multi-thread" threading mode.
set_no_mutex_option(bool enable)81         inline void set_no_mutex_option(bool enable) noexcept {
82             flags_[4] = enable;
83         }
84 
85         /// Returns `true` if no mutex option is enabled otherwise `false`.
is_no_mutex_option_enabled() const86         inline bool is_no_mutex_option_enabled() const noexcept {
87             return flags_[4];
88         }
89 
90         /// Corresponds to `SQLITE_OPEN_FULLMUTEX` flag, when set the database connection will use the "serialized" threading mode.
set_full_mutex_option(bool enable)91         inline void set_full_mutex_option(bool enable) noexcept {
92             flags_[5] = enable;
93         }
94 
95         /// Returns `true` if full mutex option is enabled otherwise `false`.
is_full_mutex_option_enabled() const96         inline bool is_full_mutex_option_enabled() const noexcept {
97             return flags_[5];
98         }
99 
100         /// Corresponds to `SQLITE_OPEN_SHAREDCACHE` flag, when set the database will be opened with shared cache enabled.
set_shared_cache_option(bool enable)101         inline void set_shared_cache_option(bool enable) noexcept {
102             flags_[6] = enable;
103         }
104 
105         /// Returns `true` if shared cache option is enabled otherwise `false`.
is_shared_cache_option_enabled() const106         inline bool is_shared_cache_option_enabled() const noexcept {
107             return flags_[6];
108         }
109 
110         /// Corresponds to `SQLITE_OPEN_URI` flag, when set the filename can be interpreted as a URI.
set_uri_option(bool enable)111         inline void set_uri_option(bool enable) noexcept {
112             flags_[7] = enable;
113         }
114 
115         /// Returns `true` if URI option is enabled otherwise `false`.
is_uri_option_enabled() const116         inline bool is_uri_option_enabled() const noexcept {
117             return flags_[7];
118         }
119 
120         /// Returns the sqlite flags that can be used to open a sqlite database from the set options.
121         int get_sqlite_flags() const noexcept;
122 
123     private:
124         std::bitset<8> flags_;
125     };
126 
127     /// Represents sqlite synchronous flag.
128     enum class SynchronousMode: uint8_t {
129         Extra = 0,
130         Full,
131         Normal,
132         Off,
133     };
134 
135     /// Represents the behavior of a sqlite transaction
136     enum class TransactionBehavior: uint8_t {
137         Deferred = 0,
138         Immediate,
139         Exclusive,
140     };
141 
142     /// Constructs a database from a file path.
Database(const std::string & filePath)143     Database(const std::string& filePath) noexcept
144     :file_path_(filePath)
145     {}
146 
147     Database(Database const&) = delete;
148     Database& operator=(Database const&) = delete;
149 
150     /// Opens a database
151     ///
152     /// @param options The options for opening the database.
153     /// @param mode   The synchronous mode for the database connection.
154     /// @param busy_timeout_ms   The busy timeout interval in milliseconds.
155     /// @param error   On failure, error is populated with the failure reason.
156     /// @retval `true` if the database is opened otherwise `false`.
157     bool open(OpenOptions options,
158               SynchronousMode mode,
159               int busy_timeout_ms,
160               std::error_code& error) noexcept;
161 
162     /// Returns `true` is the database is opened otherwise `false`.
163     bool is_open() const noexcept;
164 
165     /// Check if a table exists with the specified name.
166     ///
167     /// @param tableName The table name.
168     /// @param error   On failure, error is populated with the failure reason.
169     /// @retval `true` if the table exists otherwise `false`.
170     bool table_exists(const std::string& tableName, std::error_code& error) const noexcept;
171 
172     /// Drops a table with the specified name.
173     ///
174     /// @param tableName The table name.
175     /// @param error   On failure, error is populated with the failure reason.
176     /// @retval `true` if the table is dropped otherwise `false`.
177     bool drop_table(const std::string& tableName, std::error_code& error) const noexcept;
178 
179     /// Returns the number of rows in the table.
180     ///
181     /// @param tableName The table name.
182     /// @param error   On failure, error is populated with the failure reason.
183     /// @retval The number of rows in the table.
184     int64_t get_row_count(const std::string& tableName, std::error_code& error) const noexcept;
185 
186     /// Executes the provided statements.
187     ///
188     /// @param statements The statements to execute.
189     /// @param error   On failure, error is populated with the failure reason.
190     /// @retval `true` if the execution succeeded otherwise `false`.
191     bool execute(const std::string& statements, std::error_code& error) const noexcept;
192 
193     /// Returns the number of rows updated by the last statement.
194     int get_updated_row_count() const noexcept;
195 
196     /// Returns the error message of the last failed sqlite call.
197     std::string get_last_error_message() const noexcept;
198 
199     /// Returns the error code of the last failed sqlite call.
200     std::error_code get_last_error_code() const noexcept;
201 
202     /// Returns the extended error code of the last failed sqlite call.
203     std::error_code get_last_extended_error_code() const noexcept;
204 
205     /// Returns the value of the last inserted row id.
206     int64_t get_last_inserted_row_id() const noexcept;
207 
208     /// Returns the file path that was used to create the database.
file_path() const209     std::string_view file_path() const noexcept {
210         return file_path_;
211     }
212 
213     /// Compiles the provided statement and returns it.
214     ///
215     /// @param statement The statement to be compiled.
216     /// @param error   On failure, error is populated with the failure reason.
217     /// @retval The compiled statement.
218     std::unique_ptr<PreparedStatement>
219     prepare_statement(const std::string& statement, std::error_code& error) const noexcept;
220 
221     /// Executes the provided function inside a transaction.
222     ///
223     /// The transaction is committed only if the provided function returns `true` otherwise the transaction is rolled-back.
224     ///
225     /// @param fn The function that will  be executed inside a transaction.
226     /// @param behavior   The transaction behavior.
227     /// @param error   On failure, error is populated with the failure reason.
228     /// @retval `true` if the transaction is committed otherwise `false`.
229     bool transaction(const std::function<bool(void)>& fn,
230                      TransactionBehavior behavior,
231                      std::error_code& error) noexcept;
232 
233     /// Opens an in-memory database.
234     ///
235     /// @param mode The synchronous mode.
236     /// @param busy_timeout_ms   The total busy timeout duration, in milliseconds.
237     /// @param error   On failure, error is populated with the failure reason.
238     /// @retval The opened in-memory database.
239     static std::shared_ptr<Database> make_inmemory(SynchronousMode mode,
240                                                    int busy_timeout_ms,
241                                                    std::error_code& error);
242 
243     /// Creates and opens a  database at the specified path.
244     ///
245     /// @param filePath The file path of the database.
246     /// @param options   The open options.
247     /// @param mode   The synchronous mode.
248     /// @param busy_timeout_ms The total busy timeout duration, in milliseconds.
249     /// @param error   On failure, error is populated with the failure reason.
250     /// @retval The opened database.
251     static std::shared_ptr<Database> make(const std::string& filePath,
252                                           OpenOptions options,
253                                           SynchronousMode mode,
254                                           int busy_timeout_ms,
255                                           std::error_code& error);
256 
257 private:
258     /// Returns the internal sqlite database.
get_underlying_database() const259     inline sqlite3 *get_underlying_database() const noexcept {
260         return sqlite_database_.get();
261     }
262 
263     /// Registers an internal busy handler that keeps attempting to acquire a busy lock until the total specified time has passed.
264     bool set_busy_timeout(int busy_timeout_ms, std::error_code& error) const noexcept;
265 
266     /// Begins an explicit transaction with the specified behavior.
267     bool begin_transaction(TransactionBehavior behavior, std::error_code& error) const noexcept;
268 
269     /// Commits the last open transaction.
270     bool commit_transaction(std::error_code& error) const noexcept;
271 
272     /// Rollbacks the last open transaction.
273     bool rollback_transaction(std::error_code& error) const noexcept;
274 
275     std::string file_path_;
276     std::unique_ptr<sqlite3, DatabaseDeleter> sqlite_database_;
277 };
278 
279 } // namespace sqlite
280 } // namespace executorchcoreml
281