LiteLoaderBDS-1.16.40/ScriptEngine/API/DatabaseAPI.cpp
2022-09-21 19:47:03 +08:00

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!");
}