|
|
|
@ -13,7 +13,7 @@
|
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
int64_t GetTransactionID(const Encodable &args) |
|
|
|
|
int64_t getTransactionID(const Encodable &args) |
|
|
|
|
{ |
|
|
|
|
if (!args.HasKey(ARG_TRANSACTION_ID)) |
|
|
|
|
return static_cast<int64_t>(Database::TransactionID::None); |
|
|
|
@ -27,7 +27,7 @@ int64_t GetTransactionID(const Encodable &args)
|
|
|
|
|
return args[ARG_TRANSACTION_ID].GetInt(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Encodable::List GetSqlArguments(const Encodable &args) |
|
|
|
|
Encodable::List getSqlArguments(const Encodable &args) |
|
|
|
|
{ |
|
|
|
|
if (!args.HasKey(ARG_SQL_ARGUMENTS)) |
|
|
|
|
return Encodable::List{}; |
|
|
|
@ -44,15 +44,15 @@ Encodable::List GetSqlArguments(const Encodable &args)
|
|
|
|
|
} /* namespace */ |
|
|
|
|
|
|
|
|
|
SqfliteAuroraPlugin::SqfliteAuroraPlugin() |
|
|
|
|
: dbID(0) |
|
|
|
|
, logger(Logger::Level::None, "sqflite") |
|
|
|
|
: m_dbID(0) |
|
|
|
|
, m_logger(Logger::Level::None, "sqflite") |
|
|
|
|
{} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::RegisterWithRegistrar(PluginRegistrar ®istrar) |
|
|
|
|
{ |
|
|
|
|
registrar.RegisterMethodChannel("com.tekartik.sqflite", |
|
|
|
|
MethodCodecType::Standard, |
|
|
|
|
[this](const MethodCall &call) { this->onMethodCall(call); }); |
|
|
|
|
[this](const MethodCall &call) { onMethodCall(call); }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onMethodCall(const MethodCall &call) |
|
|
|
@ -60,76 +60,76 @@ void SqfliteAuroraPlugin::onMethodCall(const MethodCall &call)
|
|
|
|
|
const auto &method = call.GetMethod(); |
|
|
|
|
|
|
|
|
|
if (method == METHOD_GET_PLATFORM_VERSION) { |
|
|
|
|
this->onPlatformVersionCall(call); |
|
|
|
|
onPlatformVersionCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_OPEN_DATABASE) { |
|
|
|
|
this->onOpenDatabaseCall(call); |
|
|
|
|
onOpenDatabaseCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_CLOSE_DATABASE) { |
|
|
|
|
this->onCloseDatabaseCall(call); |
|
|
|
|
onCloseDatabaseCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_DELETE_DATABASE) { |
|
|
|
|
this->onDeleteDatabaseCall(call); |
|
|
|
|
onDeleteDatabaseCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_DATABASE_EXISTS) { |
|
|
|
|
this->onDatabaseExistsCall(call); |
|
|
|
|
onDatabaseExistsCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_GET_DATABASES_PATH) { |
|
|
|
|
this->onGetDatabasesPathCall(call); |
|
|
|
|
onGetDatabasesPathCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_OPTIONS) { |
|
|
|
|
this->onOptionsCall(call); |
|
|
|
|
onOptionsCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_DEBUG) { |
|
|
|
|
this->onDebugCall(call); |
|
|
|
|
onDebugCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_EXECUTE) { |
|
|
|
|
this->onExecuteCall(call); |
|
|
|
|
onExecuteCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_QUERY) { |
|
|
|
|
this->onQueryCall(call); |
|
|
|
|
onQueryCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_QUERY_CURSOR_NEXT) { |
|
|
|
|
this->onQueryCursorNextCall(call); |
|
|
|
|
onQueryCursorNextCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_UPDATE) { |
|
|
|
|
this->onUpdateCall(call); |
|
|
|
|
onUpdateCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_INSERT) { |
|
|
|
|
this->onInsertCall(call); |
|
|
|
|
onInsertCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method == METHOD_BATCH) { |
|
|
|
|
this->onBatchCall(call); |
|
|
|
|
onBatchCall(call); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call); |
|
|
|
|
sendSuccess(call); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onPlatformVersionCall(const MethodCall &call) |
|
|
|
@ -141,122 +141,114 @@ void SqfliteAuroraPlugin::onPlatformVersionCall(const MethodCall &call)
|
|
|
|
|
if (line.rfind("VERSION_ID=") != 0) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
this->success(call, "Aurora " + line.substr(11)); |
|
|
|
|
sendSuccess(call, "Aurora " + line.substr(11)); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call, "Aurora"); |
|
|
|
|
sendSuccess(call, "Aurora"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onOpenDatabaseCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbPath = args[ARG_PATH].GetString(); |
|
|
|
|
const auto readOnly = args.HasKey(ARG_READ_ONLY) ? args[ARG_READ_ONLY].GetBoolean() : false; |
|
|
|
|
const auto dbPath = call.GetArgument<Encodable::String>(ARG_PATH); |
|
|
|
|
const auto readOnly = call.GetArgument<Encodable::Boolean>(ARG_READ_ONLY, false); |
|
|
|
|
|
|
|
|
|
const auto inMemory = dbPath.empty() || dbPath == ":memory:"; |
|
|
|
|
const auto singleton = args[ARG_SINGLE_INSTANCE].GetBoolean() && !inMemory; |
|
|
|
|
const auto isSingleInstance = call.GetArgument<Encodable::Boolean>(ARG_SINGLE_INSTANCE) |
|
|
|
|
&& !inMemory; |
|
|
|
|
|
|
|
|
|
if (singleton) { |
|
|
|
|
const auto db = this->databaseByPath(dbPath); |
|
|
|
|
if (isSingleInstance) { |
|
|
|
|
const auto db = databaseByPath(dbPath); |
|
|
|
|
|
|
|
|
|
if (db) { |
|
|
|
|
if (db->IsOpen()) { |
|
|
|
|
this->logger.verb() << "re-opened single instance database" |
|
|
|
|
<< (db->IsInTransaction() ? "(in transaction) " : "") |
|
|
|
|
<< db->ID() << " " << db->Path() << std::endl; |
|
|
|
|
if (db->isOpen()) { |
|
|
|
|
m_logger.verb() << "re-opened single instance database" |
|
|
|
|
<< (db->isInTransaction() ? "(in transaction) " : "") << db->id() |
|
|
|
|
<< " " << db->path() << std::endl; |
|
|
|
|
|
|
|
|
|
this->success(call, makeOpenResult(db->ID(), true, db->IsInTransaction())); |
|
|
|
|
sendSuccess(call, makeOpenResult(db->id(), true, db->isInTransaction())); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->logger.verb() << "single instance database " << db->Path() << " not opened" |
|
|
|
|
<< std::endl; |
|
|
|
|
m_logger.verb() << "single instance database " << db->path() << " not opened" |
|
|
|
|
<< std::endl; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const auto db = std::make_shared<Database>(++dbID, dbPath, singleton, logger); |
|
|
|
|
const auto db = std::make_shared<Database>(++m_dbID, dbPath, isSingleInstance, m_logger); |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, readOnly, call] { |
|
|
|
|
this->logger.sql() << "open database " + db->Path() + " (ID=" << db->ID() << ")" |
|
|
|
|
<< std::endl; |
|
|
|
|
m_asyncQueue.push([this, db, readOnly, call] { |
|
|
|
|
m_logger.sql() << "open database " + db->path() + " (ID=" << db->id() << ")" << std::endl; |
|
|
|
|
|
|
|
|
|
const auto error = readOnly ? db->OpenReadOnly() : db->Open(); |
|
|
|
|
const auto error = readOnly ? db->openReadOnly() : db->open(); |
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_OPEN, db->Path(), *error); |
|
|
|
|
sendError(call, ERROR_OPEN, db->path(), error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->databaseAdd(db); |
|
|
|
|
this->success(call, makeOpenResult(db->ID(), false, false)); |
|
|
|
|
databaseAdd(db); |
|
|
|
|
sendSuccess(call, makeOpenResult(db->id(), false, false)); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onCloseDatabaseCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
|
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, dbID, call] { |
|
|
|
|
m_asyncQueue.push([this, db, dbID, call] { |
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->logger.sql() << "closing database with ID=" << db->ID() << std::endl; |
|
|
|
|
m_logger.sql() << "closing database with ID=" << db->id() << std::endl; |
|
|
|
|
|
|
|
|
|
const auto error = db->Close(); |
|
|
|
|
const auto error = db->close(); |
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_CLOSE, db->Path(), *error); |
|
|
|
|
sendError(call, ERROR_CLOSE, db->path(), error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->databaseRemove(db); |
|
|
|
|
this->success(call); |
|
|
|
|
databaseRemove(db); |
|
|
|
|
sendSuccess(call); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onDeleteDatabaseCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
const auto dbPath = args[ARG_PATH].GetString(); |
|
|
|
|
|
|
|
|
|
const auto dbPath = call.GetArgument<Encodable::String>(ARG_PATH); |
|
|
|
|
const auto db = databaseByPath(dbPath); |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, dbPath, call] { |
|
|
|
|
m_asyncQueue.push([this, db, dbPath, call] { |
|
|
|
|
if (db) { |
|
|
|
|
if (db->IsOpen()) { |
|
|
|
|
this->logger.verb() |
|
|
|
|
<< "close database " << db->Path() << " (ID=" << db->ID() << ")" << std::endl; |
|
|
|
|
if (db->isOpen()) { |
|
|
|
|
m_logger.verb() << "close database " << db->path() << " (ID=" << db->id() << ")" |
|
|
|
|
<< std::endl; |
|
|
|
|
|
|
|
|
|
const auto error = db->Close(); |
|
|
|
|
const auto error = db->close(); |
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_CLOSE, db->Path(), *error); |
|
|
|
|
sendError(call, ERROR_CLOSE, db->path(), error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->databaseRemove(db); |
|
|
|
|
databaseRemove(db); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (std::filesystem::exists(dbPath)) { |
|
|
|
|
this->logger.verb() << "delete not opened database " << dbPath << std::endl; |
|
|
|
|
m_logger.verb() << "delete not opened database " << dbPath << std::endl; |
|
|
|
|
std::filesystem::remove(dbPath); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call); |
|
|
|
|
sendSuccess(call); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onDatabaseExistsCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
const auto dbPath = args[ARG_PATH].GetString(); |
|
|
|
|
|
|
|
|
|
this->success(call, std::filesystem::exists(dbPath)); |
|
|
|
|
const auto dbPath = call.GetArgument<Encodable::String>(ARG_PATH); |
|
|
|
|
sendSuccess(call, std::filesystem::exists(dbPath)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onGetDatabasesPathCall(const MethodCall &call) |
|
|
|
@ -264,48 +256,46 @@ void SqfliteAuroraPlugin::onGetDatabasesPathCall(const MethodCall &call)
|
|
|
|
|
const auto home = std::getenv("HOME"); |
|
|
|
|
|
|
|
|
|
if (home == nullptr) { |
|
|
|
|
this->error(call, ERROR_INTERNAL, "environment variable $HOME not found"); |
|
|
|
|
sendError(call, ERROR_INTERNAL, "environment variable $HOME not found"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const auto [orgname, appname] = Application::GetID(); |
|
|
|
|
const auto directory = std::filesystem::path(home) / ".local/share" / orgname / appname; |
|
|
|
|
|
|
|
|
|
this->success(call, directory.generic_string()); |
|
|
|
|
sendSuccess(call, directory.generic_string()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onOptionsCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
const auto level = args[ARG_LOG_LEVEL].GetInt(); |
|
|
|
|
const auto level = call.GetArgument<Encodable::Int>(ARG_LOG_LEVEL); |
|
|
|
|
m_logger.setLogLevel(static_cast<Logger::Level>(level)); |
|
|
|
|
|
|
|
|
|
this->logger.SetLogLevel(static_cast<Logger::Level>(level)); |
|
|
|
|
this->success(call); |
|
|
|
|
sendSuccess(call); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onDebugCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
const auto cmd = args[ARG_COMMAND].GetString(); |
|
|
|
|
const auto cmd = call.GetArgument<Encodable::String>(ARG_COMMAND); |
|
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex); |
|
|
|
|
Encodable::Map result; |
|
|
|
|
|
|
|
|
|
if (cmd == "get") { |
|
|
|
|
if (this->logger.LogLevel() > Logger::Level::None) |
|
|
|
|
result.emplace(ARG_LOG_LEVEL, static_cast<Encodable::Int>(this->logger.LogLevel())); |
|
|
|
|
if (m_logger.logLevel() > Logger::Level::None) |
|
|
|
|
result.emplace(ARG_LOG_LEVEL, static_cast<Encodable::Int>(m_logger.logLevel())); |
|
|
|
|
|
|
|
|
|
if (!this->databases.empty()) { |
|
|
|
|
if (!m_databases.empty()) { |
|
|
|
|
Encodable::Map databases; |
|
|
|
|
|
|
|
|
|
for (const auto &[id, db] : this->databases) { |
|
|
|
|
for (const auto &[id, db] : m_databases) { |
|
|
|
|
Encodable::Map info; |
|
|
|
|
|
|
|
|
|
info.emplace(ARG_PATH, db->Path()); |
|
|
|
|
info.emplace(ARG_SINGLE_INSTANCE, db->IsSingleInstance()); |
|
|
|
|
info.emplace(ARG_PATH, db->path()); |
|
|
|
|
info.emplace(ARG_SINGLE_INSTANCE, db->isSingleInstance()); |
|
|
|
|
|
|
|
|
|
if (db->LogLevel() > Logger::Level::None) |
|
|
|
|
info.emplace(ARG_LOG_LEVEL, static_cast<Encodable::Int>(db->LogLevel())); |
|
|
|
|
if (db->logLevel() > Logger::Level::None) |
|
|
|
|
info.emplace(ARG_LOG_LEVEL, static_cast<Encodable::Int>(db->logLevel())); |
|
|
|
|
|
|
|
|
|
databases.emplace(std::to_string(id), info); |
|
|
|
|
} |
|
|
|
@ -314,211 +304,197 @@ void SqfliteAuroraPlugin::onDebugCall(const MethodCall &call)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call, result); |
|
|
|
|
sendSuccess(call, result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onExecuteCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
const auto sql = args[ARG_SQL].GetString(); |
|
|
|
|
const auto sqlArgs = GetSqlArguments(args); |
|
|
|
|
const auto inTransactionChange = args.HasKey(ARG_IN_TRANSACTION) |
|
|
|
|
? args[ARG_IN_TRANSACTION].GetBoolean() |
|
|
|
|
: false; |
|
|
|
|
const auto transactionID = GetTransactionID(args); |
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto sql = call.GetArgument<Encodable::String>(ARG_SQL); |
|
|
|
|
const auto inTransactionChange = call.GetArgument<Encodable::Boolean>(ARG_IN_TRANSACTION, false); |
|
|
|
|
|
|
|
|
|
const auto sqlArgs = getSqlArguments(call.GetArguments()); |
|
|
|
|
const auto transactionID = getTransactionID(call.GetArguments()); |
|
|
|
|
const auto enteringTransaction = inTransactionChange == true |
|
|
|
|
&& transactionID == Database::TransactionID::None; |
|
|
|
|
|
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->asynq.Push( |
|
|
|
|
[this, db, sql, sqlArgs, inTransactionChange, enteringTransaction, transactionID, call] { |
|
|
|
|
db->ProcessSqlCommand( |
|
|
|
|
transactionID, |
|
|
|
|
[this, db, sql, sqlArgs, inTransactionChange, enteringTransaction, call] { |
|
|
|
|
if (enteringTransaction) |
|
|
|
|
db->EnterInTransaction(); |
|
|
|
|
|
|
|
|
|
const auto error = db->Execute(sql, sqlArgs); |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
db->LeaveTransaction(); |
|
|
|
|
this->error(call, ERROR_INTERNAL, *error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (enteringTransaction) { |
|
|
|
|
this->success(call, |
|
|
|
|
Encodable::Map{ |
|
|
|
|
{ARG_TRANSACTION_ID, db->CurrentTransactionID()}, |
|
|
|
|
}); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (inTransactionChange == false) |
|
|
|
|
db->LeaveTransaction(); |
|
|
|
|
|
|
|
|
|
this->success(call); |
|
|
|
|
}); |
|
|
|
|
m_asyncQueue.push([this, |
|
|
|
|
db, |
|
|
|
|
sql, |
|
|
|
|
sqlArgs, |
|
|
|
|
inTransactionChange, |
|
|
|
|
enteringTransaction, |
|
|
|
|
transactionID, |
|
|
|
|
call] { |
|
|
|
|
db->processSqlCommand(transactionID, [this, db, sql, sqlArgs, inTransactionChange, enteringTransaction, call] { |
|
|
|
|
if (enteringTransaction) |
|
|
|
|
db->enterInTransaction(); |
|
|
|
|
|
|
|
|
|
const auto error = db->execute(sql, sqlArgs); |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
db->leaveTransaction(); |
|
|
|
|
sendError(call, ERROR_INTERNAL, error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (enteringTransaction) { |
|
|
|
|
sendSuccess(call, Encodable::Map{{ARG_TRANSACTION_ID, db->currentTransactionID()}}); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (inTransactionChange == false) |
|
|
|
|
db->leaveTransaction(); |
|
|
|
|
|
|
|
|
|
sendSuccess(call); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onQueryCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
const auto sql = args[ARG_SQL].GetString(); |
|
|
|
|
const auto sqlArgs = GetSqlArguments(args); |
|
|
|
|
const auto transactionID = GetTransactionID(args); |
|
|
|
|
const auto pageSize = args.HasKey(ARG_CURSOR_PAGE_SIZE) ? args[ARG_CURSOR_PAGE_SIZE].GetInt() |
|
|
|
|
: -1; |
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto sql = call.GetArgument<Encodable::String>(ARG_SQL); |
|
|
|
|
const auto pageSize = call.GetArgument<Encodable::Int>(ARG_CURSOR_PAGE_SIZE, -1); |
|
|
|
|
|
|
|
|
|
const auto sqlArgs = getSqlArguments(call.GetArguments()); |
|
|
|
|
const auto transactionID = getTransactionID(call.GetArguments()); |
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, sql, sqlArgs, transactionID, pageSize, call] { |
|
|
|
|
db->ProcessSqlCommand(transactionID, [this, db, sql, sqlArgs, pageSize, call] { |
|
|
|
|
m_asyncQueue.push([this, db, sql, sqlArgs, transactionID, pageSize, call] { |
|
|
|
|
db->processSqlCommand(transactionID, [this, db, sql, sqlArgs, pageSize, call] { |
|
|
|
|
Encodable::Map result; |
|
|
|
|
std::optional<std::string> error; |
|
|
|
|
utils::error error; |
|
|
|
|
|
|
|
|
|
if (pageSize <= 0) |
|
|
|
|
error = db->Query(sql, sqlArgs, result); |
|
|
|
|
error = db->query(sql, sqlArgs, result); |
|
|
|
|
else |
|
|
|
|
error = db->QueryWithPageSize(sql, sqlArgs, pageSize, result); |
|
|
|
|
error = db->queryWithPageSize(sql, sqlArgs, pageSize, result); |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_INTERNAL, *error); |
|
|
|
|
sendError(call, ERROR_INTERNAL, error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call, result); |
|
|
|
|
sendSuccess(call, result); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onQueryCursorNextCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
const auto cursorID = args[ARG_CURSOR_ID].GetInt(); |
|
|
|
|
const auto closeCursor = args.HasKey(ARG_CANCEL) ? args[ARG_CANCEL].GetBoolean() : false; |
|
|
|
|
const auto transactionID = GetTransactionID(args); |
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto cursorID = call.GetArgument<Encodable::Int>(ARG_CURSOR_ID); |
|
|
|
|
const auto closeCursor = call.GetArgument<Encodable::Boolean>(ARG_CANCEL, false); |
|
|
|
|
|
|
|
|
|
const auto transactionID = getTransactionID(call.GetArguments()); |
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, cursorID, closeCursor, transactionID, call] { |
|
|
|
|
db->ProcessSqlCommand(transactionID, [this, db, cursorID, closeCursor, call] { |
|
|
|
|
m_asyncQueue.push([this, db, cursorID, closeCursor, transactionID, call] { |
|
|
|
|
db->processSqlCommand(transactionID, [this, db, cursorID, closeCursor, call] { |
|
|
|
|
Encodable::Map result; |
|
|
|
|
const auto error = db->QueryCursorNext(cursorID, closeCursor, result); |
|
|
|
|
const auto error = db->queryCursorNext(cursorID, closeCursor, result); |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_INTERNAL, *error); |
|
|
|
|
sendError(call, ERROR_INTERNAL, error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call, result); |
|
|
|
|
sendSuccess(call, result); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onUpdateCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
const auto sql = args[ARG_SQL].GetString(); |
|
|
|
|
const auto sqlArgs = GetSqlArguments(args); |
|
|
|
|
const auto transactionID = GetTransactionID(args); |
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto sql = call.GetArgument<Encodable::String>(ARG_SQL); |
|
|
|
|
|
|
|
|
|
const auto sqlArgs = getSqlArguments(call.GetArguments()); |
|
|
|
|
const auto transactionID = getTransactionID(call.GetArguments()); |
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, sql, sqlArgs, transactionID, call] { |
|
|
|
|
db->ProcessSqlCommand(transactionID, [this, db, sql, sqlArgs, call] { |
|
|
|
|
m_asyncQueue.push([this, db, sql, sqlArgs, transactionID, call] { |
|
|
|
|
db->processSqlCommand(transactionID, [this, db, sql, sqlArgs, call] { |
|
|
|
|
int updated = 0; |
|
|
|
|
const auto error = db->Update(sql, sqlArgs, updated); |
|
|
|
|
const auto error = db->update(sql, sqlArgs, updated); |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_INTERNAL, *error); |
|
|
|
|
sendError(call, ERROR_INTERNAL, error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->success(call, updated); |
|
|
|
|
sendSuccess(call, updated); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onInsertCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
const auto sql = args[ARG_SQL].GetString(); |
|
|
|
|
const auto sqlArgs = GetSqlArguments(args); |
|
|
|
|
const auto transactionID = GetTransactionID(args); |
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto sql = call.GetArgument<Encodable::String>(ARG_SQL); |
|
|
|
|
|
|
|
|
|
const auto sqlArgs = getSqlArguments(call.GetArguments()); |
|
|
|
|
const auto transactionID = getTransactionID(call.GetArguments()); |
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, sql, sqlArgs, transactionID, call] { |
|
|
|
|
db->ProcessSqlCommand(transactionID, [this, db, sql, sqlArgs, call] { |
|
|
|
|
m_asyncQueue.push([this, db, sql, sqlArgs, transactionID, call] { |
|
|
|
|
db->processSqlCommand(transactionID, [this, db, sql, sqlArgs, call] { |
|
|
|
|
int insertID = 0; |
|
|
|
|
const auto error = db->Insert(sql, sqlArgs, insertID); |
|
|
|
|
const auto error = db->insert(sql, sqlArgs, insertID); |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_INTERNAL, *error); |
|
|
|
|
sendError(call, ERROR_INTERNAL, error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (insertID == 0) |
|
|
|
|
this->success(call); |
|
|
|
|
sendSuccess(call); |
|
|
|
|
else |
|
|
|
|
this->success(call, insertID); |
|
|
|
|
sendSuccess(call, insertID); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::onBatchCall(const MethodCall &call) |
|
|
|
|
{ |
|
|
|
|
const auto &args = call.GetArguments(); |
|
|
|
|
|
|
|
|
|
const auto dbID = args[ARG_ID].GetInt(); |
|
|
|
|
const auto &operations = args[ARG_OPERATIONS].GetList(); |
|
|
|
|
const auto noResult = args.HasKey(ARG_NO_RESULT) ? args[ARG_NO_RESULT].GetBoolean() : false; |
|
|
|
|
const auto continueOnError = args.HasKey(ARG_CONTINUE_ON_ERROR) |
|
|
|
|
? args[ARG_CONTINUE_ON_ERROR].GetBoolean() |
|
|
|
|
: false; |
|
|
|
|
const auto transactionID = GetTransactionID(args); |
|
|
|
|
const auto dbID = call.GetArgument<Encodable::Int>(ARG_ID); |
|
|
|
|
const auto &operations = call.GetArgument<Encodable::List>(ARG_OPERATIONS); |
|
|
|
|
const auto noResult = call.GetArgument<Encodable::Boolean>(ARG_NO_RESULT, false); |
|
|
|
|
const auto continueOnError = call.GetArgument<Encodable::Boolean>(ARG_CONTINUE_ON_ERROR, false); |
|
|
|
|
|
|
|
|
|
const auto transactionID = getTransactionID(call.GetArguments()); |
|
|
|
|
const auto db = databaseByID(dbID); |
|
|
|
|
|
|
|
|
|
if (!db) { |
|
|
|
|
this->error(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
sendError(call, ERROR_CLOSED, "database closed", "ID=" + std::to_string(dbID) + ")"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -527,80 +503,80 @@ void SqfliteAuroraPlugin::onBatchCall(const MethodCall &call)
|
|
|
|
|
for (const auto &operation : operations) { |
|
|
|
|
const auto method = operation[ARG_METHOD].GetString(); |
|
|
|
|
const auto sql = operation[ARG_SQL].GetString(); |
|
|
|
|
const auto sqlArgs = GetSqlArguments(operation); |
|
|
|
|
const auto sqlArgs = getSqlArguments(operation); |
|
|
|
|
|
|
|
|
|
dbOperations.emplace_back(Database::Operation{method, sql, sqlArgs}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this->asynq.Push([this, db, dbOperations, transactionID, continueOnError, noResult, call] { |
|
|
|
|
m_asyncQueue.push([this, db, dbOperations, transactionID, continueOnError, noResult, call] { |
|
|
|
|
const auto command = [this, db, dbOperations, continueOnError, noResult, call] { |
|
|
|
|
Encodable::List results; |
|
|
|
|
const auto error = db->Batch(dbOperations, continueOnError, results); |
|
|
|
|
const auto error = db->batch(dbOperations, continueOnError, results); |
|
|
|
|
if (error) { |
|
|
|
|
this->error(call, ERROR_INTERNAL, *error); |
|
|
|
|
sendError(call, ERROR_INTERNAL, error.message()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (noResult) |
|
|
|
|
this->success(call); |
|
|
|
|
sendSuccess(call); |
|
|
|
|
else |
|
|
|
|
this->success(call, results); |
|
|
|
|
sendSuccess(call, results); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
db->ProcessSqlCommand(transactionID, command); |
|
|
|
|
db->processSqlCommand(transactionID, command); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::shared_ptr<Database> SqfliteAuroraPlugin::databaseByPath(const std::string &path) |
|
|
|
|
{ |
|
|
|
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex); |
|
|
|
|
|
|
|
|
|
if (!this->singletonDatabases.count(path)) |
|
|
|
|
if (!m_singleInstanceDatabases.count(path)) |
|
|
|
|
return nullptr; |
|
|
|
|
|
|
|
|
|
return this->singletonDatabases.at(path); |
|
|
|
|
return m_singleInstanceDatabases.at(path); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::shared_ptr<Database> SqfliteAuroraPlugin::databaseByID(int64_t id) |
|
|
|
|
{ |
|
|
|
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex); |
|
|
|
|
|
|
|
|
|
if (!this->databases.count(id)) |
|
|
|
|
if (!m_databases.count(id)) |
|
|
|
|
return nullptr; |
|
|
|
|
|
|
|
|
|
return this->databases.at(id); |
|
|
|
|
return m_databases.at(id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::databaseRemove(std::shared_ptr<Database> db) |
|
|
|
|
{ |
|
|
|
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex); |
|
|
|
|
|
|
|
|
|
if (db->IsSingleInstance()) |
|
|
|
|
this->singletonDatabases.erase(db->Path()); |
|
|
|
|
if (db->isSingleInstance()) |
|
|
|
|
m_singleInstanceDatabases.erase(db->path()); |
|
|
|
|
|
|
|
|
|
this->databases.erase(db->ID()); |
|
|
|
|
m_databases.erase(db->id()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::databaseAdd(std::shared_ptr<Database> db) |
|
|
|
|
{ |
|
|
|
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex); |
|
|
|
|
|
|
|
|
|
if (db->IsSingleInstance()) |
|
|
|
|
this->singletonDatabases.emplace(db->Path(), db); |
|
|
|
|
if (db->isSingleInstance()) |
|
|
|
|
m_singleInstanceDatabases.emplace(db->path(), db); |
|
|
|
|
|
|
|
|
|
this->databases.emplace(db->ID(), db); |
|
|
|
|
m_databases.emplace(db->id(), db); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::success(const MethodCall &call, const Encodable &result) |
|
|
|
|
void SqfliteAuroraPlugin::sendSuccess(const MethodCall &call, const Encodable &result) |
|
|
|
|
{ |
|
|
|
|
call.SendSuccessResponse(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SqfliteAuroraPlugin::error(const MethodCall &call, |
|
|
|
|
const std::string &error, |
|
|
|
|
const std::string &message, |
|
|
|
|
const std::string &desc, |
|
|
|
|
const Encodable &details) |
|
|
|
|
void SqfliteAuroraPlugin::sendError(const MethodCall &call, |
|
|
|
|
const std::string &error, |
|
|
|
|
const std::string &message, |
|
|
|
|
const std::string &desc, |
|
|
|
|
const Encodable &details) |
|
|
|
|
{ |
|
|
|
|
call.SendErrorResponse(ERROR_SQFLITE, |
|
|
|
|
error + ": " + message + (desc.empty() ? "" : " (" + desc + ")"), |
|
|
|
|