244 lines
6.6 KiB
C++
244 lines
6.6 KiB
C++
#ifndef NODE_SQLITE3_SRC_STATEMENT_H
|
|
#define NODE_SQLITE3_SRC_STATEMENT_H
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <queue>
|
|
#include <vector>
|
|
#include <sqlite3.h>
|
|
#include <napi.h>
|
|
#include <uv.h>
|
|
|
|
#include "database.h"
|
|
#include "threading.h"
|
|
|
|
using namespace Napi;
|
|
|
|
namespace node_sqlite3 {
|
|
|
|
namespace Values {
|
|
struct Field {
|
|
inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
|
|
type(_type), index(_index) {}
|
|
inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
|
|
type(_type), index(0), name(_name) {}
|
|
|
|
unsigned short type;
|
|
unsigned short index;
|
|
std::string name;
|
|
|
|
virtual ~Field() = default;
|
|
};
|
|
|
|
struct Integer : Field {
|
|
template <class T> inline Integer(T _name, int64_t val) :
|
|
Field(_name, SQLITE_INTEGER), value(val) {}
|
|
int64_t value;
|
|
virtual ~Integer() override = default;
|
|
};
|
|
|
|
struct Float : Field {
|
|
template <class T> inline Float(T _name, double val) :
|
|
Field(_name, SQLITE_FLOAT), value(val) {}
|
|
double value;
|
|
virtual ~Float() override = default;
|
|
};
|
|
|
|
struct Text : Field {
|
|
template <class T> inline Text(T _name, size_t len, const char* val) :
|
|
Field(_name, SQLITE_TEXT), value(val, len) {}
|
|
std::string value;
|
|
virtual ~Text() override = default;
|
|
};
|
|
|
|
struct Blob : Field {
|
|
template <class T> inline Blob(T _name, size_t len, const void* val) :
|
|
Field(_name, SQLITE_BLOB), length(len) {
|
|
value = new char[len];
|
|
assert(value != nullptr);
|
|
memcpy(value, val, len);
|
|
}
|
|
inline virtual ~Blob() override {
|
|
delete[] value;
|
|
}
|
|
int length;
|
|
char* value;
|
|
};
|
|
|
|
typedef Field Null;
|
|
}
|
|
|
|
typedef std::vector<std::unique_ptr<Values::Field> > Row;
|
|
typedef std::vector<std::unique_ptr<Row> > Rows;
|
|
typedef Row Parameters;
|
|
|
|
|
|
|
|
class Statement : public Napi::ObjectWrap<Statement> {
|
|
public:
|
|
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
static Napi::Value New(const Napi::CallbackInfo& info);
|
|
|
|
struct Baton {
|
|
napi_async_work request = NULL;
|
|
Statement* stmt;
|
|
Napi::FunctionReference callback;
|
|
Parameters parameters;
|
|
|
|
Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) {
|
|
stmt->Ref();
|
|
callback.Reset(cb_, 1);
|
|
}
|
|
virtual ~Baton() {
|
|
parameters.clear();
|
|
if (request) napi_delete_async_work(stmt->Env(), request);
|
|
stmt->Unref();
|
|
callback.Reset();
|
|
}
|
|
};
|
|
|
|
struct RowBaton : Baton {
|
|
RowBaton(Statement* stmt_, Napi::Function cb_) :
|
|
Baton(stmt_, cb_) {}
|
|
Row row;
|
|
virtual ~RowBaton() override = default;
|
|
};
|
|
|
|
struct RunBaton : Baton {
|
|
RunBaton(Statement* stmt_, Napi::Function cb_) :
|
|
Baton(stmt_, cb_), inserted_id(0), changes(0) {}
|
|
sqlite3_int64 inserted_id;
|
|
int changes;
|
|
virtual ~RunBaton() override = default;
|
|
};
|
|
|
|
struct RowsBaton : Baton {
|
|
RowsBaton(Statement* stmt_, Napi::Function cb_) :
|
|
Baton(stmt_, cb_) {}
|
|
Rows rows;
|
|
virtual ~RowsBaton() override = default;
|
|
};
|
|
|
|
struct Async;
|
|
|
|
struct EachBaton : Baton {
|
|
Napi::FunctionReference completed;
|
|
Async* async; // Isn't deleted when the baton is deleted.
|
|
|
|
EachBaton(Statement* stmt_, Napi::Function cb_) :
|
|
Baton(stmt_, cb_) {}
|
|
virtual ~EachBaton() override {
|
|
completed.Reset();
|
|
}
|
|
};
|
|
|
|
struct PrepareBaton : Database::Baton {
|
|
Statement* stmt;
|
|
std::string sql;
|
|
PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) :
|
|
Baton(db_, cb_), stmt(stmt_) {
|
|
stmt->Ref();
|
|
}
|
|
virtual ~PrepareBaton() override {
|
|
stmt->Unref();
|
|
if (!db->IsOpen() && db->IsLocked()) {
|
|
// The database handle was closed before the statement could be
|
|
// prepared.
|
|
stmt->Finalize_();
|
|
}
|
|
}
|
|
};
|
|
|
|
typedef void (*Work_Callback)(Baton* baton);
|
|
|
|
struct Call {
|
|
Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {};
|
|
Work_Callback callback;
|
|
Baton* baton;
|
|
};
|
|
|
|
struct Async {
|
|
uv_async_t watcher;
|
|
Statement* stmt;
|
|
Rows data;
|
|
NODE_SQLITE3_MUTEX_t;
|
|
bool completed;
|
|
int retrieved;
|
|
|
|
// Store the callbacks here because we don't have
|
|
// access to the baton in the async callback.
|
|
Napi::FunctionReference item_cb;
|
|
Napi::FunctionReference completed_cb;
|
|
|
|
Async(Statement* st, uv_async_cb async_cb) :
|
|
stmt(st), completed(false), retrieved(0) {
|
|
watcher.data = this;
|
|
NODE_SQLITE3_MUTEX_INIT
|
|
stmt->Ref();
|
|
uv_loop_t *loop;
|
|
napi_get_uv_event_loop(stmt->Env(), &loop);
|
|
uv_async_init(loop, &watcher, async_cb);
|
|
}
|
|
|
|
~Async() {
|
|
stmt->Unref();
|
|
item_cb.Reset();
|
|
completed_cb.Reset();
|
|
NODE_SQLITE3_MUTEX_DESTROY
|
|
}
|
|
};
|
|
|
|
Statement(const Napi::CallbackInfo& info);
|
|
|
|
~Statement() {
|
|
if (!finalized) Finalize_();
|
|
}
|
|
|
|
WORK_DEFINITION(Bind)
|
|
WORK_DEFINITION(Get)
|
|
WORK_DEFINITION(Run)
|
|
WORK_DEFINITION(All)
|
|
WORK_DEFINITION(Each)
|
|
WORK_DEFINITION(Reset)
|
|
|
|
Napi::Value Finalize_(const Napi::CallbackInfo& info);
|
|
|
|
protected:
|
|
static void Work_BeginPrepare(Database::Baton* baton);
|
|
static void Work_Prepare(napi_env env, void* data);
|
|
static void Work_AfterPrepare(napi_env env, napi_status status, void* data);
|
|
|
|
static void AsyncEach(uv_async_t* handle);
|
|
static void CloseCallback(uv_handle_t* handle);
|
|
|
|
static void Finalize_(Baton* baton);
|
|
void Finalize_();
|
|
|
|
template <class T> inline std::unique_ptr<Values::Field> BindParameter(const Napi::Value source, T pos);
|
|
template <class T> T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1);
|
|
bool Bind(const Parameters ¶meters);
|
|
|
|
static void GetRow(Row* row, sqlite3_stmt* stmt);
|
|
static Napi::Value RowToJS(Napi::Env env, Row* row);
|
|
void Schedule(Work_Callback callback, Baton* baton);
|
|
void Process();
|
|
void CleanQueue();
|
|
template <class T> static void Error(T* baton);
|
|
|
|
protected:
|
|
Database* db;
|
|
|
|
sqlite3_stmt* _handle = NULL;
|
|
int status = SQLITE_OK;
|
|
bool prepared = false;
|
|
bool locked = true;
|
|
bool finalized = false;
|
|
|
|
std::queue<Call*> queue;
|
|
std::string message;
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|