Node-Sqlite3 的修改详解
2018-11-19 本文已影响70人
l蓝色梦幻
由于我们需要使用静态库连接, 尽可能少的使用 dll .因此我们需要对 Node-Sqlite3 的源代码做出对应的修改. 代码相对于 4.0.3
做出的修改
关于 Gyp 的修改.
由于 Node-Sqlite3 使用 Gyp 作为配置, 因此我们从本地编译代码, 就需要添加, 修改对应的代码块才可能. 如下:
原来的 bingding.gyp.
下面是我们修改的 gyp 文件. 注意, 修改部分我会加上注释表明是为什么修改, 但是该注释在使用的时候需要删除.
{
"includes": [ "deps/common-sqlite.gypi" ],
"variables": {
"sqlite%":"sqlcipher_lib",
"sqlite_libname%":"sqlcipher",
"openssl_libname%":"crypto" // 添加新的配置项, 支持 openssl 静态库编译
},
"targets": [
{
"target_name": "<(module_name)",
"include_dirs": ["<!(node -e \"require('nan')\")"],
"conditions": [
["sqlite != 'internal'", {
"include_dirs": [ "<(module_root_dir)/<(sqlite)/include", "<(module_root_dir)/<(sqlite)/include/sqlcipher", "<(module_root_dir)/<(sqlite)/include/openssl" ], // 添加 openssl 静态库支持
"libraries": [
"-l<(sqlite_libname)",
"-l<(openssl_libname)" // 添加 openssl 静态库支持
],
"conditions": [ [ "OS=='linux'", {"libraries+":["-Wl,-rpath=<(module_root_dir)/<@(sqlite)/lib"]} ] ],
"conditions": [ [ "OS!='win'", {"libraries+":["-l<(openssl_libname)", "-L<(module_root_dir)/<@(sqlite)/lib"]} ] ], // 添加 openssl 静态库支持
"conditions": [ [ "OS=='win'", {"libraries+":[ "-lmsvcrt", "crypt32.lib", "ws2_32.lib" ] } ] ], // windows 上, 我们需要添加 openssl 静态库支持, 同时需要加入openssl 的依赖
'msvs_settings': {
'VCLinkerTool': {
'AdditionalLibraryDirectories': [
'<(module_root_dir)/<(sqlite)/lib'
],
'IgnoreDefaultLibraryNames': [ // 由于在 Windows 上, Node.lib 中本身有 openssl 的代码, 因此我们如果要用 SQLCipher ,就需要智能忽略 openssl 库.
'<(openssl_libname).lib',
'crypt32.lib',
'ws2_32.lib'
],
},
}
},
{
"dependencies": [
"deps/sqlite3.gyp:sqlite3"
]
}
]
],
"sources": [
"src/database.cc",
"src/node_sqlite3.cc",
"src/statement.cc"
]
},
{
"target_name": "action_after_build",
"type": "none",
"dependencies": [ "<(module_name)" ],
"copies": [
{
"files": [ "<(PRODUCT_DIR)/<(module_name).node" ],
"destination": "<(module_path)"
}
]
}
]
}
添加 lib 库到 sqlcipher_lib 目录下. 这样, 我们就不需要再指定代码库了.
package.json 的修改
将 binary 中的 host 修改成一个无法访问的地址, 这样就无法从这里下载预编译好的 dll .这时候就只能从本地编译了.
添加 Backup 方法.
SQLite 支持 backup 方法. 但是在 Node-Sqlite3 中并不支持. 因此我们需要自己加入该方法. 在 database.cc 中加入如下代码:
NAN_METHOD(Database::Backup) {
Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());
REQUIRE_ARGUMENT_STRING(0, filename);
REQUIRE_ARGUMENT_STRING(1, passcode);
OPTIONAL_ARGUMENT_FUNCTION(2, callback);
Local<Function> completed;
int last = info.Length();
if (last >= 3 && info[last - 1]->IsFunction() && info[last - 2]->IsFunction()) {
completed = Local<Function>::Cast(info[--last]);
}
info.GetReturnValue().Set(info.This());
Nan::ForceSet(info.This(), Nan::New("filename").ToLocalChecked(), info[0].As<String>(), ReadOnly);
Nan::ForceSet(info.This(), Nan::New("passcode").ToLocalChecked(), info[0].As<String>(), ReadOnly);
// Nan::ForceSet(info.This(), Nan::New("mode").ToLocalChecked(), Nan::New(mode), ReadOnly);
// Start opening the database.
BackupBaton* baton = new BackupBaton(db, callback, *filename, *passcode);
baton->progress.Reset(completed);
db->Schedule(Work_Backup, baton, true);
info.GetReturnValue().Set(info.This());
}
/*
https://www.sqlite.org/backup.html
*/
void Database::Work_Backup(Baton* b) {
Nan::HandleScope scope;
BackupBaton* baton = static_cast<BackupBaton*>(b);
Database* db = baton->db;
int rc;
sqlite3 *pFile;
sqlite3_backup *pBackup;
assert(baton->db->locked);
assert(baton->db->open);
assert(baton->db->_handle);
assert(baton->db->pending == 0);
rc = sqlite3_open(baton->filename.c_str(), &pFile);
// pKey 是密钥,nKey 是密钥长度
sqlite3_key(pFile, baton->passcode.c_str(), strlen(baton->passcode.c_str()));
if(rc==SQLITE_OK) {
pBackup = sqlite3_backup_init(pFile, "main", baton->db->_handle, "main");
if( pBackup ) {
do {
rc = sqlite3_backup_step(pBackup, 5);
int remaining = sqlite3_backup_remaining(pBackup);
int pagecount = sqlite3_backup_pagecount(pBackup);
if (remaining > 0 && pagecount > 0) {
// Completion = 100% * (pagecount() - remaining()) / pagecount()
Local<Value> argv[] = {
Nan::Null(),
Nan::New((int)remaining),
Nan::New((int)pagecount)
};
Local<Function> cb = Nan::New(baton->progress);
if (!cb.IsEmpty() && cb->IsFunction()) {
TRY_CATCH_CALL(db->handle(), cb, 3, argv);
}
}
if( rc==SQLITE_OK || rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
sqlite3_sleep(250);
}
} while( rc==SQLITE_OK || rc==SQLITE_BUSY || rc==SQLITE_LOCKED );
(void)sqlite3_backup_finish(pBackup);
}
rc = sqlite3_errcode(pFile);
}
(void)sqlite3_close(pFile);
// 2. 返回参数
Local<Value> argv[1];
if (baton->status != SQLITE_OK) {
EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception);
argv[0] = exception;
} else {
argv[0] = Nan::Null();
}
Local<Function> cb = Nan::New(baton->callback);
if (!cb.IsEmpty() && cb->IsFunction()) {
TRY_CATCH_CALL(db->handle(), cb, 1, argv);
}
delete baton;
}
其他的地方仿照其他部分修改一下即可. 这时候我们就加入了 backup 功能.