| 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; // Isn't deleted when the baton is deleted.
|
|---|
| 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 | // The database handle was closed before the statement could be
|
|---|
| 147 | // prepared.
|
|---|
| 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 | // Store the callbacks here because we don't have
|
|---|
| 170 | // access to the baton in the async callback.
|
|---|
| 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
|
|---|