#ifndef NODE_SQLITE3_SRC_STATEMENT_H #define NODE_SQLITE3_SRC_STATEMENT_H #include #include #include #include #include #include #include #include #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; }; struct Integer : Field { template inline Integer(T _name, int64_t val) : Field(_name, SQLITE_INTEGER), value(val) {} int64_t value; }; struct Float : Field { template inline Float(T _name, double val) : Field(_name, SQLITE_FLOAT), value(val) {} double value; }; struct Text : Field { template inline Text(T _name, size_t len, const char* val) : Field(_name, SQLITE_TEXT), value(val, len) {} std::string value; }; struct Blob : Field { template inline Blob(T _name, size_t len, const void* val) : Field(_name, SQLITE_BLOB), length(len) { value = (char*)malloc(len); memcpy(value, val, len); } inline ~Blob() { free(value); } int length; char* value; }; typedef Field Null; } typedef std::vector Row; typedef std::vector Rows; typedef Row Parameters; class Statement : public Napi::ObjectWrap { public: static Napi::FunctionReference constructor; static Napi::Object Init(Napi::Env env, Napi::Object exports); static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { napi_async_work request; Statement* stmt; Napi::FunctionReference callback; Parameters parameters; Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { stmt->Ref(); callback.Reset(cb_, 1); } virtual ~Baton() { for (unsigned int i = 0; i < parameters.size(); i++) { Values::Field* field = parameters[i]; DELETE_FIELD(field); } stmt->Unref(); callback.Reset(); } }; struct RowBaton : Baton { RowBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Row row; }; struct RunBaton : Baton { RunBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_), inserted_id(0), changes(0) {} sqlite3_int64 inserted_id; int changes; }; struct RowsBaton : Baton { RowsBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Rows rows; }; 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() { 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() { 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 } }; void init(Database* db_) { db = db_; _handle = NULL; status = SQLITE_OK; prepared = false; locked = true; finalized = false; db->Ref(); } 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 inline Values::Field* BindParameter(const Napi::Value source, T pos); template 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 static void Error(T* baton); protected: Database* db; sqlite3_stmt* _handle; int status; std::string message; bool prepared; bool locked; bool finalized; std::queue queue; }; } #endif