mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-06 20:03:51 +00:00
552 lines
18 KiB
C++
552 lines
18 KiB
C++
#include "DatabaseAPI.h"
|
|
using namespace DB;
|
|
|
|
#define CATCH_AND_THROW(LOG) \
|
|
catch (const Exception& e) { \
|
|
logger.error(LOG##"\n"); \
|
|
PrintException(e); \
|
|
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName); \
|
|
return Local<Value>(); \
|
|
} \
|
|
catch (const std::exception& e) { \
|
|
throw Exception(TextEncoding::toUTF8(e.what())); \
|
|
} \
|
|
catch (const seh_exception& e) { \
|
|
logger.error("SEH Uncaught Exception Detected!"); \
|
|
logger.error(TextEncoding::toUTF8(e.what())); \
|
|
PrintScriptStackTrace(); \
|
|
logger.error(std::string("In API: ") + __FUNCTION__); \
|
|
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName); \
|
|
return Local<Value>(); \
|
|
} \
|
|
catch (...) { \
|
|
logger.error("Uncaught Exception Detected!"); \
|
|
PrintScriptStackTrace(); \
|
|
logger.error(std::string("In API: ") + __FUNCTION__); \
|
|
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName); \
|
|
return Local<Value>(); \
|
|
}
|
|
|
|
//////////////////// Class Definition ////////////////////
|
|
|
|
ClassDefine<KVDBClass> KVDBClassBuilder =
|
|
defineClass<KVDBClass>("KVDatabase")
|
|
.constructor(&KVDBClass::constructor)
|
|
.instanceFunction("get", &KVDBClass::get)
|
|
.instanceFunction("set", &KVDBClass::set)
|
|
.instanceFunction("delete", &KVDBClass::del)
|
|
.instanceFunction("close", &KVDBClass::close)
|
|
.instanceFunction("listKey", &KVDBClass::listKey)
|
|
.build();
|
|
|
|
ClassDefine<DBSessionClass> DBSessionClassBuilder =
|
|
defineClass<DBSessionClass>("DBSession")
|
|
.constructor(&DBSessionClass::constructor)
|
|
.instanceFunction("query", &DBSessionClass::query)
|
|
.instanceFunction("exec", &DBSessionClass::exec)
|
|
.instanceFunction("execute", &DBSessionClass::exec)
|
|
.instanceFunction("prepare", &DBSessionClass::prepare)
|
|
.instanceFunction("close", &DBSessionClass::close)
|
|
.instanceFunction("isOpen", &DBSessionClass::isOpen)
|
|
.build();
|
|
|
|
ClassDefine<DBStmtClass> DBStmtClassBuilder =
|
|
defineClass<DBStmtClass>("DBStmt")
|
|
.constructor(nullptr)
|
|
.instanceProperty("affectedRows", &DBStmtClass::getAffectedRows)
|
|
.instanceProperty("insertId", &DBStmtClass::getInsertId)
|
|
|
|
.instanceFunction("bind", &DBStmtClass::bind)
|
|
.instanceFunction("execute", &DBStmtClass::execute)
|
|
.instanceFunction("step", &DBStmtClass::step)
|
|
.instanceFunction("fetch", &DBStmtClass::fetch)
|
|
.instanceFunction("fetchAll", &DBStmtClass::fetchAll)
|
|
.instanceFunction("reset", &DBStmtClass::reset)
|
|
.instanceFunction("reexec", &DBStmtClass::reexec)
|
|
.instanceFunction("clear", &DBStmtClass::clear)
|
|
.build();
|
|
|
|
|
|
//////////////////// Functions ////////////////////
|
|
|
|
Any LocalValueToAny(const Local<Value>& val) {
|
|
switch (val.getKind()) {
|
|
case ValueKind::kObject:
|
|
case ValueKind::kArray:
|
|
throw std::exception("Cannot convert script object(array) to Any");
|
|
case ValueKind::kNull:
|
|
case ValueKind::kUnsupported:
|
|
return Any();
|
|
case ValueKind::kBoolean:
|
|
return Any(val.asBoolean().value());
|
|
case ValueKind::kNumber:
|
|
if (CheckIsFloat(val.asNumber()))
|
|
return Any(val.asNumber().toDouble());
|
|
else
|
|
return Any(val.asNumber().toInt64());
|
|
case ValueKind::kString:
|
|
return Any(val.asString().toString());
|
|
case ValueKind::kByteBuffer:
|
|
switch (val.asByteBuffer().getType()) {
|
|
case ByteBuffer::Type::kInt8:
|
|
case ByteBuffer::Type::kUint8: {
|
|
auto buf = (uint8_t*)val.asByteBuffer().getRawBytes();
|
|
return Any(ByteArray(buf, buf + val.asByteBuffer().elementCount()));
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return Any();
|
|
}
|
|
|
|
template <>
|
|
Local<Value> any_to(const Any& val) {
|
|
switch (val.type) {
|
|
case Any::Type::Null:
|
|
return Local<Value>();
|
|
case Any::Type::Boolean:
|
|
return Boolean::newBoolean(val.value.boolean);
|
|
case Any::Type::Integer:
|
|
return Number::newNumber(val.value.integer);
|
|
case Any::Type::UInteger:
|
|
if (val.value.uinteger > LLONG_MAX)
|
|
return Number::newNumber((double)val.value.uinteger);
|
|
return Number::newNumber((int64_t)val.value.uinteger);
|
|
case Any::Type::Floating:
|
|
return Number::newNumber(val.value.floating);
|
|
case Any::Type::String:
|
|
return String::newString(*val.value.string);
|
|
case Any::Type::Date: {
|
|
auto obj = Object::newObject();
|
|
obj.set("Y", val.value.date->year);
|
|
obj.set("M", val.value.date->month);
|
|
obj.set("D", val.value.date->day);
|
|
return obj;
|
|
}
|
|
case Any::Type::Time: {
|
|
auto obj = Object::newObject();
|
|
obj.set("h", val.value.time->hour);
|
|
obj.set("m", val.value.time->minute);
|
|
obj.set("s", val.value.time->second);
|
|
return obj;
|
|
}
|
|
case Any::Type::DateTime: {
|
|
auto obj = Object::newObject();
|
|
obj.set("Y", val.value.datetime->date.year);
|
|
obj.set("M", val.value.datetime->date.month);
|
|
obj.set("D", val.value.datetime->date.day);
|
|
obj.set("h", val.value.datetime->time.hour);
|
|
obj.set("m", val.value.datetime->time.minute);
|
|
obj.set("s", val.value.datetime->time.second);
|
|
return obj;
|
|
}
|
|
case Any::Type::Blob:
|
|
return String::newString(val.get<std::string>());
|
|
default:
|
|
break;
|
|
}
|
|
return Local<Value>();
|
|
}
|
|
|
|
Local<Value> RowSetToLocalValue(const RowSet& rows) {
|
|
if (rows.empty() || !rows.header) {
|
|
return Local<Value>();
|
|
}
|
|
Local<Array> arr = Array::newArray();
|
|
Local<Array> header = Array::newArray();
|
|
for (auto& col : *rows.header)
|
|
header.add(String::newString(col));
|
|
arr.add(header);
|
|
for (auto& row : rows) {
|
|
Local<Array> rowValues = Array::newArray();
|
|
for (auto& col : row)
|
|
rowValues.add(col.get<Local<Value>>());
|
|
arr.add(rowValues);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
Local<Value> RowToLocalValue(const Row& row) {
|
|
auto result = Object::newObject();
|
|
row.forEach([&](const std::string& key, const Any& value) {
|
|
result.set(key, value.get<Local<Value>>());
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
//////////////////// Classes KVDB ////////////////////
|
|
|
|
// 生成函数
|
|
KVDBClass::KVDBClass(const Local<Object>& scriptObj, const string& dir)
|
|
: ScriptClass(scriptObj)
|
|
, kvdb(KVDB::create(dir)) {
|
|
unloadCallbackIndex = ENGINE_OWN_DATA()->addUnloadCallback([&](ScriptEngine* engine) {
|
|
kvdb.reset();
|
|
});
|
|
}
|
|
|
|
KVDBClass::KVDBClass(const string& dir)
|
|
: ScriptClass(script::ScriptClass::ConstructFromCpp<KVDBClass>{})
|
|
, kvdb(KVDB::create(dir)) {
|
|
unloadCallbackIndex = ENGINE_OWN_DATA()->addUnloadCallback([&](ScriptEngine* engine) {
|
|
kvdb.reset();
|
|
});
|
|
}
|
|
|
|
KVDBClass::~KVDBClass() {
|
|
}
|
|
|
|
KVDBClass* KVDBClass::constructor(const Arguments& args) {
|
|
CHECK_ARGS_COUNT_C(args, 1);
|
|
CHECK_ARG_TYPE_C(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
auto res = new KVDBClass(args.thiz(), args[0].toStr());
|
|
if (res->isValid())
|
|
return res;
|
|
else
|
|
return nullptr;
|
|
}
|
|
CATCH_C("Fail in Open Database!");
|
|
}
|
|
|
|
Local<Value> KVDBClass::get(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 1);
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
if (!isValid())
|
|
return Local<Value>();
|
|
|
|
string res;
|
|
if (!kvdb->get(args[0].asString().toString(), res))
|
|
return Local<Value>();
|
|
|
|
return JsonToValue(res);
|
|
}
|
|
CATCH_AND_THROW("Fail in DbGet!");
|
|
}
|
|
|
|
Local<Value> KVDBClass::set(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 2);
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
if (!isValid())
|
|
return Local<Value>();
|
|
|
|
kvdb->put(args[0].asString().toString(), ValueToJson(args[1]));
|
|
return Boolean::newBoolean(true);
|
|
}
|
|
CATCH_AND_THROW("Fail in DbSet!");
|
|
}
|
|
|
|
Local<Value> KVDBClass::del(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 1);
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
if (!isValid())
|
|
return Local<Value>();
|
|
|
|
return Boolean::newBoolean(kvdb->del(args[0].asString().toString()));
|
|
}
|
|
CATCH_AND_THROW("Fail in DbDel!");
|
|
}
|
|
|
|
Local<Value> KVDBClass::close(const Arguments& args) {
|
|
ENGINE_OWN_DATA()->removeUnloadCallback(unloadCallbackIndex);
|
|
unloadCallbackIndex = -1;
|
|
try {
|
|
kvdb.reset();
|
|
return Boolean::newBoolean(true);
|
|
}
|
|
CATCH_AND_THROW("Fail in DbClose!");
|
|
}
|
|
|
|
Local<Value> KVDBClass::listKey(const Arguments& args) {
|
|
try {
|
|
if (!isValid())
|
|
return Local<Value>();
|
|
|
|
auto list = kvdb->getAllKeys();
|
|
Local<Array> arr = Array::newArray();
|
|
for (auto& key : list) {
|
|
arr.add(String::newString(key));
|
|
}
|
|
return arr;
|
|
}
|
|
CATCH_AND_THROW("Fail in DbListKey!");
|
|
}
|
|
|
|
//////////////////// Classes DBSession ////////////////////
|
|
|
|
// 生成函数
|
|
DBSessionClass::DBSessionClass(const Local<Object>& scriptObj, const ConnParams& params)
|
|
: ScriptClass(scriptObj)
|
|
, session(Session::create(params)) {
|
|
session->setDebugOutput(true);
|
|
}
|
|
DBSessionClass::DBSessionClass(const ConnParams& params)
|
|
: ScriptClass(script::ScriptClass::ConstructFromCpp<DBSessionClass>{})
|
|
, session(Session::create(params)) {
|
|
session->setDebugOutput(true);
|
|
}
|
|
|
|
DBSessionClass::~DBSessionClass() {
|
|
}
|
|
|
|
DBSessionClass* DBSessionClass::constructor(const Arguments& args) {
|
|
try {
|
|
DBSessionClass* result = nullptr;
|
|
switch (args.size()) {
|
|
case 1: {
|
|
// When the first argument is a string, it is a url or file path.
|
|
if (args[0].isString()) {
|
|
result = new DBSessionClass(args.thiz(), ConnParams(args[0].asString().toString()));
|
|
} else if (args[0].isObject()) {
|
|
auto obj = args[0].asObject();
|
|
ConnParams params;
|
|
for (auto& key : obj.getKeys())
|
|
params[key.toString()] = LocalValueToAny(obj.get(key));
|
|
result = new DBSessionClass(args.thiz(), params);
|
|
} else {
|
|
LOG_WRONG_ARG_TYPE();
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
CHECK_ARG_TYPE_C(args[0], ValueKind::kString);
|
|
CHECK_ARG_TYPE_C(args[1], ValueKind::kObject);
|
|
auto obj = args[1].asObject();
|
|
ConnParams params;
|
|
params["type"] = args[0].asString().toString();
|
|
for (auto& key : obj.getKeys())
|
|
params[key.toString()] = LocalValueToAny(obj.get(key));
|
|
result = new DBSessionClass(args.thiz(), ConnParams(params));
|
|
break;
|
|
}
|
|
default:
|
|
LOG_WRONG_ARG_TYPE();
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
CATCH_C("Fail in Open Database!");
|
|
}
|
|
|
|
Local<Value> DBSessionClass::query(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 1);
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
auto res = session->query(args[0].asString().toString());
|
|
return RowSetToLocalValue(res);
|
|
}
|
|
CATCH_AND_THROW("Fail in query!");
|
|
}
|
|
|
|
Local<Value> DBSessionClass::exec(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 1);
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
session->execute(args[0].asString().toString());
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in exec!");
|
|
}
|
|
|
|
Local<Value> DBSessionClass::prepare(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 1);
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kString);
|
|
|
|
try {
|
|
auto stmt = new DBStmtClass(session->prepare(args[0].asString().toString()));
|
|
return stmt->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in exec!");
|
|
}
|
|
|
|
Local<Value> DBSessionClass::close(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
session->close();
|
|
return Boolean::newBoolean(true);
|
|
}
|
|
CATCH_WITHOUT_RETURN("Fail in close!");
|
|
return Boolean::newBoolean(false);
|
|
}
|
|
Local<Value> DBSessionClass::isOpen(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
return Boolean::newBoolean(session->isOpen());
|
|
}
|
|
CATCH_AND_THROW("Fail in isOpen!");
|
|
}
|
|
|
|
//////////////////// Classes DBStmt ////////////////////
|
|
|
|
// 生成函数
|
|
DBStmtClass::DBStmtClass(const Local<Object>& scriptObj, const DB::SharedPointer<DB::Stmt>& stmt)
|
|
: ScriptClass(scriptObj)
|
|
, stmt(stmt) {
|
|
}
|
|
|
|
DBStmtClass::DBStmtClass(const DB::SharedPointer<DB::Stmt>& stmt)
|
|
: ScriptClass(script::ScriptClass::ConstructFromCpp<DBStmtClass>{})
|
|
, stmt(stmt) {
|
|
}
|
|
|
|
DBStmtClass::~DBStmtClass() {
|
|
}
|
|
|
|
Local<Value> DBStmtClass::getAffectedRows() {
|
|
try {
|
|
auto res = stmt->getAffectedRows();
|
|
if (res == (uint64_t)-1)
|
|
return Number::newNumber(-1);
|
|
if (res > LLONG_MAX)
|
|
return Number::newNumber((double)res);
|
|
return Number::newNumber((int64_t)res);
|
|
}
|
|
CATCH_AND_THROW("Fail in getAffectedRows!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::getInsertId() {
|
|
try {
|
|
auto res = stmt->getInsertId();
|
|
if (res == (uint64_t)-1)
|
|
return Number::newNumber(-1);
|
|
if (res > LLONG_MAX)
|
|
return Number::newNumber((double)res);
|
|
return Number::newNumber((int64_t)res);
|
|
}
|
|
CATCH_AND_THROW("Fail in getInsertId!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::bind(const Arguments& args) {
|
|
try {
|
|
switch (args.size()) {
|
|
case 1: {
|
|
switch (args[0].getKind()) {
|
|
case ValueKind::kArray: {
|
|
auto arr = args[0].asArray();
|
|
for (size_t i = 0; i < arr.size(); ++i)
|
|
stmt->bind(LocalValueToAny(arr.get(i)));
|
|
break;
|
|
}
|
|
case ValueKind::kObject: {
|
|
auto obj = args[0].asObject();
|
|
for (auto& key : obj.getKeys())
|
|
stmt->bind(LocalValueToAny(obj.get(key)), key.toString());
|
|
break;
|
|
}
|
|
default:
|
|
stmt->bind(LocalValueToAny(args[0]));
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
if (args[1].isNumber()) {
|
|
stmt->bind(LocalValueToAny(args[0]), (int)args[1].asNumber().toInt64());
|
|
} else if (args[1].isString()) {
|
|
stmt->bind(LocalValueToAny(args[0]), args[1].asString().toString());
|
|
} else {
|
|
LOG_WRONG_ARG_TYPE();
|
|
}
|
|
}
|
|
}
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in bind!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::execute(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
stmt->execute();
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in reset!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::step(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
return Boolean::newBoolean(stmt->step());
|
|
}
|
|
CATCH_WITHOUT_RETURN("Fail in step!");
|
|
return Boolean::newBoolean(false);
|
|
}
|
|
|
|
Local<Value> DBStmtClass::fetch(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
return RowToLocalValue(stmt->fetch());
|
|
}
|
|
CATCH_AND_THROW("Fail in fetch!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::fetchAll(const Arguments& args) {
|
|
try {
|
|
switch (args.size()) {
|
|
case 0:
|
|
return RowSetToLocalValue(stmt->fetchAll());
|
|
case 1: {
|
|
CHECK_ARG_TYPE(args[0], ValueKind::kFunction);
|
|
auto func = args[0].asFunction();
|
|
stmt->fetchAll([&](const Row& row) {
|
|
auto res = func.call({}, RowToLocalValue(row));
|
|
if (res.isBoolean()) {
|
|
return res.asBoolean().value();
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in fetchAll!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::reset(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
stmt->reset();
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in reset!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::reexec(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
stmt->reexec();
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in reexec!");
|
|
}
|
|
|
|
Local<Value> DBStmtClass::clear(const Arguments& args) {
|
|
CHECK_ARGS_COUNT(args, 0);
|
|
|
|
try {
|
|
stmt->clear();
|
|
return this->getScriptObject();
|
|
}
|
|
CATCH_AND_THROW("Fail in clear!");
|
|
}
|