Created
April 6, 2020 09:46
-
-
Save sfpgmr/0b46e809348c8e5d59cc334f129269e7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "sqlite3-napi.h" | |
#include "uv.h" | |
#include <iostream> | |
using namespace sqlite3_napi; | |
// --------------------------------------- | |
// Database Implementation | |
// --------------------------------------- | |
Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Database>(info){ | |
Napi::Env env = info.Env(); | |
Napi::HandleScope scope(env); | |
} | |
Napi::FunctionReference Database::constructor; | |
Napi::Object Database::Init(Napi::Env env, Napi::Object exports) { | |
Napi::HandleScope scope(env); | |
Napi::Function func = | |
DefineClass(env, | |
"Database", | |
{ | |
InstanceMethod("exec", &Database::exec), | |
InstanceMethod("open", &Database::open), | |
InstanceMethod("close", &Database::close), | |
}); | |
constructor = Napi::Persistent(func); | |
constructor.SuppressDestruct(); | |
exports.Set("Database", func); | |
return exports; | |
}; | |
// Napi::Value Database::exec(const Napi::CallbackInfo& info){ | |
// Napi::Env env = info.Env(); | |
// Napi::HandleScope scope(env); | |
// Napi::Promise::Deferred deffered = Napi::Promise::Deferred::New(env); | |
// return deffered.Promise(); | |
// }; | |
class OpenWorker : public Napi::AsyncWorker { | |
public: | |
OpenWorker(Database& db,Napi::Env env, const std::string& path,const int mode) | |
: AsyncWorker(env),db_(db),env_(env),deferred_(env),path_(path),mode_(mode){ | |
} | |
virtual ~OpenWorker(){ | |
} | |
void Execute() override { | |
// Napi::HandleScope scope(env_); | |
sqlite3* handle; | |
int status = sqlite3_open_v2(path_.c_str(),&handle,mode_,NULL); | |
if(status != SQLITE_OK){ | |
std::string errorMessage(sqlite3_errmsg(handle)); | |
handle = nullptr; | |
SetError(errorMessage); | |
return; | |
} else { | |
sqlite3_busy_timeout(handle,1000); | |
} | |
db_.handle(handle); | |
db_.isOpened(true); | |
} | |
void OnOK() override { | |
deferred_.Resolve(env_.Undefined()); | |
} | |
void OnError(Napi::Error const &error) override { | |
deferred_.Reject(error.Value()); | |
} | |
Napi::Promise promise() { | |
return deferred_.Promise(); | |
} | |
private: | |
Database& db_; | |
Napi::Env env_; | |
Napi::Promise::Deferred deferred_; | |
// Napi::Promise::Deferred& deferred_; | |
const std::string path_; | |
const int mode_; | |
}; | |
Napi::Value Database::open(const Napi::CallbackInfo& info){ | |
Napi::Env env = info.Env(); | |
Napi::HandleScope scope(env); | |
int paramLength = info.Length(); | |
if (paramLength <= 0 || !info[0].IsString()) { | |
throw Napi::TypeError::New(env, "Path String Expected");//.ThrowAsJavaScriptException(); | |
//return env.Undefined(); | |
} | |
// database path | |
std::string path(static_cast<std::string>(info[0].As<Napi::String>())); | |
// database mode | |
int mode = 0; | |
if(paramLength >= 2){ | |
if(info[1].IsNumber()){ | |
mode = static_cast<int>(info[1].As<Napi::Number>()); | |
} else { | |
throw Napi::TypeError::New(env, "Option Paramater is missing."); | |
} | |
} else { | |
mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; | |
} | |
OpenWorker *worker = new OpenWorker(*this,env,path,mode); | |
worker->Queue(); | |
return worker->promise(); | |
}; | |
class CloseWorker : public Napi::AsyncWorker { | |
public: | |
CloseWorker(Database& db,Napi::Env env) | |
: AsyncWorker(env),db_(db),env_(env),deferred_(env){ | |
} | |
virtual ~CloseWorker(){ | |
} | |
void Execute() override { | |
int status = sqlite3_close_v2(const_cast<sqlite3*>(db_.handle())); | |
if (status != SQLITE_OK) { | |
std::string errorMessage(sqlite3_errmsg(const_cast<sqlite3*>(db_.handle()))); | |
db_.handle(nullptr); | |
SetError(errorMessage); | |
return; | |
} else { | |
db_.handle(nullptr); | |
db_.isOpened(false); | |
} | |
} | |
void OnOK() override { | |
deferred_.Resolve(env_.Null()); | |
} | |
void OnError(Napi::Error const &error) override { | |
deferred_.Reject(error.Value()); | |
} | |
Napi::Promise promise() { | |
return deferred_.Promise(); | |
} | |
private: | |
Database& db_; | |
const Napi::Env& env_; | |
Napi::Promise::Deferred deferred_; | |
}; | |
Napi::Value Database::close(const Napi::CallbackInfo& info){ | |
Napi::Env env = info.Env(); | |
Napi::HandleScope scope(env); | |
CloseWorker *worker = new CloseWorker(*this,env); | |
worker->Queue(); | |
return worker->promise(); | |
}; | |
class ExecWorker : public Napi::AsyncWorker { | |
public: | |
ExecWorker(Database& db,Napi::Env env,const Napi::Value& callback,const std::string& sqlStatement) | |
: AsyncWorker(env),db_(db),env_(env),sqlStatement_(sqlStatement),deferred_(env){ | |
if(callback.IsFunction()){ | |
callback_.Reset(callback.As<Napi::Function>()); | |
need_callback_ = true; | |
threadFunc_ = Napi::ThreadSafeFunction::New( | |
env, | |
callback_.Value(), | |
"Sqlite3 Exec",0,1 | |
// ,[&](Napi::Env env) { | |
// uv_thread_t uv = uv_thread_self(); | |
// uv_thread_join(&uv); | |
// deferred_.Resolve(env.Undefined()); | |
// } | |
); | |
} else { | |
need_callback_ = false; | |
} | |
} | |
virtual ~ExecWorker(){ | |
} | |
void Execute() override { | |
//Napi::HandleScope scope(env_); | |
std::string message_str; | |
char* message = nullptr; | |
int status = sqlite3_exec( | |
const_cast<sqlite3*>(db_.handle()), | |
const_cast<char*>(sqlStatement_.c_str()), | |
need_callback_ ? exec_callback : 0, | |
this, | |
&message | |
); | |
if (status != SQLITE_OK && message != nullptr) { | |
message_str = "Exec Error:" + std::string(message); | |
sqlite3_free(message); | |
SetError(message_str); | |
} | |
// if(need_callback_) { | |
// threadFunc_.BlockingCall(this,end_callback); | |
// } | |
if(need_callback_ ) threadFunc_.Release(); | |
} | |
void OnOK() override { | |
deferred_.Resolve(env_.Null()); | |
} | |
void OnError(Napi::Error const &error) override { | |
deferred_.Reject(error.Value()); | |
} | |
Napi::Promise promise() { | |
return deferred_.Promise(); | |
} | |
private: | |
Database& db_; | |
const Napi::Env env_; | |
Napi::FunctionReference callback_; | |
const std::string sqlStatement_; | |
Napi::Promise::Deferred deferred_; | |
Napi::ThreadSafeFunction threadFunc_; | |
bool need_callback_; | |
int row_count; | |
// sqlite3_execのコールバック | |
typedef std::tuple<std::string,std::string> Column; | |
typedef std::vector<Column> Row; | |
static int exec_callback(void *ptr, int count, char **columnData, char **columnName){ | |
ExecWorker* this_ = reinterpret_cast<ExecWorker*>(ptr); | |
++(this_->row_count); | |
Row* row = new Row(); | |
for(int i = 0;i < count;++i){ | |
row->push_back(Column(std::string(columnName[i]),std::string(columnData[i]))); | |
} | |
//Row* row = new Row {count,columnData,columnName}; | |
this_->threadFunc_.BlockingCall(row,callback); | |
return SQLITE_OK; | |
} | |
static void callback(Napi::Env env, Napi::Function jsCallback,Row* row_) { | |
std::unique_ptr<Row> row(row_); | |
Napi::HandleScope scope(env); | |
Napi::Object obj = Napi::Object::New(env); | |
// int count(std::get<0>(*row)); | |
// char** columnData(std::get<1>(*row)); | |
// char** columnName(std::get<2>(*row)); | |
// for(int i = 0;i < count;++i){ | |
// obj.Set(columnName[i],Napi::String::New(env,columnData[i])); | |
// } | |
std::for_each(row->begin(),row->end(),[&](const Column& col){ | |
obj.Set(std::get<0>(col),Napi::String::New(env,std::get<1>(col))); | |
}); | |
jsCallback.Call({obj}); | |
} | |
static void end_callback(Napi::Env env, Napi::Function jsCallback,ExecWorker * worker) { | |
worker->deferred_.Resolve(env.Undefined()); | |
} | |
}; | |
Napi::Value Database::exec(const Napi::CallbackInfo& info){ | |
Napi::Env env = info.Env(); | |
Napi::HandleScope scope(env); | |
int paramLength = info.Length(); | |
if (paramLength <= 0 || !info[0].IsString()) { | |
throw Napi::TypeError::New(env, "SQL Statement String Expected"); | |
} | |
// sql statement string | |
std::string sqlStaement(info[0].As<Napi::String>()); | |
if(sqlStaement.empty()){ | |
throw Napi::TypeError::New(env, "SQL Statement String was empty."); | |
} | |
Napi::FunctionReference func; | |
if(paramLength >= 2){ | |
if(info[1].IsFunction()){ | |
func.Reset(info[1].As<Napi::Function>()); | |
} else { | |
throw Napi::TypeError::New(env, "parameter is not callback function."); | |
} | |
} | |
ExecWorker *worker = new ExecWorker(*this,env,info[1],sqlStaement); | |
worker->Queue(); | |
return worker->promise(); | |
//return Napi::Promise::Deferred::New(env).Promise(); | |
} | |
// --------------------------------------- | |
// Statement Implementation | |
// --------------------------------------- | |
Statement::Statement(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Statement>(info){ | |
Napi::Env env = info.Env(); | |
Napi::HandleScope scope(env); | |
} | |
Napi::FunctionReference Statement::constructor; | |
Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { | |
Napi::HandleScope scope(env); | |
Napi::Function func = | |
DefineClass(env, | |
"Database", | |
{ | |
// InstanceMethod("exec", &Database::exec), | |
// InstanceMethod("open", &Statement::open), | |
// InstanceMethod("close", &Database::close), | |
}); | |
constructor = Napi::Persistent(func); | |
constructor.SuppressDestruct(); | |
exports.Set("Statement", func); | |
return exports; | |
}; | |
// --------------------------------------- | |
// Module Implementation | |
// --------------------------------------- | |
Napi::Object InitAll(Napi::Env env, Napi::Object exports) { | |
Napi::HandleScope scope(env); | |
Database::Init(env, exports); | |
return Statement::Init(env,exports); | |
} | |
NODE_API_MODULE(sqlite3_napi, InitAll) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef DATABASE_NAPI_H | |
#define DATABASE_NAPI_H | |
//#define NAPI_DISABLE_CPP_EXCEPTIONS | |
#define NAPI_CPP_EXCEPTIONS | |
#define NAPI_VERSION 13 | |
#include <sqlite3.h> | |
#include <napi.h> | |
namespace sqlite3_napi { | |
class Statement; | |
class Database : public Napi::ObjectWrap<Database> { | |
public: | |
static Napi::Object Init(Napi::Env env, Napi::Object exports); | |
Database(const Napi::CallbackInfo& info); | |
Napi::Value open(const Napi::CallbackInfo& info); | |
Napi::Value exec(const Napi::CallbackInfo& info); | |
Napi::Value close(const Napi::CallbackInfo& info); | |
// Napi::Value loadExtension(const Napi::CallbackInfo& info); | |
// Napi::Value configure(const Napi::CallbackInfo& info); | |
// Napi::Value intereupt(const Napi::CallbackInfo& info); | |
sqlite3 const * handle() {return handle_;} | |
void handle(sqlite3* handle) { | |
handle_ = handle; | |
} | |
bool isOpened() {return isOpened_;} | |
void isOpened(const bool v) { isOpened_ = true;} | |
private: | |
static Napi::FunctionReference constructor; | |
bool isOpened_; | |
sqlite3* handle_; | |
}; | |
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 <class T> inline Integer(T _name, int64_t val) : | |
Field(_name, SQLITE_INTEGER), value(val) {} | |
int64_t value; | |
}; | |
struct Float : Field { | |
template <class T> inline Float(T _name, double val) : | |
Field(_name, SQLITE_FLOAT), value(val) {} | |
double value; | |
}; | |
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; | |
}; | |
struct Blob : Field { | |
template <class T> 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<values::Field*> Row; | |
typedef std::vector<Row*> Rows; | |
typedef Row Parameters; | |
class Statement : public Napi::ObjectWrap<Statement> | |
{ | |
public: | |
static Napi::Object Init(Napi::Env env, Napi::Object exports); | |
Statement(const Napi::CallbackInfo& info); | |
private: | |
static Napi::FunctionReference constructor; | |
}; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment