| 1 | #pragma once |
| 2 | |
| 3 | #include <string> |
| 4 | #include <vector> |
| 5 | #include <stdexcept> |
| 6 | #include <chrono> |
| 7 | #include <memory> |
| 8 | #include <mapbox/variant.hpp> |
| 9 | |
| 10 | namespace mapbox { |
| 11 | namespace sqlite { |
| 12 | |
| 13 | enum OpenFlag : int { |
| 14 | ReadOnly = 0b001, |
| 15 | ReadWriteCreate = 0b110, |
| 16 | }; |
| 17 | |
| 18 | enum class ResultCode : int { |
| 19 | OK = 0, |
| 20 | Error = 1, |
| 21 | Internal = 2, |
| 22 | Perm = 3, |
| 23 | Abort = 4, |
| 24 | Busy = 5, |
| 25 | Locked = 6, |
| 26 | NoMem = 7, |
| 27 | ReadOnly = 8, |
| 28 | Interrupt = 9, |
| 29 | IOErr = 10, |
| 30 | Corrupt = 11, |
| 31 | NotFound = 12, |
| 32 | Full = 13, |
| 33 | CantOpen = 14, |
| 34 | Protocol = 15, |
| 35 | Schema = 17, |
| 36 | TooBig = 18, |
| 37 | Constraint = 19, |
| 38 | Mismatch = 20, |
| 39 | Misuse = 21, |
| 40 | NoLFS = 22, |
| 41 | Auth = 23, |
| 42 | Range = 25, |
| 43 | NotADB = 26 |
| 44 | }; |
| 45 | |
| 46 | class Exception : public std::runtime_error { |
| 47 | public: |
| 48 | Exception(int err, const char* msg) |
| 49 | : std::runtime_error(msg), code(static_cast<ResultCode>(err)) { |
| 50 | } |
| 51 | Exception(ResultCode err, const char* msg) |
| 52 | : std::runtime_error(msg), code(err) { |
| 53 | } |
| 54 | Exception(int err, const std::string& msg) |
| 55 | : std::runtime_error(msg), code(static_cast<ResultCode>(err)) { |
| 56 | } |
| 57 | Exception(ResultCode err, const std::string& msg) |
| 58 | : std::runtime_error(msg), code(err) { |
| 59 | } |
| 60 | const ResultCode code = ResultCode::OK; |
| 61 | }; |
| 62 | |
| 63 | class DatabaseImpl; |
| 64 | class Statement; |
| 65 | class StatementImpl; |
| 66 | class Query; |
| 67 | class Transaction; |
| 68 | |
| 69 | class Database { |
| 70 | private: |
| 71 | Database(std::unique_ptr<DatabaseImpl>); |
| 72 | Database(const Database &) = delete; |
| 73 | Database &operator=(const Database &) = delete; |
| 74 | |
| 75 | public: |
| 76 | static mapbox::util::variant<Database, Exception> tryOpen(const std::string &filename, int flags = 0); |
| 77 | static Database open(const std::string &filename, int flags = 0); |
| 78 | |
| 79 | Database(Database &&); |
| 80 | ~Database(); |
| 81 | Database &operator=(Database &&); |
| 82 | |
| 83 | void setBusyTimeout(std::chrono::milliseconds); |
| 84 | void exec(const std::string &sql); |
| 85 | |
| 86 | private: |
| 87 | std::unique_ptr<DatabaseImpl> impl; |
| 88 | |
| 89 | friend class Statement; |
| 90 | friend class Transaction; |
| 91 | }; |
| 92 | |
| 93 | // A Statement object represents a prepared statement that can be run repeatedly run with a Query object. |
| 94 | class Statement { |
| 95 | public: |
| 96 | Statement(Database& db, const char* sql); |
| 97 | Statement(const Statement&) = delete; |
| 98 | Statement(Statement&&) = delete; |
| 99 | Statement& operator=(const Statement&) = delete; |
| 100 | Statement& operator=(Statement&&) = delete; |
| 101 | ~Statement(); |
| 102 | |
| 103 | friend class Query; |
| 104 | |
| 105 | private: |
| 106 | std::unique_ptr<StatementImpl> impl; |
| 107 | |
| 108 | #ifndef NDEBUG |
| 109 | // This flag stores whether there exists a Query object that uses this prepared statement. |
| 110 | // There may only be one Query object at a time. Statement objects must outlive Query objects. |
| 111 | // While a Query object exists, a Statement object may not be moved or deleted. |
| 112 | bool used = false; |
| 113 | #endif |
| 114 | }; |
| 115 | |
| 116 | // A Query object is used to run a database query with a prepared statement (stored in a Statement |
| 117 | // object). There may only exist one Query object per Statement object. Query objects are designed |
| 118 | // to be constructed and destroyed frequently. |
| 119 | class Query { |
| 120 | public: |
| 121 | Query(Statement&); |
| 122 | Query(const Query&) = delete; |
| 123 | Query(Query&&) = delete; |
| 124 | Query& operator=(const Query&) = delete; |
| 125 | Query& operator=(Query&&) = delete; |
| 126 | ~Query(); |
| 127 | |
| 128 | template <typename T> |
| 129 | void bind(int offset, T value); |
| 130 | |
| 131 | // Text |
| 132 | void bind(int offset, const char*, std::size_t length, bool retain = true); |
| 133 | void bind(int offset, const std::string&, bool retain = true); |
| 134 | |
| 135 | // Blob |
| 136 | void bindBlob(int offset, const void*, std::size_t length, bool retain = true); |
| 137 | void bindBlob(int offset, const std::vector<uint8_t>&, bool retain = true); |
| 138 | |
| 139 | template <typename T> |
| 140 | T get(int offset); |
| 141 | |
| 142 | bool run(); |
| 143 | void reset(); |
| 144 | void clearBindings(); |
| 145 | |
| 146 | int64_t lastInsertRowId() const; |
| 147 | uint64_t changes() const; |
| 148 | |
| 149 | private: |
| 150 | Statement& stmt; |
| 151 | }; |
| 152 | |
| 153 | class Transaction { |
| 154 | private: |
| 155 | Transaction(const Transaction&) = delete; |
| 156 | Transaction(Transaction&&) = delete; |
| 157 | Transaction& operator=(const Transaction&) = delete; |
| 158 | |
| 159 | public: |
| 160 | enum Mode { |
| 161 | Deferred, |
| 162 | Immediate, |
| 163 | Exclusive |
| 164 | }; |
| 165 | |
| 166 | Transaction(Database&, Mode = Deferred); |
| 167 | ~Transaction(); |
| 168 | |
| 169 | void commit(); |
| 170 | void rollback(); |
| 171 | |
| 172 | private: |
| 173 | DatabaseImpl& dbImpl; |
| 174 | bool needRollback = true; |
| 175 | }; |
| 176 | |
| 177 | } |
| 178 | } |
| 179 | |