diff --git a/HanddleAI/HandleAiUi-new/CMakeLists.txt b/HanddleAI/HandleAiUi-new/CMakeLists.txt index fc4e2a06..31e13ae8 100644 --- a/HanddleAI/HandleAiUi-new/CMakeLists.txt +++ b/HanddleAI/HandleAiUi-new/CMakeLists.txt @@ -55,6 +55,7 @@ if(ANDROID) ${APP_HEADERPP_FILES} ${APP_HEADER_FILES} ${APP_SRC_FILES} + ${BREAKPAD_WINDOWS_CC_FILES} ${APP_UI_FILES} ) else() @@ -65,6 +66,7 @@ else() ${APP_HEADERPP_FILES} ${APP_HEADER_FILES} ${APP_SRC_FILES} + ${BREAKPAD_WINDOWS_CC_FILES} ${APP_UI_FILES} "ff.rc" "icon.qrc") else() message( "/////////////RELEASE///////////////") @@ -75,6 +77,7 @@ else() ${APP_HEADERPP_FILES} ${APP_HEADER_FILES} ${APP_SRC_FILES} + ${BREAKPAD_WINDOWS_CC_FILES} ${APP_UI_FILES} "ff.rc" "icon.qrc" ) endif() @@ -83,7 +86,6 @@ endif() SET(ALL_DLL ${ENV_TARGET} ${lib_list} - ) message("all_lib :" ${ALL_DLL}) diff --git a/HanddleAI/HandleAiUi-new/appConfig/sqlManager/sqlManager.cpp b/HanddleAI/HandleAiUi-new/appConfig/sqlManager/sqlManager.cpp index c7e897b6..4990a60f 100644 --- a/HanddleAI/HandleAiUi-new/appConfig/sqlManager/sqlManager.cpp +++ b/HanddleAI/HandleAiUi-new/appConfig/sqlManager/sqlManager.cpp @@ -3,6 +3,8 @@ sqlManager::sqlError sqlManager::GetTables(std::vector& input) { + sqlTableDatas output; + dbPtr->Select("", std::vector(),"","", output); return success; } std::vector getFutureDates(int days) @@ -120,6 +122,8 @@ void sqlManager::initialization(std::string dataBasePath, bool backup) } } std::cout << "SQL init end" << std::endl; + std::vector input; + GetTables(input); mutex.unlock(); } diff --git a/HanddleAI/HandleAiUi-new/core.hpp b/HanddleAI/HandleAiUi-new/core.hpp index 3f337970..b8311890 100644 --- a/HanddleAI/HandleAiUi-new/core.hpp +++ b/HanddleAI/HandleAiUi-new/core.hpp @@ -2,10 +2,12 @@ #include #include #include +#define QtExist 1 class core { public: //APP BASIC core() {}; logService _logService; -}; \ No newline at end of file +}; +#pragma once \ No newline at end of file diff --git a/HanddleAI/HandleAiUi-new/front/pages/logPageWidget.h b/HanddleAI/HandleAiUi-new/front/pages/logPageWidget.h index e159e820..0ab63918 100644 --- a/HanddleAI/HandleAiUi-new/front/pages/logPageWidget.h +++ b/HanddleAI/HandleAiUi-new/front/pages/logPageWidget.h @@ -13,13 +13,10 @@ #include #include #include - -static QTextEdit* globalQtTextEdit=nullptr; -static QFile* qtLogFile; -static QFile* stdLogFile; -static QTextStream* globalQtlogFile = nullptr; -static QTextStream* globalStdlogFile = nullptr; - +#include +#include +#include +#include class logWidget :public QWidget { public: @@ -68,13 +65,13 @@ public: stdConsoleOutput.setReadOnly(true); qtConsoleOutput.setReadOnly(true); - addConfigWidget(u8"控制台", &stdConsoleOutput, logType::stdConsle); - addConfigWidget(u8"QT日志", &qtConsoleOutput, logType::qtConsle); + addLogWidget(u8"控制台", &stdConsoleOutput, logType::stdConsle); + addLogWidget(u8"QT日志", &qtConsoleOutput, logType::qtConsle); btns[0]->pressed(); Layout->addWidget(widgets); } - void addConfigWidget(QString btnText,QTextEdit* textWidget, logType type) + void addLogWidget(QString btnText,QTextEdit* textWidget, logType type) { QPalette palette; palette.setColor(QPalette::WindowText, Qt::white); @@ -126,35 +123,30 @@ public: toolHLayout->addWidget(lineedit); widgetLayout->addWidget(textWidget); - if(type==stdConsle) - { - /*stdConsoleRedirector _stdConsoleRedirector(std::cout, textWidget); - stdLogFile = new QFile(QString("./Log/%1StdLog.log").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd"))); - if (stdLogFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { - globalStdlogFile = new QTextStream(); - globalStdlogFile->setCodec("UTF-8"); - globalStdlogFile->setDevice(stdLogFile); - }*/ - } - else if (type == qtConsle) - { - qtLogFile =new QFile(QString("./Log/%1QtLog.log").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd"))); - if (qtLogFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { - globalQtlogFile = new QTextStream(); - globalQtlogFile->setCodec("UTF-8"); - globalQtlogFile->setDevice(qtLogFile); - } - - globalQtTextEdit = textWidget; - //globalQtTextEdit->setUpdatesEnabled(false); - /* qInstallMessageHandler(MessageHandler); - qInfo() << "This is a Qt Info message."; - qWarning() << "This is a Qt Warning message."; - qDebug() << "This is a Qt Debug message."; - qCritical() << "This is a Qt Critical message.";*/ - //qFatal("This is a Qt Fatal message."); - } + textWidget->setStyleSheet("background-color: rgb(30,30,30);"); + } + QString getCurrentDate() { + QDate currentDate = QDate::currentDate(); + return currentDate.toString("yyyy-MM-dd"); + } + QString getCurrentTime() { + QTime currentTime = QTime::currentTime(); + return currentTime.toString("HH:mm:ss.zzz"); + } +public slots: + void recvStdLogMsg(QString logMsg) + { + QString text = "" + getCurrentDate() + " " + "" + getCurrentTime() + " " + "" + logMsg + " "; + stdConsoleOutput.append(text); + } + void recvQtLogMsg(QString logMsg) + { + QString text = "" + getCurrentDate() + " " + "" + getCurrentTime() + " " + "" + logMsg + " "; + qtConsoleOutput.append(text); } - }; #pragma once \ No newline at end of file diff --git a/HanddleAI/HandleAiUi-new/front/pages/sqlControlWidget.h b/HanddleAI/HandleAiUi-new/front/pages/sqlControlWidget.h index a93c2ded..2205eefb 100644 --- a/HanddleAI/HandleAiUi-new/front/pages/sqlControlWidget.h +++ b/HanddleAI/HandleAiUi-new/front/pages/sqlControlWidget.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include class sqliteReader : public QWidget @@ -32,6 +34,7 @@ public: QLabel* icon, * Title; QStackedWidget* widgets = new QStackedWidget(); sqliteReader _sqliteReader; + QTableWidget* sqlWidget = new QTableWidget(); QLabel* disconnnectedLabel = new QLabel("服务已离线"); sqlControlWidget() { @@ -55,21 +58,25 @@ public: Title->setPalette(palette); Title->setAlignment(Qt::AlignLeft); - disconnnectedLabel->setFont(font2); - disconnnectedLabel->setPalette(palette); - disconnnectedLabel->setAlignment(Qt::AlignCenter); + sqlWidget->setFont(font2); + sqlWidget->setPalette(palette); + //sqlWidget->setAlignment(Qt::AlignCenter); Layout->addWidget(Title); btnLayout->setContentsMargins(0, 0, 0, 0); btnLayout->setAlignment(Qt::AlignLeft); - btnLayout->addWidget(disconnnectedLabel); + btnLayout->addWidget(sqlWidget); Layout->addLayout(btnLayout); Layout->addWidget(&_sqliteReader); + parsingSqlData(); + } + void parsingSqlData() + { + bridge._appConfig->_sqlManager; } - }; #pragma once \ No newline at end of file diff --git a/HanddleAI/HandleAiUi-new/logger/logService.hpp b/HanddleAI/HandleAiUi-new/logger/logService.hpp index b0fe9371..7d90607f 100644 --- a/HanddleAI/HandleAiUi-new/logger/logService.hpp +++ b/HanddleAI/HandleAiUi-new/logger/logService.hpp @@ -1,13 +1,52 @@ #include #include #include +static std::string logBasePath = "./Log"; +class logToQtWidget : public QObject +{ + Q_OBJECT +public: + void sendStdLogSignal(std::string str) + { + emit stdLogSignal(QString::fromStdString(str)); + } + void sendQtLogSignal(std::string str) + { + emit qtLogSignal(QString::fromStdString(str)); + } +signals: + void stdLogSignal(QString logMsg); + void qtLogSignal(QString logMsg); +}; class logService { public: stdConsoleRedirector* _stdConsoleRedirector; + FileLogger* stdFileLogger; + logToQtWidget _logToQtWidget; logService() { + //STD日志 _stdConsoleRedirector = new stdConsoleRedirector(std::cout); - LogMsg::Create(LogMsg::LogMsgType::DebugMsg); + //STD日志储存到文件 + stdFileLogger = new FileLogger(logBasePath); + _stdConsoleRedirector->registerCallback([&](const std::string& output) { + stdFileLogger->processOutput(output); + }); + //STD日志到Qt界面 + _stdConsoleRedirector->registerCallback([&](const std::string& output) { + _logToQtWidget.sendStdLogSignal(output); + }); + //QT日志 + LogMsg::Create(LogMsg::LogMsgType::DebugMsg, logBasePath); + qtLogCallbacks.push_back(([&](const std::string& output) { + _logToQtWidget.sendQtLogSignal(output); + })); } -}; \ No newline at end of file + ~logService() + { + delete _stdConsoleRedirector; + delete stdFileLogger; + } +}; +#pragma once \ No newline at end of file diff --git a/HanddleAI/HandleAiUi-new/logger/qtLogger.cpp b/HanddleAI/HandleAiUi-new/logger/qtLogger.cpp index 97902204..fdd16b05 100644 --- a/HanddleAI/HandleAiUi-new/logger/qtLogger.cpp +++ b/HanddleAI/HandleAiUi-new/logger/qtLogger.cpp @@ -9,7 +9,7 @@ std::once_flag LogMsg::ms_CreateOnce; QString LogMsg::ms_LogDirPath; LogMsg::LogMsgType LogMsg::ms_LogMstType; QString LogMsg::ms_TrailMSG; - +std::vector qtLogCallbacks; LogMsg::LogMsg(const LogMsgType logmsgtype) { ms_LogMstType = logmsgtype; diff --git a/HanddleAI/HandleAiUi-new/logger/qtLogger.h b/HanddleAI/HandleAiUi-new/logger/qtLogger.h index dde3e7ea..f2331f5d 100644 --- a/HanddleAI/HandleAiUi-new/logger/qtLogger.h +++ b/HanddleAI/HandleAiUi-new/logger/qtLogger.h @@ -39,6 +39,9 @@ typedef std::functionkey_sendMsg;//time msg extern key_sendMsg m_key_callback; + +using CallbackType = std::function; +extern std::vector qtLogCallbacks; class LogMsg final { private: @@ -57,9 +60,6 @@ public: CriticalMsg, FatalMsg, }; - -public: - static inline void outputMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg) { if (LogMsgType::InfoMsg == ms_LogMstType && QtDebugMsg == type) @@ -121,27 +121,32 @@ public: const QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz ddd"); const QString current_date = QString("(%1)").arg(current_date_time); const QString message = QString("%1 ,%2 ,%3 ,%4 ,%5").arg(current_date).arg(pretext).arg(context_info).arg(msg).arg(ms_TrailMSG) + "\n"; + const QString message2 = QString("%1 ,%2 ,%3 ,%4 ").arg(pretext).arg(context_info).arg(msg).arg(ms_TrailMSG); { std::lock_guard lock2(ms_manager->m_LogMsgBufDeqLock); ms_manager->m_LogMsgBufVec.push_back(message); } + for (const auto& callback : qtLogCallbacks) { + callback(message2.toStdString()); + } ms_manager->m_BufferCondVar.notify_all(); } static void SetExtMsg(const std::string& trailmsg) { - ms_TrailMSG = QString::fromStdString(trailmsg); } - static int Create(const LogMsgType logmsgtype) + static int Create(const LogMsgType logmsgtype,std::string basePath) { int retval{ -1 }; std::call_once(ms_CreateOnce, [&]() { - const QString workdir = QCoreApplication::applicationDirPath(); + /*const QString workdir = QCoreApplication::applicationDirPath(); ms_LogDirPath = workdir + QDir::separator() + "Log"; - ms_LogDirPath = QDir::toNativeSeparators(ms_LogDirPath); + ms_LogDirPath = QDir::toNativeSeparators(ms_LogDirPath);*/ + const QString workdir = QString::fromStdString(basePath); + ms_LogDirPath = QDir::toNativeSeparators(workdir); const QDir dir(ms_LogDirPath); if (!dir.exists()) { diff --git a/HanddleAI/HandleAiUi-new/logger/stdLogger.hpp b/HanddleAI/HandleAiUi-new/logger/stdLogger.hpp index c1a08945..da33f2f5 100644 --- a/HanddleAI/HandleAiUi-new/logger/stdLogger.hpp +++ b/HanddleAI/HandleAiUi-new/logger/stdLogger.hpp @@ -2,7 +2,84 @@ #include #include #include - +#include +#include +#include +#include +//SAMPLE +class customCallbackFunc { +public: + virtual void processOutput(const std::string& output) { + std::cout << "customCallbackFunc received: " << output; + } +}; + +//输出到文件 +class FileLogger : public customCallbackFunc { +public: + FileLogger(std::string basePath):logBasePath(basePath){ + updateLogFile(); + } + + ~FileLogger() { + if (logFile.is_open()) { + logFile.close(); + } + } + + virtual void processOutput(const std::string& output) override { + updateLogFileIfNeeded(); + if (logFile.is_open()) { + logFile << (getCurrentTime()+">>>"+output); + logFile.flush(); + } + else { + std::cerr << "Failed to write to log file: file is not open" << std::endl; + } + } + +private: + std::string logBasePath; + std::ofstream logFile; + std::string currentDate; + std::string getCurrentDate() { + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); + std::ostringstream oss; + oss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d"); + return oss.str(); + } + std::string getCurrentTime() { + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); + auto microseconds = std::chrono::duration_cast(now.time_since_epoch()) % 1000000; + + std::ostringstream oss; + oss << std::put_time(std::localtime(&in_time_t), "(%Y-%m-%d %H-%M-%S"); + oss << " " << std::setfill('0') << std::setw(6) << microseconds.count()<<"):"; + return oss.str(); + } + void updateLogFile() { + currentDate = getCurrentDate(); + std::string filename = logBasePath+"/"+currentDate + "std.log"; + if (logFile.is_open()) { + logFile.close(); + } + logFile.open(filename, std::ios::app); + if (!logFile.is_open()) { + throw std::runtime_error("Unable to open log file: " + filename); + } + } + + void updateLogFileIfNeeded() { + std::string newDate = getCurrentDate(); + if (newDate != currentDate) { + updateLogFile(); + } + } +}; + +//截取std::cout的内容 class stdConsoleRedirector : public std::streambuf { public: using CallbackType = std::function; @@ -11,7 +88,6 @@ public: : originalStream(stream), originalBuffer(stream.rdbuf(this)) {} ~stdConsoleRedirector() { - // 恢复原始的缓冲区 originalStream.rdbuf(originalBuffer); } @@ -21,7 +97,6 @@ public: } protected: - // 重载overflow方法来拦截输出 virtual int overflow(int c) override { if (c == EOF) { return EOF; @@ -30,7 +105,6 @@ protected: char ch = static_cast(c); buffer += ch; if (ch == '\n') { - // 当遇到换行符时,调用所有回调函数 for (const auto& callback : callbacks) { callback(buffer); } diff --git a/HanddleAI/HandleAiUi-new/main.cpp b/HanddleAI/HandleAiUi-new/main.cpp index cf90eed2..ca1db70c 100644 --- a/HanddleAI/HandleAiUi-new/main.cpp +++ b/HanddleAI/HandleAiUi-new/main.cpp @@ -18,8 +18,7 @@ #include #include - -void connectSignalAndSlots(MainWindow* front, backEndManager* back) +void connectSignalAndSlots(MainWindow* front, backEndManager* back, core* _core) { QObject::connect(back, &backEndManager::sendPixmapImageWidget, front, &MainWindow::recvPixmapImageWidget); QObject::connect(back, &backEndManager::sendInitString, front, &MainWindow::recvInitString); @@ -38,9 +37,21 @@ void connectSignalAndSlots(MainWindow* front, backEndManager* back) back, &backEndManager::startManualDetSlot); QObject::connect(front->_displayWidget->_switchProductWidget, &switchProductWidget::startArmProgramSignal, back, &backEndManager::writeArmStartSignal); + ///////////TEMP + QObject::connect(&_core->_logService._logToQtWidget, &logToQtWidget::stdLogSignal, + front->_logWidget, &logWidget::recvStdLogMsg); + QObject::connect(&_core->_logService._logToQtWidget, &logToQtWidget::qtLogSignal, + front->_logWidget, &logWidget::recvQtLogMsg); } + + int main(int argc, char* argv[]) { + HWND hWnd = GetConsoleWindow(); + if (hWnd) { + ShowWindow(hWnd, SW_HIDE); + }// 闅愯棌鎺у埗鍙扮獥鍙� + core _core; QApplication a(argc, argv); //妫�娴嬫槸鍚﹀凡杩愯 @@ -66,7 +77,7 @@ int main(int argc, char* argv[]) MainWindow w(&_appConfig); w.show(); backEndManager _backEnd(&_appConfig); - connectSignalAndSlots(&w,&_backEnd); + connectSignalAndSlots(&w,&_backEnd,&_core); _backEnd.initialization(); return a.exec(); } diff --git a/env/3DAI.zip b/env/3DAI.zip deleted file mode 100644 index 743df17a..00000000 Binary files a/env/3DAI.zip and /dev/null differ diff --git a/env/halcon (2).zip b/env/halcon (2).zip deleted file mode 100644 index c8ccc00a..00000000 Binary files a/env/halcon (2).zip and /dev/null differ diff --git a/env/halcon (3).zip b/env/halcon (3).zip deleted file mode 100644 index 3a3db8ab..00000000 Binary files a/env/halcon (3).zip and /dev/null differ diff --git a/env/halcon.zip b/env/halcon.zip deleted file mode 100644 index 5db1c992..00000000 Binary files a/env/halcon.zip and /dev/null differ diff --git a/env/myTools.zip b/env/myTools.zip deleted file mode 100644 index 34aa4121..00000000 Binary files a/env/myTools.zip and /dev/null differ diff --git a/env/sqliteCpp/dll/SQLiteCpp.dll b/env/sqliteCpp/dll/SQLiteCpp.dll new file mode 100644 index 00000000..b5f3642e Binary files /dev/null and b/env/sqliteCpp/dll/SQLiteCpp.dll differ diff --git a/env/sqliteCpp/dll/sqlite3.dll b/env/sqliteCpp/dll/sqlite3.dll new file mode 100644 index 00000000..9fd9b5c6 Binary files /dev/null and b/env/sqliteCpp/dll/sqlite3.dll differ diff --git a/env/sqliteCpp/include/SQLiteCpp/Assertion.h b/env/sqliteCpp/include/SQLiteCpp/Assertion.h new file mode 100644 index 00000000..3d404e2d --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Assertion.h @@ -0,0 +1,47 @@ +/** + * @file Assertion.h + * @ingroup SQLiteCpp + * @brief Definition of the SQLITECPP_ASSERT() macro. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include + +/** + * SQLITECPP_ASSERT SQLITECPP_ASSERT() is used in destructors, where exceptions shall not be thrown + * + * Define SQLITECPP_ENABLE_ASSERT_HANDLER at the project level + * and define a SQLite::assertion_failed() assertion handler + * to tell SQLiteC++ to use it instead of assert() when an assertion fail. +*/ +#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER + +// if an assert handler is provided by user code, use it instead of assert() +namespace SQLite +{ + +// declaration of the assert handler to define in user code +void assertion_failed(const char* apFile, const int apLine, const char* apFunc, + const char* apExpr, const char* apMsg); + +#ifdef _MSC_VER + #define __func__ __FUNCTION__ +#endif +// call the assert handler provided by user code +#define SQLITECPP_ASSERT(expression, message) \ + if (!(expression)) SQLite::assertion_failed(__FILE__, __LINE__, __func__, #expression, message) + +} // namespace SQLite + +#else + +// if no assert handler provided by user code, use standard assert() +// (note: in release mode assert() does nothing) +#define SQLITECPP_ASSERT(expression, message) assert(expression && message) + +#endif diff --git a/env/sqliteCpp/include/SQLiteCpp/Backup.h b/env/sqliteCpp/include/SQLiteCpp/Backup.h new file mode 100644 index 00000000..aa685fb0 --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Backup.h @@ -0,0 +1,131 @@ +/** + * @file Backup.h + * @ingroup SQLiteCpp + * @brief Backup is used to backup a database file in a safe and online way. + * + * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) + * Copyright (c) 2015-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include + +#include +#include + +// Forward declaration to avoid inclusion of in a header +struct sqlite3_backup; + +namespace SQLite +{ + +/** + * @brief RAII encapsulation of a SQLite Database Backup process. + * + * A Backup object is used to backup a source database file to a destination database file + * in a safe and online way. + * + * See also the a reference implementation of live backup taken from the official site: + * https://www.sqlite.org/backup.html + */ +class SQLITECPP_API Backup +{ +public: + /** + * @brief Initialize a SQLite Backup object. + * + * Initialize a SQLite Backup object for the source database and destination database. + * The database name is "main" for the main database, "temp" for the temporary database, + * or the name specified after the AS keyword in an ATTACH statement for an attached database. + * + * Exception is thrown in case of error, then the Backup object is NOT constructed. + * + * @param[in] aDestDatabase Destination database connection + * @param[in] apDestDatabaseName Destination database name + * @param[in] aSrcDatabase Source database connection + * @param[in] apSrcDatabaseName Source database name + * + * @throw SQLite::Exception in case of error + */ + Backup(Database& aDestDatabase, + const char* apDestDatabaseName, + Database& aSrcDatabase, + const char* apSrcDatabaseName); + + /** + * @brief Initialize a SQLite Backup object. + * + * Initialize a SQLite Backup object for source database and destination database. + * The database name is "main" for the main database, "temp" for the temporary database, + * or the name specified after the AS keyword in an ATTACH statement for an attached database. + * + * Exception is thrown in case of error, then the Backup object is NOT constructed. + * + * @param[in] aDestDatabase Destination database connection + * @param[in] aDestDatabaseName Destination database name + * @param[in] aSrcDatabase Source database connection + * @param[in] aSrcDatabaseName Source database name + * + * @throw SQLite::Exception in case of error + */ + Backup(Database& aDestDatabase, + const std::string& aDestDatabaseName, + Database& aSrcDatabase, + const std::string& aSrcDatabaseName); + + /** + * @brief Initialize a SQLite Backup object for main databases. + * + * Initialize a SQLite Backup object for source database and destination database. + * Backup the main databases between the source and the destination. + * + * Exception is thrown in case of error, then the Backup object is NOT constructed. + * + * @param[in] aDestDatabase Destination database connection + * @param[in] aSrcDatabase Source database connection + * + * @throw SQLite::Exception in case of error + */ + Backup(Database& aDestDatabase, + Database& aSrcDatabase); + + // Backup is non-copyable + Backup(const Backup&) = delete; + Backup& operator=(const Backup&) = delete; + + /** + * @brief Execute a step of backup with a given number of source pages to be copied + * + * Exception is thrown when SQLITE_IOERR_XXX, SQLITE_NOMEM, or SQLITE_READONLY is returned + * in sqlite3_backup_step(). These errors are considered fatal, so there is no point + * in retrying the call to executeStep(). + * + * @param[in] aNumPage The number of source pages to be copied, with a negative value meaning all remaining source pages + * + * @return SQLITE_OK/SQLITE_DONE/SQLITE_BUSY/SQLITE_LOCKED + * + * @throw SQLite::Exception in case of error + */ + int executeStep(const int aNumPage = -1); + + /// Return the number of source pages still to be backed up as of the most recent call to executeStep(). + int getRemainingPageCount() const; + + /// Return the total number of pages in the source database as of the most recent call to executeStep(). + int getTotalPageCount() const; + +private: + // Deleter functor to use with smart pointers to close the SQLite database backup in an RAII fashion. + struct Deleter + { + void operator()(sqlite3_backup* apBackup); + }; + + std::unique_ptr mpSQLiteBackup; ///< Pointer to SQLite Database Backup Handle +}; + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/Column.h b/env/sqliteCpp/include/SQLiteCpp/Column.h new file mode 100644 index 00000000..81af4042 --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Column.h @@ -0,0 +1,267 @@ +/** + * @file Column.h + * @ingroup SQLiteCpp + * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include +#include + +#include +#include + +// Forward declarations to avoid inclusion of in a header +struct sqlite3_stmt; + +namespace SQLite +{ + +SQLITECPP_API extern const int INTEGER; ///< SQLITE_INTEGER +SQLITECPP_API extern const int FLOAT; ///< SQLITE_FLOAT +SQLITECPP_API extern const int TEXT; ///< SQLITE_TEXT +SQLITECPP_API extern const int BLOB; ///< SQLITE_BLOB +SQLITECPP_API extern const int Null; ///< SQLITE_NULL + +/** + * @brief Encapsulation of a Column in a row of the result pointed by the prepared Statement. + * + * A Column is a particular field of SQLite data in the current row of result + * of the Statement : it points to a single cell. + * + * Its value can be expressed as a text, and, when applicable, as a numeric + * (integer or floating point) or a binary blob. + * + * Thread-safety: a Column object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class SQLITECPP_API Column +{ +public: + /** + * @brief Encapsulation of a Column in a Row of the result. + * + * @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object. + * @param[in] aIndex Index of the column in the row of result, starting at 0 + */ + explicit Column(const Statement::TStatementPtr& aStmtPtr, int aIndex); + + /** + * @brief Return a pointer to the named assigned to this result column (potentially aliased) + * + * @see getOriginName() to get original column name (not aliased) + */ + const char* getName() const noexcept; + +#ifdef SQLITE_ENABLE_COLUMN_METADATA + /** + * @brief Return a pointer to the table column name that is the origin of this result column + * + * Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro : + * - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance), + * - and also when compiling this wrapper. + */ + const char* getOriginName() const noexcept; +#endif + + /// Return the integer value of the column. + int32_t getInt() const noexcept; + /// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits). + uint32_t getUInt() const noexcept; + /// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits). + int64_t getInt64() const noexcept; + /// Return the double (64bits float) value of the column + double getDouble() const noexcept; + /** + * @brief Return a pointer to the text value (NULL terminated string) of the column. + * + * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), + * thus you must copy it before using it beyond its scope (to a std::string for instance). + */ + const char* getText(const char* apDefaultValue = "") const noexcept; + /** + * @brief Return a pointer to the binary blob value of the column. + * + * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), + * thus you must copy it before using it beyond its scope (to a std::string for instance). + */ + const void* getBlob() const noexcept; + /** + * @brief Return a std::string for a TEXT or BLOB column. + * + * Note this correctly handles strings that contain null bytes. + */ + std::string getString() const; + + /** + * @brief Return the type of the value of the column using sqlite3_column_type() + * + * Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null. + * This type may change from one row to the next, since + * SQLite stores data types dynamically for each value and not per column. + * Use Statement::getColumnDeclaredType() to retrieve the declared column type from a SELECT statement. + * + * @warning After a type conversion (by a call to a getXxx on a Column of a Yyy type), + * the value returned by sqlite3_column_type() is undefined. + */ + int getType() const noexcept; + + /// Test if the column is an integer type value (meaningful only before any conversion) + bool isInteger() const noexcept + { + return (SQLite::INTEGER == getType()); + } + /// Test if the column is a floating point type value (meaningful only before any conversion) + bool isFloat() const noexcept + { + return (SQLite::FLOAT == getType()); + } + /// Test if the column is a text type value (meaningful only before any conversion) + bool isText() const noexcept + { + return (SQLite::TEXT == getType()); + } + /// Test if the column is a binary blob type value (meaningful only before any conversion) + bool isBlob() const noexcept + { + return (SQLite::BLOB == getType()); + } + /// Test if the column is NULL (meaningful only before any conversion) + bool isNull() const noexcept + { + return (SQLite::Null == getType()); + } + + /** + * @brief Return the number of bytes used by the text (or blob) value of the column + * + * Return either : + * - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator + * - size in bytes of the string representation of the numerical value (integer or double) + * - size in bytes of the binary blob returned by getBlob() + * - 0 for a NULL value + */ + int getBytes() const noexcept; + + /// Alias returning the number of bytes used by the text (or blob) value of the column + int size() const noexcept + { + return getBytes (); + } + + /// Inline cast operators to basic types + operator char() const + { + return static_cast(getInt()); + } + operator int8_t() const + { + return static_cast(getInt()); + } + operator uint8_t() const + { + return static_cast(getInt()); + } + operator int16_t() const + { + return static_cast(getInt()); + } + operator uint16_t() const + { + return static_cast(getInt()); + } + operator int32_t() const + { + return getInt(); + } + operator uint32_t() const + { + return getUInt(); + } + operator int64_t() const + { + return getInt64(); + } + operator double() const + { + return getDouble(); + } + /** + * @brief Inline cast operator to char* + * + * @see getText + */ + operator const char*() const + { + return getText(); + } + /** + * @brief Inline cast operator to void* + * + * @see getBlob + */ + operator const void*() const + { + return getBlob(); + } + + /** + * @brief Inline cast operator to std::string + * + * Handles BLOB or TEXT, which may contain null bytes within + * + * @see getString + */ + operator std::string() const + { + return getString(); + } + +private: + Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object + int mIndex; ///< Index of the column in the row of result, starting at 0 +}; + +/** + * @brief Standard std::ostream text inserter + * + * Insert the text value of the Column object, using getText(), into the provided stream. + * + * @param[in] aStream Stream to use + * @param[in] aColumn Column object to insert into the provided stream + * + * @return Reference to the stream used + */ +SQLITECPP_API std::ostream& operator<<(std::ostream& aStream, const Column& aColumn); + +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015 + +// Create an instance of T from the first N columns, see declaration in Statement.h for full details +template +T Statement::getColumns() +{ + checkRow(); + checkIndex(N - 1); + return getColumns(std::make_integer_sequence{}); +} + +// Helper function called by getColums +template +T Statement::getColumns(const std::integer_sequence) +{ + return T{Column(mpPreparedStatement, Is)...}; +} + +#endif + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/Database.h b/env/sqliteCpp/include/SQLiteCpp/Database.h new file mode 100644 index 00000000..4475324f --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Database.h @@ -0,0 +1,624 @@ +/** + * @file Database.h + * @ingroup SQLiteCpp + * @brief Management of a SQLite Database Connection. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include + +// c++17: MinGW GCC version > 8 +// c++17: Visual Studio 2017 version 15.7 +// c++17: macOS unless targetting compatibility with macOS < 10.15 +#ifndef SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM +#if __cplusplus >= 201703L + #if defined(__MINGW32__) || defined(__MINGW64__) + #if __GNUC__ > 8 // MinGW requires GCC version > 8 for std::filesystem + #define SQLITECPP_HAVE_STD_FILESYSTEM + #endif + #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + // macOS clang won't let us touch std::filesystem if we're targetting earlier than 10.15 + #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_13_0) && \ +__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0 + // build for iOS clang won't let us touch std::filesystem if we're targetting earlier than iOS 13 + #else + #define SQLITECPP_HAVE_STD_FILESYSTEM + #endif +#elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L + #define SQLITECPP_HAVE_STD_FILESYSTEM +#endif + +// disable the support if the required header is not available +#ifdef __has_include + #if !__has_include() + #undef SQLITECPP_HAVE_STD_FILESYSTEM + #endif + #if !__has_include() + #undef SQLITECPP_HAVE_EXPERIMENTAL_FILESYSTEM + #endif +#endif + +// C++17 allow to disable std::filesystem support +#ifdef SQLITECPP_DISABLE_STD_FILESYSTEM + #undef SQLITECPP_HAVE_STD_FILESYSTEM + #undef SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM +#endif + +#ifdef SQLITECPP_HAVE_STD_FILESYSTEM +#include +#endif // c++17 and a suitable compiler + +#else // SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM + +#define SQLITECPP_HAVE_STD_FILESYSTEM +#include +namespace std { +namespace filesystem = experimental::filesystem; +} + +#endif // SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM + +#include +#include + +// Forward declarations to avoid inclusion of in a header +struct sqlite3; +struct sqlite3_context; + +#ifndef SQLITE_USE_LEGACY_STRUCT // Since SQLITE 3.19 (used by default since SQLiteCpp 2.1.0) +typedef struct sqlite3_value sqlite3_value; +#else // Before SQLite 3.19 (legacy struct forward declaration can be activated with CMake SQLITECPP_LEGACY_STRUCT var) +struct Mem; +typedef struct Mem sqlite3_value; +#endif + + +namespace SQLite +{ + +// Those public constants enable most usages of SQLiteCpp without including in the client application. + +/// The database is opened in read-only mode. If the database does not already exist, an error is returned. +SQLITECPP_API extern const int OPEN_READONLY; // SQLITE_OPEN_READONLY +/// The database is opened for reading and writing if possible, or reading only if the file is write protected +/// by the operating system. In either case the database must already exist, otherwise an error is returned. +SQLITECPP_API extern const int OPEN_READWRITE; // SQLITE_OPEN_READWRITE +/// With OPEN_READWRITE: The database is opened for reading and writing, and is created if it does not already exist. +SQLITECPP_API extern const int OPEN_CREATE; // SQLITE_OPEN_CREATE +/// Enable URI filename interpretation, parsed according to RFC 3986 (ex. "file:data.db?mode=ro&cache=private") +SQLITECPP_API extern const int OPEN_URI; // SQLITE_OPEN_URI +/// Open in memory database +SQLITECPP_API extern const int OPEN_MEMORY; // SQLITE_OPEN_MEMORY +/// Open database in multi-thread threading mode +SQLITECPP_API extern const int OPEN_NOMUTEX; // SQLITE_OPEN_NOMUTEX +/// Open database with thread-safety in serialized threading mode +SQLITECPP_API extern const int OPEN_FULLMUTEX; // SQLITE_OPEN_FULLMUTEX +/// Open database with shared cache enabled +SQLITECPP_API extern const int OPEN_SHAREDCACHE; // SQLITE_OPEN_SHAREDCACHE +/// Open database with shared cache disabled +SQLITECPP_API extern const int OPEN_PRIVATECACHE; // SQLITE_OPEN_PRIVATECACHE +/// Database filename is not allowed to be a symbolic link (Note: only since SQlite 3.31.0 from 2020-01-22) +SQLITECPP_API extern const int OPEN_NOFOLLOW; // SQLITE_OPEN_NOFOLLOW + + +SQLITECPP_API extern const int OK; ///< SQLITE_OK (used by check() bellow) + +SQLITECPP_API extern const char* const VERSION; ///< SQLITE_VERSION string from sqlite3.h used at compile time +SQLITECPP_API extern const int VERSION_NUMBER; ///< SQLITE_VERSION_NUMBER from sqlite3.h used at compile time + +/// Return SQLite version string using runtime call to the compiled library +SQLITECPP_API const char* getLibVersion() noexcept; +/// Return SQLite version number using runtime call to the compiled library +SQLITECPP_API int getLibVersionNumber() noexcept; + +// Public structure for representing all fields contained within the SQLite header. +// Official documentation for fields: https://www.sqlite.org/fileformat.html#the_database_header +struct Header { + unsigned char headerStr[16]; + unsigned int pageSizeBytes; + unsigned char fileFormatWriteVersion; + unsigned char fileFormatReadVersion; + unsigned char reservedSpaceBytes; + unsigned char maxEmbeddedPayloadFrac; + unsigned char minEmbeddedPayloadFrac; + unsigned char leafPayloadFrac; + unsigned long fileChangeCounter; + unsigned long databaseSizePages; + unsigned long firstFreelistTrunkPage; + unsigned long totalFreelistPages; + unsigned long schemaCookie; + unsigned long schemaFormatNumber; + unsigned long defaultPageCacheSizeBytes; + unsigned long largestBTreePageNumber; + unsigned long databaseTextEncoding; + unsigned long userVersion; + unsigned long incrementalVaccumMode; + unsigned long applicationId; + unsigned long versionValidFor; + unsigned long sqliteVersion; +}; + +/** + * @brief RAII management of a SQLite Database Connection. + * + * A Database object manage a list of all SQLite Statements associated with the + * underlying SQLite 3 database connection. + * + * Resource Acquisition Is Initialization (RAII) means that the Database Connection + * is opened in the constructor and closed in the destructor, so that there is + * no need to worry about memory management or the validity of the underlying SQLite Connection. + * + * Thread-safety: a Database object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class SQLITECPP_API Database +{ + friend class Statement; // Give Statement constructor access to the mSQLitePtr Connection Handle + +public: + /** + * @brief Open the provided database UTF-8 filename. + * + * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior + * of the old sqlite3_open() function (READWRITE+CREATE). + * This makes sense if you want to use it on a readonly filesystem + * or to prevent creation of a void file when a required file is missing. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + * + * @param[in] apFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) + * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default + * + * @throw SQLite::Exception in case of error + */ + Database(const char* apFilename, + const int aFlags = SQLite::OPEN_READONLY, + const int aBusyTimeoutMs = 0, + const char* apVfs = nullptr); + + /** + * @brief Open the provided database UTF-8 filename. + * + * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior + * of the old sqlite3_open() function (READWRITE+CREATE). + * This makes sense if you want to use it on a readonly filesystem + * or to prevent creation of a void file when a required file is missing. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + * + * @param[in] aFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) + * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default + * + * @throw SQLite::Exception in case of error + */ + Database(const std::string& aFilename, + const int aFlags = SQLite::OPEN_READONLY, + const int aBusyTimeoutMs = 0, + const std::string& aVfs = "") : + Database(aFilename.c_str(), aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) + { + } + + #ifdef SQLITECPP_HAVE_STD_FILESYSTEM + + /** + * @brief Open the provided database std::filesystem::path. + * + * @note This feature requires std=C++17 + * + * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior + * of the old sqlite3_open() function (READWRITE+CREATE). + * This makes sense if you want to use it on a readonly filesystem + * or to prevent creation of a void file when a required file is missing. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + * + * @param[in] apFilename Path/uri to the database file ("filename" sqlite3 parameter) + * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default + * + * @throw SQLite::Exception in case of error + */ + Database(const std::filesystem::path& apFilename, + const int aFlags = SQLite::OPEN_READONLY, + const int aBusyTimeoutMs = 0, + const std::string& aVfs = "") : + Database(reinterpret_cast(apFilename.u8string().c_str()), + aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) + { + } + + #endif // have std::filesystem + + // Database is non-copyable + Database(const Database&) = delete; + Database& operator=(const Database&) = delete; + + // Database is movable + Database(Database&& aDatabase) = default; + Database& operator=(Database&& aDatabase) = default; + + /** + * @brief Close the SQLite database connection. + * + * All SQLite statements must have been finalized before, + * so all Statement objects must have been unregistered. + * + * @warning assert in case of error + */ + ~Database() = default; + + // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. + struct Deleter + { + SQLITECPP_API void operator()(sqlite3* apSQLite); + }; + + /** + * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked. + * + * This is useful in multithreaded program to handle case where a table is locked for writing by a thread. + * Any other thread cannot access the table and will receive a SQLITE_BUSY error: + * setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error. + * Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;". + * Default busy timeout is 0ms. + * + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY + * + * @throw SQLite::Exception in case of error + */ + void setBusyTimeout(const int aBusyTimeoutMs); + + /** + * @brief Shortcut to execute one or multiple statements without results. Return the number of changes. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see Database::tryExec() to execute, returning the sqlite result code + * @see Statement::exec() to handle precompiled statements (for better performances) without results + * @see Statement::executeStep() to handle "SELECT" queries with results + * + * @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) + * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. + * + * @throw SQLite::Exception in case of error + */ + int exec(const char* apQueries); + + /** + * @brief Shortcut to execute one or multiple statements without results. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see Database::tryExec() to execute, returning the sqlite result code + * @see Statement::exec() to handle precompiled statements (for better performances) without results + * @see Statement::executeStep() to handle "SELECT" queries with results + * + * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) + * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. + * + * @throw SQLite::Exception in case of error + */ + int exec(const std::string& aQueries) + { + return exec(aQueries.c_str()); + } + + /** + * @brief Try to execute one or multiple statements, returning the sqlite result code. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see exec() to execute, returning number of rows modified + * + * @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return the sqlite result code. + */ + int tryExec(const char* apQueries) noexcept; + + /** + * @brief Try to execute one or multiple statements, returning the sqlite result code. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see exec() to execute, returning number of rows modified + * + * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return the sqlite result code. + */ + int tryExec(const std::string& aQueries) noexcept + { + return tryExec(aQueries.c_str()); + } + + /** + * @brief Shortcut to execute a one step query and fetch the first column of the result. + * + * This is a shortcut to execute a simple statement with a single result. + * This should be used only for non reusable queries (else you should use a Statement with bind()). + * This should be used only for queries with expected results (else an exception is fired). + * + * @warning WARNING: Be very careful with this dangerous method: you have to + * make a COPY OF THE result, else it will be destroy before the next line + * (when the underlying temporary Statement and Column objects are destroyed) + * + * @see also Statement class for handling queries with multiple results + * + * @param[in] apQuery an UTF-8 encoded SQL query + * + * @return a temporary Column object with the first column of result. + * + * @throw SQLite::Exception in case of error + */ + Column execAndGet(const char* apQuery); + + /** + * @brief Shortcut to execute a one step query and fetch the first column of the result. + * + * This is a shortcut to execute a simple statement with a single result. + * This should be used only for non reusable queries (else you should use a Statement with bind()). + * This should be used only for queries with expected results (else an exception is fired). + * + * @warning WARNING: Be very careful with this dangerous method: you have to + * make a COPY OF THE result, else it will be destroy before the next line + * (when the underlying temporary Statement and Column objects are destroyed) + * + * @see also Statement class for handling queries with multiple results + * + * @param[in] aQuery an UTF-8 encoded SQL query + * + * @return a temporary Column object with the first column of result. + * + * @throw SQLite::Exception in case of error + */ + Column execAndGet(const std::string& aQuery) + { + return execAndGet(aQuery.c_str()); + } + + /** + * @brief Shortcut to test if a table exists. + * + * Table names are case sensitive. + * + * @param[in] apTableName an UTF-8 encoded case sensitive Table name + * + * @return true if the table exists. + * + * @throw SQLite::Exception in case of error + */ + bool tableExists(const char* apTableName) const; + + /** + * @brief Shortcut to test if a table exists. + * + * Table names are case sensitive. + * + * @param[in] aTableName an UTF-8 encoded case sensitive Table name + * + * @return true if the table exists. + * + * @throw SQLite::Exception in case of error + */ + bool tableExists(const std::string& aTableName) const + { + return tableExists(aTableName.c_str()); + } + + /** + * @brief Get the rowid of the most recent successful INSERT into the database from the current connection. + * + * Each entry in an SQLite table always has a unique 64-bit signed integer key called the rowid. + * If the table has a column of type INTEGER PRIMARY KEY, then it is an alias for the rowid. + * + * @return Rowid of the most recent successful INSERT into the database, or 0 if there was none. + */ + int64_t getLastInsertRowid() const noexcept; + + /// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). + int getChanges() const noexcept; + + /// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection (not DROP table). + int getTotalChanges() const noexcept; + + /// Return the numeric result code for the most recent failed API call (if any). + int getErrorCode() const noexcept; + /// Return the extended numeric result code for the most recent failed API call (if any). + int getExtendedErrorCode() const noexcept; + /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). + const char* getErrorMsg() const noexcept; + + /// Return the filename used to open the database. + const std::string& getFilename() const noexcept + { + return mFilename; + } + + /** + * @brief Return raw pointer to SQLite Database Connection Handle. + * + * This is often needed to mix this wrapper with other libraries or for advance usage not supported by SQLiteCpp. + */ + sqlite3* getHandle() const noexcept + { + return mSQLitePtr.get(); + } + + /** + * @brief Create or redefine a SQL function or aggregate in the sqlite database. + * + * This is the equivalent of the sqlite3_create_function_v2 command. + * @see http://www.sqlite.org/c3ref/create_function.html + * + * @note UTF-8 text encoding assumed. + * + * @param[in] apFuncName Name of the SQL function to be created or redefined + * @param[in] aNbArg Number of arguments in the function + * @param[in] abDeterministic Optimize for deterministic functions (most are). A random number generator is not. + * @param[in] apApp Arbitrary pointer of user data, accessible with sqlite3_user_data(). + * @param[in] apFunc Pointer to a C-function to implement a scalar SQL function (apStep & apFinal nullptr) + * @param[in] apStep Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) + * @param[in] apFinal Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) + * @param[in] apDestroy If not nullptr, then it is the destructor for the application data pointer. + * + * @throw SQLite::Exception in case of error + */ + void createFunction(const char* apFuncName, + int aNbArg, + bool abDeterministic, + void* apApp, + void (*apFunc)(sqlite3_context *, int, sqlite3_value **), + void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr, + void (*apFinal)(sqlite3_context *) = nullptr, // NOLINT(readability/casting) + void (*apDestroy)(void *) = nullptr); + + /** + * @brief Load a module into the current sqlite database instance. + * + * This is the equivalent of the sqlite3_load_extension call, but additionally enables + * module loading support prior to loading the requested module. + * + * @see http://www.sqlite.org/c3ref/load_extension.html + * + * @note UTF-8 text encoding assumed. + * + * @param[in] apExtensionName Name of the shared library containing extension + * @param[in] apEntryPointName Name of the entry point (nullptr to let sqlite work it out) + * + * @throw SQLite::Exception in case of error + */ + void loadExtension(const char* apExtensionName, const char* apEntryPointName); + + /** + * @brief Set the key for the current sqlite database instance. + * + * This is the equivalent of the sqlite3_key call and should thus be called + * directly after opening the database. + * Open encrypted database -> call db.key("secret") -> database ready + * + * @param[in] aKey Key to decode/encode the database + * + * @throw SQLite::Exception in case of error + */ + void key(const std::string& aKey) const; + + /** + * @brief Reset the key for the current sqlite database instance. + * + * This is the equivalent of the sqlite3_rekey call and should thus be called + * after the database has been opened with a valid key. To decrypt a + * database, call this method with an empty string. + * Open normal database -> call db.rekey("secret") -> encrypted database, database ready + * Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready + * Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready + * + * @param[in] aNewKey New key to encode the database + * + * @throw SQLite::Exception in case of error + */ + void rekey(const std::string& aNewKey) const; + + /** + * @brief Test if a file contains an unencrypted database. + * + * This is a simple test that reads the first bytes of a database file and + * compares them to the standard header for unencrypted databases. If the + * header does not match the standard string, we assume that we have an + * encrypted file. + * + * @param[in] aFilename path/uri to a file + * + * @return true if the database has the standard header. + * + * @throw SQLite::Exception in case of error + */ + static bool isUnencrypted(const std::string& aFilename); + + /** + * @brief Parse SQLite header data from a database file. + * + * This function reads the first 100 bytes of a SQLite database file + * and reconstructs groups of individual bytes into the associated fields + * in a Header object. + * + * @param[in] aFilename path/uri to a file + * + * @return Header object containing file data + * + * @throw SQLite::Exception in case of error + */ + static Header getHeaderInfo(const std::string& aFilename); + + // Parse SQLite header data from a database file. + Header getHeaderInfo() const + { + return getHeaderInfo(mFilename); + } + + /** + * @brief BackupType for the backup() method + */ + enum BackupType { Save, Load }; + + /** + * @brief Load or save the database content. + * + * This function is used to load the contents of a database file on disk + * into the "main" database of open database connection, or to save the current + * contents of the database into a database file on disk. + * + * @throw SQLite::Exception in case of error + */ + void backup(const char* apFilename, BackupType aType); + + /** + * @brief Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message + */ + void check(const int aRet) const + { + if (SQLite::OK != aRet) + { + throw SQLite::Exception(getHandle(), aRet); + } + } + +private: + // TODO: perhaps switch to having Statement sharing a pointer to the Connexion + std::unique_ptr mSQLitePtr; ///< Pointer to SQLite Database Connection Handle + std::string mFilename; ///< UTF-8 filename used to open the database +}; + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/Exception.h b/env/sqliteCpp/include/SQLiteCpp/Exception.h new file mode 100644 index 00000000..b8231cd1 --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Exception.h @@ -0,0 +1,91 @@ +/** + * @file Exception.h + * @ingroup SQLiteCpp + * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include +#include + +// Forward declaration to avoid inclusion of in a header +struct sqlite3; + +namespace SQLite +{ + +/** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + */ +class SQLITECPP_API Exception : public std::runtime_error +{ +public: + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] aErrorMessage The string message describing the SQLite error + * @param[in] ret Return value from function call that failed. + */ + Exception(const char* aErrorMessage, int ret); + + Exception(const std::string& aErrorMessage, int ret) : + Exception(aErrorMessage.c_str(), ret) + { + } + + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] aErrorMessage The string message describing the SQLite error + */ + explicit Exception(const char* aErrorMessage) : + Exception(aErrorMessage, -1) // 0 would be SQLITE_OK, which doesn't make sense + { + } + explicit Exception(const std::string& aErrorMessage) : + Exception(aErrorMessage.c_str(), -1) // 0 would be SQLITE_OK, which doesn't make sense + { + } + + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + */ + explicit Exception(sqlite3* apSQLite); + + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + * @param[in] ret Return value from function call that failed. + */ + Exception(sqlite3* apSQLite, int ret); + + /// Return the result code (if any, otherwise -1). + int getErrorCode() const noexcept + { + return mErrcode; + } + + /// Return the extended numeric result code (if any, otherwise -1). + int getExtendedErrorCode() const noexcept + { + return mExtendedErrcode; + } + + /// Return a string, solely based on the error code + const char* getErrorStr() const noexcept; + +private: + int mErrcode; ///< Error code value + int mExtendedErrcode; ///< Detailed error code if any +}; + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/ExecuteMany.h b/env/sqliteCpp/include/SQLiteCpp/ExecuteMany.h new file mode 100644 index 00000000..b5de3a23 --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/ExecuteMany.h @@ -0,0 +1,91 @@ +/** + * @file ExecuteMany.h + * @ingroup SQLiteCpp + * @brief Convenience function to execute a Statement with multiple Parameter sets + * + * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) + * Copyright (c) 2019-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 + +#include +#include + +/// @cond +#include +#include +#include + +namespace SQLite +{ + +/// @endcond + +/** + * \brief Convenience function to execute a Statement with multiple Parameter sets once for each parameter set given. + * + * + * This feature requires a c++14 capable compiler. + * + * \code{.cpp} + * execute_many(db, "INSERT INTO test VALUES (?, ?)", + * 1, + * std::make_tuple(2), + * std::make_tuple(3, "three") + * ); + * \endcode + * @param aDatabase Database to use + * @param apQuery Query to use with all parameter sets + * @param aArg first tuple with parameters + * @param aParams the following tuples with parameters + */ +template +void execute_many(Database& aDatabase, const char* apQuery, Arg&& aArg, Types&&... aParams) +{ + SQLite::Statement query(aDatabase, apQuery); + bind_exec(query, std::forward(aArg)); + (void)std::initializer_list + { + ((void)reset_bind_exec(query, std::forward(aParams)), 0)... + }; +} + +/** + * \brief Convenience function to reset a statement and call bind_exec to + * bind new values to the statement and execute it + * + * This feature requires a c++14 capable compiler. + * + * @param apQuery Query to use + * @param aTuple Tuple to bind + */ +template +void reset_bind_exec(Statement& apQuery, TupleT&& aTuple) +{ + apQuery.reset(); + bind_exec(apQuery, std::forward(aTuple)); +} + +/** + * \brief Convenience function to bind values a the statement and execute it + * + * This feature requires a c++14 capable compiler. + * + * @param apQuery Query to use + * @param aTuple Tuple to bind + */ +template +void bind_exec(Statement& apQuery, TupleT&& aTuple) +{ + SQLite::bind(apQuery, std::forward(aTuple)); + while (apQuery.executeStep()) {} +} + +} // namespace SQLite + +#endif // c++14 diff --git a/env/sqliteCpp/include/SQLiteCpp/SQLiteCpp.h b/env/sqliteCpp/include/SQLiteCpp/SQLiteCpp.h new file mode 100644 index 00000000..c817e980 --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/SQLiteCpp.h @@ -0,0 +1,46 @@ +/** + * @file SQLiteCpp.h + * @ingroup SQLiteCpp + * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. + * + * Include this main header file in your project to gain access to all functionality provided by the wrapper. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +/** + * @defgroup SQLiteCpp SQLiteC++ + * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. + */ +#pragma once + + +// Include useful headers of SQLiteC++ +#include +#include +#include +#include +#include +#include +#include + + +/** + * @brief Version numbers for SQLiteC++ are provided in the same way as sqlite3.h + * + * The [SQLITECPP_VERSION] C preprocessor macro in the SQLiteC++.h header + * evaluates to a string literal that is the SQLite version in the + * format "X.Y.Z" where X is the major version number + * and Y is the minor version number and Z is the release number. + * + * The [SQLITECPP_VERSION_NUMBER] C preprocessor macro resolves to an integer + * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same + * numbers used in [SQLITECPP_VERSION]. + * + * WARNING: shall always be updated in sync with PROJECT_VERSION in CMakeLists.txt + */ +#define SQLITECPP_VERSION "3.03.01" // 3.3.1 +#define SQLITECPP_VERSION_NUMBER 3003001 // 3.3.1 + diff --git a/env/sqliteCpp/include/SQLiteCpp/SQLiteCppExport.h b/env/sqliteCpp/include/SQLiteCpp/SQLiteCppExport.h new file mode 100644 index 00000000..d6e9a06a --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/SQLiteCppExport.h @@ -0,0 +1,38 @@ +/** + * @file SQLiteCppExport.h + * @ingroup SQLiteCpp + * @brief File with macros needed in the generation of Windows DLLs + * + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ + +#pragma once + +/* +* #define SQLITECPP_COMPILE_DLL to compile a DLL under Windows +* #define SQLITECPP_EXPORT to export symbols when creating the DLL, otherwise it defaults to importing symbols +*/ + +/* Windows DLL export/import */ +#if defined(_WIN32)&& !defined(__GNUC__) && defined(SQLITECPP_COMPILE_DLL) + #if SQLITECPP_DLL_EXPORT + #define SQLITECPP_API __declspec(dllexport) + #else + #define SQLITECPP_API __declspec(dllimport) + #endif +#else + #if __GNUC__ >= 4 + #define SQLITECPP_API __attribute__ ((visibility ("default"))) + #else + #define SQLITECPP_API + #endif +#endif + +#if defined(WIN32) && defined(SQLITECPP_COMPILE_DLL) + #pragma warning( disable : 4251 ) + #pragma warning( disable : 4275 ) +#endif diff --git a/env/sqliteCpp/include/SQLiteCpp/Savepoint.h b/env/sqliteCpp/include/SQLiteCpp/Savepoint.h new file mode 100644 index 00000000..8dd5e894 --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Savepoint.h @@ -0,0 +1,98 @@ +/** + * @file Savepoint.h + * @ingroup SQLiteCpp + * @brief A Savepoint is a way to group multiple SQL statements into an atomic + * secured operation. Similar to a transaction while allowing child savepoints. + * + * Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@gmail.com) + * Copyright (c) 2020-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or + * copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include + +namespace SQLite +{ + +// Forward declaration +class Database; + +/** + * @brief RAII encapsulation of a SQLite Savepoint. + * + * SAVEPOINTs are a method of creating Transactions, similar to BEGIN and COMMIT, + * except that the SAVEPOINT and RELEASE commands are named and may be nested.. + * + * Resource Acquisition Is Initialization (RAII) means that the Savepoint + * begins in the constructor and is rolled back in the destructor (unless committed before), so that there is + * no need to worry about memory management or the validity of the underlying SQLite Connection. + * + * This method also offers big performances improvements compared to + * individually executed statements. + * + * Caveats: + * + * 1) Calling COMMIT or committing a parent transaction or RELEASE on a parent + * savepoint will cause this savepoint to be released. + * + * 2) Calling ROLLBACK TO or rolling back a parent savepoint will cause this + * savepoint to be rolled back. + * + * 3) This savepoint is not saved to the database until this and all savepoints + * or transaction in the savepoint stack have been released or committed. + * + * See also: https://sqlite.org/lang_savepoint.html + * + * Thread-safety: a Savepoint object shall not be shared by multiple threads, because: + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class SQLITECPP_API Savepoint +{ +public: + /** + * @brief Begins the SQLite savepoint + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] aName the name of the Savepoint + * + * Exception is thrown in case of error, then the Savepoint is NOT + * initiated. + */ + Savepoint(Database& aDatabase, const std::string& aName); + + // Savepoint is non-copyable + Savepoint(const Savepoint&) = delete; + Savepoint& operator=(const Savepoint&) = delete; + + /** + * @brief Safely rollback the savepoint if it has not been committed. + */ + ~Savepoint(); + + /** + * @brief Commit and release the savepoint. + */ + void release(); + + /** + * @brief Rollback to the savepoint, but don't release it. + */ + void rollbackTo(); + // @deprecated same as rollbackTo(); + void rollback() { rollbackTo(); } + +private: + Database& mDatabase; ///< Reference to the SQLite Database Connection + std::string msName; ///< Name of the Savepoint + bool mbReleased = false; ///< True when release has been called +}; + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/Statement.h b/env/sqliteCpp/include/SQLiteCpp/Statement.h new file mode 100644 index 00000000..ba846a8d --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Statement.h @@ -0,0 +1,711 @@ +/** + * @file Statement.h + * @ingroup SQLiteCpp + * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include +#include // SQLITECPP_PURE_FUNC + +#include +#include +#include + +// Forward declarations to avoid inclusion of in a header +struct sqlite3; +struct sqlite3_stmt; + +namespace SQLite +{ + +// Forward declaration +class Database; +class Column; + +SQLITECPP_API extern const int OK; ///< SQLITE_OK + +/** + * @brief RAII encapsulation of a prepared SQLite Statement. + * + * A Statement is a compiled SQL query ready to be executed step by step + * to provide results one row at a time. + * + * Resource Acquisition Is Initialization (RAII) means that the Statement + * is compiled in the constructor and finalized in the destructor, so that there is + * no need to worry about memory management or the validity of the underlying SQLite Statement. + * + * Thread-safety: a Statement object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class SQLITECPP_API Statement +{ +public: + /** + * @brief Compile and register the SQL query for the provided SQLite Database Connection + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] apQuery an UTF-8 encoded query string + * + * Exception is thrown in case of error, then the Statement object is NOT constructed. + */ + Statement(const Database& aDatabase, const char* apQuery); + + /** + * @brief Compile and register the SQL query for the provided SQLite Database Connection + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] aQuery an UTF-8 encoded query string + * + * Exception is thrown in case of error, then the Statement object is NOT constructed. + */ + Statement(const Database& aDatabase, const std::string& aQuery) : + Statement(aDatabase, aQuery.c_str()) + {} + + // Statement is non-copyable + Statement(const Statement&) = delete; + Statement& operator=(const Statement&) = delete; + + // TODO: Change Statement move constructor to default + Statement(Statement&& aStatement) noexcept; + Statement& operator=(Statement&& aStatement) noexcept = default; + + /// Finalize and unregister the SQL query from the SQLite Database Connection. + /// The finalization will be done by the destructor of the last shared pointer + ~Statement() = default; + + /// Reset the statement to make it ready for a new execution by calling sqlite3_reset. + /// Throws an exception on error. + /// Call this function before any news calls to bind() if the statement was already executed before. + /// Calling reset() does not clear the bindings (see clearBindings()). + void reset(); + + /// Reset the statement. Returns the sqlite result code instead of throwing an exception on error. + int tryReset() noexcept; + + /** + * @brief Clears away all the bindings of a prepared statement. + * + * Contrary to the intuition of many, reset() does not reset the bindings on a prepared statement. + * Use this routine to reset all parameters to NULL. + */ + void clearBindings(); // throw(SQLite::Exception) + + //////////////////////////////////////////////////////////////////////////// + // Bind a value to a parameter of the SQL statement, + // in the form "?" (unnamed), "?NNN", ":VVV", "@VVV" or "$VVV". + // + // Can use the parameter index, starting from "1", to the higher NNN value, + // or the complete parameter name "?NNN", ":VVV", "@VVV" or "$VVV" + // (prefixed with the corresponding sign "?", ":", "@" or "$") + // + // Note that for text and blob values, the SQLITE_TRANSIENT flag is used, + // which tell the sqlite library to make its own copy of the data before the bind() call returns. + // This choice is done to prevent any common misuses, like passing a pointer to a + // dynamic allocated and temporary variable (a std::string for instance). + // This is under-optimized for static data (a static text define in code) + // as well as for dynamic allocated buffer which could be transfer to sqlite + // instead of being copied. + // => if you know what you are doing, use bindNoCopy() instead of bind() + + SQLITECPP_PURE_FUNC + int getIndex(const char * const apName) const; + + /** + * @brief Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const int32_t aValue); + /** + * @brief Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const uint32_t aValue); + /** + * @brief Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const int64_t aValue); + /** + * @brief Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const double aValue); + /** + * @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const int aIndex, const std::string& aValue); + /** + * @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const int aIndex, const char* apValue); + /** + * @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const int aIndex, const void* apValue, const int aSize); + /** + * @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1). + * + * The string can contain null characters as it is binded using its size. + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const int aIndex, const std::string& aValue); + /** + * @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * Main usage is with null-terminated literal text (aka in code static strings) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const int aIndex, const char* apValue); + /** + * @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const int aIndex, const void* apValue, const int aSize); + /** + * @brief Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @see clearBindings() to set all bound parameters to NULL. + */ + void bind(const int aIndex); + + /** + * @brief Bind an int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const int32_t aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a 32bits unsigned int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const uint32_t aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a 64bits int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const int64_t aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a double (64bits float) value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const double aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const char* apName, const std::string& aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const char* apName, const char* apValue) + { + bind(getIndex(apName), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const char* apName, const void* apValue, const int aSize) + { + bind(getIndex(apName), apValue, aSize); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * The string can contain null characters as it is binded using its size. + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const char* apName, const std::string& aValue) + { + bindNoCopy(getIndex(apName), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * Main usage is with null-terminated literal text (aka in code static strings) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const char* apName, const char* apValue) + { + bindNoCopy(getIndex(apName), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const char* apName, const void* apValue, const int aSize) + { + bindNoCopy(getIndex(apName), apValue, aSize); + } + /** + * @brief Bind a NULL value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @see clearBindings() to set all bound parameters to NULL. + */ + void bind(const char* apName) // bind NULL value + { + bind(getIndex(apName)); + } + + + /** + * @brief Bind an int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const int32_t aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a 32bits unsigned int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const uint32_t aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a 64bits int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const int64_t aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a double (64bits float) value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const double aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const std::string& aName, const std::string& aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const std::string& aName, const char* apValue) + { + bind(aName.c_str(), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const std::string& aName, const void* apValue, const int aSize) + { + bind(aName.c_str(), apValue, aSize); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * The string can contain null characters as it is binded using its size. + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const std::string& aName, const std::string& aValue) + { + bindNoCopy(aName.c_str(), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * Main usage is with null-terminated literal text (aka in code static strings) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const std::string& aName, const char* apValue) + { + bindNoCopy(aName.c_str(), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const std::string& aName, const void* apValue, const int aSize) + { + bindNoCopy(aName.c_str(), apValue, aSize); + } + /** + * @brief Bind a NULL value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @see clearBindings() to set all bound parameters to NULL. + */ + void bind(const std::string& aName) // bind NULL value + { + bind(aName.c_str()); + } + + //////////////////////////////////////////////////////////////////////////// + + /** + * @brief Execute a step of the prepared query to fetch one row of results. + * + * While true is returned, a row of results is available, and can be accessed + * through the getColumn() method + * + * @see exec() execute a one-step prepared statement with no expected result + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it + * then you have to call executeStep() again to fetch more rows until the query is finished + * - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result + * (case of a query with no result, or after N rows fetched successfully) + * + * @throw SQLite::Exception in case of error + */ + bool executeStep(); + + /** + * @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * + * + * + * @see exec() execute a one-step prepared statement with no expected result + * @see executeStep() execute a step of the prepared query to fetch one row of results + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return the sqlite result code. + */ + int tryExecuteStep() noexcept; + + /** + * @brief Execute a one-step query with no expected result, and return the number of changes. + * + * This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * It is similar to Database::exec(), but using a precompiled statement, it adds : + * - the ability to bind() arguments to it (best way to insert data), + * - reusing it allows for better performances (efficient for multiple insertion). + * + * @see executeStep() execute a step of the prepared query to fetch one row of results + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE) + * + * @throw SQLite::Exception in case of error, or if row of results are returned while they are not expected! + */ + int exec(); + + //////////////////////////////////////////////////////////////////////////// + + /** + * @brief Return a copy of the column data specified by its index + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from: + * - if provided index is out of bound + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + * + * @param[in] aIndex Index of the column, starting at 0 + * + * @note This method is not const, reflecting the fact that the returned Column object will + * share the ownership of the underlying sqlite3_stmt. + * + * @warning The resulting Column object must not be memorized "as-is". + * Is is only a wrapper around the current result row, so it is only valid + * while the row from the Statement remains valid, that is only until next executeStep() call. + * Thus, you should instead extract immediately its data (getInt(), getText()...) + * and use or copy this data for any later usage. + */ + Column getColumn(const int aIndex) const; + + /** + * @brief Return a copy of the column data specified by its column name (less efficient than using an index) + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from : + * - if provided name is not one of the aliased column names + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified name is not an on of the aliased name of the columns in the result. + * + * @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name) + * + * @note Uses a map of column names to indexes, build on first call. + * + * @note This method is not const, reflecting the fact that the returned Column object will + * share the ownership of the underlying sqlite3_stmt. + * + * @warning The resulting Column object must not be memorized "as-is". + * Is is only a wrapper around the current result row, so it is only valid + * while the row from the Statement remains valid, that is only until next executeStep() call. + * Thus, you should instead extract immediately its data (getInt(), getText()...) + * and use or copy this data for any later usage. + * + * Throw an exception if the specified name is not one of the aliased name of the columns in the result. + */ + Column getColumn(const char* apName) const; + +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015 + /** + * @brief Return an instance of T constructed from copies of the first N columns + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from: + * - if provided column count is out of bound + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified column count is out of the [0, getColumnCount()) range. + * + * @tparam T Object type to construct + * @tparam N Number of columns + * + * @note Requires std=C++14 + */ + template + T getColumns(); + +private: + /** + * @brief Helper function used by getColumns to expand an integer_sequence used to generate + * the required Column objects + */ + template + T getColumns(const std::integer_sequence); + +public: +#endif + + /** + * @brief Test if the column value is NULL + * + * @param[in] aIndex Index of the column, starting at 0 + * + * @return true if the column value is NULL + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + bool isColumnNull(const int aIndex) const; + + /** + * @brief Test if the column value is NULL + * + * @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name) + * + * @return true if the column value is NULL + * + * Throw an exception if the specified name is not one of the aliased name of the columns in the result. + */ + bool isColumnNull(const char* apName) const; + + /** + * @brief Return a pointer to the named assigned to the specified result column (potentially aliased) + * + * @param[in] aIndex Index of the column in the range [0, getColumnCount()). + * + * @see getColumnOriginName() to get original column name (not aliased) + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + const char* getColumnName(const int aIndex) const; + +#ifdef SQLITE_ENABLE_COLUMN_METADATA + /** + * @brief Return a pointer to the table column name that is the origin of the specified result column + * + * Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro : + * - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance), + * - and also when compiling this wrapper. + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + const char* getColumnOriginName(const int aIndex) const; +#endif + + /** + * @brief Return the index of the specified (potentially aliased) column name + * + * @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name) + * + * @note Uses a map of column names to indexes, build on first call. + * + * Throw an exception if the specified name is not known. + */ + int getColumnIndex(const char* apName) const; + + + /** + * @brief Return the declared type of the specified result column for a SELECT statement. + * + * This is the type given at creation of the column and not the actual data type. + * SQLite stores data types dynamically for each value and not per column. + * + * @param[in] aIndex Index of the column in the range [0, getColumnCount()). + * + * Throw an exception if the type can't be determined because: + * - the specified index is out of the [0, getColumnCount()) range + * - the statement is not a SELECT query + * - the column at aIndex is not a table column but an expression or subquery + */ + const char * getColumnDeclaredType(const int aIndex) const; + + + /// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). + int getChanges() const noexcept; + + + //////////////////////////////////////////////////////////////////////////// + + /// Return the UTF-8 SQL Query. + const std::string& getQuery() const + { + return mQuery; + } + + // Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded. + std::string getExpandedSQL() const; + + /// Return the number of columns in the result set returned by the prepared statement + int getColumnCount() const + { + return mColumnCount; + } + /// true when a row has been fetched with executeStep() + bool hasRow() const + { + return mbHasRow; + } + /// true when the last executeStep() had no more row to fetch + bool isDone() const + { + return mbDone; + } + + /// Return the number of bind parameters in the statement + int getBindParameterCount() const noexcept; + + /// Return the numeric result code for the most recent failed API call (if any). + int getErrorCode() const noexcept; + /// Return the extended numeric result code for the most recent failed API call (if any). + int getExtendedErrorCode() const noexcept; + /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). + const char* getErrorMsg() const noexcept; + + /// Shared pointer to SQLite Prepared Statement Object + using TStatementPtr = std::shared_ptr; + +private: + /** + * @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message + * + * @param[in] aRet SQLite return code to test against the SQLITE_OK expected value + */ + void check(const int aRet) const + { + if (SQLite::OK != aRet) + { + throw SQLite::Exception(mpSQLite, aRet); + } + } + + /** + * @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception. + */ + void checkRow() const + { + if (false == mbHasRow) + { + throw SQLite::Exception("No row to get a column from. executeStep() was not called, or returned false."); + } + } + + /** + * @brief Check if there is a Column index is in the range of columns in the result. + */ + void checkIndex(const int aIndex) const + { + if ((aIndex < 0) || (aIndex >= mColumnCount)) + { + throw SQLite::Exception("Column index out of range."); + } + } + + /** + * @brief Prepare statement object. + * + * @return Shared pointer to prepared statement object + */ + TStatementPtr prepareStatement(); + + /** + * @brief Return a prepared statement object. + * + * Throw an exception if the statement object was not prepared. + * @return raw pointer to Prepared Statement Object + */ + sqlite3_stmt* getPreparedStatement() const; + + std::string mQuery; //!< UTF-8 SQL Query + sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle + TStatementPtr mpPreparedStatement; //!< Shared Pointer to the prepared SQLite Statement Object + int mColumnCount = 0; //!< Number of columns in the result of the prepared statement + bool mbHasRow = false; //!< true when a row has been fetched with executeStep() + bool mbDone = false; //!< true when the last executeStep() had no more row to fetch + + /// Map of columns index by name (mutable so getColumnIndex can be const) + mutable std::map mColumnNames; +}; + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/Transaction.h b/env/sqliteCpp/include/SQLiteCpp/Transaction.h new file mode 100644 index 00000000..305de30a --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Transaction.h @@ -0,0 +1,98 @@ +/** + * @file Transaction.h + * @ingroup SQLiteCpp + * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include + +namespace SQLite +{ + +// Forward declaration +class Database; + +/** + * @brief Transaction behaviors when opening an SQLite transaction. + * Names correspond directly to the behavior. + */ +enum class TransactionBehavior { + DEFERRED, + IMMEDIATE, + EXCLUSIVE, +}; + +/** + * @brief RAII encapsulation of a SQLite Transaction. + * + * A Transaction is a way to group multiple SQL statements into an atomic secured operation; + * either it succeeds, with all the changes committed to the database file, + * or if it fails, all the changes are rolled back to the initial state. + * + * Resource Acquisition Is Initialization (RAII) means that the Transaction + * begins in the constructor and is rolled back in the destructor (unless committed before), so that there is + * no need to worry about memory management or the validity of the underlying SQLite Connection. + * + * This method also offers big performances improvements compared to individually executed statements. + * + * Thread-safety: a Transaction object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class SQLITECPP_API Transaction +{ +public: + /** + * @brief Begins the SQLite transaction using the default transaction behavior. + * + * @param[in] aDatabase the SQLite Database Connection + * + * Exception is thrown in case of error, then the Transaction is NOT initiated. + */ + explicit Transaction(Database& aDatabase); + + /** + * @brief Begins the SQLite transaction with the specified behavior. + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] behavior the requested transaction behavior + * + * Exception is thrown in case of error, then the Transaction is NOT initiated. + */ + explicit Transaction(Database& aDatabase, TransactionBehavior behavior); + + // Transaction is non-copyable + Transaction(const Transaction&) = delete; + Transaction& operator=(const Transaction&) = delete; + + /** + * @brief Safely rollback the transaction if it has not been committed. + */ + ~Transaction(); + + /** + * @brief Commit the transaction. + */ + void commit(); + + /** + * @brief Rollback the transaction + */ + void rollback(); + +private: + Database& mDatabase; ///< Reference to the SQLite Database Connection + bool mbCommited = false; ///< True when commit has been called +}; + +} // namespace SQLite diff --git a/env/sqliteCpp/include/SQLiteCpp/Utils.h b/env/sqliteCpp/include/SQLiteCpp/Utils.h new file mode 100644 index 00000000..7845ec0e --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/Utils.h @@ -0,0 +1,31 @@ +/** + * @file Utils.h + * @ingroup SQLiteCpp + * @brief Definition of the SQLITECPP_PURE_FUNC macro. + * + * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +// macro taken from https://github.com/nemequ/hedley/blob/master/hedley.h that was in public domain at this time +#if defined(__GNUC__) || defined(__GNUG__) || defined(__clang__) ||\ +(defined(__INTEL_COMPILER) && __INTEL_COMPILER > 1600) ||\ +(defined(__ARMCC_VERSION) && __ARMCC_VERSION > 4010000) ||\ +(\ + defined(__TI_COMPILER_VERSION__) && (\ + __TI_COMPILER_VERSION__ > 8003000 ||\ + (__TI_COMPILER_VERSION__ > 7003000 && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\ + )\ +) +#if defined(__has_attribute) +#if !defined(SQLITECPP_PURE_FUNC) && __has_attribute(pure) +#define SQLITECPP_PURE_FUNC __attribute__((pure)) +#endif +#endif +#endif +#if !defined(SQLITECPP_PURE_FUNC) +#define SQLITECPP_PURE_FUNC +#endif diff --git a/env/sqliteCpp/include/SQLiteCpp/VariadicBind.h b/env/sqliteCpp/include/SQLiteCpp/VariadicBind.h new file mode 100644 index 00000000..3261da4d --- /dev/null +++ b/env/sqliteCpp/include/SQLiteCpp/VariadicBind.h @@ -0,0 +1,98 @@ +/** + * @file VariadicBind.h + * @ingroup SQLiteCpp + * @brief Convenience function for Statement::bind(...) + * + * Copyright (c) 2016 Paul Dreik (github@pauldreik.se) + * Copyright (c) 2016-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include + +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 +#include +#endif // c++14 + +/// @cond +#include +#include + +namespace SQLite +{ +/// @endcond + +/** + * \brief Convenience function for calling Statement::bind(...) once for each argument given. + * + * This takes care of incrementing the index between each calls to bind. + * + * This feature requires a c++11 capable compiler. + * + * \code{.cpp} + * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC +void bind(SQLite::Statement& query, const Args& ... args) +{ + int pos = 0; + (void)std::initializer_list{ + ((void)query.bind(++pos, std::forward(args)), 0)... + }; +} + +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 + +/** + * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, + * by forwarding them to the variadic template + * + * This feature requires a c++14 capable compiler. + * + * \code{.cpp} + * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC +void bind(SQLite::Statement& query, const std::tuple &tuple) +{ + bind(query, tuple, std::index_sequence_for()); +} + +/** + * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, + * by forwarding them to the variadic template. This function is just needed to convert the tuples + * to parameter packs + * + * This feature requires a c++14 capable compiler. + * + * @param query statement + * @param tuple tuple with values to bind + */ +template +void bind(SQLite::Statement& query, const std::tuple &tuple, std::index_sequence) +{ + bind(query, std::get(tuple)...); +} +#endif // c++14 + +} // namespace SQLite diff --git a/env/sqliteCpp/lib/SQLiteCpp.lib b/env/sqliteCpp/lib/SQLiteCpp.lib new file mode 100644 index 00000000..7acf5e16 Binary files /dev/null and b/env/sqliteCpp/lib/SQLiteCpp.lib differ diff --git a/lib/Hd_3DCameraLMI_module_PointLazer.exp b/lib/Hd_3DCameraLMI_module_PointLazer.exp index 83d8b980..c128f721 100644 Binary files a/lib/Hd_3DCameraLMI_module_PointLazer.exp and b/lib/Hd_3DCameraLMI_module_PointLazer.exp differ diff --git a/lib/Hd_3DCameraLMI_module_PointLazer.lib b/lib/Hd_3DCameraLMI_module_PointLazer.lib index b04150d6..b506c2ad 100644 Binary files a/lib/Hd_3DCameraLMI_module_PointLazer.lib and b/lib/Hd_3DCameraLMI_module_PointLazer.lib differ diff --git a/lib/Hd_DalsaCamera_module.exp b/lib/Hd_DalsaCamera_module.exp index efae2b9e..d51dd347 100644 Binary files a/lib/Hd_DalsaCamera_module.exp and b/lib/Hd_DalsaCamera_module.exp differ diff --git a/lib/Hd_DalsaCamera_module.lib b/lib/Hd_DalsaCamera_module.lib index 9c451b2e..c271a24e 100644 Binary files a/lib/Hd_DalsaCamera_module.lib and b/lib/Hd_DalsaCamera_module.lib differ diff --git a/lib/Hd_FinsTcp_module.exp b/lib/Hd_FinsTcp_module.exp index 3de4f63b..63243947 100644 Binary files a/lib/Hd_FinsTcp_module.exp and b/lib/Hd_FinsTcp_module.exp differ diff --git a/lib/Hd_FinsTcp_module.lib b/lib/Hd_FinsTcp_module.lib index f679fdae..e7784b3e 100644 Binary files a/lib/Hd_FinsTcp_module.lib and b/lib/Hd_FinsTcp_module.lib differ diff --git a/lib/Hd_MvCamera_module_GENTL.exp b/lib/Hd_MvCamera_module_GENTL.exp index d4933722..f0a89b55 100644 Binary files a/lib/Hd_MvCamera_module_GENTL.exp and b/lib/Hd_MvCamera_module_GENTL.exp differ diff --git a/lib/Hd_MvCamera_module_GENTL.lib b/lib/Hd_MvCamera_module_GENTL.lib index 578b535e..4010f0a3 100644 Binary files a/lib/Hd_MvCamera_module_GENTL.lib and b/lib/Hd_MvCamera_module_GENTL.lib differ diff --git a/lib/Hd_MvCamera_module_GIGE.exp b/lib/Hd_MvCamera_module_GIGE.exp index 3dad3e86..baf010b9 100644 Binary files a/lib/Hd_MvCamera_module_GIGE.exp and b/lib/Hd_MvCamera_module_GIGE.exp differ diff --git a/lib/Hd_MvCamera_module_GIGE.lib b/lib/Hd_MvCamera_module_GIGE.lib index f5ba12df..110ae8d7 100644 Binary files a/lib/Hd_MvCamera_module_GIGE.lib and b/lib/Hd_MvCamera_module_GIGE.lib differ diff --git a/lib/Hd_ResultsSummary_module.exp b/lib/Hd_ResultsSummary_module.exp index b54e9ed3..72272a70 100644 Binary files a/lib/Hd_ResultsSummary_module.exp and b/lib/Hd_ResultsSummary_module.exp differ diff --git a/lib/Hd_ResultsSummary_module.lib b/lib/Hd_ResultsSummary_module.lib index ff33633f..8baafc1c 100644 Binary files a/lib/Hd_ResultsSummary_module.lib and b/lib/Hd_ResultsSummary_module.lib differ diff --git a/lib/Hd_SocketModule_ModbusTcp.exp b/lib/Hd_SocketModule_ModbusTcp.exp index b95b63b1..25890329 100644 Binary files a/lib/Hd_SocketModule_ModbusTcp.exp and b/lib/Hd_SocketModule_ModbusTcp.exp differ diff --git a/lib/Hd_SocketModule_ModbusTcp.lib b/lib/Hd_SocketModule_ModbusTcp.lib index 7d987e79..b3ff8f19 100644 Binary files a/lib/Hd_SocketModule_ModbusTcp.lib and b/lib/Hd_SocketModule_ModbusTcp.lib differ