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:
Exception(int err,const char * msg)48     Exception(int err, const char* msg)
49         : std::runtime_error(msg), code(static_cast<ResultCode>(err)) {
50     }
Exception(ResultCode err,const char * msg)51     Exception(ResultCode err, const char* msg)
52         : std::runtime_error(msg), code(err) {
53     }
Exception(int err,const std::string & msg)54     Exception(int err, const std::string& msg)
55         : std::runtime_error(msg), code(static_cast<ResultCode>(err)) {
56     }
Exception(ResultCode err,const std::string & msg)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