Skip to content

Instantly share code, notes, and snippets.

@sfpgmr
Created April 6, 2020 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sfpgmr/0b46e809348c8e5d59cc334f129269e7 to your computer and use it in GitHub Desktop.
Save sfpgmr/0b46e809348c8e5d59cc334f129269e7 to your computer and use it in GitHub Desktop.
#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)
#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