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 | |