DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
SqliteDatabaseBackend.cpp
Go to the documentation of this file.
1#include "pch.h"
4
5#if defined(DOTNETDUPE_USE_SQLITE)
6#include <sqlite3.h>
7
8namespace DotNetDupe {
9 namespace System {
10 namespace Data {
11 namespace Internal {
12
13 struct SqliteDatabaseBackend::Impl : public DotNetDupe::System::Object {
14 sqlite3* m_pDb = nullptr;
15 std::string m_connectionString;
16
17 ~Impl() {
18 if (m_pDb) sqlite3_close(m_pDb);
19 }
20 };
21
22 static std::string ExtractSqliteDataSource(const std::string& connStr) {
24 size_t srcIdx = connStr.find("Data Source=");
25 if (srcIdx == std::string::npos) return "sqlite_default.db";
26 srcIdx += 12;
27 size_t endIdx = connStr.find(";", srcIdx);
28 return (endIdx != std::string::npos) ? connStr.substr(srcIdx, endIdx - srcIdx) : connStr.substr(srcIdx);
29 }
30
31 SqliteDatabaseBackend::SqliteDatabaseBackend(const DotNetDupe::System::String& connStr) : m_pImpl(SmartPointer<Impl>::NewShared()) {
33 std::string connStrStd = connStr.GetRawString() ? connStr.GetRawString() : "";
34 m_pImpl->m_connectionString = connStrStd;
35 std::string filename = ExtractSqliteDataSource(connStrStd);
36
38 if (sqlite3_open(filename.c_str(), &m_pImpl->m_pDb) != SQLITE_OK) {
39 std::string errorMsg = m_pImpl->m_pDb ? sqlite3_errmsg(m_pImpl->m_pDb) : "Failed to open SQLite database.";
40 if (m_pImpl->m_pDb) { sqlite3_close(m_pImpl->m_pDb); m_pImpl->m_pDb = nullptr; }
42 }
43 }
44
45 SqliteDatabaseBackend::~SqliteDatabaseBackend() {
46 }
47
48 void SqliteDatabaseBackend::ClearDatabase() {
50 }
51
52 static void BindSqliteParameters(sqlite3_stmt* pStmt, const Collections::Generic::Dictionary<String, String>& parameters) {
54 for (auto const& [paramName, paramVal] : parameters) {
55 std::string stdParamName = paramName.GetRawString() ? paramName.GetRawString() : "";
56 std::string stdParamVal = paramVal.GetRawString() ? paramVal.GetRawString() : "";
57 int idx = sqlite3_bind_parameter_index(pStmt, stdParamName.c_str());
58 if (idx > 0) sqlite3_bind_text(pStmt, idx, stdParamVal.c_str(), -1, SQLITE_TRANSIENT);
59 }
60 }
61
62 static void FetchSqliteRows(sqlite3_stmt* pStmt, int colCount, Collections::Generic::List<Row>& resultRows, int& rc) {
64 while (rc == SQLITE_ROW) {
65 Row r;
66 for (int i = 0; i < colCount; ++i) {
67 const unsigned char* valText = sqlite3_column_text(pStmt, i);
68 r.Values.Add(valText ? String(reinterpret_cast<const char*>(valText)) : String(""));
69 }
70 resultRows.Add(std::move(r));
71 rc = sqlite3_step(pStmt);
72 }
73 }
74
75 static void CollectSqliteColumnNames(sqlite3_stmt* pStmt, Collections::Generic::List<String>& columnNames) {
77 int colCount = sqlite3_column_count(pStmt);
78 for (int i = 0; i < colCount; ++i) {
79 const char* colName = sqlite3_column_name(pStmt, i);
80 columnNames.Add(colName ? String(colName) : String(""));
81 }
82 }
83
84 static sqlite3_stmt* PrepareSqliteStatement(sqlite3* pDb, const String& sql) {
86 if (!pDb) throw InvalidOperationException("Database is not open.");
87 sqlite3_stmt* pStmt = nullptr;
88 if (sqlite3_prepare_v2(pDb, sql.GetRawString() ? sql.GetRawString() : "", -1, &pStmt, nullptr) != SQLITE_OK) {
89 throw InvalidOperationException(sqlite3_errmsg(pDb));
90 }
91 return pStmt;
92 }
93
94 static void FinalizeExecution(sqlite3* pDb, sqlite3_stmt* pStmt, int rc, int& rowsAffected) {
96 if (rc == SQLITE_DONE) rowsAffected = sqlite3_changes(pDb);
97 else { std::string err = sqlite3_errmsg(pDb); sqlite3_finalize(pStmt); throw InvalidOperationException(err.c_str()); }
98 sqlite3_finalize(pStmt);
99 }
100
101 Collections::Generic::List<Row> SqliteDatabaseBackend::Execute(
102 const String& sql,
103 const Collections::Generic::Dictionary<String, String>& parameters,
104 Collections::Generic::List<String>& columnNames,
105 int& rowsAffected
106 ) {
108 rowsAffected = 0; columnNames.Clear();
109 sqlite3_stmt* pStmt = PrepareSqliteStatement(m_pImpl->m_pDb, sql);
110
112 BindSqliteParameters(pStmt, parameters);
113 int rc = sqlite3_step(pStmt);
114 CollectSqliteColumnNames(pStmt, columnNames);
115
117 Collections::Generic::List<Row> resultRows;
118 FetchSqliteRows(pStmt, sqlite3_column_count(pStmt), resultRows, rc);
119 FinalizeExecution(m_pImpl->m_pDb, pStmt, rc, rowsAffected);
120 return resultRows;
121 }
122
123 }
124 }
125 }
126}
127#endif
Defines the exception thrown when a method call is invalid for the object's current state.
The exception that is thrown when a method call is invalid for the object's current state.
const char * GetRawString() const
Definition String.cpp:230