1 | #ifndef NODE_SQLITE3_SRC_STATEMENT_H
|
2 | #define NODE_SQLITE3_SRC_STATEMENT_H
|
3 |
|
4 | #include <cstdlib>
|
5 | #include <cstring>
|
6 | #include <string>
|
7 | #include <queue>
|
8 | #include <vector>
|
9 | #include <sqlite3.h>
|
10 | #include <napi.h>
|
11 | #include <uv.h>
|
12 |
|
13 | #include "database.h"
|
14 | #include "threading.h"
|
15 |
|
16 | using namespace Napi;
|
17 |
|
18 | namespace node_sqlite3 {
|
19 |
|
20 | namespace Values {
|
21 | struct Field {
|
22 | inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
|
23 | type(_type), index(_index) {}
|
24 | inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
|
25 | type(_type), index(0), name(_name) {}
|
26 |
|
27 | unsigned short type;
|
28 | unsigned short index;
|
29 | std::string name;
|
30 |
|
31 | virtual ~Field() = default;
|
32 | };
|
33 |
|
34 | struct Integer : Field {
|
35 | template <class T> inline Integer(T _name, int64_t val) :
|
36 | Field(_name, SQLITE_INTEGER), value(val) {}
|
37 | int64_t value;
|
38 | virtual ~Integer() override = default;
|
39 | };
|
40 |
|
41 | struct Float : Field {
|
42 | template <class T> inline Float(T _name, double val) :
|
43 | Field(_name, SQLITE_FLOAT), value(val) {}
|
44 | double value;
|
45 | virtual ~Float() override = default;
|
46 | };
|
47 |
|
48 | struct Text : Field {
|
49 | template <class T> inline Text(T _name, size_t len, const char* val) :
|
50 | Field(_name, SQLITE_TEXT), value(val, len) {}
|
51 | std::string value;
|
52 | virtual ~Text() override = default;
|
53 | };
|
54 |
|
55 | struct Blob : Field {
|
56 | template <class T> inline Blob(T _name, size_t len, const void* val) :
|
57 | Field(_name, SQLITE_BLOB), length(len) {
|
58 | value = new char[len];
|
59 | assert(value != nullptr);
|
60 | memcpy(value, val, len);
|
61 | }
|
62 | inline virtual ~Blob() override {
|
63 | delete[] value;
|
64 | }
|
65 | int length;
|
66 | char* value;
|
67 | };
|
68 |
|
69 | typedef Field Null;
|
70 | }
|
71 |
|
72 | typedef std::vector<std::unique_ptr<Values::Field> > Row;
|
73 | typedef std::vector<std::unique_ptr<Row> > Rows;
|
74 | typedef Row Parameters;
|
75 |
|
76 |
|
77 |
|
78 | class Statement : public Napi::ObjectWrap<Statement> {
|
79 | public:
|
80 | static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
81 | static Napi::Value New(const Napi::CallbackInfo& info);
|
82 |
|
83 | struct Baton {
|
84 | napi_async_work request = NULL;
|
85 | Statement* stmt;
|
86 | Napi::FunctionReference callback;
|
87 | Parameters parameters;
|
88 |
|
89 | Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) {
|
90 | stmt->Ref();
|
91 | callback.Reset(cb_, 1);
|
92 | }
|
93 | virtual ~Baton() {
|
94 | parameters.clear();
|
95 | if (request) napi_delete_async_work(stmt->Env(), request);
|
96 | stmt->Unref();
|
97 | callback.Reset();
|
98 | }
|
99 | };
|
100 |
|
101 | struct RowBaton : Baton {
|
102 | RowBaton(Statement* stmt_, Napi::Function cb_) :
|
103 | Baton(stmt_, cb_) {}
|
104 | Row row;
|
105 | virtual ~RowBaton() override = default;
|
106 | };
|
107 |
|
108 | struct RunBaton : Baton {
|
109 | RunBaton(Statement* stmt_, Napi::Function cb_) :
|
110 | Baton(stmt_, cb_), inserted_id(0), changes(0) {}
|
111 | sqlite3_int64 inserted_id;
|
112 | int changes;
|
113 | virtual ~RunBaton() override = default;
|
114 | };
|
115 |
|
116 | struct RowsBaton : Baton {
|
117 | RowsBaton(Statement* stmt_, Napi::Function cb_) :
|
118 | Baton(stmt_, cb_) {}
|
119 | Rows rows;
|
120 | virtual ~RowsBaton() override = default;
|
121 | };
|
122 |
|
123 | struct Async;
|
124 |
|
125 | struct EachBaton : Baton {
|
126 | Napi::FunctionReference completed;
|
127 | Async* async;
|
128 |
|
129 | EachBaton(Statement* stmt_, Napi::Function cb_) :
|
130 | Baton(stmt_, cb_) {}
|
131 | virtual ~EachBaton() override {
|
132 | completed.Reset();
|
133 | }
|
134 | };
|
135 |
|
136 | struct PrepareBaton : Database::Baton {
|
137 | Statement* stmt;
|
138 | std::string sql;
|
139 | PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) :
|
140 | Baton(db_, cb_), stmt(stmt_) {
|
141 | stmt->Ref();
|
142 | }
|
143 | virtual ~PrepareBaton() override {
|
144 | stmt->Unref();
|
145 | if (!db->IsOpen() && db->IsLocked()) {
|
146 |
|
147 |
|
148 | stmt->Finalize_();
|
149 | }
|
150 | }
|
151 | };
|
152 |
|
153 | typedef void (*Work_Callback)(Baton* baton);
|
154 |
|
155 | struct Call {
|
156 | Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {};
|
157 | Work_Callback callback;
|
158 | Baton* baton;
|
159 | };
|
160 |
|
161 | struct Async {
|
162 | uv_async_t watcher;
|
163 | Statement* stmt;
|
164 | Rows data;
|
165 | NODE_SQLITE3_MUTEX_t;
|
166 | bool completed;
|
167 | int retrieved;
|
168 |
|
169 |
|
170 |
|
171 | Napi::FunctionReference item_cb;
|
172 | Napi::FunctionReference completed_cb;
|
173 |
|
174 | Async(Statement* st, uv_async_cb async_cb) :
|
175 | stmt(st), completed(false), retrieved(0) {
|
176 | watcher.data = this;
|
177 | NODE_SQLITE3_MUTEX_INIT
|
178 | stmt->Ref();
|
179 | uv_loop_t *loop;
|
180 | napi_get_uv_event_loop(stmt->Env(), &loop);
|
181 | uv_async_init(loop, &watcher, async_cb);
|
182 | }
|
183 |
|
184 | ~Async() {
|
185 | stmt->Unref();
|
186 | item_cb.Reset();
|
187 | completed_cb.Reset();
|
188 | NODE_SQLITE3_MUTEX_DESTROY
|
189 | }
|
190 | };
|
191 |
|
192 | Statement(const Napi::CallbackInfo& info);
|
193 |
|
194 | ~Statement() {
|
195 | if (!finalized) Finalize_();
|
196 | }
|
197 |
|
198 | WORK_DEFINITION(Bind)
|
199 | WORK_DEFINITION(Get)
|
200 | WORK_DEFINITION(Run)
|
201 | WORK_DEFINITION(All)
|
202 | WORK_DEFINITION(Each)
|
203 | WORK_DEFINITION(Reset)
|
204 |
|
205 | Napi::Value Finalize_(const Napi::CallbackInfo& info);
|
206 |
|
207 | protected:
|
208 | static void Work_BeginPrepare(Database::Baton* baton);
|
209 | static void Work_Prepare(napi_env env, void* data);
|
210 | static void Work_AfterPrepare(napi_env env, napi_status status, void* data);
|
211 |
|
212 | static void AsyncEach(uv_async_t* handle);
|
213 | static void CloseCallback(uv_handle_t* handle);
|
214 |
|
215 | static void Finalize_(Baton* baton);
|
216 | void Finalize_();
|
217 |
|
218 | template <class T> inline std::unique_ptr<Values::Field> BindParameter(const Napi::Value source, T pos);
|
219 | template <class T> T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1);
|
220 | bool Bind(const Parameters ¶meters);
|
221 |
|
222 | static void GetRow(Row* row, sqlite3_stmt* stmt);
|
223 | static Napi::Value RowToJS(Napi::Env env, Row* row);
|
224 | void Schedule(Work_Callback callback, Baton* baton);
|
225 | void Process();
|
226 | void CleanQueue();
|
227 | template <class T> static void Error(T* baton);
|
228 |
|
229 | protected:
|
230 | Database* db;
|
231 |
|
232 | sqlite3_stmt* _handle = NULL;
|
233 | int status = SQLITE_OK;
|
234 | bool prepared = false;
|
235 | bool locked = true;
|
236 | bool finalized = false;
|
237 |
|
238 | std::queue<Call*> queue;
|
239 | std::string message;
|
240 | };
|
241 |
|
242 | }
|
243 |
|
244 | #endif
|