From 6ddf0d1909a9cd02e0fb12c62a71ef3034ab7a4c Mon Sep 17 00:00:00 2001 From: mackenzie Date: Sun, 1 Jan 2023 14:11:40 -0600 Subject: [PATCH 1/8] Fix `createFromLocation` for Windows --- platforms/windows/SQLitePlugin/SQLitePlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp index 2693a0ea..22f25d0d 100644 --- a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp +++ b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp @@ -307,7 +307,7 @@ namespace SQLitePlugin { try { - CopyDbAsync(assetFile, to_hstring(*dbFileName)).GetResults(); + CopyDbAsync(assetFile, to_hstring(*dbFileName)); } catch (hresult_error const& ex) { From 786133423cf636171657c6ad45d1438c4f1cdf6d Mon Sep 17 00:00:00 2001 From: Ahab Date: Sun, 1 Jan 2023 14:18:36 -0600 Subject: [PATCH 2/8] Update README.md Include Windows pre-populated instructions --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 197c2324..a2f43317 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ Add `PackageProviders().Append(winrt::SQLitePlugin::ReactPackageProvider());` be Refer to this guide for more details: https://microsoft.github.io/react-native-windows/docs/next/native-modules-using -## Setting up your project to import a pre-populated SQLite database from application for iOS +## Pre-populated SQLite database (iOS) #### Step 1 - Create 'www' folder. @@ -327,6 +327,14 @@ Modify you openDatabase call in your application adding createFromLocation param ``` For Android, the www directory is always relative to the assets directory for the app: src/main/assets +## Pre-populated SQLite database (Windows) +Almost identical to iOS, but we'll be using Visual Studio + +1. Put your database in `windows\\www` +2. Open `windows\.sln` with Visual Studio +3. Right-click `\Assets` in the Solution Explorer +4. `Add -> Existing Item` and select your database + Enjoy! ## Opening a database From 3eb7a4afc519f537ca4caea4f679e58c49ecab5a Mon Sep 17 00:00:00 2001 From: mackenzie Date: Sun, 1 Jan 2023 22:35:55 -0600 Subject: [PATCH 3/8] Await and ignore exception (If the db already exists, that's not a problem) --- platforms/windows/SQLitePlugin/SQLitePlugin.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp index 22f25d0d..68c1eb06 100644 --- a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp +++ b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp @@ -307,14 +307,9 @@ namespace SQLitePlugin { try { - CopyDbAsync(assetFile, to_hstring(*dbFileName)); - } - catch (hresult_error const& ex) - { - // CopyDbAsync throws when the file already exists. - onFailure("Unable to copy asset file: " + winrt::to_string(ex.message())); - return; + CopyDbAsync(assetFile, to_hstring(*dbFileName)).get(); } + catch (hresult_error const& ex){} } } From a5f2cddae5ed9db7f45c5f28d9a5d42e03d28995 Mon Sep 17 00:00:00 2001 From: mackenzie Date: Thu, 12 Jan 2023 19:41:27 -0600 Subject: [PATCH 4/8] More robust error-handling --- .../windows/SQLitePlugin/SQLitePlugin.cpp | 480 +++++++++--------- 1 file changed, 236 insertions(+), 244 deletions(-) diff --git a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp index 68c1eb06..c9f6e682 100644 --- a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp +++ b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp @@ -17,63 +17,62 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [ - options{ std::move(options) }, - onSuccess{ std::move(onSuccess) }, - onFailure{ std::move(onFailure) }, - weak_this = std::weak_ptr(shared_from_this()) - ]() - { - if (auto strongThis = weak_this.lock()) { - - if (options.MainDB == nullptr) + [options{std::move(options)}, + onSuccess{std::move(onSuccess)}, + onFailure{std::move(onFailure)}, + weak_this = std::weak_ptr(shared_from_this())]() + { + if (auto strongThis = weak_this.lock()) { - onFailure("You must specify a database to attach to"); - return; - } - if (options.DBAlias == nullptr) - { - onFailure("You must specify a database alias to use"); - return; - } + if (options.MainDB == nullptr) + { + onFailure("You must specify a database to attach to"); + return; + } - if (options.DBFileToAttach == nullptr) - { - onFailure("You must specify database to attach"); - return; - } + if (options.DBAlias == nullptr) + { + onFailure("You must specify a database alias to use"); + return; + } - hstring absoluteDbPath; - absoluteDbPath = ResolveDbFilePath(to_hstring(options.MainDB)); + if (options.DBFileToAttach == nullptr) + { + onFailure("You must specify database to attach"); + return; + } - if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) - { - onFailure("No such database, you must open it first"); - return; - } + hstring absoluteDbPath; + absoluteDbPath = ResolveDbFilePath(to_hstring(options.MainDB)); - hstring absoluteDbPathToAttach; - absoluteDbPathToAttach = ResolveDbFilePath(to_hstring(options.DBFileToAttach)); + if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) + { + onFailure("No such database, you must open it first"); + return; + } - std::string statement = ("ATTACH DATABASE '" + to_string(absoluteDbPathToAttach) + "' AS " + options.DBAlias); + hstring absoluteDbPathToAttach; + absoluteDbPathToAttach = ResolveDbFilePath(to_hstring(options.DBFileToAttach)); - sqlite3* dbHandle = strongThis->openDBs[absoluteDbPath]; + std::string statement = ("ATTACH DATABASE '" + to_string(absoluteDbPathToAttach) + "' AS " + options.DBAlias); - if (sqlite3_exec(dbHandle, statement.c_str(), NULL, NULL, NULL) == SQLITE_OK) - { - onSuccess("Database successfully attached"); - return; - } - else - { - const char* strPtr = sqlite3_errmsg(dbHandle); - std::string errorMessage(strPtr, strlen(strPtr)); - onFailure("Failed to attach database: " + errorMessage); - return; + sqlite3 *dbHandle = strongThis->openDBs[absoluteDbPath]; + + if (sqlite3_exec(dbHandle, statement.c_str(), NULL, NULL, NULL) == SQLITE_OK) + { + onSuccess("Database successfully attached"); + return; + } + else + { + const char *strPtr = sqlite3_errmsg(dbHandle); + std::string errorMessage(strPtr, strlen(strPtr)); + onFailure("Failed to attach database: " + errorMessage); + return; + } } - } - }); + }); } void SQLitePlugin::Close( @@ -82,40 +81,39 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [ - options{ std::move(options) }, - onSuccess{ std::move(onSuccess) }, - onFailure{ std::move(onFailure) }, - weak_this = std::weak_ptr(shared_from_this()) - ]() - { - - if (auto strongThis = weak_this.lock()) { - - if (options.Path == nullptr || options.Path.empty()) + [options{std::move(options)}, + onSuccess{std::move(onSuccess)}, + onFailure{std::move(onFailure)}, + weak_this = std::weak_ptr(shared_from_this())]() + { + if (auto strongThis = weak_this.lock()) { - onFailure("You must specify database path"); - return; - } - hstring absoluteDbPath{ ResolveDbFilePath(to_hstring(options.Path)) }; + if (options.Path == nullptr || options.Path.empty()) + { + onFailure("You must specify database path"); + return; + } + + hstring absoluteDbPath{ResolveDbFilePath(to_hstring(options.Path))}; - if (CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs)) - { - onSuccess("DB Closed"); - } - else - { - onFailure("Specified db was not open"); + if (CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs)) + { + onSuccess("DB Closed"); + } + else + { + onFailure("Specified db was not open"); + } } - } - }); + }); } - SQLitePlugin::SQLitePlugin() {}; + SQLitePlugin::SQLitePlugin(){}; - SQLitePlugin::~SQLitePlugin() { - for (auto& db : openDBs) + SQLitePlugin::~SQLitePlugin() + { + for (auto &db : openDBs) { sqlite3_close(db.second); } @@ -127,43 +125,39 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [ - options{ std::move(options) }, - onSuccess{ std::move(onSuccess) }, - onFailure{ std::move(onFailure) }, - weak_this = std::weak_ptr(shared_from_this()) - ]() - { - if (auto strongThis = weak_this.lock()) + [options{std::move(options)}, + onSuccess{std::move(onSuccess)}, + onFailure{std::move(onFailure)}, + weak_this = std::weak_ptr(shared_from_this())]() { - if (options.Path == nullptr || options.Path.empty()) + if (auto strongThis = weak_this.lock()) { - onFailure("You must specify database name"); - return; - } - - hstring absoluteDbPath{ ResolveDbFilePath(to_hstring(options.Path)) }; + if (options.Path == nullptr || options.Path.empty()) + { + onFailure("You must specify database name"); + return; + } - CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs); + hstring absoluteDbPath{ResolveDbFilePath(to_hstring(options.Path))}; - try - { - StorageFile dbFile = StorageFile::GetFileFromPathAsync(absoluteDbPath).get(); - dbFile.DeleteAsync().get(); - } - catch (winrt::hresult_error const& ex) - { - winrt::hstring message = ex.message(); - std::string errorMessage = "Error deleting database: " + to_string(message) + " " + to_string(absoluteDbPath) + "\n"; - onFailure(errorMessage); - return; - } - - onSuccess("Database Deleted"); - } - }); + CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs); + try + { + StorageFile dbFile = StorageFile::GetFileFromPathAsync(absoluteDbPath).get(); + dbFile.DeleteAsync().get(); + } + catch (winrt::hresult_error const &ex) + { + winrt::hstring message = ex.message(); + std::string errorMessage = "Error deleting database: " + to_string(message) + " " + to_string(absoluteDbPath) + "\n"; + onFailure(errorMessage); + return; + } + onSuccess("Database Deleted"); + } + }); } void SQLitePlugin::EchoStringValue( @@ -172,13 +166,11 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [ - options{ std::move(options) }, - onSuccess{ std::move(onSuccess) } - ]() - { - onSuccess(options.Value); - }); + [options{std::move(options)}, + onSuccess{std::move(onSuccess)}]() + { + onSuccess(options.Value); + }); } void SQLitePlugin::ExecuteSqlBatch( @@ -187,62 +179,55 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [ - options{ std::move(options) }, - onSuccess{ std::move(onSuccess) }, - onFailure{ std::move(onFailure) }, - weak_this = std::weak_ptr(shared_from_this()) - ]() - { - if (auto strongThis = weak_this.lock()) { - if (options.DBArgs.DBName == nullptr) - { - onFailure("You must specify database path"); - return; - } - - hstring absoluteDbPath; - absoluteDbPath = ResolveDbFilePath(to_hstring(options.DBArgs.DBName)); - - if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) + [options{std::move(options)}, + onSuccess{std::move(onSuccess)}, + onFailure{std::move(onFailure)}, + weak_this = std::weak_ptr(shared_from_this())]() + { + if (auto strongThis = weak_this.lock()) { - onFailure("No such database, you must open it first"); - return; - } + if (options.DBArgs.DBName == nullptr) + { + onFailure("You must specify database path"); + return; + } - sqlite3* dbHandle = strongThis->openDBs[absoluteDbPath]; - std::vector results; + hstring absoluteDbPath; + absoluteDbPath = ResolveDbFilePath(to_hstring(options.DBArgs.DBName)); - for (auto& query : options.Executes) - { - JSValue result; - if (ExecuteQuery(dbHandle, query, result)) + if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) { - // Query succeeded - results.push_back( - { - { "qid", query.QID }, - { "type", "success" }, - { "result", std::move(result)} - } - ); + onFailure("No such database, you must open it first"); + return; } - else + + sqlite3 *dbHandle = strongThis->openDBs[absoluteDbPath]; + std::vector results; + + for (auto &query : options.Executes) { - // Query failed - results.push_back( - { - { "qid", query.QID }, - { "type", "error" }, - { "error", result.AsString()}, - { "result", result.AsString()} - } - ); + JSValue result; + if (ExecuteQuery(dbHandle, query, result)) + { + // Query succeeded + results.push_back( + {{"qid", query.QID}, + {"type", "success"}, + {"result", std::move(result)}}); + } + else + { + // Query failed + results.push_back( + {{"qid", query.QID}, + {"type", "error"}, + {"error", result.AsString()}, + {"result", result.AsString()}}); + } } + onSuccess(std::move(results)); } - onSuccess(std::move(results)); - } - }); + }); } void SQLitePlugin::Open( @@ -251,87 +236,95 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [ - options{ std::move(options) }, - onSuccess{ std::move(onSuccess) }, - onFailure{ std::move(onFailure) }, - weak_this = std::weak_ptr(shared_from_this()) - ]() - { - if (auto strongThis = weak_this.lock()) { - const std::string* dbFileName = &options.Name; - - if (dbFileName == nullptr || dbFileName->empty()) - { - onFailure("You must specify the database name"); - return; - } - - hstring absoluteDbPath{ ResolveDbFilePath(to_hstring(*dbFileName)) }; - - if (strongThis->openDBs.find(absoluteDbPath) != strongThis->openDBs.end()) + [options{std::move(options)}, + onSuccess{std::move(onSuccess)}, + onFailure{std::move(onFailure)}, + weak_this = std::weak_ptr(shared_from_this())]() + { + if (auto strongThis = weak_this.lock()) { - onSuccess("Database opened"); - return; - } + const std::string *dbFileName = &options.Name; - IAsyncOperation assetFileOp = ResolveAssetFile(options.AssetFileName, *dbFileName); - StorageFile assetFile = nullptr; - if (assetFileOp != nullptr) - { - try - { - assetFile = assetFileOp.get(); - } - catch (hresult_error const& ex) + if (dbFileName == nullptr || dbFileName->empty()) { - onFailure("Unable to open asset file: " + winrt::to_string(ex.message())); + onFailure("You must specify the database name"); return; } - } - int openFlags{ 0 }; - openFlags |= SQLITE_OPEN_NOMUTEX; + hstring absoluteDbPath{ResolveDbFilePath(to_hstring(*dbFileName))}; - if (options.ReadOnly && assetFileOp != nullptr) - { - openFlags |= SQLITE_OPEN_READONLY; - absoluteDbPath = assetFile.Path(); - } - else - { - openFlags |= SQLITE_OPEN_READWRITE; - openFlags |= SQLITE_OPEN_CREATE; + if (strongThis->openDBs.find(absoluteDbPath) != strongThis->openDBs.end()) + { + onSuccess("Database opened"); + return; + } + IAsyncOperation assetFileOp = ResolveAssetFile(options.AssetFileName, *dbFileName); + StorageFile assetFile = nullptr; if (assetFileOp != nullptr) { try { - CopyDbAsync(assetFile, to_hstring(*dbFileName)).get(); + assetFile = assetFileOp.get(); + } + catch (hresult_error const &ex) + { + onFailure("Unable to open asset file: " + winrt::to_string(ex.message())); + return; } - catch (hresult_error const& ex){} } - } - sqlite3* dbHandle = nullptr; + int openFlags{0}; + openFlags |= SQLITE_OPEN_NOMUTEX; - int result = sqlite3_open_v2(to_string(absoluteDbPath).c_str(), &dbHandle, openFlags, nullptr); - if (result == SQLITE_OK) - { - strongThis->openDBs[absoluteDbPath] = dbHandle; - onSuccess("Database opened"); - } - else - { - onFailure("Unable to open DB"); - } - } - }); + if (options.ReadOnly && assetFileOp != nullptr) + { + openFlags |= SQLITE_OPEN_READONLY; + absoluteDbPath = assetFile.Path(); + } + else + { + openFlags |= SQLITE_OPEN_READWRITE; + openFlags |= SQLITE_OPEN_CREATE; - } + if (assetFileOp != nullptr) + { + try + { + CopyDbAsync(assetFile, to_hstring(*dbFileName)).get(); + } + catch (hresult_error const &ex) + { + if (ex.code() == 0x800700B7) + { + // Ignore, CopyDbAsync throws when the file already exists. + } + else + { + onFailure("Unable to copy asset file: " + winrt::to_string(ex.message())); + return; + } + } + } + } + + sqlite3 *dbHandle = nullptr; + int result = sqlite3_open_v2(to_string(absoluteDbPath).c_str(), &dbHandle, openFlags, nullptr); + if (result == SQLITE_OK) + { + strongThis->openDBs[absoluteDbPath] = dbHandle; + onSuccess("Database opened"); + } + else + { + onFailure("Unable to open DB"); + } + } + }); + } - void SQLitePlugin::BindStatement(sqlite3_stmt* stmt_ptr, int argIndex, const JSValue& arg) + void SQLitePlugin::BindStatement(sqlite3_stmt *stmt_ptr, int argIndex, const JSValue &arg) { switch (arg.Type()) { @@ -356,11 +349,11 @@ namespace SQLitePlugin } } - bool SQLitePlugin::CloseDatabaseIfOpen(const hstring& absoluteDbPath, std::map& openDBs) + bool SQLitePlugin::CloseDatabaseIfOpen(const hstring &absoluteDbPath, std::map &openDBs) { if (openDBs.find(absoluteDbPath) != openDBs.end()) { - sqlite3* dbHandle = openDBs[absoluteDbPath]; + sqlite3 *dbHandle = openDBs[absoluteDbPath]; if (sqlite3_close(dbHandle) != SQLITE_OK) { @@ -374,27 +367,27 @@ namespace SQLitePlugin return false; } - IAsyncOperation SQLitePlugin::CopyDbAsync(const StorageFile& srcDbFile, const hstring& destDbFileName) + IAsyncOperation SQLitePlugin::CopyDbAsync(const StorageFile &srcDbFile, const hstring &destDbFileName) { // This implementation is closely related to ResolveDbFilePath. return srcDbFile.CopyAsync(ApplicationData::Current().LocalFolder(), destDbFileName, NameCollisionOption::FailIfExists); } - bool SQLitePlugin::ExecuteQuery(sqlite3* dbHandle, const DBQuery& query, JSValue& result) + bool SQLitePlugin::ExecuteQuery(sqlite3 *dbHandle, const DBQuery &query, JSValue &result) { if (query.SQL == nullptr || query.SQL == "") { - result = JSValue{ "You must specify a sql query to execute" }; + result = JSValue{"You must specify a sql query to execute"}; return false; } int previousRowsAffected = sqlite3_total_changes(dbHandle); - sqlite3_stmt* stmt; + sqlite3_stmt *stmt; int prepResult = sqlite3_prepare_v2(dbHandle, query.SQL.c_str(), -1, &stmt, nullptr); if (prepResult != SQLITE_OK) { - result = JSValue{ sqlite3_errmsg(dbHandle) }; + result = JSValue{sqlite3_errmsg(dbHandle)}; return false; } @@ -402,7 +395,7 @@ namespace SQLitePlugin { // sqlite bind uses 1-based indexing for the arguments int argIndex = 1; - for (auto& arg : query.Params) + for (auto &arg : query.Params) { BindStatement(stmt, argIndex, arg); argIndex++; @@ -428,7 +421,7 @@ namespace SQLitePlugin { int currentRowsAffected = sqlite3_total_changes(dbHandle); rowsAffected = currentRowsAffected - previousRowsAffected; - sqlite3_int64 currentInsertId = sqlite3_last_insert_rowid(dbHandle); + sqlite3_int64 currentInsertId = sqlite3_last_insert_rowid(dbHandle); if (rowsAffected > 0 && currentInsertId != 0) { insertId = currentInsertId; @@ -438,9 +431,9 @@ namespace SQLitePlugin } default: { - const char* strPtr = sqlite3_errmsg(dbHandle); + const char *strPtr = sqlite3_errmsg(dbHandle); std::string errorMessage(strPtr, strlen(strPtr)); - result = JSValue{ errorMessage }; + result = JSValue{errorMessage}; keepGoing = false; isError = true; } @@ -456,10 +449,9 @@ namespace SQLitePlugin } JSValueObject resultSet = - { - { "rows", std::move(resultRows) }, - { "rowsAffected", rowsAffected } - }; + { + {"rows", std::move(resultRows)}, + {"rowsAffected", rowsAffected}}; if (insertId != 0) { @@ -470,7 +462,7 @@ namespace SQLitePlugin return true; } - JSValue SQLitePlugin::ExtractColumn(sqlite3_stmt* stmt, int columnIndex) + JSValue SQLitePlugin::ExtractColumn(sqlite3_stmt *stmt, int columnIndex) { switch (sqlite3_column_type(stmt, columnIndex)) { @@ -480,16 +472,16 @@ namespace SQLitePlugin return sqlite3_column_double(stmt, columnIndex); case SQLITE_TEXT: { - const char* strPtr = (char*)sqlite3_column_text(stmt, columnIndex); + const char *strPtr = (char *)sqlite3_column_text(stmt, columnIndex); return std::string(strPtr, strlen(strPtr)); } case SQLITE_BLOB: { // JSON does not support raw binary data. You can't write a binary blob using this module - // In case we have a pre-populated database with a binary blob in it, + // In case we have a pre-populated database with a binary blob in it, // we are going to base64 encode it and return as a string. // This is consistent with iOS implementation. - const void* ptr = sqlite3_column_blob(stmt, columnIndex); + const void *ptr = sqlite3_column_blob(stmt, columnIndex); int length = sqlite3_column_bytes(stmt, columnIndex); Buffer buffer = Buffer(length); memcpy(buffer.data(), ptr, length); @@ -502,13 +494,13 @@ namespace SQLitePlugin } } - JSValueObject SQLitePlugin::ExtractRow(sqlite3_stmt* stmt) + JSValueObject SQLitePlugin::ExtractRow(sqlite3_stmt *stmt) { JSValueObject row{}; int columnCount = sqlite3_column_count(stmt); for (int i = 0; i < columnCount; i++) { - const char* strPtr = (char*)sqlite3_column_name(stmt, i); + const char *strPtr = (char *)sqlite3_column_name(stmt, i); std::string columnName(strPtr, strlen(strPtr)); JSValue columnValue = ExtractColumn(stmt, i); if (!columnValue.IsNull()) @@ -519,7 +511,7 @@ namespace SQLitePlugin return row; } - IAsyncOperation SQLitePlugin::ResolveAssetFile(const std::string& assetFilePath, const std::string& dbFileName) + IAsyncOperation SQLitePlugin::ResolveAssetFile(const std::string &assetFilePath, const std::string &dbFileName) { if (assetFilePath == nullptr || assetFilePath.length() == 0) { From fc20f1af3ccfcade21a0cf336c10b6e3c24b6598 Mon Sep 17 00:00:00 2001 From: mackenzie Date: Thu, 12 Jan 2023 19:46:56 -0600 Subject: [PATCH 5/8] Forgot to turn off auto-format --- .../windows/SQLitePlugin/SQLitePlugin.cpp | 482 +++++++++--------- 1 file changed, 250 insertions(+), 232 deletions(-) diff --git a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp index c9f6e682..ce6215ac 100644 --- a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp +++ b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp @@ -17,62 +17,63 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [options{std::move(options)}, - onSuccess{std::move(onSuccess)}, - onFailure{std::move(onFailure)}, - weak_this = std::weak_ptr(shared_from_this())]() - { - if (auto strongThis = weak_this.lock()) - { + [ + options{ std::move(options) }, + onSuccess{ std::move(onSuccess) }, + onFailure{ std::move(onFailure) }, + weak_this = std::weak_ptr(shared_from_this()) + ]() + { + if (auto strongThis = weak_this.lock()) { - if (options.MainDB == nullptr) - { - onFailure("You must specify a database to attach to"); - return; - } + if (options.MainDB == nullptr) + { + onFailure("You must specify a database to attach to"); + return; + } - if (options.DBAlias == nullptr) - { - onFailure("You must specify a database alias to use"); - return; - } + if (options.DBAlias == nullptr) + { + onFailure("You must specify a database alias to use"); + return; + } - if (options.DBFileToAttach == nullptr) - { - onFailure("You must specify database to attach"); - return; - } + if (options.DBFileToAttach == nullptr) + { + onFailure("You must specify database to attach"); + return; + } - hstring absoluteDbPath; - absoluteDbPath = ResolveDbFilePath(to_hstring(options.MainDB)); + hstring absoluteDbPath; + absoluteDbPath = ResolveDbFilePath(to_hstring(options.MainDB)); - if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) - { - onFailure("No such database, you must open it first"); - return; - } + if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) + { + onFailure("No such database, you must open it first"); + return; + } - hstring absoluteDbPathToAttach; - absoluteDbPathToAttach = ResolveDbFilePath(to_hstring(options.DBFileToAttach)); + hstring absoluteDbPathToAttach; + absoluteDbPathToAttach = ResolveDbFilePath(to_hstring(options.DBFileToAttach)); - std::string statement = ("ATTACH DATABASE '" + to_string(absoluteDbPathToAttach) + "' AS " + options.DBAlias); + std::string statement = ("ATTACH DATABASE '" + to_string(absoluteDbPathToAttach) + "' AS " + options.DBAlias); - sqlite3 *dbHandle = strongThis->openDBs[absoluteDbPath]; + sqlite3* dbHandle = strongThis->openDBs[absoluteDbPath]; - if (sqlite3_exec(dbHandle, statement.c_str(), NULL, NULL, NULL) == SQLITE_OK) - { - onSuccess("Database successfully attached"); - return; - } - else - { - const char *strPtr = sqlite3_errmsg(dbHandle); - std::string errorMessage(strPtr, strlen(strPtr)); - onFailure("Failed to attach database: " + errorMessage); - return; - } + if (sqlite3_exec(dbHandle, statement.c_str(), NULL, NULL, NULL) == SQLITE_OK) + { + onSuccess("Database successfully attached"); + return; } - }); + else + { + const char* strPtr = sqlite3_errmsg(dbHandle); + std::string errorMessage(strPtr, strlen(strPtr)); + onFailure("Failed to attach database: " + errorMessage); + return; + } + } + }); } void SQLitePlugin::Close( @@ -81,39 +82,40 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [options{std::move(options)}, - onSuccess{std::move(onSuccess)}, - onFailure{std::move(onFailure)}, - weak_this = std::weak_ptr(shared_from_this())]() - { - if (auto strongThis = weak_this.lock()) - { + [ + options{ std::move(options) }, + onSuccess{ std::move(onSuccess) }, + onFailure{ std::move(onFailure) }, + weak_this = std::weak_ptr(shared_from_this()) + ]() + { - if (options.Path == nullptr || options.Path.empty()) - { - onFailure("You must specify database path"); - return; - } + if (auto strongThis = weak_this.lock()) { - hstring absoluteDbPath{ResolveDbFilePath(to_hstring(options.Path))}; + if (options.Path == nullptr || options.Path.empty()) + { + onFailure("You must specify database path"); + return; + } - if (CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs)) - { - onSuccess("DB Closed"); - } - else - { - onFailure("Specified db was not open"); - } + hstring absoluteDbPath{ ResolveDbFilePath(to_hstring(options.Path)) }; + + if (CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs)) + { + onSuccess("DB Closed"); } - }); + else + { + onFailure("Specified db was not open"); + } + } + }); } - SQLitePlugin::SQLitePlugin(){}; + SQLitePlugin::SQLitePlugin() {}; - SQLitePlugin::~SQLitePlugin() - { - for (auto &db : openDBs) + SQLitePlugin::~SQLitePlugin() { + for (auto& db : openDBs) { sqlite3_close(db.second); } @@ -125,39 +127,43 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [options{std::move(options)}, - onSuccess{std::move(onSuccess)}, - onFailure{std::move(onFailure)}, - weak_this = std::weak_ptr(shared_from_this())]() + [ + options{ std::move(options) }, + onSuccess{ std::move(onSuccess) }, + onFailure{ std::move(onFailure) }, + weak_this = std::weak_ptr(shared_from_this()) + ]() + { + if (auto strongThis = weak_this.lock()) { - if (auto strongThis = weak_this.lock()) + if (options.Path == nullptr || options.Path.empty()) { - if (options.Path == nullptr || options.Path.empty()) - { - onFailure("You must specify database name"); - return; - } + onFailure("You must specify database name"); + return; + } - hstring absoluteDbPath{ResolveDbFilePath(to_hstring(options.Path))}; + hstring absoluteDbPath{ ResolveDbFilePath(to_hstring(options.Path)) }; - CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs); + CloseDatabaseIfOpen(absoluteDbPath, strongThis->openDBs); - try - { - StorageFile dbFile = StorageFile::GetFileFromPathAsync(absoluteDbPath).get(); - dbFile.DeleteAsync().get(); - } - catch (winrt::hresult_error const &ex) - { - winrt::hstring message = ex.message(); - std::string errorMessage = "Error deleting database: " + to_string(message) + " " + to_string(absoluteDbPath) + "\n"; - onFailure(errorMessage); - return; - } - - onSuccess("Database Deleted"); + try + { + StorageFile dbFile = StorageFile::GetFileFromPathAsync(absoluteDbPath).get(); + dbFile.DeleteAsync().get(); + } + catch (winrt::hresult_error const& ex) + { + winrt::hstring message = ex.message(); + std::string errorMessage = "Error deleting database: " + to_string(message) + " " + to_string(absoluteDbPath) + "\n"; + onFailure(errorMessage); + return; } - }); + + onSuccess("Database Deleted"); + } + }); + + } void SQLitePlugin::EchoStringValue( @@ -166,11 +172,13 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [options{std::move(options)}, - onSuccess{std::move(onSuccess)}]() - { - onSuccess(options.Value); - }); + [ + options{ std::move(options) }, + onSuccess{ std::move(onSuccess) } + ]() + { + onSuccess(options.Value); + }); } void SQLitePlugin::ExecuteSqlBatch( @@ -179,55 +187,62 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [options{std::move(options)}, - onSuccess{std::move(onSuccess)}, - onFailure{std::move(onFailure)}, - weak_this = std::weak_ptr(shared_from_this())]() - { - if (auto strongThis = weak_this.lock()) + [ + options{ std::move(options) }, + onSuccess{ std::move(onSuccess) }, + onFailure{ std::move(onFailure) }, + weak_this = std::weak_ptr(shared_from_this()) + ]() + { + if (auto strongThis = weak_this.lock()) { + if (options.DBArgs.DBName == nullptr) { - if (options.DBArgs.DBName == nullptr) - { - onFailure("You must specify database path"); - return; - } + onFailure("You must specify database path"); + return; + } - hstring absoluteDbPath; - absoluteDbPath = ResolveDbFilePath(to_hstring(options.DBArgs.DBName)); + hstring absoluteDbPath; + absoluteDbPath = ResolveDbFilePath(to_hstring(options.DBArgs.DBName)); - if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) - { - onFailure("No such database, you must open it first"); - return; - } + if (strongThis->openDBs.find(absoluteDbPath) == strongThis->openDBs.end()) + { + onFailure("No such database, you must open it first"); + return; + } - sqlite3 *dbHandle = strongThis->openDBs[absoluteDbPath]; - std::vector results; + sqlite3* dbHandle = strongThis->openDBs[absoluteDbPath]; + std::vector results; - for (auto &query : options.Executes) + for (auto& query : options.Executes) + { + JSValue result; + if (ExecuteQuery(dbHandle, query, result)) { - JSValue result; - if (ExecuteQuery(dbHandle, query, result)) - { - // Query succeeded - results.push_back( - {{"qid", query.QID}, - {"type", "success"}, - {"result", std::move(result)}}); - } - else - { - // Query failed - results.push_back( - {{"qid", query.QID}, - {"type", "error"}, - {"error", result.AsString()}, - {"result", result.AsString()}}); - } + // Query succeeded + results.push_back( + { + { "qid", query.QID }, + { "type", "success" }, + { "result", std::move(result)} + } + ); + } + else + { + // Query failed + results.push_back( + { + { "qid", query.QID }, + { "type", "error" }, + { "error", result.AsString()}, + { "result", result.AsString()} + } + ); } - onSuccess(std::move(results)); } - }); + onSuccess(std::move(results)); + } + }); } void SQLitePlugin::Open( @@ -236,95 +251,97 @@ namespace SQLitePlugin std::function onFailure) noexcept { serialReactDispatcher.Post( - [options{std::move(options)}, - onSuccess{std::move(onSuccess)}, - onFailure{std::move(onFailure)}, - weak_this = std::weak_ptr(shared_from_this())]() - { - if (auto strongThis = weak_this.lock()) + [ + options{ std::move(options) }, + onSuccess{ std::move(onSuccess) }, + onFailure{ std::move(onFailure) }, + weak_this = std::weak_ptr(shared_from_this()) + ]() + { + if (auto strongThis = weak_this.lock()) { + const std::string* dbFileName = &options.Name; + + if (dbFileName == nullptr || dbFileName->empty()) { - const std::string *dbFileName = &options.Name; + onFailure("You must specify the database name"); + return; + } - if (dbFileName == nullptr || dbFileName->empty()) - { - onFailure("You must specify the database name"); - return; - } + hstring absoluteDbPath{ ResolveDbFilePath(to_hstring(*dbFileName)) }; - hstring absoluteDbPath{ResolveDbFilePath(to_hstring(*dbFileName))}; + if (strongThis->openDBs.find(absoluteDbPath) != strongThis->openDBs.end()) + { + onSuccess("Database opened"); + return; + } - if (strongThis->openDBs.find(absoluteDbPath) != strongThis->openDBs.end()) + IAsyncOperation assetFileOp = ResolveAssetFile(options.AssetFileName, *dbFileName); + StorageFile assetFile = nullptr; + if (assetFileOp != nullptr) + { + try { - onSuccess("Database opened"); + assetFile = assetFileOp.get(); + } + catch (hresult_error const& ex) + { + onFailure("Unable to open asset file: " + winrt::to_string(ex.message())); return; } + } + + int openFlags{ 0 }; + openFlags |= SQLITE_OPEN_NOMUTEX; + + if (options.ReadOnly && assetFileOp != nullptr) + { + openFlags |= SQLITE_OPEN_READONLY; + absoluteDbPath = assetFile.Path(); + } + else + { + openFlags |= SQLITE_OPEN_READWRITE; + openFlags |= SQLITE_OPEN_CREATE; - IAsyncOperation assetFileOp = ResolveAssetFile(options.AssetFileName, *dbFileName); - StorageFile assetFile = nullptr; if (assetFileOp != nullptr) { try { - assetFile = assetFileOp.get(); + CopyDbAsync(assetFile, to_hstring(*dbFileName)).GetResults(); } - catch (hresult_error const &ex) + catch (hresult_error const& ex) { - onFailure("Unable to open asset file: " + winrt::to_string(ex.message())); - return; - } - } - - int openFlags{0}; - openFlags |= SQLITE_OPEN_NOMUTEX; - - if (options.ReadOnly && assetFileOp != nullptr) - { - openFlags |= SQLITE_OPEN_READONLY; - absoluteDbPath = assetFile.Path(); - } - else - { - openFlags |= SQLITE_OPEN_READWRITE; - openFlags |= SQLITE_OPEN_CREATE; - - if (assetFileOp != nullptr) - { - try + if (ex.code() == 0x800700B7) { - CopyDbAsync(assetFile, to_hstring(*dbFileName)).get(); - } - catch (hresult_error const &ex) + // Ignore, CopyDbAsync throws when the file already exists. + } else { - if (ex.code() == 0x800700B7) - { - // Ignore, CopyDbAsync throws when the file already exists. - } - else - { - onFailure("Unable to copy asset file: " + winrt::to_string(ex.message())); - return; - } + onFailure("Unable to copy asset file: " + winrt::to_string(ex.message())); + return; } } } + } - sqlite3 *dbHandle = nullptr; + sqlite3* dbHandle = nullptr; - int result = sqlite3_open_v2(to_string(absoluteDbPath).c_str(), &dbHandle, openFlags, nullptr); - if (result == SQLITE_OK) - { - strongThis->openDBs[absoluteDbPath] = dbHandle; - onSuccess("Database opened"); - } - else - { - onFailure("Unable to open DB"); - } + int result = sqlite3_open_v2(to_string(absoluteDbPath).c_str(), &dbHandle, openFlags, nullptr); + if (result == SQLITE_OK) + { + strongThis->openDBs[absoluteDbPath] = dbHandle; + onSuccess("Database opened"); + } + else + { + onFailure("Unable to open DB"); } - }); + } + }); + } - void SQLitePlugin::BindStatement(sqlite3_stmt *stmt_ptr, int argIndex, const JSValue &arg) + + void SQLitePlugin::BindStatement(sqlite3_stmt* stmt_ptr, int argIndex, const JSValue& arg) { switch (arg.Type()) { @@ -349,11 +366,11 @@ namespace SQLitePlugin } } - bool SQLitePlugin::CloseDatabaseIfOpen(const hstring &absoluteDbPath, std::map &openDBs) + bool SQLitePlugin::CloseDatabaseIfOpen(const hstring& absoluteDbPath, std::map& openDBs) { if (openDBs.find(absoluteDbPath) != openDBs.end()) { - sqlite3 *dbHandle = openDBs[absoluteDbPath]; + sqlite3* dbHandle = openDBs[absoluteDbPath]; if (sqlite3_close(dbHandle) != SQLITE_OK) { @@ -367,27 +384,27 @@ namespace SQLitePlugin return false; } - IAsyncOperation SQLitePlugin::CopyDbAsync(const StorageFile &srcDbFile, const hstring &destDbFileName) + IAsyncOperation SQLitePlugin::CopyDbAsync(const StorageFile& srcDbFile, const hstring& destDbFileName) { // This implementation is closely related to ResolveDbFilePath. return srcDbFile.CopyAsync(ApplicationData::Current().LocalFolder(), destDbFileName, NameCollisionOption::FailIfExists); } - bool SQLitePlugin::ExecuteQuery(sqlite3 *dbHandle, const DBQuery &query, JSValue &result) + bool SQLitePlugin::ExecuteQuery(sqlite3* dbHandle, const DBQuery& query, JSValue& result) { if (query.SQL == nullptr || query.SQL == "") { - result = JSValue{"You must specify a sql query to execute"}; + result = JSValue{ "You must specify a sql query to execute" }; return false; } int previousRowsAffected = sqlite3_total_changes(dbHandle); - sqlite3_stmt *stmt; + sqlite3_stmt* stmt; int prepResult = sqlite3_prepare_v2(dbHandle, query.SQL.c_str(), -1, &stmt, nullptr); if (prepResult != SQLITE_OK) { - result = JSValue{sqlite3_errmsg(dbHandle)}; + result = JSValue{ sqlite3_errmsg(dbHandle) }; return false; } @@ -395,7 +412,7 @@ namespace SQLitePlugin { // sqlite bind uses 1-based indexing for the arguments int argIndex = 1; - for (auto &arg : query.Params) + for (auto& arg : query.Params) { BindStatement(stmt, argIndex, arg); argIndex++; @@ -421,7 +438,7 @@ namespace SQLitePlugin { int currentRowsAffected = sqlite3_total_changes(dbHandle); rowsAffected = currentRowsAffected - previousRowsAffected; - sqlite3_int64 currentInsertId = sqlite3_last_insert_rowid(dbHandle); + sqlite3_int64 currentInsertId = sqlite3_last_insert_rowid(dbHandle); if (rowsAffected > 0 && currentInsertId != 0) { insertId = currentInsertId; @@ -431,9 +448,9 @@ namespace SQLitePlugin } default: { - const char *strPtr = sqlite3_errmsg(dbHandle); + const char* strPtr = sqlite3_errmsg(dbHandle); std::string errorMessage(strPtr, strlen(strPtr)); - result = JSValue{errorMessage}; + result = JSValue{ errorMessage }; keepGoing = false; isError = true; } @@ -449,9 +466,10 @@ namespace SQLitePlugin } JSValueObject resultSet = - { - {"rows", std::move(resultRows)}, - {"rowsAffected", rowsAffected}}; + { + { "rows", std::move(resultRows) }, + { "rowsAffected", rowsAffected } + }; if (insertId != 0) { @@ -462,7 +480,7 @@ namespace SQLitePlugin return true; } - JSValue SQLitePlugin::ExtractColumn(sqlite3_stmt *stmt, int columnIndex) + JSValue SQLitePlugin::ExtractColumn(sqlite3_stmt* stmt, int columnIndex) { switch (sqlite3_column_type(stmt, columnIndex)) { @@ -472,16 +490,16 @@ namespace SQLitePlugin return sqlite3_column_double(stmt, columnIndex); case SQLITE_TEXT: { - const char *strPtr = (char *)sqlite3_column_text(stmt, columnIndex); + const char* strPtr = (char*)sqlite3_column_text(stmt, columnIndex); return std::string(strPtr, strlen(strPtr)); } case SQLITE_BLOB: { // JSON does not support raw binary data. You can't write a binary blob using this module - // In case we have a pre-populated database with a binary blob in it, + // In case we have a pre-populated database with a binary blob in it, // we are going to base64 encode it and return as a string. // This is consistent with iOS implementation. - const void *ptr = sqlite3_column_blob(stmt, columnIndex); + const void* ptr = sqlite3_column_blob(stmt, columnIndex); int length = sqlite3_column_bytes(stmt, columnIndex); Buffer buffer = Buffer(length); memcpy(buffer.data(), ptr, length); @@ -494,13 +512,13 @@ namespace SQLitePlugin } } - JSValueObject SQLitePlugin::ExtractRow(sqlite3_stmt *stmt) + JSValueObject SQLitePlugin::ExtractRow(sqlite3_stmt* stmt) { JSValueObject row{}; int columnCount = sqlite3_column_count(stmt); for (int i = 0; i < columnCount; i++) { - const char *strPtr = (char *)sqlite3_column_name(stmt, i); + const char* strPtr = (char*)sqlite3_column_name(stmt, i); std::string columnName(strPtr, strlen(strPtr)); JSValue columnValue = ExtractColumn(stmt, i); if (!columnValue.IsNull()) @@ -511,7 +529,7 @@ namespace SQLitePlugin return row; } - IAsyncOperation SQLitePlugin::ResolveAssetFile(const std::string &assetFilePath, const std::string &dbFileName) + IAsyncOperation SQLitePlugin::ResolveAssetFile(const std::string& assetFilePath, const std::string& dbFileName) { if (assetFilePath == nullptr || assetFilePath.length() == 0) { @@ -541,4 +559,4 @@ namespace SQLitePlugin { return ApplicationData::Current().LocalFolder().Path() + L"\\" + dbFileName; } -} +} \ No newline at end of file From b11016fac34feef294f1062ebac388f694531bd6 Mon Sep 17 00:00:00 2001 From: Ahab Date: Thu, 12 Jan 2023 19:57:19 -0600 Subject: [PATCH 6/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a2f43317..ac828552 100644 --- a/README.md +++ b/README.md @@ -334,6 +334,7 @@ Almost identical to iOS, but we'll be using Visual Studio 2. Open `windows\.sln` with Visual Studio 3. Right-click `\Assets` in the Solution Explorer 4. `Add -> Existing Item` and select your database +5. Finally, set `Content` to `True` in File Properties (right side of Visual Studio, by default) Enjoy! From 0c7f51b3df33980cb6f1ce63aab5660d2ecca79c Mon Sep 17 00:00:00 2001 From: mackenzie Date: Thu, 12 Jan 2023 20:06:55 -0600 Subject: [PATCH 7/8] Last mistake, I swear --- platforms/windows/SQLitePlugin/SQLitePlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp index ce6215ac..65a245eb 100644 --- a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp +++ b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp @@ -307,7 +307,7 @@ namespace SQLitePlugin { try { - CopyDbAsync(assetFile, to_hstring(*dbFileName)).GetResults(); + CopyDbAsync(assetFile, to_hstring(*dbFileName)).get(); } catch (hresult_error const& ex) { From 591ae67ad82854aa8c171586fa9541226e56e7aa Mon Sep 17 00:00:00 2001 From: Ahab Date: Fri, 13 Jan 2023 18:58:13 -0600 Subject: [PATCH 8/8] More descriptive error-coding Works as suggested without change Co-authored-by: Chris Glein <26607885+chrisglein@users.noreply.github.com> --- platforms/windows/SQLitePlugin/SQLitePlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp index 65a245eb..45414b88 100644 --- a/platforms/windows/SQLitePlugin/SQLitePlugin.cpp +++ b/platforms/windows/SQLitePlugin/SQLitePlugin.cpp @@ -311,7 +311,7 @@ namespace SQLitePlugin } catch (hresult_error const& ex) { - if (ex.code() == 0x800700B7) + if (ex.code() == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) { // Ignore, CopyDbAsync throws when the file already exists. } else