This commit is contained in:
Alexander Aleksandrovič Klimov 2026-02-03 15:23:51 +01:00 committed by GitHub
commit 38b5942801
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
71 changed files with 252989 additions and 23 deletions

View file

@ -190,7 +190,7 @@ find_package(OpenSSL REQUIRED)
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
set(base_DEPS ${CMAKE_DL_LIBS} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES})
set(base_OBJS $<TARGET_OBJECTS:mmatch> $<TARGET_OBJECTS:socketpair> $<TARGET_OBJECTS:base>)
set(base_OBJS $<TARGET_OBJECTS:mmatch> $<TARGET_OBJECTS:socketpair> $<TARGET_OBJECTS:SQLiteCpp> $<TARGET_OBJECTS:sqlite3> $<TARGET_OBJECTS:base>)
# JSON
find_package(JSON)
@ -200,6 +200,9 @@ include_directories(SYSTEM ${JSON_INCLUDE})
find_package(UTF8CPP)
include_directories(SYSTEM ${UTF8CPP_INCLUDE})
include_directories(${icinga2_SOURCE_DIR}/third-party/SQLiteCpp/include)
link_directories(${icinga2_BINARY_DIR}/third-party/SQLiteCpp)
find_package(Editline)
set(HAVE_EDITLINE "${EDITLINE_FOUND}")

View file

@ -55,6 +55,7 @@ public:
if (ptr == nullptr) {
ptr = new T(m_Initializer());
m_Underlying.store(ptr, std::memory_order_release);
m_Initializer = decltype(m_Initializer)();
}
}

View file

@ -38,6 +38,7 @@ set(remote_SOURCES
modifyobjecthandler.cpp modifyobjecthandler.hpp
objectqueryhandler.cpp objectqueryhandler.hpp
pkiutility.cpp pkiutility.hpp
replay-log.cpp replay-log.hpp
statushandler.cpp statushandler.hpp
templatequeryhandler.cpp templatequeryhandler.hpp
typequeryhandler.cpp typequeryhandler.hpp

View file

@ -1199,28 +1199,59 @@ void ApiListener::PersistMessage(const Dictionary::Ptr& message, const ConfigObj
ASSERT(ts != 0);
Dictionary::Ptr pmessage = new Dictionary();
pmessage->Set("timestamp", ts);
auto localZone (Zone::GetLocalZone());
std::set<Zone::Ptr> targetZones ({localZone});
pmessage->Set("message", JsonEncode(message));
{
auto parent (localZone->GetParent());
if (secobj) {
Dictionary::Ptr secname = new Dictionary();
secname->Set("type", secobj->GetReflectionType()->GetName());
secname->Set("name", secobj->GetName());
pmessage->Set("secobj", secname);
if (parent) {
targetZones.emplace(std::move(parent));
}
}
std::unique_lock<std::mutex> lock(m_LogLock);
if (m_LogFile) {
NetString::WriteStringToStream(m_LogFile, JsonEncode(pmessage));
m_LogMessageCount++;
SetLogMessageTimestamp(ts);
for (auto& zone : ConfigType::GetObjectsByType<Zone>()) {
/* immediate child zone */
if (zone->GetParent() == localZone) {
targetZones.emplace(zone);
}
}
if (m_LogMessageCount > 50000) {
CloseLogFile();
RotateLogFile();
OpenLogFile();
if (secobj) {
auto current (targetZones.begin());
auto end (targetZones.end());
while (current != end) {
if ((*current)->CanAccessObject(secobj)) {
++current;
} else {
targetZones.erase(current++);
}
}
}
std::set<Endpoint::Ptr> targetEndpoints;
for (auto& zone : targetZones) {
auto endpoints (zone->GetEndpoints());
targetEndpoints.insert(endpoints.begin(), endpoints.end());
}
targetEndpoints.erase(GetLocalEndpoint());
auto now (Utility::GetTime());
for (auto& endpoint : targetEndpoints) {
if (ts > endpoint->GetLocalLogPosition()) {
{
auto logDuration (endpoint->GetLogDuration());
if (logDuration >= 0u) {
endpoint->GetReplayLog().Cleanup(now - logDuration);
}
}
endpoint->GetReplayLog().Log(ts, JsonEncode(message));
}
}
}
@ -1476,9 +1507,6 @@ void ApiListener::RotateLogFile()
if (!Utility::PathExists(newpath)) {
try {
Utility::RenameFile(oldpath, newpath);
// We're rotating the current log file, so reset the log message counter as well.
m_LogMessageCount = 0;
} catch (const std::exception& ex) {
Log(LogCritical, "ApiListener")
<< "Cannot rotate replay log file from '" << oldpath << "' to '"
@ -1651,6 +1679,45 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client)
return;
}
}
Log(LogNotice, "ApiListener")
<< "Replaying log.";
count = 0;
for (auto& message : endpoint->GetReplayLog()) {
try {
client->SendRawMessage(message.second);
} catch (const std::exception& ex) {
Log(LogWarning, "ApiListener")
<< "Error while replaying log for endpoint '" << endpoint->GetName() << "': " << DiagnosticInformation(ex, false);
Log(LogDebug, "ApiListener")
<< "Error while replaying log for endpoint '" << endpoint->GetName() << "': " << DiagnosticInformation(ex);
break;
}
++count;
if (message.first > logpos_ts + 10) {
logpos_ts = message.first;
client->SendMessage(new Dictionary({
{ "jsonrpc", "2.0" },
{ "method", "log::SetLogPosition" },
{ "params", new Dictionary({
{ "log_position", logpos_ts }
}) }
}));
}
}
Log(count > 0u ? LogInformation : LogNotice, "ApiListener")
<< "Replayed " << count << " messages.";
ObjectLock olock2(endpoint);
endpoint->SetSyncing(false);
}
void ApiListener::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)

View file

@ -222,7 +222,6 @@ private:
std::mutex m_LogLock;
Stream::Ptr m_LogFile;
size_t m_LogMessageCount{0};
bool RelayMessageOne(const Zone::Ptr& zone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentZoneMaster);
void SyncRelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log);

View file

@ -18,6 +18,15 @@ REGISTER_TYPE(Endpoint);
boost::signals2::signal<void(const Endpoint::Ptr&, const JsonRpcConnection::Ptr&)> Endpoint::OnConnected;
boost::signals2::signal<void(const Endpoint::Ptr&, const JsonRpcConnection::Ptr&)> Endpoint::OnDisconnected;
INITIALIZE_ONCE(&Endpoint::ConfigStaticInitialize);
void Endpoint::ConfigStaticInitialize()
{
OnLocalLogPositionChanged.connect([](const Endpoint::Ptr& ep, const Value&) {
ep->GetReplayLog().Cleanup(ep->GetLocalLogPosition());
});
}
void Endpoint::OnAllConfigLoaded()
{
ObjectImpl<Endpoint>::OnAllConfigLoaded();
@ -36,7 +45,7 @@ void Endpoint::SetCachedZone(const Zone::Ptr& zone)
m_Zone = zone;
}
Endpoint::Endpoint()
Endpoint::Endpoint() : m_ReplayLog([this]() { return ReplayLog(GetName()); })
{
for (auto& [name, afunc] : ApiFunctionRegistry::GetInstance()->GetItems()) {
m_MessageCounters.emplace(afunc, 0);

View file

@ -5,7 +5,9 @@
#include "remote/i2-remote.hpp"
#include "remote/endpoint-ti.hpp"
#include "remote/replay-log.hpp"
#include "base/atomic.hpp"
#include "base/lazy-init.hpp"
#include "base/ringbuffer.hpp"
#include <cstdint>
#include <set>
@ -43,6 +45,12 @@ public:
bool GetConnected() const override;
static Endpoint::Ptr GetLocalEndpoint();
static void ConfigStaticInitialize();
inline ReplayLog& GetReplayLog()
{
return m_ReplayLog.Get();
}
void SetCachedZone(const intrusive_ptr<Zone>& zone);
@ -69,6 +77,7 @@ private:
std::set<intrusive_ptr<JsonRpcConnection> > m_Clients;
intrusive_ptr<Zone> m_Zone;
std::unordered_map<intrusive_ptr<ApiFunction>, Atomic<uint_fast64_t>> m_MessageCounters;
LazyInit<ReplayLog> m_ReplayLog;
mutable RingBuffer m_MessagesSent{60};
mutable RingBuffer m_MessagesReceived{60};

77
lib/remote/replay-log.cpp Normal file
View file

@ -0,0 +1,77 @@
/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
#include "base/string.hpp"
#include "remote/apilistener.hpp"
#include "remote/replay-log.hpp"
#include <cstdio>
#include <SQLiteCpp/Database.h>
#include <utility>
using namespace icinga;
ReplayLogIterator::ReplayLogIterator(SQLite::Database& db)
: m_Stmt(Shared<SQLite::Statement>::Make(db, "SELECT ts, content FROM message ORDER BY ts"))
{
Next();
}
void ReplayLogIterator::Next()
{
if (m_Stmt) {
if (m_Stmt->executeStep()) {
m_Message.first = m_Stmt->getColumn(0).getDouble();
auto content (m_Stmt->getColumn(1));
auto begin ((const char*)content.getBlob());
m_Message.second = String(begin, begin + content.getBytes());
} else {
m_Stmt = nullptr;
}
}
}
static inline
String EndpointToFile(const String& endpoint)
{
auto file (ApiListener::GetApiDir() + "log/");
for (auto c : endpoint) {
char buf[3] = { 0 };
sprintf(buf, "%02x", (unsigned int)(((int)c + 256) % 256));
file += (char*)buf;
}
file += ".sqlite3";
return std::move(file);
}
ReplayLog::ReplayLog(const String& endpoint) : ReplayLog(endpoint, EndpointToFile(endpoint))
{
}
void ReplayLog::Log(double ts, const String& message)
{
SQLite::Statement stmt (m_DB, "INSERT INTO message (ts, content) VALUES (?, ?)");
stmt.bind(1, ts);
stmt.bindNoCopy(2, message.GetData());
(void)stmt.exec();
}
void ReplayLog::Cleanup(double upTo)
{
SQLite::Statement stmt (m_DB, "DELETE FROM message WHERE ts < ?");
stmt.bind(1, upTo);
(void)stmt.exec();
}
ReplayLog::ReplayLog(const String& endpoint, const String& file)
: m_DB(file.CStr(), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE)
{
m_DB.exec("CREATE TABLE IF NOT EXISTS message ( ts REAL, content BLOB )");
m_DB.exec("CREATE INDEX IF NOT EXISTS ix_message_ts ON message ( ts )");
}

88
lib/remote/replay-log.hpp Normal file
View file

@ -0,0 +1,88 @@
/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
#ifndef REPLAY_LOG_H
#define REPLAY_LOG_H
#include "base/shared.hpp"
#include "base/string.hpp"
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
namespace icinga
{
/**
* An endpoint's cluster messages iterator.
*
* @ingroup remote
*/
class ReplayLogIterator
{
public:
ReplayLogIterator() = default;
ReplayLogIterator(SQLite::Database& db);
ReplayLogIterator(const ReplayLogIterator&) = delete;
ReplayLogIterator(ReplayLogIterator&&) = default;
ReplayLogIterator& operator=(const ReplayLogIterator&) = delete;
ReplayLogIterator& operator=(ReplayLogIterator&&) = default;
inline bool operator!=(const ReplayLogIterator& rhs)
{
return m_Stmt != rhs.m_Stmt;
}
inline ReplayLogIterator& operator++()
{
Next();
return *this;
}
inline const std::pair<double, String>& operator*()
{
return m_Message;
}
private:
void Next();
Shared<SQLite::Statement>::Ptr m_Stmt;
std::pair<double, String> m_Message;
};
/**
* An endpoint's cluster messages log.
*
* @ingroup remote
*/
class ReplayLog
{
public:
ReplayLog(const String& endpoint);
ReplayLog(const ReplayLog&) = delete;
ReplayLog(ReplayLog&&) = default;
ReplayLog& operator=(const ReplayLog&) = delete;
ReplayLog& operator=(ReplayLog&&) = default;
inline ReplayLogIterator begin()
{
return ReplayLogIterator(m_DB);
}
inline ReplayLogIterator end()
{
return ReplayLogIterator();
}
void Log(double ts, const String& message);
void Cleanup(double upTo);
private:
ReplayLog(const String& endpoint, const String& file);
SQLite::Database m_DB;
};
}
#endif /* REPLAY_LOG_H */

View file

@ -7,3 +7,5 @@ if(UNIX OR CYGWIN)
endif()
add_subdirectory(socketpair)
add_subdirectory(SQLiteCpp)

538
third-party/SQLiteCpp/.cproject vendored Normal file
View file

@ -0,0 +1,538 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="cdt.managedbuild.config.gnu.exe.debug.1034724773">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.debug.1034724773" moduleId="org.eclipse.cdt.core.settings" name="Debug">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.exe.debug.1034724773" name="Debug" parent="cdt.managedbuild.config.gnu.exe.debug">
<folderInfo id="cdt.managedbuild.config.gnu.exe.debug.1034724773." name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.exe.debug.898681687" name="Linux GCC" nonInternalBuilderId="cdt.managedbuild.target.gnu.builder.exe.debug" superClass="cdt.managedbuild.toolchain.gnu.exe.debug">
<targetPlatform id="cdt.managedbuild.target.gnu.platform.exe.debug.25715897" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.exe.debug"/>
<builder buildPath="${ProjDirPath}/build" id="cdt.managedbuild.target.gnu.builder.exe.debug.1103730408" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.target.gnu.builder.exe.debug"/>
<tool id="cdt.managedbuild.tool.gnu.archiver.base.836634439" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool command="g++" id="cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug.1817615032" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug">
<option id="gnu.cpp.compiler.exe.debug.option.optimization.level.750523151" name="Optimization Level" superClass="gnu.cpp.compiler.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
<option id="gnu.cpp.compiler.exe.debug.option.debugging.level.1248186067" name="Debug Level" superClass="gnu.cpp.compiler.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1310903331" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.exe.debug.796464367" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.exe.debug">
<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.exe.debug.option.optimization.level.1202477623" name="Optimization Level" superClass="gnu.c.compiler.exe.debug.option.optimization.level" valueType="enumerated"/>
<option id="gnu.c.compiler.exe.debug.option.debugging.level.280470620" name="Debug Level" superClass="gnu.c.compiler.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1655653012" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.exe.debug.1058275134" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.debug"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug.1061662206" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug">
<option id="gnu.cpp.link.option.libs.1966761747" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs">
<listOptionValue builtIn="false" value="pthread"/>
<listOptionValue builtIn="false" value="dl"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1636986986" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.assembler.exe.debug.258570404" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.exe.debug">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1571334436" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings">
<doc-comment-owner id="org.eclipse.cdt.ui.doxygen">
<path value=""/>
</doc-comment-owner>
</storageModule>
</cconfiguration>
<cconfiguration id="cdt.managedbuild.config.gnu.exe.release.326780594">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.release.326780594" moduleId="org.eclipse.cdt.core.settings" name="Release">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.exe.release.326780594" name="Release" parent="cdt.managedbuild.config.gnu.exe.release">
<folderInfo id="cdt.managedbuild.config.gnu.exe.release.326780594." name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.exe.release.1521127462" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.exe.release">
<targetPlatform id="cdt.managedbuild.target.gnu.platform.exe.release.1294814790" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.exe.release"/>
<builder arguments="BUILD=Release" buildPath="${ProjDirPath}" command="make" id="cdt.managedbuild.target.gnu.builder.exe.release.1733496537" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.target.gnu.builder.exe.release"/>
<tool id="cdt.managedbuild.tool.gnu.archiver.base.446006787" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.2105828055" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.exe.release">
<option id="gnu.cpp.compiler.exe.release.option.optimization.level.1921346334" name="Optimization Level" superClass="gnu.cpp.compiler.exe.release.option.optimization.level" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
<option id="gnu.cpp.compiler.exe.release.option.debugging.level.1660521780" name="Debug Level" superClass="gnu.cpp.compiler.exe.release.option.debugging.level" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1064866729" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.exe.release.1329805514" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.exe.release">
<option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.exe.release.option.optimization.level.2130170048" name="Optimization Level" superClass="gnu.c.compiler.exe.release.option.optimization.level" valueType="enumerated"/>
<option id="gnu.c.compiler.exe.release.option.debugging.level.1028604453" name="Debug Level" superClass="gnu.c.compiler.exe.release.option.debugging.level" value="gnu.c.debugging.level.none" valueType="enumerated"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1173224902" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.exe.release.1484631410" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.release"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.exe.release.1493084285" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.release">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1398815353" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.assembler.exe.release.1362356526" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.exe.release">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.831278578" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings">
<doc-comment-owner id="org.eclipse.cdt.ui.doxygen">
<path value=""/>
</doc-comment-owner>
</storageModule>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="SQLiteC++.cdt.managedbuild.target.gnu.exe.2007535171" name="Executable" projectType="cdt.managedbuild.target.gnu.exe"/>
</storageModule>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="Release">
<resource resourceType="PROJECT" workspacePath="/SQLiteCpp"/>
</configuration>
<configuration configurationName="Debug">
<resource resourceType="PROJECT" workspacePath="/SQLiteCpp"/>
</configuration>
</storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.326780594;cdt.managedbuild.config.gnu.exe.release.326780594.;cdt.managedbuild.tool.gnu.c.compiler.exe.release.1329805514;cdt.managedbuild.tool.gnu.c.compiler.input.1173224902">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.debug.1034724773;cdt.managedbuild.config.gnu.exe.debug.1034724773.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug.1817615032;cdt.managedbuild.tool.gnu.cpp.compiler.input.1310903331">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.326780594;cdt.managedbuild.config.gnu.exe.release.326780594.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.2105828055;cdt.managedbuild.tool.gnu.cpp.compiler.input.1064866729">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.debug.1034724773;cdt.managedbuild.config.gnu.exe.debug.1034724773.;cdt.managedbuild.tool.gnu.c.compiler.exe.debug.796464367;cdt.managedbuild.tool.gnu.c.compiler.input.1655653012">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
</cproject>

14
third-party/SQLiteCpp/.editorconfig vendored Normal file
View file

@ -0,0 +1,14 @@
root = true
# 4 space indentation
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
end_of_line = lf
# 2 space indentation for CI configuration
[*.yml]
indent_style = space
indent_size = 2

7
third-party/SQLiteCpp/.gitbugtraq vendored Normal file
View file

@ -0,0 +1,7 @@
# .gitbugtraq for Git GUIs (SmartGit/TortoiseGit) to show links to the Github issue tracker.
# Instead of the repository root directory, it could be added as an additional section to $GIT_DIR/config.
# (note that '\' need to be escaped).
[bugtraq]
url = https://github.com/SRombauts/SQLiteCpp/issues/%BUGID%
loglinkregex = "#\\d+"
logregex = \\d+

View file

@ -0,0 +1,59 @@
name: build
on: [push]
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- {
name: "Windows Latest MSVC",
os: windows-latest,
build_type: "Debug", cc: "cl", cxx: "cl",
}
# TODO: this is not working, since CMake is using generator for latest Visual Studio
#- {
# name: "Windows Latest MinGW",
# os: windows-latest,
# build_type: "Debug", cc: "gcc", cxx: "g++"
# }
- {
name: "Ubuntu Latest GCC",
os: ubuntu-latest,
build_type: "Debug", cc: "gcc", cxx: "g++"
}
- {
name: "Ubuntu 16.04 GCC",
os: ubuntu-16.04,
build_type: "Debug", cc: "gcc", cxx: "g++"
}
- {
name: "macOS Latest Clang",
os: macos-latest,
build_type: "Debug", cc: "clang", cxx: "clang++"
}
steps:
- uses: actions/checkout@v2
- name: submodule
run: git submodule update --init --recursive
- name: configure
shell: cmake -P {0}
run: |
set(ENV{CC} ${{matrix.config.cc}})
set(ENV{CXX} ${{matrix.config.cxx}})
- name: generate
run: |
mkdir build
cd build
cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_RUN_CPPCHECK=OFF -DSQLITECPP_RUN_CPPLINT=OFF ..
- name: build
run: cmake --build build --config ${{matrix.config.build_type}}
- name: test
run: |
cd build
ctest --verbose --output-on-failure

View file

@ -0,0 +1,48 @@
name: subdir_example
on: [push]
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- {
name: "Windows Latest MSVC",
os: windows-latest,
build_type: "Debug", cc: "cl", cxx: "cl",
}
- {
name: "Ubuntu Latest GCC",
os: ubuntu-latest,
build_type: "Debug", cc: "gcc", cxx: "g++"
}
- {
name: "Ubuntu 16.04 GCC",
os: ubuntu-16.04,
build_type: "Debug", cc: "gcc", cxx: "g++"
}
- {
name: "macOS Latest Clang",
os: macos-latest,
build_type: "Debug", cc: "clang", cxx: "clang++"
}
steps:
- uses: actions/checkout@v2
- name: configure
shell: cmake -P {0}
run: |
set(ENV{CC} ${{matrix.config.cc}})
set(ENV{CXX} ${{matrix.config.cxx}})
- name: generate
run: |
cd examples/example2
mkdir build
cd build
cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ..
- name: build
run: cmake --build examples/example2/build --config ${{matrix.config.build_type}}

29
third-party/SQLiteCpp/.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
Debug
Release
build
example1
*.a
.vscode/
/SQLiteCpp.sln
*.ncb
*.suo
*.user
*sdf
*.vc*
*~
doc
core
*ipch
.settings/
CMakeCache.txt
CMakeFiles
*.dir
Testing
Win32
SQLiteCpp_example1
SQLiteCpp_tests
!FindSQLiteCpp.cmake

3
third-party/SQLiteCpp/.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "googletest"]
path = googletest
url = https://github.com/google/googletest.git

85
third-party/SQLiteCpp/.project vendored Normal file
View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SQLiteC++</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?name?</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.append_environment</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
<value>all</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value>-j</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>
<value>make</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
<value>clean</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.contents</key>
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
<value>false</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
<value>all</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.stopOnError</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

141
third-party/SQLiteCpp/.travis.yml vendored Normal file
View file

@ -0,0 +1,141 @@
# Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
language: cpp
sudo: false
# Use Linux unless specified otherwise
os: linux
cache:
apt: true
env:
global:
- BUILD_TYPE=Debug
- ASAN=ON
- INTERNAL_SQLITE=ON
- VALGRIND=OFF
# Build variants (should test a reasonable number of combination of CMake options)
# TODO GCC 8+ using ubuntu-toolchain-r-test packages
# TODO cppcheck
# TODO Doxygen
# TODO link against googletest system library
matrix:
include:
##########################################################################
# GCC on Linux
##########################################################################
# Coverity static code analysis
- dist: bionic
env:
- COVERITY_SCAN_PROJECT_NAME=SRombauts/SQLiteCpp
- COVERITY_SCAN_BRANCH_PATTERN=master
- COVERITY_SCAN_NOTIFICATION_EMAIL=sebastien.rombauts@gmail.com
- COVERITY_SCAN_BUILD_COMMAND_PREPEND="cmake ."
- COVERITY_SCAN_BUILD_COMMAND="make -j8"
# Encrypted COVERITY_SCAN_TOKEN, created via the "travis encrypt" command using the project repo's public key
- secure: "Qm4d8NEDPBtYZCYav46uPEvDCtaRsjLXlkVS+C+WCJAPcwXCGkrr96wEi7RWcq2xD86QCh0XiqaPT+xdUmlohOYIovRhaaBmZ1lwIJ4GsG/ZR6xoFr3DYsZ3o4GyXk2vNXNxEl82AC+Xs6e6gkLOV9XRkBcjpVIvoIXgNlKWeGY="
# GCC 7.4.0 Debug build with GCov for coverage build
- dist: bionic
env:
- cc=gcc cxx=g++
- GCOV=ON
- COVERALLS=true
# GCC 7.4.0 Debug build with Valgrind instead of Address Sanitizer
- dist: bionic
env:
- cc=gcc cxx=g++
- ASAN=OFF
- VALGRIND=true
# GCC 7.4.0 Release build
- dist: bionic
env:
- cc=gcc cxx=g++
- BUILD_TYPE=Release
# GCC 7.4.0 test linking with libsqlite3-dev package
- dist: bionic
env:
- cc=gcc cxx=g++
- INTERNAL_SQLITE=OFF
# GCC 5.4.0
- dist: xenial
env:
- cc=gcc cxx=g++
# GCC 4.8.4
- dist: trusty
env:
- cc=gcc cxx=g++
##########################################################################
# Clang on Linux
##########################################################################
# Clang 7.0.0
- dist: bionic
env:
- cc=clang cxx=clang++
# Clang 7.0.0
- dist: xenial
env:
- cc=clang cxx=clang++
# Clang 5.0.0
- dist: trusty
env:
- cc=clang cxx=clang++
##########################################################################
# Clang on OSX
##########################################################################
# Latest XCode - AppleClang 9.1.0
- os: osx
env:
- cc=clang cxx=clang++
# XCode 8.3 - AppleClang 8.1.0
- os: osx
osx_image: xcode8.3
env:
- cc=clang cxx=clang++
before_install:
# Coverity: don't use addons.coverity_scan since it run on every job of the build matrix, which waste resources and exhausts quotas
# Note: the job dedicated to Coverity need to only run the shell script and then exit (to not try to build and run unit tests etc.)
- if [[ -n "$COVERITY_SCAN_PROJECT_NAME" ]] ; then curl -s https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh | bash ; exit 0 ; fi
- if [[ "$INTERNAL_SQLITE" == "OFF" ]]; then sudo apt-get install libsqlite3-dev ; fi
- if [[ "$VALGRIND" == "true" ]]; then sudo apt-get install valgrind ; fi
- if [[ "$COVERALLS" == "true" ]]; then pip install --user cpp-coveralls ; fi
# Set the compiler environment variables properly
- export CC=${cc}
- export CXX=${cxx}
# scripts to run before build
before_script:
- mkdir build
- cd build
- cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSQLITECPP_INTERNAL_SQLITE=$INTERNAL_SQLITE -DSQLITECPP_USE_ASAN=$ASAN -DSQLITECPP_USE_GCOV=$GCOV -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
# build examples, and run tests (ie make & make test)
script:
- cmake --build .
- export ASAN_OPTIONS=verbosity=1:debug=1
- ctest --verbose --output-on-failure
- if [[ "$VALGRIND" == "true" ]]; then valgrind --leak-check=full --error-exitcode=1 ./SQLiteCpp_example1 ; fi
- if [[ "$VALGRIND" == "true" ]]; then valgrind --leak-check=full --error-exitcode=1 ./SQLiteCpp_tests ; fi
# generate and publish GCov coveralls results
after_success:
- if [[ "$COVERALLS" == "true" ]]; then coveralls --root .. -e examples -e googletest -e sqlite3 -e tests -E ".*feature_tests.*" -E ".*CompilerId.*" --gcov-options '\-lp' ; fi

178
third-party/SQLiteCpp/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,178 @@
Mar 30 2012
- Start of a new thin C++ SQLite wrapper
Apr 2 2012
- The wrapper is functional
- Added documentation and examples
- Publication on GitHub
Version 0.1.0 - Apr 4 2012
- Added a Database::exec() method to execute simple SQL statement
- Added a version number like in sqlite3.h, starting with 0.1.0
Version 0.2.0 - Apr 11 2012
- Added getLastInsertId() and setBusyTimout()
- Added bind() by name methods
Version 0.3.0 - Apr 16 2012
- Added an easy wrapper Database::execAngGet()
Version 0.4.0 - Apr 23 2012
- Added a Database::tableExists() easy to use function
Dec 10 2012
- Added a Statement::exec() method to execute a one-step query with no expected result
Version 0.5.0 - March 9 2013
- Added assert() on errors on destructors
- Added getBytes()
- Added getBlob(), getType() and isInteger/isFloat/isText/isBlob/isNull
- Added bind() for binary blob data
Version 0.5.1 - April 7 2013
- Added Column::getName()
Version 0.6.0 - November 22 2013
- Renamed Column::getName() to Column::getOriginName()
- Added Column::getName()
Version 0.7.0 - January 9 2014
- Added Database::createFunction()
- Added std::string version of existing APIs
- Improved CMake with more build options and Doxygen auto-detection
Version 0.8.0 - February 26 2014
- Database constructor support opening a database with a custom VFS (default to NULL)
- Changed Column::getText() to return empty string "" by default instead of NULL pointer (to handle std::string conversion)
Version 1.0.0 - May 3 2015
- Public headers file moved to include/ dir
- Added support to biicode in CMakeLists.txt
- Added Unit Tests
- Added aBusyTimeoutMs parameter to Database() constructors
- Added Database::getTotalChanges()
- Added Database::getErrorCode()
- Added Statement::clearBindings()
- Added Statement::getColumn(aName)
- Added Statement::getErrorCode()
- Added Statement::getColumnName(aIndex)
- Added Statement::getColumnOriginName(aIndex)
Version 1.1.0 - May 18 2015
- Fixed valgrind error on Database destructor
- Added Database::loadExtension
Version 1.2.0 - September 9 2015
- Fixed build with GCC 5.1.0
- Fixed MSVC release build warning
- Fixed CppDepends warnings
- Updated documentation on installation
- Added Database::getHandle()
Version 1.3.0 - November 1 2015
- Fixed build with Visual Studio 2015
- Further improvements to README
- Added Backup class
Version 1.3.1 - February 10 2016
- Switch Linux/Mac build to the provided SQLite3 C library
- Update SQLite3 from 3.8.8.3 to latest 3.10.2 (2016-01-20)
- Remove warnings
- Remove biicode support (defunct service, servers will shutdown the 16th of February 2016)
Version 2.0.0 - July 25 2016
- Update SQLite3 from 3.10.2 to latest 3.13 (2016-05-18)
- Move #include <sqlite3.h> from headers to .cpp files only using forward declarations
- Add Database::VERSION to reach SQLITE_VERSION without including sqlite3.h in application code
- Add getLibVersion() and getLibVersionNumber() to get runtime version of the library
- Better exception messages when Statements fail PR #84
- Variadic templates for bind() (C++14) PR #85
- Add Statement::bindNoCopy() methods for strings, using SQLITE_STATIC to avoid internal copy by SQLite3 PR #86
- Add Statement::bind() overload for uint32_t, and Column::getUint() and cast operator to uint32_t PR #86
- Use the new SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION from SQLite 3.13 for security reason
- Rename Backup::remainingPageCount()/totalPageCount() to Backup::getRemainingPageCount()/getTotalPageCount()
- Remove Column::errmsg() method : use Database or Statement equivalents
- More unit tests, with code coverage status on the GitHub page
- Do not force MSVC to use static runtime if unit-tests are not build
Version 2.1.0 - July 18 2017
- Update SQLite3 from 3.13 to latest 3.19.3 (2017-06-08)
- Fixed Incompatibility in 3.19.0 (to use older SQLite version set the CMake variable SQLITE_USE_LEGACY_STRUCT) #125
- Fixed link error (inline in cpp) and compiler warnings (unused variable...) #96
- Added ability to open encrypted databases (using SQLCipher, eg. libsqlcipher-dev) #107
- Added convenience functions for constructing objects from a row #114
- Added CMake install step #118
- Fix warnings #119
- Make cpplint.py Python-3 compatible #120
- Link libssp when targeted #100
- Removed redundant const #102
Version 2.2.0 - Sept 19 2017
- Update SQLite3 from 3.19.3 to latest 3.20.1 (2017-08-24) #143
- Added tryExecuteStep and tryReset #142
- Removed virtual keywords from destructors #140
- Removed misplaced noexcept keyword #139
- Improved Exception class C++ conformance #138
- Fix warnings #134
- Deprecated Statement::isOk() to Statement::hasRow()
Version 2.3.0 - March 3 2019
- Update SQLite3 from 3.20.1 to latest 3.27.2 (2019-02-25) #183 #187
- Add Statement binding for long int values #147
- Allows long int for bind when used with name #148
- More cmake instructions for Linux #151
- Add comparison with sqlite_orm #141
- Fix Statement::bind truncates long integer to 32 bits on x86_64 Linux #155
- Add a move constructor to Database #157
- Added tests for all MSVC compilers available on AppVeyor (2013, 2015, 2017) #169
- Update VariadicBind.h #172
- Better CMake compatibility #170
- Add implicit cast operator to char and short types #179 #180
Version 2.4.0 - August 25 2019
- Update SQLite3 from 3.27.2 to 3.29.0 (2019-07-10) #217
- #191 CMake Warning line 299
- #190 Implement move constructors
- #192 Add wrapper for bind parameter count
- #197 Add tuple_bind and execute_many (requested by #24)
- #199 Fix #156 misleading error message in exception from Statement::exec
- #201 Add Statement::getExpandedSQL() to get the SQL text of prepared statement with bound parameters expanded
- #211 Implement Database::backup()
- #215 Disable implicit fallthrough warning when building internal sqlite3
- #216 Set PROJECT_VERSION to fix CMP0048 Policy warnings
Version 2.5.0 - December 31 2019
- Update SQLite3 from 3.29.0 to 3.30.1 (2019-10-10)
- 100% Unit Test coverage
- #212 fix sqlite3 compile properties (jzt)
- #219 Disable cast-function-type warning when building internal sqlite (zxey)
- #230 Fixed installation on other than Ubuntu GNU/Linux distributions (xvitaly)
- #228 use transitive compile definitions via cmake (BioDataAnalysis/emmenlau)
- #232 Added support of packaged GTest for running unit tests (xvitaly)
- #231 Added SOVERSION field for shared library (xvitaly)
- #229 Explicitly find and link against system sqlite library (xvitaly)
- #235 Added support for cmake dependencies and version information (BioDataAnalysis/emmenlau)
- #249 Added SQLite header parsing functionality and associated tests (patrick--)
- #251 Added example for getHeaderInfo()
Version 3.0.0 - January 31 2020
- C++11 is now required
- CMake 3.1 minimum
- Visual Studio 2015 minimum
- Update Googletest to latest release 1.10
- Add Github Actions continuous integration solution
- Add Valgrind memcheck tool to Travis CI
- Remove Statement::isOk() deprecated in 2.2.0 when renamed to Statement::hasRow()
- Replace Database::backup() "C" implementation by calling the Backup class
- #252 Run Valgrind memcheck on Travis CI
- #253 Keep inline functions for GCov code coverage
- #254 Re-enable Coverity static analysis
- #256 Fix linking with system library (libsqlite3)
- #242 Added a `getIndex` method and used it (KOLANICH)
- #257 Improve Statement unit tests coverage (bind by name with a std::string)
- #234 support for external sqlite3 (BioDataAnalysis/emmenlau)
- #243 adding a pure attribute to getIndex() (KOLANICH)

398
third-party/SQLiteCpp/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,398 @@
# Main CMake file for compiling the library itself, examples and tests.
#
# Copyright (c) 2012-2020 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)
cmake_minimum_required(VERSION 3.1) # for "CMAKE_CXX_STANDARD" version
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # custom CMake modules like FindSQLiteCpp
project(SQLiteCpp VERSION 3.0.0)
# SQLiteC++ 3.x requires C++11 features
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
elseif (CMAKE_CXX_STANDARD LESS 11)
message(WARNING "CMAKE_CXX_STANDARD has been set to '${CMAKE_CXX_STANDARD}' which is lower than the minimum required standard (c++11).")
endif ()
message(STATUS "Using c++ standard c++${CMAKE_CXX_STANDARD}")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
message (STATUS "CMake version: ${CMAKE_VERSION}")
message (STATUS "Project version: ${PROJECT_VERSION}")
# Define useful variables to handle OS differences:
if (WIN32)
set(DEV_NULL "NUL")
else (WIN32) # UNIX
set(DEV_NULL "/dev/null")
endif (WIN32)
# then Compiler/IDE differences:
if (MSVC)
set(CPPLINT_ARG_OUTPUT "--output=vs7")
set(CPPCHECK_ARG_TEMPLATE "--template=vs")
# disable Visual Studio warnings for fopen() used in the example
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
# Flags for linking with multithread static C++ runtime, required by googletest
if (SQLITECPP_BUILD_TESTS)
message(STATUS "Linking against multithread static C++ runtime for unit tests with googletest")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
endif (SQLITECPP_BUILD_TESTS)
# MSVC versions prior to 2015 are not supported anymore by SQLiteC++ 3.x
if (MSVC_VERSION LESS 1900) # OR MSVC_TOOLSET_VERSION LESS 140)
message(ERROR "Visual Studio prior to 2015 is not supported anymore.")
endif (MSVC_VERSION LESS 1900)
else (MSVC)
set(CPPLINT_ARG_OUTPUT "--output=eclipse")
set(CPPCHECK_ARG_TEMPLATE "--template=gcc")
# Useful compile flags and extra warnings
add_compile_options(-fstack-protector)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Wswitch-enum -Wshadow -Wno-long-long") # C++ only, don't bother with sqlite3
if (CMAKE_COMPILER_IS_GNUCXX)
# GCC flags
option(SQLITECPP_USE_GCOV "USE GCov instrumentation." OFF)
if (SQLITECPP_USE_GCOV)
message (STATUS "Using GCov instrumentation")
add_compile_options (-coverage)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage")
endif ()
endif (CMAKE_COMPILER_IS_GNUCXX)
endif (MSVC)
# and then common variables
set(CPPLINT_ARG_VERBOSE "--verbose=3")
set(CPPLINT_ARG_LINELENGTH "--linelength=120")
# Print CXX compiler information
message (STATUS "CMAKE_CXX_COMPILER '${CMAKE_CXX_COMPILER}' '${CMAKE_CXX_COMPILER_ID}' '${CMAKE_CXX_COMPILER_VERSION}'")
# Print CXX FLAGS
message (STATUS "CMAKE_CXX_FLAGS '${CMAKE_CXX_FLAGS}'")
if (MSVC)
message (STATUS "CMAKE_CXX_FLAGS_DEBUG '${CMAKE_CXX_FLAGS_DEBUG}'")
message (STATUS "CMAKE_CXX_FLAGS_RELEASE '${CMAKE_CXX_FLAGS_RELEASE}'")
message (STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO '${CMAKE_CXX_FLAGS_RELWITHDEBINFO}'")
message (STATUS "CMAKE_CXX_FLAGS_MINSIZEREL '${CMAKE_CXX_FLAGS_MINSIZEREL}'")
else (NOT MSVC)
if (CMAKE_BUILD_TYPE STREQUAL Debug)
message (STATUS "CMAKE_CXX_FLAGS_DEBUG '${CMAKE_CXX_FLAGS_DEBUG}'")
elseif (CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
message (STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO '${CMAKE_CXX_FLAGS_RELWITHDEBINFO}'")
elseif (CMAKE_BUILD_TYPE STREQUAL MinSizeRel)
message (STATUS "CMAKE_CXX_FLAGS_MINSIZEREL '${CMAKE_CXX_FLAGS_MINSIZEREL}'")
else ()
message (STATUS "CMAKE_CXX_FLAGS_RELEASE '${CMAKE_CXX_FLAGS_RELEASE}'")
endif ()
endif ()
## Build the C++ Wrapper ##
# adding a new file require explicitly modifying the CMakeLists.txt
# so that CMake knows that it should rebuild the project (it is best practice)
# list of sources files of the library
set(SQLITECPP_SRC
${PROJECT_SOURCE_DIR}/src/Backup.cpp
${PROJECT_SOURCE_DIR}/src/Column.cpp
${PROJECT_SOURCE_DIR}/src/Database.cpp
${PROJECT_SOURCE_DIR}/src/Exception.cpp
${PROJECT_SOURCE_DIR}/src/Statement.cpp
${PROJECT_SOURCE_DIR}/src/Transaction.cpp
)
source_group(src FILES ${SQLITECPP_SRC})
# list of header files of the library
set(SQLITECPP_INC
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/SQLiteCpp.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Assertion.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Backup.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Column.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Database.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Exception.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Statement.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Transaction.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/VariadicBind.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/ExecuteMany.h
)
source_group(include FILES ${SQLITECPP_INC})
# list of test files of the library
set(SQLITECPP_TESTS
tests/Column_test.cpp
tests/Database_test.cpp
tests/Statement_test.cpp
tests/Backup_test.cpp
tests/Transaction_test.cpp
tests/VariadicBind_test.cpp
tests/Exception_test.cpp
tests/ExecuteMany_test.cpp
)
source_group(tests FILES ${SQLITECPP_TESTS})
# list of example files of the library
set(SQLITECPP_EXAMPLES
examples/example1/main.cpp
)
source_group(example1 FILES ${SQLITECPP_EXAMPLES})
# list of doc files of the library
set(SQLITECPP_DOC
README.md
LICENSE.txt
CHANGELOG.md
TODO.txt
)
source_group(doc FILES ${SQLITECPP_DOC})
# list of config & script files of the library
set(SQLITECPP_SCRIPT
.editorconfig
.gitbugtraq
.github/workflows/build.yml
.github/workflows/subdir_example.yml
.gitignore
.gitmodules
.travis.yml
appveyor.yml
build.bat
build.sh
cpplint.py
Doxyfile
cmake/FindSQLiteCpp.cmake
cmake/FindSQLite3.cmake
cmake/SQLiteCppConfig.cmake.in
)
source_group(scripts FILES ${SQLITECPP_SCRIPT})
# add sources of the wrapper as a "SQLiteCpp" static library
add_library(SQLiteCpp ${SQLITECPP_SRC} ${SQLITECPP_INC} ${SQLITECPP_DOC} ${SQLITECPP_SCRIPT})
# Options relative to SQLite and SQLiteC++ functions
option(SQLITE_ENABLE_COLUMN_METADATA "Enable Column::getColumnOriginName(). Require support from sqlite3 library." ON)
if (SQLITE_ENABLE_COLUMN_METADATA)
# Enable the use of SQLite column metadata and Column::getColumnOriginName() method,
# Require that the sqlite3 library is also compiled with this flag (default under Debian/Ubuntu, but not on Mac OS X).
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_ENABLE_COLUMN_METADATA)
endif (SQLITE_ENABLE_COLUMN_METADATA)
option(SQLITE_ENABLE_ASSERT_HANDLER "Enable the user definition of a assertion_failed() handler." OFF)
if (SQLITE_ENABLE_ASSERT_HANDLER)
# Enable the user definition of a assertion_failed() handler (default to false, easier to handler for beginners).
target_compile_definitions(SQLiteCpp PUBLIC SQLITECPP_ENABLE_ASSERT_HANDLER)
endif (SQLITE_ENABLE_ASSERT_HANDLER)
option(SQLITE_HAS_CODEC "Enable database encryption API. Not available in the public release of SQLite." OFF)
if (SQLITE_HAS_CODEC)
# Enable database encryption API. Requires implementations of sqlite3_key & sqlite3_key_v2.
# Eg. SQLCipher (libsqlcipher-dev) is an SQLite extension that provides 256 bit AES encryption of database files.
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_HAS_CODEC)
endif (SQLITE_HAS_CODEC)
option(SQLITE_USE_LEGACY_STRUCT "Fallback to forward declaration of legacy struct sqlite3_value (pre SQLite 3.19)" OFF)
if (SQLITE_USE_LEGACY_STRUCT)
# Force forward declaration of legacy struct sqlite3_value (pre SQLite 3.19)
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_USE_LEGACY_STRUCT)
endif (SQLITE_USE_LEGACY_STRUCT)
if (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-fPIC")
endif (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
option(SQLITECPP_USE_ASAN "Use Address Sanitizer." OFF)
if (SQLITECPP_USE_ASAN)
if ((CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 6) OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang"))
message (STATUS "Using Address Sanitizer")
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
if (CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
endif ()
endif ()
endif (SQLITECPP_USE_ASAN)
if (SQLITECPP_USE_GCOV)
# Prevent the compiler from removing the unused inline functions so that they get tracked as "non-covered"
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-fkeep-inline-functions -fkeep-static-functions")
endif ()
## Build provided copy of SQLite3 C library ##
option(SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ON)
if (SQLITECPP_INTERNAL_SQLITE)
message(STATUS "Compile sqlite3 from source in subdirectory")
# build the SQLite3 C library (for ease of use/compatibility) versus Linux sqlite3-dev package
add_subdirectory(sqlite3)
target_link_libraries(SQLiteCpp PUBLIC sqlite3)
else (SQLITECPP_INTERNAL_SQLITE)
find_package (SQLite3 REQUIRED)
message(STATUS "Link to sqlite3 system library")
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3)
if(SQLite3_VERSION VERSION_LESS "3.19")
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-DSQLITECPP_HAS_MEM_STRUCT")
endif()
endif (SQLITECPP_INTERNAL_SQLITE)
# Link target with pthread and dl for Unix
if (UNIX)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(SQLiteCpp PUBLIC Threads::Threads ${CMAKE_DL_LIBS})
endif (UNIX)
# Set includes for target and transitive downstream targets
target_include_directories(SQLiteCpp
PRIVATE
$<$<BOOL:${SQLITECPP_INTERNAL_SQLITE}>:${CMAKE_CURRENT_SOURCE_DIR}/sqlite3>
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/>)
# Allow the library to be installed via "make install" and found with "find_package"
include(GNUInstallDirs)
install(TARGETS SQLiteCpp
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT libraries)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT headers FILES_MATCHING REGEX ".*\\.(hpp|h)$")
install(EXPORT ${PROJECT_NAME}Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
cmake/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
configure_package_config_file(
cmake/${PROJECT_NAME}Config.cmake.in
cmake/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME})
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION lib/cmake/${PROJECT_NAME})
# Optional additional targets:
option(SQLITECPP_RUN_CPPLINT "Run cpplint.py tool for Google C++ StyleGuide." ON)
if (SQLITECPP_RUN_CPPLINT)
find_package(PythonInterp)
if (PYTHONINTERP_FOUND)
# add a cpplint target to the "all" target
add_custom_target(SQLiteCpp_cpplint
ALL
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/cpplint.py ${CPPLINT_ARG_OUTPUT} ${CPPLINT_ARG_VERBOSE} ${CPPLINT_ARG_LINELENGTH} ${SQLITECPP_SRC} ${SQLITECPP_INC}
)
endif (PYTHONINTERP_FOUND)
else (SQLITECPP_RUN_CPPLINT)
message(STATUS "SQLITECPP_RUN_CPPLINT OFF")
endif (SQLITECPP_RUN_CPPLINT)
option(SQLITECPP_RUN_CPPCHECK "Run cppcheck C++ static analysis tool." ON)
if (SQLITECPP_RUN_CPPCHECK)
find_program(CPPCHECK_EXECUTABLE NAMES cppcheck)
if (CPPCHECK_EXECUTABLE)
# add a cppcheck target to the "all" target
add_custom_target(SQLiteCpp_cppcheck
ALL
COMMAND ${CPPCHECK_EXECUTABLE} -j 8 cppcheck --enable=style --quiet ${CPPCHECK_ARG_TEMPLATE} ${PROJECT_SOURCE_DIR}/src
)
execute_process(COMMAND "${CPPCHECK_EXECUTABLE}" --version OUTPUT_VARIABLE CPPCHECK_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Found Cppcheck: ${CPPCHECK_EXECUTABLE} ${CPPCHECK_VERSION}")
else (CPPCHECK_EXECUTABLE)
message(STATUS "Could NOT find cppcheck")
endif (CPPCHECK_EXECUTABLE)
else (SQLITECPP_RUN_CPPCHECK)
message(STATUS "SQLITECPP_RUN_CPPCHECK OFF")
endif (SQLITECPP_RUN_CPPCHECK)
option(SQLITECPP_RUN_DOXYGEN "Run Doxygen C++ documentation tool." OFF)
if (SQLITECPP_RUN_DOXYGEN)
find_package(Doxygen)
if (DOXYGEN_FOUND)
# add a Doxygen target to the "all" target
add_custom_target(SQLiteCpp_doxygen
ALL
COMMAND doxygen Doxyfile > ${DEV_NULL}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
endif (DOXYGEN_FOUND)
else (SQLITECPP_RUN_DOXYGEN)
message(STATUS "SQLITECPP_RUN_DOXYGEN OFF")
endif (SQLITECPP_RUN_DOXYGEN)
option(SQLITECPP_BUILD_EXAMPLES "Build examples." OFF)
if (SQLITECPP_BUILD_EXAMPLES)
# add the basic example executable
add_executable(SQLiteCpp_example1 ${SQLITECPP_EXAMPLES})
target_link_libraries(SQLiteCpp_example1 SQLiteCpp)
target_include_directories(SQLiteCpp_example1 PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
$<$<BOOL:${SQLITECPP_INTERNAL_SQLITE}>:${CMAKE_CURRENT_SOURCE_DIR}/sqlite3>)
if (MSYS OR MINGW)
target_link_libraries(SQLiteCpp_example1 ssp)
endif ()
else (SQLITECPP_BUILD_EXAMPLES)
message(STATUS "SQLITECPP_BUILD_EXAMPLES OFF")
endif (SQLITECPP_BUILD_EXAMPLES)
option(SQLITECPP_BUILD_TESTS "Build and run tests." OFF)
if (SQLITECPP_BUILD_TESTS)
# add the unit test executable
add_executable(SQLiteCpp_tests ${SQLITECPP_TESTS})
target_link_libraries(SQLiteCpp_tests SQLiteCpp)
target_include_directories(SQLiteCpp_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
$<$<BOOL:${SQLITECPP_INTERNAL_SQLITE}>:${CMAKE_CURRENT_SOURCE_DIR}/sqlite3>)
find_package(GTest)
if (GTEST_FOUND)
message(STATUS "Link to GTest system library")
target_link_libraries(SQLiteCpp_tests GTest::GTest GTest::Main)
else (GTEST_FOUND)
message(STATUS "Compile googletest from source in submodule")
# deactivate some warnings for compiling the googletest library
if (NOT MSVC)
add_compile_options(-Wno-switch-enum)
endif (NOT MSVC)
# add the subdirectory containing the CMakeLists.txt for the googletest library
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/googletest/CMakeLists.txt")
message(FATAL_ERROR "Missing 'googletest' submodule! Either use 'git submodule init' and 'git submodule update' to get googletest according to the README, or deactivate unit tests with -DSQLITECPP_BUILD_TESTS=OFF")
endif ()
add_subdirectory(googletest)
include_directories("${PROJECT_SOURCE_DIR}/googletest/googletest/include")
# Add definitions to keep googletest from making the compilation fail
if (MSVC)
if (MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS_EQUAL 1919) # OR MSVC_TOOLSET_VERSION EQUAL 141)
target_compile_definitions(gtest PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
target_compile_definitions(gtest_main PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
target_compile_definitions(gmock PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
target_compile_definitions(gmock_main PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
endif (MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS_EQUAL 1919)
endif (MSVC)
target_link_libraries(SQLiteCpp_tests gtest_main)
endif (GTEST_FOUND)
# add a "test" target:
enable_testing()
# does the tests pass?
add_test(UnitTests SQLiteCpp_tests)
if (SQLITECPP_BUILD_EXAMPLES)
# does the example1 runs successfully?
add_test(Example1Run SQLiteCpp_example1)
endif (SQLITECPP_BUILD_EXAMPLES)
else (SQLITECPP_BUILD_TESTS)
message(STATUS "SQLITECPP_BUILD_TESTS OFF")
endif (SQLITECPP_BUILD_TESTS)
# API version for SQLiteCpp shared library.
set_property(TARGET SQLiteCpp PROPERTY SOVERSION 0)

2304
third-party/SQLiteCpp/Doxyfile vendored Normal file

File diff suppressed because it is too large Load diff

20
third-party/SQLiteCpp/LICENSE.txt vendored Normal file
View file

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

323
third-party/SQLiteCpp/README.md vendored Normal file
View file

@ -0,0 +1,323 @@
SQLiteC++
---------
[![release](https://img.shields.io/github/release/SRombauts/SQLiteCpp.svg)](https://github.com/SRombauts/SQLiteCpp/releases)
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/SRombauts/SQLiteCpp/blob/master/LICENSE.txt)
[![Travis CI Linux Build Status](https://travis-ci.org/SRombauts/SQLiteCpp.svg?branch=master)](https://travis-ci.org/SRombauts/SQLiteCpp "Travis CI Linux Build Status")
[![AppVeyor Windows Build status](https://ci.appveyor.com/api/projects/status/github/SRombauts/SQLiteCpp?svg=true)](https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp "AppVeyor Windows Build status")
[![GitHub Actions Build status](https://github.com/SRombauts/SQLiteCpp/workflows/build/badge.svg)](https://github.com/SRombauts/SQLiteCpp/actions "GitHhub Actions Build status")
[![Coveralls](https://img.shields.io/coveralls/SRombauts/SQLiteCpp.svg)](https://coveralls.io/github/SRombauts/SQLiteCpp "Coveralls test coverage")
[![Coverity](https://img.shields.io/coverity/scan/14508.svg)](https://scan.coverity.com/projects/srombauts-sqlitecpp "Coverity Scan Build Status")
[![Join the chat at https://gitter.im/SRombauts/SQLiteCpp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SRombauts/SQLiteCpp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
SQLiteC++ (SQLiteCpp) is a smart and easy to use C++ SQLite3 wrapper.
Keywords: sqlite, sqlite3, C, library, wrapper C++
## About SQLiteC++:
SQLiteC++ offers an encapsulation around the native C APIs of SQLite,
with a few intuitive and well documented C++ classes.
### License:
Copyright (c) 2012-2020 Sébastien Rombauts (sebastien.rombauts@gmail.com)
<a href="https://www.paypal.me/SRombauts" title="Pay Me a Beer! Donate with PayPal :)"><img src="https://www.paypalobjects.com/webstatic/paypalme/images/pp_logo_small.png" width="118"></a>
Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
or copy at http://opensource.org/licenses/MIT)
#### Note on redistribution of SQLite source files
As stated by the MIT License, you are welcome to reuse, modify, and redistribute the SQLiteCpp source code
the way you want it to, be it a git submodule, a subdirectory, or a selection of some source files.
I would love a mention in your README, a web link to the SQLite repository, and a mention of the author,
but none of those are mandatory.
### About SQLite underlying library:
SQLite is a library that implements a serverless transactional SQL database engine.
It is the most widely deployed SQL database engine in the world.
All of the code and documentation in SQLite has been dedicated to the public domain by the authors.
http://www.sqlite.org/about.html
### The goals of SQLiteC++ are:
- to offer the best of the existing simple C++ SQLite wrappers
- to be elegantly written with good C++11 design, STL, exceptions and RAII idiom
- to keep dependencies to a minimum (C++11 STL and SQLite3)
- to be portable
- to be light and fast
- to be thread-safe only as much as SQLite "Multi-thread" mode (see below)
- to have a good unit test coverage
- to use API names sticking with those of the SQLite library
- to be well documented with Doxygen tags, and with some good examples
- to be well maintained
- to use a permissive MIT license, similar to BSD or Boost, for proprietary/commercial usage
It is designed using the Resource Acquisition Is Initialization (RAII) idiom
(see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization),
and throwing exceptions in case of SQLite errors (except in destructors,
where assert() are used instead).
Each SQLiteC++ object must be constructed with a valid SQLite database connection,
and then is always valid until destroyed.
### Supported platforms:
Now requires a C++11 compiler. Use branch [sqlitecpp-2.x](https://github.com/SRombauts/SQLiteCpp/tree/sqlitecpp-2.x) for latest pre-C++11 developments.
Developments and tests are done under the following OSs:
- Ubuntu 14.04, 16.04 and 18.04 (Travis CI)
- Windows 10, and Windows Server 2012 R2 & Windows Server 2016 (AppVeyor)
- OS X 10.11 (Travis CI)
- Github Actions
- Valgrind memcheck tool
And the following IDEs/Compilers
- GCC 4.8.4, 5.3.0 and 7.1.1 (C++11, C++14, C++17)
- Clang 5
- Xcode 8 & 9
- Visual Studio Community 2019, 2017, and 2015 (AppVeyor)
### Dependencies
- a modern C++11 STL implementation with GCC, Clang, or Visual Studio 2015
- exception support (the class Exception inherits from std::runtime_error)
- the SQLite library (3.7.15 minimum from 2012-12-12) either by linking to it dynamically or statically (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux),
or by adding its source file in your project code base (source code provided in src/sqlite3 for Windows),
with the SQLITE_ENABLE_COLUMN_METADATA macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata).
## Getting started
### Installation
To use this wrapper, you need to add the SQLiteC++ source files from the src/ directory
in your project code base, and compile/link against the sqlite library.
The easiest way to do this is to add the wrapper as a library.
The "CMakeLists.txt" file defining the static library is provided in the root directory,
so you simply have to add_subdirectory(SQLiteCpp) to you main CMakeLists.txt
and link to the "SQLiteCpp" wrapper library.
Example for Linux:
```cmake
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp/include
)
add_executable(main src/main.cpp)
target_link_libraries(main
SQLiteCpp
sqlite3
pthread
dl
)
```
Thus this SQLiteCpp repository can be directly used as a Git submodule.
See the [SQLiteCpp_Example](https://github.com/SRombauts/SQLiteCpp_Example) side repository for a standalone "from scratch" example.
Under Debian/Ubuntu/Mint Linux, you can install the libsqlite3-dev package if you don't want to use the embedded sqlite3 library.
### Building example and unit-tests:
Use git to clone the repository. Then init and update submodule "googletest".
```Shell
git clone https://github.com/SRombauts/SQLiteCpp.git
cd SQLiteCpp
git submodule init
git submodule update
```
#### CMake and tests
A CMake configuration file is also provided for multi-platform support and testing.
Typical generic build for MS Visual Studio under Windows (from [build.bat](build.bat)):
```Batchfile
mkdir build
cd build
cmake .. # cmake .. -G "Visual Studio 16 2019" # for Visual Studio 2019
@REM Generate a Visual Studio solution for latest version found
cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
@REM Build default configuration (ie 'Debug')
cmake --build .
@REM Build and run tests
ctest --output-on-failure
```
Generating the Linux Makefile, building in Debug and executing the tests (from [build.sh](build.sh)):
```Shell
mkdir Debug
cd Debug
# Generate a Makefile for GCC (or Clang, depanding on CC/CXX envvar)
cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
# Build (ie 'make')
cmake --build .
# Build and run unit-tests (ie 'make test')
ctest --output-on-failure
```
#### CMake options
* For more options on customizing the build, see the [CMakeLists.txt](https://github.com/SRombauts/SQLiteCpp/blob/master/CMakeLists.txt) file.
#### Troubleshooting
Under Linux, if you get multiple linker errors like "undefined reference to sqlite3_xxx",
it's that you lack the "sqlite3" library: install the libsqlite3-dev package.
If you get a single linker error "Column.cpp: undefined reference to sqlite3_column_origin_name",
it's that your "sqlite3" library was not compiled with
the SQLITE_ENABLE_COLUMN_METADATA macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata).
You can either recompile it yourself (seek help online) or you can comment out the following line in src/Column.h:
```C++
#define SQLITE_ENABLE_COLUMN_METADATA
```
### Continuous Integration
This project is continuously tested under Ubuntu Linux with the gcc and clang compilers
using the Travis CI community service with the above CMake building and testing procedure.
It is also tested in the same way under Windows Server 2012 R2 with Visual Studio 2013 compiler
using the AppVeyor continuous integration service.
Detailed results can be seen online:
- https://travis-ci.org/SRombauts/SQLiteCpp
- https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp
### Thread-safety
SQLite supports three modes of thread safety, as describe in "SQLite And Multiple Threads":
see http://www.sqlite.org/threadsafe.html
This SQLiteC++ wrapper does no add any locks (no mutexes) nor any other thread-safety mechanism
above the SQLite library itself, by design, for lightness and speed.
Thus, SQLiteC++ naturally supports the "Multi Thread" mode of SQLite:
"In this mode, SQLite can be safely used by multiple threads
provided that no single database connection is used simultaneously in two or more threads."
But SQLiteC++ does not support the fully thread-safe "Serialized" mode of SQLite,
because of the way it shares the underlying SQLite precompiled statement
in a custom shared pointer (See the inner class "Statement::Ptr").
## Examples
### The first sample demonstrates how to query a database and get results:
```C++
try
{
// Open a database file
SQLite::Database db("example.db3");
// Compile a SQL query, containing one parameter (index 1)
SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?");
// Bind the integer value 6 to the first parameter of the SQL query
query.bind(1, 6);
// Loop to execute the query step by step, to get rows of result
while (query.executeStep())
{
// Demonstrate how to get some typed column value
int id = query.getColumn(0);
const char* value = query.getColumn(1);
int size = query.getColumn(2);
std::cout << "row: " << id << ", " << value << ", " << size << std::endl;
}
}
catch (std::exception& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
```
### The second sample shows how to manage a transaction:
```C++
try
{
SQLite::Database db("transaction.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
db.exec("DROP TABLE IF EXISTS test");
// Begin transaction
SQLite::Transaction transaction(db);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// Commit transaction
transaction.commit();
}
catch (std::exception& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
```
### How to handle assertion in SQLiteC++:
Exceptions shall not be used in destructors, so SQLiteC++ uses SQLITECPP_ASSERT() to check for errors in destructors.
If you don't want assert() to be called, you have to enable and define an assert handler as shown below,
and by setting the flag SQLITECPP_ENABLE_ASSERT_HANDLER when compiling the lib.
```C++
#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
{
/// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
{
// Print a message to the standard error output stream, and abort the program.
std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n";
std::abort();
}
}
#endif
```
## How to contribute
### GitHub website
The most efficient way to help and contribute to this wrapper project is to
use the tools provided by GitHub:
- please fill bug reports and feature requests here: https://github.com/SRombauts/SQLiteCpp/issues
- fork the repository, make some small changes and submit them with pull-request
### Contact
You can also email me directly, I will try to answer questions and requests whenever I get the time for it.
### Coding Style Guidelines
The source code use the CamelCase naming style variant where:
- type names (class, struct, typedef, enums...) begin with a capital letter
- files (.cpp/.h) are named like the class they contain
- function and variable names begin with a lower case letter
- member variables begin with a 'm', function arguments begin with a 'a', booleans with a 'b', pointers with a 'p'
- each file, class, method and member variable is documented using Doxygen tags
- braces on their own line
See also http://www.appinf.com/download/CppCodingStyleGuide.pdf for good guidelines
## See also - Some other simple C++ SQLite wrappers:
See bellow a short comparison of other wrappers done at the time of writing:
- [sqdbcpp](http://code.google.com/p/sqdbcpp/): RAII design, simple, no dependencies, UTF-8/UTF-16, new BSD license
- [sqlite3cc](http://ed.am/dev/sqlite3cc): uses boost, modern design, LPGPL
- [sqlite3pp](https://github.com/iwongu/sqlite3pp): modern design inspired by boost, MIT License
- [SQLite++](http://sqlitepp.berlios.de/): uses boost build system, Boost License 1.0
- [CppSQLite](http://www.codeproject.com/Articles/6343/CppSQLite-C-Wrapper-for-SQLite/): famous Code Project but old design, BSD License
- [easySQLite](http://code.google.com/p/easysqlite/): manages table as structured objects, complex
- [sqlite_modern_cpp](https://github.com/keramer/sqlite_modern_cpp): modern C++11, all in one file, MIT license
- [sqlite_orm](https://github.com/fnc12/sqlite_orm): modern C++14, header only all in one file, no raw string queries, BSD-3 license

27
third-party/SQLiteCpp/TODO.txt vendored Normal file
View file

@ -0,0 +1,27 @@
Add a Tutorial for SQLite newbies
Add a real example in the form of a small interactive console application
Improve Github Wiki pages with the FAQ: Installation, Examples, Tutorial, How to contribute
Publish the Doxygen Documentation in the Github Pages (gh-pages branch)
Missing features in v2.0.0:
- #34: Better type for getColumn
Missing documentation in v2.0.0:
- explain the non-copyable property for RAII design
- comment on returning error code instead of exception that shall not be thrown when expected (!?)
Missing unit tests in v2.0.0:
- Load Extension (not practicable, and easy to verify by code review)
Advanced missing features:
- #39: SAVEPOINT https://www.sqlite.org/lang_savepoint.html
- Add optional usage of experimental sqlite3_trace() function to enable statistics
- Aggregate ?
- support for different transaction mode ? NO: too specific
- operator<< binding ? NO: redundant with bind()
- ATTACH Database ? NO: can already be done by "ATTACH" Statement
Post an article to CodeProject: Is there a license issue ?

1
third-party/SQLiteCpp/_config.yml vendored Normal file
View file

@ -0,0 +1 @@
theme: jekyll-theme-slate

44
third-party/SQLiteCpp/appveyor.yml vendored Normal file
View file

@ -0,0 +1,44 @@
# Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
# build format
version: "{build}"
# scripts that run after cloning repository
install:
- git submodule update --init --recursive
image:
- Visual Studio 2019
- Visual Studio 2017
- Visual Studio 2015
# configurations to add to build matrix
# TODO: MinGW Makefiles and MSYS Makefiles
configuration:
- Debug
- Release
environment:
matrix:
- arch: Win32
- arch: x64
init:
- echo %APPVEYOR_BUILD_WORKER_IMAGE% - %configuration% - %arch%
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set vs=Visual Studio 15 2017)
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set vs=Visual Studio 14 2015)
- if "%arch%"=="x64" (set generator="%vs% Win64") else (set generator="%vs%")
# CMake uses a different grammar for Visual Studio 2019, with -A to specify architecture:
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (set generator="Visual Studio 16 2019" -A %arch%)
- echo %generator%
# scripts to run before build
before_build:
- mkdir build
- cd build
- cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_RUN_CPPCHECK=OFF .. -G %generator%
# build examples, and run tests (ie make & make test)
build_script:
- cmake --build . --config %configuration%
- ctest --output-on-failure

25
third-party/SQLiteCpp/build.bat vendored Normal file
View file

@ -0,0 +1,25 @@
@REM Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
@REM
@REM Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
@REM or copy at http://opensource.org/licenses/MIT)
mkdir build
cd build
@REM Generate a Visual Studio solution for latest version found
cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
@if ERRORLEVEL 1 goto onError
@REM Build default configuration (ie 'Debug')
cmake --build .
@if ERRORLEVEL 1 goto onError
@REM Build and run tests
ctest --output-on-failure
@if ERRORLEVEL 1 goto onError
goto onSuccess
:onError
@echo An error occured!
:onSuccess
cd ..

24
third-party/SQLiteCpp/build.sh vendored Executable file
View file

@ -0,0 +1,24 @@
#!/bin/sh
# Copyright (c) 2012-2020 Sébastien Rombauts (sebastien.rombauts@gmail.com)
#
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
# or copy at http://opensource.org/licenses/MIT)
# exit on first error
set -e
mkdir -p build
cd build
# Generate a Makefile for GCC (or Clang, depanding on CC/CXX envvar)
cmake -DCMAKE_BUILD_TYPE=Debug -DSQLITECPP_USE_ASAN=ON -DSQLITECPP_USE_GCOV=OFF -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
# Build (ie 'make')
cmake --build .
# Build and run unit-tests (ie 'make test')
ctest --output-on-failure
# And with Valgrind
#valgrind --leak-check=full --error-exitcode=1 ./SQLiteCpp_example1
#valgrind --leak-check=full --error-exitcode=1 ./SQLiteCpp_tests

View file

@ -0,0 +1,66 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindSQLite3
-----------
Find the SQLite libraries, v3
IMPORTED targets
^^^^^^^^^^^^^^^^
This module defines the following :prop_tgt:`IMPORTED` target:
``SQLite::SQLite3``
Result variables
^^^^^^^^^^^^^^^^
This module will set the following variables if found:
``SQLite3_INCLUDE_DIRS``
where to find sqlite3.h, etc.
``SQLite3_LIBRARIES``
the libraries to link against to use SQLite3.
``SQLite3_VERSION``
version of the SQLite3 library found
``SQLite3_FOUND``
TRUE if found
#]=======================================================================]
# Look for the necessary header
find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h)
mark_as_advanced(SQLite3_INCLUDE_DIR)
# Look for the necessary library
find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite)
mark_as_advanced(SQLite3_LIBRARY)
# Extract version information from the header file
if(SQLite3_INCLUDE_DIR)
file(STRINGS ${SQLite3_INCLUDE_DIR}/sqlite3.h _ver_line
REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
SQLite3_VERSION "${_ver_line}")
unset(_ver_line)
endif()
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
find_package_handle_standard_args(SQLite3
REQUIRED_VARS SQLite3_INCLUDE_DIR SQLite3_LIBRARY
VERSION_VAR SQLite3_VERSION)
# Create the imported target
if(SQLite3_FOUND)
set(SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR})
set(SQLite3_LIBRARIES ${SQLite3_LIBRARY})
if(NOT TARGET SQLite::SQLite3)
add_library(SQLite::SQLite3 UNKNOWN IMPORTED)
set_target_properties(SQLite::SQLite3 PROPERTIES
IMPORTED_LOCATION "${SQLite3_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}")
endif()
endif()

View file

@ -0,0 +1,58 @@
# @file CMakeLists.txt
# @ingroup SQLiteCpp
# @brief SQLiteCpp CMake module.
#
# Copyright (c) 2010-2014 Kartik Kumar (me@kartikkumar.com)
#
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
# or copy at http://opensource.org/licenses/MIT)
macro(_sqlitecpp_check_version)
file(READ "${SQLITECPP_INCLUDE_DIR}/SQLiteCpp.h" _sqlitecpp_header)
string(REGEX MATCH "define[ \t]+SQLITECPP_VERSION_NUMBER[ \t]+([0-9]+)"
_sqlitecpp_version_match "${_sqlitecpp_header}")
set(SQLITECPP_VERSION "${CMAKE_MATCH_1}")
if(${SQLITECPP_VERSION} VERSION_LESS ${SQLiteCpp_FIND_VERSION})
set(SQLITECPP_VERSION_OK FALSE)
else(${SQLITECPP_VERSION} VERSION_LESS ${SQLiteCpp_FIND_VERSION})
set(SQLITECPP_VERSION_OK TRUE)
endif(${SQLITECPP_VERSION} VERSION_LESS ${SQLiteCpp_FIND_VERSION})
if(NOT SQLITECPP_VERSION_OK)
message(STATUS "SQLiteCpp version ${SQLITECPP_VERSION} found in ${SQLITECPP_INCLUDE_DIR}, "
"but at least version ${SQLiteCpp_FIND_VERSION} is required!")
endif(NOT SQLITECPP_VERSION_OK)
set(SQLITECPP_LIBRARY "SQLiteCpp")
link_directories(${SQLITECPP_LIBRARY_DIR})
endmacro(_sqlitecpp_check_version)
if(SQLITECPP_INCLUDE_DIR)
# Check if SQLiteCpp is already present in cache.
_sqlitecpp_check_version()
set(SQLITECPP_FOUND ${SQLITECPP_VERSION_OK})
else (SQLITECPP_INCLUDE_DIR)
find_path(SQLITECPP_BASE_PATH NAMES SQLiteCpp.h
PATHS
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/..
${PROJECT_SOURCE_DIR}/../..
${PROJECT_SOURCE_DIR}/../../..
${PROJECT_SOURCE_DIR}/../../../..
PATH_SUFFIXES SQLiteCpp/include/SQLiteCpp
)
set(SQLITECPP_INCLUDE_DIR ${SQLITECPP_BASE_PATH})
set(SQLITECPP_LIBRARY_DIR ${SQLITECPP_BASE_PATH}/../../build)
if(SQLITECPP_INCLUDE_DIR)
_sqlitecpp_check_version()
endif(SQLITECPP_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SQLITECPP DEFAULT_MSG SQLITECPP_INCLUDE_DIR SQLITECPP_VERSION_OK)
mark_as_advanced(SQLITECPP_INCLUDE_DIR)
endif(SQLITECPP_INCLUDE_DIR)

View file

@ -0,0 +1,7 @@
include(CMakeFindDependencyMacro)
find_dependency(SQLite3)
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

4807
third-party/SQLiteCpp/cpplint.py vendored Executable file

File diff suppressed because it is too large Load diff

323
third-party/SQLiteCpp/docs/README.md vendored Normal file
View file

@ -0,0 +1,323 @@
SQLiteC++
---------
[![release](https://img.shields.io/github/release/SRombauts/SQLiteCpp.svg)](https://github.com/SRombauts/SQLiteCpp/releases)
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/SRombauts/SQLiteCpp/blob/master/LICENSE.txt)
[![Travis CI Linux Build Status](https://travis-ci.org/SRombauts/SQLiteCpp.svg?branch=master)](https://travis-ci.org/SRombauts/SQLiteCpp "Travis CI Linux Build Status")
[![AppVeyor Windows Build status](https://ci.appveyor.com/api/projects/status/github/SRombauts/SQLiteCpp?svg=true)](https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp "AppVeyor Windows Build status")
[![GitHub Actions Build status](https://github.com/SRombauts/SQLiteCpp/workflows/build/badge.svg)](https://github.com/SRombauts/SQLiteCpp/actions "GitHhub Actions Build status")
[![Coveralls](https://img.shields.io/coveralls/SRombauts/SQLiteCpp.svg)](https://coveralls.io/github/SRombauts/SQLiteCpp "Coveralls test coverage")
[![Coverity](https://img.shields.io/coverity/scan/14508.svg)](https://scan.coverity.com/projects/srombauts-sqlitecpp "Coverity Scan Build Status")
[![Join the chat at https://gitter.im/SRombauts/SQLiteCpp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SRombauts/SQLiteCpp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
SQLiteC++ (SQLiteCpp) is a smart and easy to use C++ SQLite3 wrapper.
Keywords: sqlite, sqlite3, C, library, wrapper C++
## About SQLiteC++:
SQLiteC++ offers an encapsulation around the native C APIs of SQLite,
with a few intuitive and well documented C++ classes.
### License:
Copyright (c) 2012-2020 Sébastien Rombauts (sebastien.rombauts@gmail.com)
<a href="https://www.paypal.me/SRombauts" title="Pay Me a Beer! Donate with PayPal :)"><img src="https://www.paypalobjects.com/webstatic/paypalme/images/pp_logo_small.png" width="118"></a>
Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
or copy at http://opensource.org/licenses/MIT)
#### Note on redistribution of SQLite source files
As stated by the MIT License, you are welcome to reuse, modify, and redistribute the SQLiteCpp source code
the way you want it to, be it a git submodule, a subdirectory, or a selection of some source files.
I would love a mention in your README, a web link to the SQLite repository, and a mention of the author,
but none of those are mandatory.
### About SQLite underlying library:
SQLite is a library that implements a serverless transactional SQL database engine.
It is the most widely deployed SQL database engine in the world.
All of the code and documentation in SQLite has been dedicated to the public domain by the authors.
http://www.sqlite.org/about.html
### The goals of SQLiteC++ are:
- to offer the best of the existing simple C++ SQLite wrappers
- to be elegantly written with good C++11 design, STL, exceptions and RAII idiom
- to keep dependencies to a minimum (C++11 STL and SQLite3)
- to be portable
- to be light and fast
- to be thread-safe only as much as SQLite "Multi-thread" mode (see below)
- to have a good unit test coverage
- to use API names sticking with those of the SQLite library
- to be well documented with Doxygen tags, and with some good examples
- to be well maintained
- to use a permissive MIT license, similar to BSD or Boost, for proprietary/commercial usage
It is designed using the Resource Acquisition Is Initialization (RAII) idiom
(see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization),
and throwing exceptions in case of SQLite errors (except in destructors,
where assert() are used instead).
Each SQLiteC++ object must be constructed with a valid SQLite database connection,
and then is always valid until destroyed.
### Supported platforms:
Now requires a C++11 compiler. Use branch [sqlitecpp-2.x](https://github.com/SRombauts/SQLiteCpp/tree/sqlitecpp-2.x) for latest pre-C++11 developments.
Developments and tests are done under the following OSs:
- Ubuntu 14.04, 16.04 and 18.04 (Travis CI)
- Windows 10, and Windows Server 2012 R2 & Windows Server 2016 (AppVeyor)
- OS X 10.11 (Travis CI)
- Github Actions
- Valgrind memcheck tool
And the following IDEs/Compilers
- GCC 4.8.4, 5.3.0 and 7.1.1 (C++11, C++14, C++17)
- Clang 5
- Xcode 8 & 9
- Visual Studio Community 2019, 2017, and 2015 (AppVeyor)
### Dependencies
- a modern C++11 STL implementation with GCC, Clang, or Visual Studio 2015
- exception support (the class Exception inherits from std::runtime_error)
- the SQLite library (3.7.15 minimum from 2012-12-12) either by linking to it dynamically or statically (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux),
or by adding its source file in your project code base (source code provided in src/sqlite3 for Windows),
with the SQLITE_ENABLE_COLUMN_METADATA macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata).
## Getting started
### Installation
To use this wrapper, you need to add the SQLiteC++ source files from the src/ directory
in your project code base, and compile/link against the sqlite library.
The easiest way to do this is to add the wrapper as a library.
The "CMakeLists.txt" file defining the static library is provided in the root directory,
so you simply have to add_subdirectory(SQLiteCpp) to you main CMakeLists.txt
and link to the "SQLiteCpp" wrapper library.
Example for Linux:
```cmake
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp/include
)
add_executable(main src/main.cpp)
target_link_libraries(main
SQLiteCpp
sqlite3
pthread
dl
)
```
Thus this SQLiteCpp repository can be directly used as a Git submodule.
See the [SQLiteCpp_Example](https://github.com/SRombauts/SQLiteCpp_Example) side repository for a standalone "from scratch" example.
Under Debian/Ubuntu/Mint Linux, you can install the libsqlite3-dev package if you don't want to use the embedded sqlite3 library.
### Building example and unit-tests:
Use git to clone the repository. Then init and update submodule "googletest".
```Shell
git clone https://github.com/SRombauts/SQLiteCpp.git
cd SQLiteCpp
git submodule init
git submodule update
```
#### CMake and tests
A CMake configuration file is also provided for multi-platform support and testing.
Typical generic build for MS Visual Studio under Windows (from [build.bat](build.bat)):
```Batchfile
mkdir build
cd build
cmake .. # cmake .. -G "Visual Studio 16 2019" # for Visual Studio 2019
@REM Generate a Visual Studio solution for latest version found
cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
@REM Build default configuration (ie 'Debug')
cmake --build .
@REM Build and run tests
ctest --output-on-failure
```
Generating the Linux Makefile, building in Debug and executing the tests (from [build.sh](build.sh)):
```Shell
mkdir Debug
cd Debug
# Generate a Makefile for GCC (or Clang, depanding on CC/CXX envvar)
cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..
# Build (ie 'make')
cmake --build .
# Build and run unit-tests (ie 'make test')
ctest --output-on-failure
```
#### CMake options
* For more options on customizing the build, see the [CMakeLists.txt](https://github.com/SRombauts/SQLiteCpp/blob/master/CMakeLists.txt) file.
#### Troubleshooting
Under Linux, if you get multiple linker errors like "undefined reference to sqlite3_xxx",
it's that you lack the "sqlite3" library: install the libsqlite3-dev package.
If you get a single linker error "Column.cpp: undefined reference to sqlite3_column_origin_name",
it's that your "sqlite3" library was not compiled with
the SQLITE_ENABLE_COLUMN_METADATA macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata).
You can either recompile it yourself (seek help online) or you can comment out the following line in src/Column.h:
```C++
#define SQLITE_ENABLE_COLUMN_METADATA
```
### Continuous Integration
This project is continuously tested under Ubuntu Linux with the gcc and clang compilers
using the Travis CI community service with the above CMake building and testing procedure.
It is also tested in the same way under Windows Server 2012 R2 with Visual Studio 2013 compiler
using the AppVeyor continuous integration service.
Detailed results can be seen online:
- https://travis-ci.org/SRombauts/SQLiteCpp
- https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp
### Thread-safety
SQLite supports three modes of thread safety, as describe in "SQLite And Multiple Threads":
see http://www.sqlite.org/threadsafe.html
This SQLiteC++ wrapper does no add any locks (no mutexes) nor any other thread-safety mechanism
above the SQLite library itself, by design, for lightness and speed.
Thus, SQLiteC++ naturally supports the "Multi Thread" mode of SQLite:
"In this mode, SQLite can be safely used by multiple threads
provided that no single database connection is used simultaneously in two or more threads."
But SQLiteC++ does not support the fully thread-safe "Serialized" mode of SQLite,
because of the way it shares the underlying SQLite precompiled statement
in a custom shared pointer (See the inner class "Statement::Ptr").
## Examples
### The first sample demonstrates how to query a database and get results:
```C++
try
{
// Open a database file
SQLite::Database db("example.db3");
// Compile a SQL query, containing one parameter (index 1)
SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?");
// Bind the integer value 6 to the first parameter of the SQL query
query.bind(1, 6);
// Loop to execute the query step by step, to get rows of result
while (query.executeStep())
{
// Demonstrate how to get some typed column value
int id = query.getColumn(0);
const char* value = query.getColumn(1);
int size = query.getColumn(2);
std::cout << "row: " << id << ", " << value << ", " << size << std::endl;
}
}
catch (std::exception& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
```
### The second sample shows how to manage a transaction:
```C++
try
{
SQLite::Database db("transaction.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
db.exec("DROP TABLE IF EXISTS test");
// Begin transaction
SQLite::Transaction transaction(db);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// Commit transaction
transaction.commit();
}
catch (std::exception& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
```
### How to handle assertion in SQLiteC++:
Exceptions shall not be used in destructors, so SQLiteC++ uses SQLITECPP_ASSERT() to check for errors in destructors.
If you don't want assert() to be called, you have to enable and define an assert handler as shown below,
and by setting the flag SQLITECPP_ENABLE_ASSERT_HANDLER when compiling the lib.
```C++
#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
{
/// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
{
// Print a message to the standard error output stream, and abort the program.
std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n";
std::abort();
}
}
#endif
```
## How to contribute
### GitHub website
The most efficient way to help and contribute to this wrapper project is to
use the tools provided by GitHub:
- please fill bug reports and feature requests here: https://github.com/SRombauts/SQLiteCpp/issues
- fork the repository, make some small changes and submit them with pull-request
### Contact
You can also email me directly, I will try to answer questions and requests whenever I get the time for it.
### Coding Style Guidelines
The source code use the CamelCase naming style variant where:
- type names (class, struct, typedef, enums...) begin with a capital letter
- files (.cpp/.h) are named like the class they contain
- function and variable names begin with a lower case letter
- member variables begin with a 'm', function arguments begin with a 'a', booleans with a 'b', pointers with a 'p'
- each file, class, method and member variable is documented using Doxygen tags
- braces on their own line
See also http://www.appinf.com/download/CppCodingStyleGuide.pdf for good guidelines
## See also - Some other simple C++ SQLite wrappers:
See bellow a short comparison of other wrappers done at the time of writing:
- [sqdbcpp](http://code.google.com/p/sqdbcpp/): RAII design, simple, no dependencies, UTF-8/UTF-16, new BSD license
- [sqlite3cc](http://ed.am/dev/sqlite3cc): uses boost, modern design, LPGPL
- [sqlite3pp](https://github.com/iwongu/sqlite3pp): modern design inspired by boost, MIT License
- [SQLite++](http://sqlitepp.berlios.de/): uses boost build system, Boost License 1.0
- [CppSQLite](http://www.codeproject.com/Articles/6343/CppSQLite-C-Wrapper-for-SQLite/): famous Code Project but old design, BSD License
- [easySQLite](http://code.google.com/p/easysqlite/): manages table as structured objects, complex
- [sqlite_modern_cpp](https://github.com/keramer/sqlite_modern_cpp): modern C++11, all in one file, MIT license
- [sqlite_orm](https://github.com/fnc12/sqlite_orm): modern C++14, header only all in one file, no raw string queries, BSD-3 license

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,501 @@
/**
* @file main.cpp
* @brief A few short examples in a row.
*
* Demonstrates how-to use the SQLite++ wrapper
*
* Copyright (c) 2012-2020 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)
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <SQLiteCpp/SQLiteCpp.h>
#include <SQLiteCpp/VariadicBind.h>
#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
{
/// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
{
// Print a message to the standard error output stream, and abort the program.
std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n";
std::abort();
}
}
#endif
/// Get example path
static inline std::string getExamplePath()
{
std::string filePath(__FILE__);
return filePath.substr( 0, filePath.length() - std::string("main.cpp").length());
}
/// Example Database
static const std::string filename_example_db3 = getExamplePath() + "/example.db3";
/// Image
static const std::string filename_logo_png = getExamplePath() + "/logo.png";
/// Object Oriented Basic example
class Example
{
public:
// Constructor
Example() :
mDb(filename_example_db3), // Open a database file in readonly mode
mQuery(mDb, "SELECT * FROM test WHERE weight > :min_weight")// Compile a SQL query, containing one parameter (index 1)
{
}
virtual ~Example()
{
}
/// List the rows where the "weight" column is greater than the provided aParamValue
void ListGreaterThan (const int aParamValue)
{
std::cout << "ListGreaterThan (" << aParamValue << ")\n";
// Bind the integer value provided to the first parameter of the SQL query
mQuery.bind(":min_weight", aParamValue); // same as mQuery.bind(1, aParamValue);
// Loop to execute the query step by step, to get one a row of results at a time
while (mQuery.executeStep())
{
std::cout << "row (" << mQuery.getColumn(0) << ", \"" << mQuery.getColumn(1) << "\", " << mQuery.getColumn(2) << ")\n";
}
// Reset the query to be able to use it again later
mQuery.reset();
}
private:
SQLite::Database mDb; ///< Database connection
SQLite::Statement mQuery; ///< Database prepared SQL query
};
int main ()
{
// Using SQLITE_VERSION would require #include <sqlite3.h> which we want to avoid: use SQLite::VERSION if possible.
// std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl;
std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl;
std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl;
////////////////////////////////////////////////////////////////////////////
// Inspect a database via SQLite header information
try
{
const SQLite::Header header = SQLite::Database::getHeaderInfo(filename_example_db3);
// Print values for all header fields
// Official documentation for fields can be found here: https://www.sqlite.org/fileformat.html#the_database_header
std::cout << "Magic header string: " << header.headerStr << std::endl;
std::cout << "Page size bytes: " << header.pageSizeBytes << std::endl;
std::cout << "File format write version: " << (int)header.fileFormatWriteVersion << std::endl;
std::cout << "File format read version: " << (int)header.fileFormatReadVersion << std::endl;
std::cout << "Reserved space bytes: " << (int)header.reservedSpaceBytes << std::endl;
std::cout << "Max embedded payload fraction " << (int)header.maxEmbeddedPayloadFrac << std::endl;
std::cout << "Min embedded payload fraction: " << (int)header.minEmbeddedPayloadFrac << std::endl;
std::cout << "Leaf payload fraction: " << (int)header.leafPayloadFrac << std::endl;
std::cout << "File change counter: " << header.fileChangeCounter << std::endl;
std::cout << "Database size pages: " << header.databaseSizePages << std::endl;
std::cout << "First freelist trunk page: " << header.firstFreelistTrunkPage << std::endl;
std::cout << "Total freelist trunk pages: " << header.totalFreelistPages << std::endl;
std::cout << "Schema cookie: " << header.schemaCookie << std::endl;
std::cout << "Schema format number: " << header.schemaFormatNumber << std::endl;
std::cout << "Default page cache size bytes: " << header.defaultPageCacheSizeBytes << std::endl;
std::cout << "Largest B tree page number: " << header.largestBTreePageNumber << std::endl;
std::cout << "Database text encoding: " << header.databaseTextEncoding << std::endl;
std::cout << "User version: " << header.userVersion << std::endl;
std::cout << "Incremental vaccum mode: " << header.incrementalVaccumMode << std::endl;
std::cout << "Application ID: " << header.applicationId << std::endl;
std::cout << "Version valid for: " << header.versionValidFor << std::endl;
std::cout << "SQLite version: " << header.sqliteVersion << std::endl;
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
////////////////////////////////////////////////////////////////////////////
// Very basic first example (1/7) :
try
{
// Open a database file in readonly mode
SQLite::Database db(filename_example_db3); // SQLite::OPEN_READONLY
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
// Test if the 'test' table exists
const bool bExists = db.tableExists("test");
std::cout << "SQLite table 'test' exists=" << bExists << "\n";
// Get a single value result with an easy to use shortcut
const std::string value = db.execAndGet("SELECT value FROM test WHERE id=2");
std::cout << "execAndGet=" << value.c_str() << std::endl;
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
////////////////////////////////////////////////////////////////////////////
// Simple select query - few variations (2/7) :
try
{
// Open a database file in readonly mode
SQLite::Database db(filename_example_db3); // SQLite::OPEN_READONLY
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
///// a) Loop to get values of column by index, using auto cast to variable type
// Compile a SQL query, containing one parameter (index 1)
SQLite::Statement query(db, "SELECT id as test_id, value as test_val, weight as test_weight FROM test WHERE weight > ?");
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' compiled (" << query.getColumnCount () << " columns in the result)\n";
// Bind the integer value 2 to the first parameter of the SQL query
query.bind(1, 2);
std::cout << "binded with integer value '2' :\n";
// Loop to execute the query step by step, to get one a row of results at a time
while (query.executeStep())
{
// Demonstrates how to get some typed column value (and the equivalent explicit call)
const int id = query.getColumn(0); // = query.getColumn(0).getInt();
//const char* pvalue = query.getColumn(1); // = query.getColumn(1).getText();
const std::string value = query.getColumn(1); // = query.getColumn(1).getText();
const int bytes = query.getColumn(1).size(); // .getColumn(1).getBytes();
const double weight = query.getColumn(2); // = query.getColumn(2).getInt();
std::cout << "row (" << id << ", \"" << value.c_str() << "\"(" << bytes << ") " << weight << ")\n";
}
///// b) Get aliased column names (and original column names if possible)
// Reset the query to use it again
query.reset();
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' reseted (" << query.getColumnCount() << " columns in the result)\n";
// Show how to get the aliased names of the result columns.
const std::string name0 = query.getColumnName(0);
const std::string name1 = query.getColumnName(1);
const std::string name2 = query.getColumnName(2);
std::cout << "aliased result [\"" << name0.c_str() << "\", \"" << name1.c_str() << "\", \"" << name2.c_str() << "\"]\n";
#ifdef SQLITE_ENABLE_COLUMN_METADATA
// Show how to get origin names of the table columns from which theses result columns come from.
// Requires the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro to be
// also defined at compile times of the SQLite library itself.
const std::string oname0 = query.getColumnOriginName(0);
const std::string oname1 = query.getColumnOriginName(1);
const std::string oname2 = query.getColumnOriginName(2);
std::cout << "origin table 'test' [\"" << oname0.c_str() << "\", \"" << oname1.c_str() << "\", \"" << oname2.c_str() << "\"]\n";
#endif
// Loop to execute the query step by step, to get one a row of results at a time
while (query.executeStep())
{
// Demonstrates that inserting column value in a std:ostream is natural
std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\", " << query.getColumn(2) << ")\n";
}
///// c) Get columns by name
// Reset the query to use it again
query.reset();
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' reseted (" << query.getColumnCount() << " columns in the result)\n";
// Loop to execute the query step by step, to get one a row of results at a time
while (query.executeStep())
{
// Demonstrates how to get column value by aliased name (not the original table names, see above)
const int id = query.getColumn("test_id");
const std::string value = query.getColumn("test_val");
const double weight = query.getColumn("test_weight");
std::cout << "row (" << id << ", \"" << value.c_str() << "\" " << weight << ")\n";
}
///// d) Uses explicit typed getters instead of auto cast operators
// Reset the query to use it again
query.reset();
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' reseted (" << query.getColumnCount () << " columns in the result)\n";
// Bind the string value "6" to the first parameter of the SQL query
query.bind(1, "6");
std::cout << "binded with string value \"6\" :\n";
// Reuses variables: uses assignement operator in the loop instead of constructor with initialization
int id = 0;
std::string value;
double weight = 0.0;
while (query.executeStep())
{
id = query.getColumn(0).getInt();
value = query.getColumn(1).getText();
weight = query.getColumn(2).getInt();
std::cout << "row (" << id << ", \"" << value << "\", " << weight << ")\n";
}
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
////////////////////////////////////////////////////////////////////////////
// Object Oriented Basic example (3/7) :
try
{
// Open the database and compile the query
Example example;
// Demonstrates the way to use the same query with different parameter values
example.ListGreaterThan(8);
example.ListGreaterThan(6);
example.ListGreaterThan(2);
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
// The execAndGet wrapper example (4/7) :
try
{
// Open a database file in readonly mode
SQLite::Database db(filename_example_db3); // SQLite::OPEN_READONLY
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
// 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)
std::string value = db.execAndGet("SELECT value FROM test WHERE id=2");
std::cout << "execAndGet=" << value.c_str() << std::endl;
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
////////////////////////////////////////////////////////////////////////////
// Simple batch queries example (5/7) :
try
{
// Open a database file in create/write mode
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
// Create a new table with an explicit "id" column aliasing the underlying rowid
db.exec("DROP TABLE IF EXISTS test");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
// first row
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// second row
nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")");
std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl;
// update the second row
nb = db.exec("UPDATE test SET value=\"second-updated\" WHERE id='2'");
std::cout << "UPDATE test SET value=\"second-updated\" WHERE id='2', returned " << nb << std::endl;
// Check the results : expect two row of result
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
while (query.executeStep())
{
std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\")\n";
}
db.exec("DROP TABLE test");
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
remove("test.db3");
////////////////////////////////////////////////////////////////////////////
// RAII transaction example (6/7) :
try
{
// Open a database file in create/write mode
SQLite::Database db("transaction.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
db.exec("DROP TABLE IF EXISTS test");
// Exemple of a successful transaction :
try
{
// Begin transaction
SQLite::Transaction transaction(db);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// Commit transaction
transaction.commit();
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
// Exemple of a rollbacked transaction :
try
{
// Begin transaction
SQLite::Transaction transaction(db);
int nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")");
std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl;
nb = db.exec("INSERT INTO test ObviousError");
std::cout << "INSERT INTO test \"error\", returned " << nb << std::endl;
return EXIT_FAILURE; // we should never get there : exit the example program
// Commit transaction
transaction.commit();
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
// expected error, see above
}
// Check the results (expect only one row of result, as the second one has been rollbacked by the error)
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
while (query.executeStep())
{
std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\")\n";
}
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
remove("transaction.db3");
////////////////////////////////////////////////////////////////////////////
// Binary blob and in-memory database example (7/7) :
try
{
// Open a database file in create/write mode
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
db.exec("DROP TABLE IF EXISTS test");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value BLOB)");
FILE* fp = fopen(filename_logo_png.c_str(), "rb");
if (NULL != fp)
{
char buffer[16*1024];
void* blob = &buffer;
int size = static_cast<int>(fread(blob, 1, 16*1024, fp));
buffer[size] = '\0';
fclose (fp);
std::cout << "blob size=" << size << " :\n";
// Insert query
SQLite::Statement query(db, "INSERT INTO test VALUES (NULL, ?)");
// Bind the blob value to the first parameter of the SQL query
query.bind(1, blob, size);
std::cout << "blob binded successfully\n";
// Execute the one-step query to insert the blob
int nb = query.exec ();
std::cout << "INSERT INTO test VALUES (NULL, ?)\", returned " << nb << std::endl;
}
else
{
std::cout << "file " << filename_logo_png << " not found !\n";
return EXIT_FAILURE; // unexpected error : exit the example program
}
fp = fopen("out.png", "wb");
if (NULL != fp)
{
const void* blob = NULL;
size_t size;
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
if (query.executeStep())
{
SQLite::Column colBlob = query.getColumn(1);
blob = colBlob.getBlob ();
size = colBlob.getBytes ();
std::cout << "row (" << query.getColumn(0) << ", size=" << size << ")\n";
size_t sizew = fwrite(blob, 1, size, fp);
SQLITECPP_ASSERT(sizew == size, "fwrite failed"); // See SQLITECPP_ENABLE_ASSERT_HANDLER
fclose (fp);
}
}
else
{
std::cout << "file out.png not created !\n";
return EXIT_FAILURE; // unexpected error : exit the example program
}
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
remove("out.png");
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
// example with C++14 variadic bind
try
{
// Open a database file in create/write mode
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
db.exec("DROP TABLE IF EXISTS test");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
{
SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)");
SQLite::bind(query, 42, "fortytwo");
// Execute the one-step query to insert the blob
int nb = query.exec();
std::cout << "INSERT INTO test VALUES (NULL, ?)\", returned " << nb << std::endl;
}
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
if (query.executeStep())
{
std::cout << query.getColumn(0).getInt() << "\t\"" << query.getColumn(1).getText() << "\"\n";
}
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
#endif
std::cout << "everything ok, quitting\n";
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,25 @@
# Example CMake file for compiling & linking a project with the the SQLiteCpp wrapper
#
# Copyright (c) 2012-2020 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)
cmake_minimum_required(VERSION 3.1) # for "CMAKE_CXX_STANDARD" version
project(SQLiteCpp_Example VERSION 2.0)
# SQLiteC++ 3.x now requires C++11 compiler
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Add SQLite3 C++ wrapper around sqlite3 library (and sqlite3 itself provided for ease of use)
# Here you can set CMake variables to avoid building Example, as well as cpplint, cppcheck...
# or set them in the cmake command line (see for instance provided build.bat/build.sh scripts)
set(SQLITECPP_RUN_CPPCHECK OFF CACHE BOOL "" FORCE)
set(SQLITECPP_RUN_CPPLINT OFF CACHE BOOL "" FORCE)
add_subdirectory(../.. SQLiteCpp) # out-of-source build requires explicit subdir name for compilation artifacts
# Add main.cpp example source code to the executable
add_executable(SQLiteCpp_Example src/main.cpp)
# Link SQLiteCpp_example1 with SQLiteCpp
target_link_libraries(SQLiteCpp_Example SQLiteCpp)

View file

@ -0,0 +1,3 @@
SQLiteCpp_Example demonstrates how to use SQLiteCpp as a subdirectory of a CMake project.
See https://github.com/SRombauts/SQLiteCpp_Example

View file

@ -0,0 +1,21 @@
@REM Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
@REM
@REM Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
@REM or copy at http://opensource.org/licenses/MIT)
mkdir build
cd build
@REM Generate a Visual Studio solution for latest version found
cmake ..
@if ERRORLEVEL 1 goto onError
@REM Build default configuration (ie 'Debug')
cmake --build .
@if ERRORLEVEL 1 goto onError
goto onSuccess
:onError
@echo An error occured!
:onSuccess
cd ..

View file

@ -0,0 +1,18 @@
#!/bin/sh
# Copyright (c) 2012-2020 Sébastien Rombauts (sebastien.rombauts@gmail.com)
#
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
# or copy at http://opensource.org/licenses/MIT)
# exit on first error
set -e
mkdir -p build
cd build
# Generate a Makefile for GCC (or Clang, depanding on CC/CXX envvar)
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Build (ie 'make')
cmake --build .

View file

@ -0,0 +1,84 @@
/**
* @file main.cpp
* @brief A few short examples in a row.
*
* Demonstrates how-to use the SQLite++ wrapper
*
* Copyright (c) 2012-2020 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)
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <SQLiteCpp/SQLiteCpp.h>
#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
{
/// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
{
// Print a message to the standard error output stream, and abort the program.
std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n";
std::abort();
}
}
#endif
int main ()
{
// Using SQLITE_VERSION would require #include <sqlite3.h> which we want to avoid: use SQLite::VERSION if possible.
// std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl;
std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl;
std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl;
////////////////////////////////////////////////////////////////////////////
// Simple batch queries example :
try
{
// Open a database file in create/write mode
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
// Create a new table with an explicit "id" column aliasing the underlying rowid
db.exec("DROP TABLE IF EXISTS test");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
// first row
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// second row
nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")");
std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl;
// update the second row
nb = db.exec("UPDATE test SET value=\"second-updated\" WHERE id='2'");
std::cout << "UPDATE test SET value=\"second-updated\" WHERE id='2', returned " << nb << std::endl;
// Check the results : expect two row of result
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
while (query.executeStep())
{
std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\")\n";
}
db.exec("DROP TABLE test");
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
remove("test.db3");
std::cout << "everything ok, quitting\n";
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,46 @@
/**
* @file Assertion.h
* @ingroup SQLiteCpp
* @brief Definition of the SQLITECPP_ASSERT() macro.
*
* Copyright (c) 2012-2020 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 <cassert>
/**
* 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 long 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

View file

@ -0,0 +1,127 @@
/**
* @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-2020 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/Database.h>
#include <string>
// Forward declaration to avoid inclusion of <sqlite3.h> 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 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;
/// Release the SQLite Backup resource.
~Backup();
/**
* @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();
/// Return the total number of pages in the source database as of the most recent call to executeStep().
int getTotalPageCount();
private:
// TODO: use std::unique_ptr with a custom deleter to call sqlite3_backup_finish()
sqlite3_backup* mpSQLiteBackup = nullptr; ///< Pointer to SQLite Database Backup Handle
};
} // namespace SQLite

View file

@ -0,0 +1,289 @@
/**
* @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-2020 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/Statement.h>
#include <SQLiteCpp/Exception.h>
#include <string>
#include <climits> // For INT_MAX
namespace SQLite
{
extern const int INTEGER; ///< SQLITE_INTEGER
extern const int FLOAT; ///< SQLITE_FLOAT
extern const int TEXT; ///< SQLITE_TEXT
extern const int BLOB; ///< SQLITE_BLOB
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 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
*/
Column(Statement::Ptr& aStmtPtr, int aIndex) noexcept;
// default destructor: the finalization will be done by the destructor of the last shared pointer
// default copy constructor and assignment operator are perfectly suited :
// they copy the Statement::Ptr which in turn increments the reference counter.
/**
* @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.
int getInt() const noexcept;
/// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits).
unsigned getUInt() const noexcept;
/// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits).
long long 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
*
* Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null.
*
* @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 operator to char
operator char() const
{
return static_cast<char>(getInt());
}
/// Inline cast operator to unsigned char
operator unsigned char() const
{
return static_cast<unsigned char>(getInt());
}
/// Inline cast operator to short
operator short() const
{
return static_cast<short>(getInt());
}
/// Inline cast operator to unsigned short
operator unsigned short() const
{
return static_cast<unsigned short>(getInt());
}
/// Inline cast operator to int
operator int() const
{
return getInt();
}
/// Inline cast operator to 32bits unsigned integer
operator unsigned int() const
{
return getUInt();
}
#if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW)
/// Inline cast operator to 32bits long
operator long() const
{
return getInt();
}
/// Inline cast operator to 32bits unsigned long
operator unsigned long() const
{
return getUInt();
}
#else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS)
/// Inline cast operator to 64bits long when the data model of the system is LP64 (Linux 64 bits...)
operator long() const
{
return getInt64();
}
#endif
/// Inline cast operator to 64bits integer
operator long long() const
{
return getInt64();
}
/// Inline cast operator to double
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::Ptr 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
*/
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<typename T, int N>
T Statement::getColumns()
{
checkRow();
checkIndex(N - 1);
return getColumns<T>(std::make_integer_sequence<int, N>{});
}
// Helper function called by getColums<typename T, int N>
template<typename T, const int... Is>
T Statement::getColumns(const std::integer_sequence<int, Is...>)
{
return T{Column(mStmtPtr, Is)...};
}
#endif
} // namespace SQLite

View file

@ -0,0 +1,488 @@
/**
* @file Database.h
* @ingroup SQLiteCpp
* @brief Management of a SQLite Database Connection.
*
* Copyright (c) 2012-2020 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/Column.h>
#include <memory>
#include <string.h>
// Forward declarations to avoid inclusion of <sqlite3.h> 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 <sqlite3.h> in the client application.
/// The database is opened in read-only mode. If the database does not already exist, an error is returned.
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.
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.
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")
extern const int OPEN_URI; // SQLITE_OPEN_URI
extern const int OK; ///< SQLITE_OK (used by check() bellow)
extern const char* VERSION; ///< SQLITE_VERSION string from the sqlite3.h used at compile time
extern const int VERSION_NUMBER; ///< SQLITE_VERSION_NUMBER from the sqlite3.h used at compile time
/// Return SQLite version string using runtime call to the compiled library
const char* getLibVersion() noexcept;
/// Return SQLite version number using runtime call to the compiled library
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 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())
{
}
// 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
{
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.
*
* 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 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 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 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);
/**
* @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)
{
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.
*/
long long getLastInsertRowid() 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()
{
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<sqlite3, Deleter> mSQLitePtr; ///< Pointer to SQLite Database Connection Handle
std::string mFilename; ///< UTF-8 filename used to open the database
};
} // namespace SQLite

View file

@ -0,0 +1,92 @@
/**
* @file Exception.h
* @ingroup SQLiteCpp
* @brief Encapsulation of the error message from SQLite3 on a std::runtime_error.
*
* Copyright (c) 2012-2020 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 <stdexcept>
#include <string>
// Forward declaration to avoid inclusion of <sqlite3.h> in a header
struct sqlite3;
namespace SQLite
{
/**
* @brief Encapsulation of the error message from SQLite3, based on std::runtime_error.
*/
class 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

View file

@ -0,0 +1,90 @@
/**
* @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-2020 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 <SQLiteCpp/Statement.h>
#include <SQLiteCpp/VariadicBind.h>
/// @cond
#include <tuple>
#include <utility>
#include <initializer_list>
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 <typename Arg, typename... Types>
void execute_many(Database& aDatabase, const char* apQuery, Arg&& aArg, Types&&... aParams)
{
SQLite::Statement query(aDatabase, apQuery);
bind_exec(query, std::forward<Arg>(aArg));
(void)std::initializer_list<int>
{
((void)reset_bind_exec(query, std::forward<Types>(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 <typename TupleT>
void reset_bind_exec(Statement& apQuery, TupleT&& aTuple)
{
apQuery.reset();
bind_exec(apQuery, std::forward<TupleT>(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 <typename TupleT>
void bind_exec(Statement& apQuery, TupleT&& aTuple)
{
SQLite::bind(apQuery, std::forward<TupleT>(aTuple));
while (apQuery.executeStep()) {}
}
} // namespace SQLite
#endif // c++14

View file

@ -0,0 +1,44 @@
/**
* @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-2020 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 <SQLiteCpp/Assertion.h>
#include <SQLiteCpp/Exception.h>
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/Column.h>
#include <SQLiteCpp/Transaction.h>
/**
* @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.00.00" // 3.0.0
#define SQLITECPP_VERSION_NUMBER 3000000 // 3.0.0

View file

@ -0,0 +1,780 @@
/**
* @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-2020 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/Exception.h>
#include <SQLiteCpp/Utils.h> // SQLITECPP_PURE_FUNC
#include <string>
#include <map>
#include <climits> // For INT_MAX
// Forward declarations to avoid inclusion of <sqlite3.h> in a header
struct sqlite3;
struct sqlite3_stmt;
namespace SQLite
{
// Forward declaration
class Database;
class Column;
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 Statement
{
friend class Column; // For access to Statement::Ptr inner class
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(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(Database &aDatabase, const std::string& aQuery) :
Statement(aDatabase, aQuery.c_str())
{}
/**
* @brief Move an SQLite statement.
*
* @param[in] aStatement Statement to move
*/
Statement(Statement&& aStatement) noexcept;
// Statement is non-copyable
Statement(const Statement&) = delete;
Statement& operator=(const Statement&) = delete;
/// 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. Throws an exception on error.
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);
/**
* @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 int 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 unsigned aValue);
#if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW)
/**
* @brief Bind a 32bits long value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const int aIndex, const long aValue)
{
bind(aIndex, static_cast<int>(aValue));
}
#else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS)
/**
* @brief Bind a 64bits long value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const int aIndex, const long aValue)
{
bind(aIndex, static_cast<long long>(aValue));
}
#endif
/**
* @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 long long 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 int 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 unsigned aValue)
{
bind(getIndex(apName), aValue);
}
#if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW)
/**
* @brief Bind a 32bits long value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const char* apName, const long aValue)
{
bind(apName, static_cast<int>(aValue));
}
#else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS)
/**
* @brief Bind a 64bits long value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const char* apName, const long aValue)
{
bind(apName, static_cast<long long>(aValue));
}
#endif
/**
* @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 long long 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 int 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 unsigned aValue)
{
bind(aName.c_str(), aValue);
}
#if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW)
/**
* @brief Bind a 32bits long value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const std::string& aName, const long aValue)
{
bind(aName.c_str(), static_cast<int>(aValue));
}
#else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS)
/**
* @brief Bind a 64bits long value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const std::string& aName, const long aValue)
{
bind(aName.c_str(), static_cast<long long>(aValue));
}
#endif
/**
* @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 long long 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.
*
* 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 !
*/
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);
/**
* @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);
#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<typename T, int N>
T getColumns();
private:
/**
* @brief Helper function used by getColumns<typename T, int N> to expand an integer_sequence used to generate
* the required Column objects
*/
template<typename T, const int... Is>
T getColumns(const std::integer_sequence<int, Is...>);
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;
////////////////////////////////////////////////////////////////////////////
/// 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();
/// 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;
private:
/**
* @brief Shared pointer to the sqlite3_stmt SQLite Statement Object.
*
* Manage the finalization of the sqlite3_stmt with a reference counter.
*
* This is a internal class, not part of the API (hence full documentation is in the cpp).
*/
// TODO Convert this whole custom pointer to a C++11 std::shared_ptr with a custom deleter
class Ptr
{
public:
// Prepare the statement and initialize its reference counter
Ptr(sqlite3* apSQLite, std::string& aQuery);
// Copy constructor increments the ref counter
Ptr(const Ptr& aPtr);
// Move constructor
Ptr(Ptr&& aPtr);
// Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0
~Ptr();
/// Inline cast operator returning the pointer to SQLite Database Connection Handle
operator sqlite3*() const
{
return mpSQLite;
}
/// Inline cast operator returning the pointer to SQLite Statement Object
operator sqlite3_stmt*() const
{
return mpStmt;
}
private:
/// @{ Unused/forbidden copy/assignment operator
Ptr& operator=(const Ptr& aPtr);
/// @}
private:
sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle
sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object
unsigned int* mpRefCount; //!< Pointer to the heap allocated reference counter of the sqlite3_stmt
//!< (to share it with Column objects)
};
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(mStmtPtr, 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.");
}
}
private:
/// Map of columns index by name (mutable so getColumnIndex can be const)
typedef std::map<std::string, int> TColumnNames;
private:
std::string mQuery; //!< UTF-8 SQL Query
Ptr mStmtPtr; //!< Shared Pointer to the prepared SQLite Statement Object
int mColumnCount; //!< Number of columns in the result of the prepared statement
mutable TColumnNames mColumnNames; //!< Map of columns index by name (mutable so getColumnIndex can be const)
bool mbHasRow; //!< true when a row has been fetched with executeStep()
bool mbDone; //!< true when the last executeStep() had no more row to fetch
};
} // namespace SQLite

View file

@ -0,0 +1,75 @@
/**
* @file Transaction.h
* @ingroup SQLiteCpp
* @brief A Transaction is way to group multiple SQL statements into an atomic secured operation.
*
* Copyright (c) 2012-2020 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/Exception.h>
namespace SQLite
{
// Forward declaration
class Database;
/**
* @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 rollbacked in the destructor, 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 Transaction
{
public:
/**
* @brief Begins the SQLite transaction
*
* @param[in] aDatabase the SQLite Database Connection
*
* Exception is thrown in case of error, then the Transaction is NOT initiated.
*/
explicit Transaction(Database& aDatabase);
// 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();
private:
Database& mDatabase; ///< Reference to the SQLite Database Connection
bool mbCommited; ///< True when commit has been called
};
} // namespace SQLite

View file

@ -0,0 +1,31 @@
/**
* @file Utils.h
* @ingroup SQLiteCpp
* @brief Definition of the SQLITECPP_PURE_FUNC macro.
*
* Copyright (c) 2012-2020 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

View file

@ -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-2020 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 <SQLiteCpp/Statement.h>
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
#include <tuple>
#endif // c++14
/// @cond
#include <utility>
#include <initializer_list>
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<?");
* SQLite::bind(stm,a,b,c);
* //...is equivalent to
* stm.bind(1,a);
* stm.bind(2,b);
* stm.bind(3,c);
* \endcode
* @param query statement
* @param args zero or more args to bind.
*/
template<class ...Args>
void bind(SQLite::Statement& query, const Args& ... args)
{
int pos = 0;
(void)std::initializer_list<int>{
((void)query.bind(++pos, std::forward<decltype(args)>(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<?");
* SQLite::bind(stm, std::make_tuple(a, b, c));
* //...is equivalent to
* stm.bind(1,a);
* stm.bind(2,b);
* stm.bind(3,c);
* \endcode
* @param query statement
* @param tuple tuple with values to bind
*/
template <typename ... Types>
void bind(SQLite::Statement& query, const std::tuple<Types...> &tuple)
{
bind(query, tuple, std::index_sequence_for<Types...>());
}
/**
* \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 <typename ... Types, std::size_t ... Indices>
void bind(SQLite::Statement& query, const std::tuple<Types...> &tuple, std::index_sequence<Indices...>)
{
bind(query, std::get<Indices>(tuple)...);
}
#endif // c++14
} // namespace SQLite

View file

@ -0,0 +1,45 @@
# CMake file for compiling the sqlite3 static library under Windows (for ease of use)
#
# Copyright (c) 2012-2020 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)
# add sources of the "sqlite3" static library
add_library(sqlite3
sqlite3.c
sqlite3.h
)
target_include_directories(sqlite3
PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PUBLIC $<INSTALL_INTERFACE:include/>)
if (SQLITE_ENABLE_COLUMN_METADATA)
# Enable the use of SQLite column metadata method
# Require that the sqlite3 library is also compiled with this flag:
target_compile_definitions(sqlite3 PUBLIC SQLITE_ENABLE_COLUMN_METADATA)
endif (SQLITE_ENABLE_COLUMN_METADATA)
if (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
set_target_properties(sqlite3 PROPERTIES COMPILE_FLAGS "-fPIC")
endif (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
if (UNIX AND CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)
target_compile_options(sqlite3 PRIVATE "-Wimplicit-fallthrough=0")
endif()
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)
target_compile_options(sqlite3 PRIVATE "-Wno-cast-function-type")
endif()
endif()
# Allow the library to be installed via "make install" and found with "find_package"
include(GNUInstallDirs)
install(TARGETS sqlite3
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT libraries)
install(FILES sqlite3.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT headers)

14
third-party/SQLiteCpp/sqlite3/README.md vendored Normal file
View file

@ -0,0 +1,14 @@
sqlite3
-------
"sqlite3.c" and "sqlite3.h" files from sqlite-amalgamation-3300100.zip (SQLite 3.30.1 2019-10-10)
Those files are provided for easy setup and compatibility under Windows/Linux/MacOS.
They are used by default by the CMake build.
Use -DSQLITECPP_INTERNAL_SQLITE=OFF to link against the Linux "libsqlite3-dev" package instead.
### License:
All of the code and documentation in SQLite has been dedicated to the public domain by the authors.

225062
third-party/SQLiteCpp/sqlite3/sqlite3.c vendored Normal file

File diff suppressed because it is too large Load diff

11857
third-party/SQLiteCpp/sqlite3/sqlite3.h vendored Normal file

File diff suppressed because it is too large Load diff

83
third-party/SQLiteCpp/src/Backup.cpp vendored Normal file
View file

@ -0,0 +1,83 @@
/**
* @file Backup.cpp
* @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-2020 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)
*/
#include <SQLiteCpp/Backup.h>
#include <SQLiteCpp/Exception.h>
#include <sqlite3.h>
namespace SQLite
{
// Initialize resource for SQLite database backup
Backup::Backup(Database& aDestDatabase,
const char* apDestDatabaseName,
Database& aSrcDatabase,
const char* apSrcDatabaseName)
{
mpSQLiteBackup = sqlite3_backup_init(aDestDatabase.getHandle(),
apDestDatabaseName,
aSrcDatabase.getHandle(),
apSrcDatabaseName);
if (nullptr == mpSQLiteBackup)
{
// If an error occurs, the error code and message are attached to the destination database connection.
throw SQLite::Exception(aDestDatabase.getHandle());
}
}
Backup::Backup(Database& aDestDatabase,
const std::string& aDestDatabaseName,
Database& aSrcDatabase,
const std::string& aSrcDatabaseName) :
Backup(aDestDatabase, aDestDatabaseName.c_str(), aSrcDatabase, aSrcDatabaseName.c_str())
{
}
Backup::Backup(Database &aDestDatabase, Database &aSrcDatabase) :
Backup(aDestDatabase, "main", aSrcDatabase, "main")
{
}
// Release resource for SQLite database backup
Backup::~Backup()
{
if (mpSQLiteBackup)
{
sqlite3_backup_finish(mpSQLiteBackup);
}
}
// Execute backup step with a given number of source pages to be copied
int Backup::executeStep(const int aNumPage /* = -1 */)
{
const int res = sqlite3_backup_step(mpSQLiteBackup, aNumPage);
if (SQLITE_OK != res && SQLITE_DONE != res && SQLITE_BUSY != res && SQLITE_LOCKED != res)
{
throw SQLite::Exception(sqlite3_errstr(res), res);
}
return res;
}
// Get the number of remaining source pages to be copied in this backup process
int Backup::getRemainingPageCount()
{
return sqlite3_backup_remaining(mpSQLiteBackup);
}
// Get the number of total source pages to be copied in this backup process
int Backup::getTotalPageCount()
{
return sqlite3_backup_pagecount(mpSQLiteBackup);
}
} // namespace SQLite

118
third-party/SQLiteCpp/src/Column.cpp vendored Normal file
View file

@ -0,0 +1,118 @@
/**
* @file Column.cpp
* @ingroup SQLiteCpp
* @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Column.h>
#include <sqlite3.h>
#include <iostream>
namespace SQLite
{
const int INTEGER = SQLITE_INTEGER;
const int FLOAT = SQLITE_FLOAT;
const int TEXT = SQLITE_TEXT;
const int BLOB = SQLITE_BLOB;
const int Null = SQLITE_NULL;
// Encapsulation of a Column in a row of the result pointed by the prepared Statement.
Column::Column(Statement::Ptr& aStmtPtr, int aIndex) noexcept :
mStmtPtr(aStmtPtr),
mIndex(aIndex)
{
}
// Return the named assigned to this result column (potentially aliased)
const char* Column::getName() const noexcept
{
return sqlite3_column_name(mStmtPtr, mIndex);
}
#ifdef SQLITE_ENABLE_COLUMN_METADATA
// Return the name of the table column that is the origin of this result column
const char* Column::getOriginName() const noexcept
{
return sqlite3_column_origin_name(mStmtPtr, mIndex);
}
#endif
// Return the integer value of the column specified by its index starting at 0
int Column::getInt() const noexcept
{
return sqlite3_column_int(mStmtPtr, mIndex);
}
// Return the unsigned integer value of the column specified by its index starting at 0
unsigned Column::getUInt() const noexcept
{
return static_cast<unsigned>(getInt64());
}
// Return the 64bits integer value of the column specified by its index starting at 0
long long Column::getInt64() const noexcept
{
return sqlite3_column_int64(mStmtPtr, mIndex);
}
// Return the double value of the column specified by its index starting at 0
double Column::getDouble() const noexcept
{
return sqlite3_column_double(mStmtPtr, mIndex);
}
// Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0
const char* Column::getText(const char* apDefaultValue /* = "" */) const noexcept
{
const char* pText = reinterpret_cast<const char*>(sqlite3_column_text(mStmtPtr, mIndex));
return (pText?pText:apDefaultValue);
}
// Return a pointer to the blob value (*not* NULL terminated) of the column specified by its index starting at 0
const void* Column::getBlob() const noexcept
{
return sqlite3_column_blob(mStmtPtr, mIndex);
}
// Return a std::string to a TEXT or BLOB column
std::string Column::getString() const
{
// Note: using sqlite3_column_blob and not sqlite3_column_text
// - no need for sqlite3_column_text to add a \0 on the end, as we're getting the bytes length directly
const char *data = static_cast<const char *>(sqlite3_column_blob(mStmtPtr, mIndex));
// SQLite docs: "The safest policy is to invoke… sqlite3_column_blob() followed by sqlite3_column_bytes()"
// Note: std::string is ok to pass nullptr as first arg, if length is 0
return std::string(data, sqlite3_column_bytes(mStmtPtr, mIndex));
}
// Return the type of the value of the column
int Column::getType() const noexcept
{
return sqlite3_column_type(mStmtPtr, mIndex);
}
// Return the number of bytes used by the text value of the column
int Column::getBytes() const noexcept
{
return sqlite3_column_bytes(mStmtPtr, mIndex);
}
// Standard std::ostream inserter
std::ostream& operator<<(std::ostream& aStream, const Column& aColumn)
{
aStream.write(aColumn.getText(), aColumn.getBytes());
return aStream;
}
} // namespace SQLite

429
third-party/SQLiteCpp/src/Database.cpp vendored Normal file
View file

@ -0,0 +1,429 @@
/**
* @file Database.cpp
* @ingroup SQLiteCpp
* @brief Management of a SQLite Database Connection.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Assertion.h>
#include <SQLiteCpp/Backup.h>
#include <SQLiteCpp/Exception.h>
#include <SQLiteCpp/Statement.h>
#include <sqlite3.h>
#include <fstream>
#include <string.h>
#ifndef SQLITE_DETERMINISTIC
#define SQLITE_DETERMINISTIC 0x800
#endif // SQLITE_DETERMINISTIC
namespace SQLite
{
const int OPEN_READONLY = SQLITE_OPEN_READONLY;
const int OPEN_READWRITE = SQLITE_OPEN_READWRITE;
const int OPEN_CREATE = SQLITE_OPEN_CREATE;
const int OPEN_URI = SQLITE_OPEN_URI;
const int OK = SQLITE_OK;
const char* VERSION = SQLITE_VERSION;
const int VERSION_NUMBER = SQLITE_VERSION_NUMBER;
// Return SQLite version string using runtime call to the compiled library
const char* getLibVersion() noexcept
{
return sqlite3_libversion();
}
// Return SQLite version number using runtime call to the compiled library
int getLibVersionNumber() noexcept
{
return sqlite3_libversion_number();
}
// Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags.
Database::Database(const char* apFilename,
const int aFlags /* = SQLite::OPEN_READONLY*/,
const int aBusyTimeoutMs /* = 0 */,
const char* apVfs /* = nullptr*/) :
mFilename(apFilename)
{
sqlite3* handle;
const int ret = sqlite3_open_v2(apFilename, &handle, aFlags, apVfs);
mSQLitePtr.reset(handle);
if (SQLITE_OK != ret)
{
throw SQLite::Exception(handle, ret);
}
if (aBusyTimeoutMs > 0)
{
setBusyTimeout(aBusyTimeoutMs);
}
}
// Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion.
void Database::Deleter::operator()(sqlite3* apSQLite)
{
const int ret = sqlite3_close(apSQLite); // Calling sqlite3_close() with a nullptr argument is a harmless no-op.
// Avoid unreferenced variable warning when build in release mode
(void) ret;
// Only case of error is SQLITE_BUSY: "database is locked" (some statements are not finalized)
// Never throw an exception in a destructor :
SQLITECPP_ASSERT(SQLITE_OK == ret, "database is locked"); // See SQLITECPP_ENABLE_ASSERT_HANDLER
}
/**
* @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 writting 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 Database::setBusyTimeout(const int aBusyTimeoutMs)
{
const int ret = sqlite3_busy_timeout(getHandle(), aBusyTimeoutMs);
check(ret);
}
// Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...).
int Database::exec(const char* apQueries)
{
const int ret = sqlite3_exec(getHandle(), apQueries, nullptr, nullptr, nullptr);
check(ret);
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only)
return sqlite3_changes(getHandle());
}
// Shortcut to execute a one step query and fetch the first column of the result.
// 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)
// this is an issue only for pointer type result (ie. char* and blob)
// (use the Column copy-constructor)
Column Database::execAndGet(const char* apQuery)
{
Statement query(*this, apQuery);
(void)query.executeStep(); // Can return false if no result, which will throw next line in getColumn()
return query.getColumn(0);
}
// Shortcut to test if a table exists.
bool Database::tableExists(const char* apTableName)
{
Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?");
query.bind(1, apTableName);
(void)query.executeStep(); // Cannot return false, as the above query always return a result
return (1 == query.getColumn(0).getInt());
}
// Get the rowid of the most recent successful INSERT into the database from the current connection.
long long Database::getLastInsertRowid() const noexcept
{
return sqlite3_last_insert_rowid(getHandle());
}
// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection.
int Database::getTotalChanges() const noexcept
{
return sqlite3_total_changes(getHandle());
}
// Return the numeric result code for the most recent failed API call (if any).
int Database::getErrorCode() const noexcept
{
return sqlite3_errcode(getHandle());
}
// Return the extended numeric result code for the most recent failed API call (if any).
int Database::getExtendedErrorCode() const noexcept
{
return sqlite3_extended_errcode(getHandle());
}
// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
const char* Database::getErrorMsg() const noexcept
{
return sqlite3_errmsg(getHandle());
}
// Attach a custom function to your sqlite database. Assumes UTF8 text representation.
// Parameter details can be found here: http://www.sqlite.org/c3ref/create_function.html
void Database::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 */)
{
int textRep = SQLITE_UTF8;
// optimization if deterministic function (e.g. of nondeterministic function random())
if (abDeterministic)
{
textRep = textRep | SQLITE_DETERMINISTIC;
}
const int ret = sqlite3_create_function_v2(getHandle(), apFuncName, aNbArg, textRep,
apApp, apFunc, apStep, apFinal, apDestroy);
check(ret);
}
// Load an extension into the sqlite database. Only affects the current connection.
// Parameter details can be found here: http://www.sqlite.org/c3ref/load_extension.html
void Database::loadExtension(const char* apExtensionName, const char *apEntryPointName)
{
#ifdef SQLITE_OMIT_LOAD_EXTENSION
// Unused
(void)apExtensionName;
(void)apEntryPointName;
throw SQLite::Exception("sqlite extensions are disabled");
#else
#ifdef SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION // Since SQLite 3.13 (2016-05-18):
// Security warning:
// It is recommended that the SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION method be used to enable only this interface.
// The use of the sqlite3_enable_load_extension() interface should be avoided to keep the SQL load_extension()
// disabled and prevent SQL injections from giving attackers access to extension loading capabilities.
// (NOTE: not using nullptr: cannot pass object of non-POD type 'std::__1::nullptr_t' through variadic function)
int ret = sqlite3_db_config(getHandle(), SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); // NOTE: not using nullptr
#else
int ret = sqlite3_enable_load_extension(getHandle(), 1);
#endif
check(ret);
ret = sqlite3_load_extension(getHandle(), apExtensionName, apEntryPointName, 0);
check(ret);
#endif
}
// Set the key for the current sqlite database instance.
void Database::key(const std::string& aKey) const
{
int passLen = static_cast<int>(aKey.length());
#ifdef SQLITE_HAS_CODEC
if (passLen > 0)
{
const int ret = sqlite3_key(getHandle(), aKey.c_str(), passLen);
check(ret);
}
#else // SQLITE_HAS_CODEC
if (passLen > 0)
{
throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable.");
}
#endif // SQLITE_HAS_CODEC
}
// Reset the key for the current sqlite database instance.
void Database::rekey(const std::string& aNewKey) const
{
#ifdef SQLITE_HAS_CODEC
int passLen = aNewKey.length();
if (passLen > 0)
{
const int ret = sqlite3_rekey(getHandle(), aNewKey.c_str(), passLen);
check(ret);
}
else
{
const int ret = sqlite3_rekey(getHandle(), nullptr, 0);
check(ret);
}
#else // SQLITE_HAS_CODEC
static_cast<void>(aNewKey); // silence unused parameter warning
throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable.");
#endif // SQLITE_HAS_CODEC
}
// Test if a file contains an unencrypted database.
bool Database::isUnencrypted(const std::string& aFilename)
{
if (aFilename.empty())
{
throw SQLite::Exception("Could not open database, the aFilename parameter was empty.");
}
std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary);
char header[16];
if (fileBuffer.is_open())
{
fileBuffer.seekg(0, std::ios::beg);
fileBuffer.getline(header, 16);
fileBuffer.close();
}
else
{
throw SQLite::Exception("Error opening file: " + aFilename);
}
return strncmp(header, "SQLite format 3\000", 16) == 0;
}
// Parse header data from a database.
Header Database::getHeaderInfo(const std::string& aFilename)
{
Header h;
unsigned char buf[100];
char* pBuf = reinterpret_cast<char*>(&buf[0]);
char* pHeaderStr = reinterpret_cast<char*>(&h.headerStr[0]);
if (aFilename.empty())
{
throw SQLite::Exception("Filename parameter is empty");
}
{
std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary);
if (fileBuffer.is_open())
{
fileBuffer.seekg(0, std::ios::beg);
fileBuffer.read(pBuf, 100);
fileBuffer.close();
if (fileBuffer.gcount() < 100)
{
throw SQLite::Exception("File " + aFilename + " is too short");
}
}
else
{
throw SQLite::Exception("Error opening file " + aFilename);
}
}
// If the "magic string" can't be found then header is invalid, corrupt or unreadable
strncpy(pHeaderStr, pBuf, 16);
if (strncmp(pHeaderStr, "SQLite format 3", 15) != 0)
{
throw SQLite::Exception("Invalid or encrypted SQLite header in file " + aFilename);
}
h.pageSizeBytes = (buf[16] << 8) | buf[17];
h.fileFormatWriteVersion = buf[18];
h.fileFormatReadVersion = buf[19];
h.reservedSpaceBytes = buf[20];
h.maxEmbeddedPayloadFrac = buf[21];
h.minEmbeddedPayloadFrac = buf[22];
h.leafPayloadFrac = buf[23];
h.fileChangeCounter =
(buf[24] << 24) |
(buf[25] << 16) |
(buf[26] << 8) |
(buf[27] << 0);
h.databaseSizePages =
(buf[28] << 24) |
(buf[29] << 16) |
(buf[30] << 8) |
(buf[31] << 0);
h.firstFreelistTrunkPage =
(buf[32] << 24) |
(buf[33] << 16) |
(buf[34] << 8) |
(buf[35] << 0);
h.totalFreelistPages =
(buf[36] << 24) |
(buf[37] << 16) |
(buf[38] << 8) |
(buf[39] << 0);
h.schemaCookie =
(buf[40] << 24) |
(buf[41] << 16) |
(buf[42] << 8) |
(buf[43] << 0);
h.schemaFormatNumber =
(buf[44] << 24) |
(buf[45] << 16) |
(buf[46] << 8) |
(buf[47] << 0);
h.defaultPageCacheSizeBytes =
(buf[48] << 24) |
(buf[49] << 16) |
(buf[50] << 8) |
(buf[51] << 0);
h.largestBTreePageNumber =
(buf[52] << 24) |
(buf[53] << 16) |
(buf[54] << 8) |
(buf[55] << 0);
h.databaseTextEncoding =
(buf[56] << 24) |
(buf[57] << 16) |
(buf[58] << 8) |
(buf[59] << 0);
h.userVersion =
(buf[60] << 24) |
(buf[61] << 16) |
(buf[62] << 8) |
(buf[63] << 0);
h.incrementalVaccumMode =
(buf[64] << 24) |
(buf[65] << 16) |
(buf[66] << 8) |
(buf[67] << 0);
h.applicationId =
(buf[68] << 24) |
(buf[69] << 16) |
(buf[70] << 8) |
(buf[71] << 0);
h.versionValidFor =
(buf[92] << 24) |
(buf[93] << 16) |
(buf[94] << 8) |
(buf[95] << 0);
h.sqliteVersion =
(buf[96] << 24) |
(buf[97] << 16) |
(buf[98] << 8) |
(buf[99] << 0);
return h;
}
void Database::backup(const char* apFilename, BackupType aType)
{
// Open the database file identified by apFilename
Database otherDatabase(apFilename, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
// For a 'Save' operation, data is copied from the current Database to the other. A 'Load' is the reverse.
Database& src = (aType == Save ? *this : otherDatabase);
Database& dest = (aType == Save ? otherDatabase : *this);
// Set up the backup procedure to copy between the "main" databases of each connection
Backup bkp(dest, src);
bkp.executeStep(); // Execute all steps at once
// RAII Finish Backup an Close the other Database
}
} // namespace SQLite

47
third-party/SQLiteCpp/src/Exception.cpp vendored Normal file
View file

@ -0,0 +1,47 @@
/**
* @file Exception.cpp
* @ingroup SQLiteCpp
* @brief Encapsulation of the error message from SQLite3 on a std::runtime_error.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Exception.h>
#include <sqlite3.h>
namespace SQLite
{
Exception::Exception(const char* aErrorMessage, int ret) :
std::runtime_error(aErrorMessage),
mErrcode(ret),
mExtendedErrcode(-1)
{
}
Exception::Exception(sqlite3* apSQLite) :
std::runtime_error(sqlite3_errmsg(apSQLite)),
mErrcode(sqlite3_errcode(apSQLite)),
mExtendedErrcode(sqlite3_extended_errcode(apSQLite))
{
}
Exception::Exception(sqlite3* apSQLite, int ret) :
std::runtime_error(sqlite3_errmsg(apSQLite)),
mErrcode(ret),
mExtendedErrcode(sqlite3_extended_errcode(apSQLite))
{
}
// Return a string, solely based on the error code
const char* Exception::getErrorStr() const noexcept
{
return sqlite3_errstr(mErrcode);
}
} // namespace SQLite

411
third-party/SQLiteCpp/src/Statement.cpp vendored Normal file
View file

@ -0,0 +1,411 @@
/**
* @file Statement.cpp
* @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-2020 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)
*/
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Column.h>
#include <SQLiteCpp/Assertion.h>
#include <SQLiteCpp/Exception.h>
#include <sqlite3.h>
namespace SQLite
{
Statement::Statement(Database &aDatabase, const char* apQuery) :
mQuery(apQuery),
mStmtPtr(aDatabase.getHandle(), mQuery), // prepare the SQL query, and ref count (needs Database friendship)
mColumnCount(0),
mbHasRow(false),
mbDone(false)
{
mColumnCount = sqlite3_column_count(mStmtPtr);
}
Statement::Statement(Statement&& aStatement) noexcept :
mQuery(std::move(aStatement.mQuery)),
mStmtPtr(std::move(aStatement.mStmtPtr)),
mColumnCount(aStatement.mColumnCount),
mbHasRow(aStatement.mbHasRow),
mbDone(aStatement.mbDone)
{
aStatement.mColumnCount = 0;
aStatement.mbHasRow = false;
aStatement.mbDone = false;
}
// Reset the statement to make it ready for a new execution (see also #clearBindings() bellow)
void Statement::reset()
{
const int ret = tryReset();
check(ret);
}
int Statement::tryReset() noexcept
{
mbHasRow = false;
mbDone = false;
return sqlite3_reset(mStmtPtr);
}
// Clears away all the bindings of a prepared statement (can be associated with #reset() above).
void Statement::clearBindings()
{
const int ret = sqlite3_clear_bindings(mStmtPtr);
check(ret);
}
int Statement::getIndex(const char * const apName)
{
return sqlite3_bind_parameter_index(mStmtPtr, apName);
}
// Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const int aValue)
{
const int ret = sqlite3_bind_int(mStmtPtr, aIndex, aValue);
check(ret);
}
// Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const unsigned aValue)
{
const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue);
check(ret);
}
// Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const long long aValue)
{
const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue);
check(ret);
}
// Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const double aValue)
{
const int ret = sqlite3_bind_double(mStmtPtr, aIndex, aValue);
check(ret);
}
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const std::string& aValue)
{
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(),
static_cast<int>(aValue.size()), SQLITE_TRANSIENT);
check(ret);
}
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const char* apValue)
{
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT);
check(ret);
}
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const void* apValue, const int aSize)
{
const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT);
check(ret);
}
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bindNoCopy(const int aIndex, const std::string& aValue)
{
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(),
static_cast<int>(aValue.size()), SQLITE_STATIC);
check(ret);
}
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bindNoCopy(const int aIndex, const char* apValue)
{
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_STATIC);
check(ret);
}
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize)
{
const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_STATIC);
check(ret);
}
// Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex)
{
const int ret = sqlite3_bind_null(mStmtPtr, aIndex);
check(ret);
}
// Execute a step of the query to fetch one row of results
bool Statement::executeStep()
{
const int ret = tryExecuteStep();
if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem
{
if (ret == sqlite3_errcode(mStmtPtr))
{
throw SQLite::Exception(mStmtPtr, ret);
}
else
{
throw SQLite::Exception("Statement needs to be reseted", ret);
}
}
return mbHasRow; // true only if one row is accessible by getColumn(N)
}
// Execute a one-step query with no expected result
int Statement::exec()
{
const int ret = tryExecuteStep();
if (SQLITE_DONE != ret) // the statement has finished executing successfully
{
if (SQLITE_ROW == ret)
{
throw SQLite::Exception("exec() does not expect results. Use executeStep.");
}
else if (ret == sqlite3_errcode(mStmtPtr))
{
throw SQLite::Exception(mStmtPtr, ret);
}
else
{
throw SQLite::Exception("Statement needs to be reseted", ret);
}
}
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE)
return sqlite3_changes(mStmtPtr);
}
int Statement::tryExecuteStep() noexcept
{
if (false == mbDone)
{
const int ret = sqlite3_step(mStmtPtr);
if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it
{
mbHasRow = true;
}
else if (SQLITE_DONE == ret) // no (more) row ready : the query has finished executing
{
mbHasRow = false;
mbDone = true;
}
else
{
mbHasRow = false;
mbDone = false;
}
return ret;
}
else
{
// Statement needs to be reseted !
return SQLITE_MISUSE;
}
}
// Return a copy of the column data specified by its index starting at 0
// (use the Column copy-constructor)
Column Statement::getColumn(const int aIndex)
{
checkRow();
checkIndex(aIndex);
// Share the Statement Object handle with the new Column created
return Column(mStmtPtr, aIndex);
}
// Return a copy of the column data specified by its column name starting at 0
// (use the Column copy-constructor)
Column Statement::getColumn(const char* apName)
{
checkRow();
const int index = getColumnIndex(apName);
// Share the Statement Object handle with the new Column created
return Column(mStmtPtr, index);
}
// Test if the column is NULL
bool Statement::isColumnNull(const int aIndex) const
{
checkRow();
checkIndex(aIndex);
return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, aIndex));
}
bool Statement::isColumnNull(const char* apName) const
{
checkRow();
const int index = getColumnIndex(apName);
return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, index));
}
// Return the named assigned to the specified result column (potentially aliased)
const char* Statement::getColumnName(const int aIndex) const
{
checkIndex(aIndex);
return sqlite3_column_name(mStmtPtr, aIndex);
}
#ifdef SQLITE_ENABLE_COLUMN_METADATA
// Return the named assigned to the specified result column (potentially aliased)
const char* Statement::getColumnOriginName(const int aIndex) const
{
checkIndex(aIndex);
return sqlite3_column_origin_name(mStmtPtr, aIndex);
}
#endif
// Return the index of the specified (potentially aliased) column name
int Statement::getColumnIndex(const char* apName) const
{
// Build the map of column index by name on first call
if (mColumnNames.empty())
{
for (int i = 0; i < mColumnCount; ++i)
{
const char* pName = sqlite3_column_name(mStmtPtr, i);
mColumnNames[pName] = i;
}
}
const TColumnNames::const_iterator iIndex = mColumnNames.find(apName);
if (iIndex == mColumnNames.end())
{
throw SQLite::Exception("Unknown column name.");
}
return (*iIndex).second;
}
int Statement::getBindParameterCount() const noexcept
{
return sqlite3_bind_parameter_count(mStmtPtr);
}
// Return the numeric result code for the most recent failed API call (if any).
int Statement::getErrorCode() const noexcept
{
return sqlite3_errcode(mStmtPtr);
}
// Return the extended numeric result code for the most recent failed API call (if any).
int Statement::getExtendedErrorCode() const noexcept
{
return sqlite3_extended_errcode(mStmtPtr);
}
// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
const char* Statement::getErrorMsg() const noexcept
{
return sqlite3_errmsg(mStmtPtr);
}
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
std::string Statement::getExpandedSQL() {
char* expanded = sqlite3_expanded_sql(mStmtPtr);
std::string expandedString(expanded);
sqlite3_free(expanded);
return expandedString;
}
////////////////////////////////////////////////////////////////////////////////
// Internal class : shared pointer to the sqlite3_stmt SQLite Statement Object
////////////////////////////////////////////////////////////////////////////////
/**
* @brief Prepare the statement and initialize its reference counter
*
* @param[in] apSQLite The sqlite3 database connexion
* @param[in] aQuery The SQL query string to prepare
*/
Statement::Ptr::Ptr(sqlite3* apSQLite, std::string& aQuery) :
mpSQLite(apSQLite),
mpStmt(NULL),
mpRefCount(NULL)
{
const int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast<int>(aQuery.size()), &mpStmt, NULL);
if (SQLITE_OK != ret)
{
throw SQLite::Exception(apSQLite, ret);
}
// Initialize the reference counter of the sqlite3_stmt :
// used to share the mStmtPtr between Statement and Column objects;
// This is needed to enable Column objects to live longer than the Statement objet it refers to.
mpRefCount = new unsigned int(1); // NOLINT(readability/casting)
}
/**
* @brief Copy constructor increments the ref counter
*
* @param[in] aPtr Pointer to copy
*/
Statement::Ptr::Ptr(const Statement::Ptr& aPtr) :
mpSQLite(aPtr.mpSQLite),
mpStmt(aPtr.mpStmt),
mpRefCount(aPtr.mpRefCount)
{
assert(mpRefCount);
assert(0 != *mpRefCount);
// Increment the reference counter of the sqlite3_stmt,
// asking not to finalize the sqlite3_stmt during the lifetime of the new objet
++(*mpRefCount);
}
Statement::Ptr::Ptr(Ptr&& aPtr) :
mpSQLite(aPtr.mpSQLite),
mpStmt(aPtr.mpStmt),
mpRefCount(aPtr.mpRefCount)
{
aPtr.mpSQLite = nullptr;
aPtr.mpStmt = nullptr;
aPtr.mpRefCount = nullptr;
}
/**
* @brief Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0
*/
Statement::Ptr::~Ptr()
{
if (mpRefCount)
{
assert(0 != *mpRefCount);
// Decrement and check the reference counter of the sqlite3_stmt
--(*mpRefCount);
if (0 == *mpRefCount)
{
// If count reaches zero, finalize the sqlite3_stmt, as no Statement nor Column objet use it anymore.
// No need to check the return code, as it is the same as the last statement evaluation.
sqlite3_finalize(mpStmt);
// and delete the reference counter
delete mpRefCount;
mpRefCount = nullptr;
mpStmt = nullptr;
}
// else, the finalization will be done later, by the last object
}
}
} // namespace SQLite

View file

@ -0,0 +1,60 @@
/**
* @file Transaction.cpp
* @ingroup SQLiteCpp
* @brief A Transaction is way to group multiple SQL statements into an atomic secured operation.
*
* Copyright (c) 2012-2019 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)
*/
#include <SQLiteCpp/Transaction.h>
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Assertion.h>
namespace SQLite
{
// Begins the SQLite transaction
Transaction::Transaction(Database& aDatabase) :
mDatabase(aDatabase),
mbCommited(false)
{
mDatabase.exec("BEGIN");
}
// Safely rollback the transaction if it has not been committed.
Transaction::~Transaction()
{
if (false == mbCommited)
{
try
{
mDatabase.exec("ROLLBACK");
}
catch (SQLite::Exception&)
{
// Never throw an exception in a destructor: error if already rollbacked, but no harm is caused by this.
}
}
}
// Commit the transaction.
void Transaction::commit()
{
if (false == mbCommited)
{
mDatabase.exec("COMMIT");
mbCommited = true;
}
else
{
throw SQLite::Exception("Transaction already committed.");
}
}
} // namespace SQLite

View file

@ -0,0 +1,127 @@
/**
* @file Backup_test.cpp
* @ingroup tests
* @brief Test of a SQLite Backup.
*
* Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com)
* Copyright (c) 2015-2020 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)
*/
#include <SQLiteCpp/Backup.h>
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/Exception.h>
#include <sqlite3.h> // for SQLITE_ERROR, SQLITE_RANGE and SQLITE_DONE
#include <gtest/gtest.h>
#include <cstdio>
TEST(Backup, initException)
{
remove("backup_test.db3");
{
SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)");
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, \"first\")"));
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, \"second\")"));
EXPECT_THROW(SQLite::Backup backup(srcDB, srcDB), SQLite::Exception);
EXPECT_THROW(SQLite::Backup backup(srcDB, "main", srcDB, "main"), SQLite::Exception);
const std::string name("main");
EXPECT_THROW(SQLite::Backup backup(srcDB, name, srcDB, name), SQLite::Exception);
}
remove("backup_test.db3");
}
TEST(Backup, executeStepOne)
{
remove("backup_test.db3");
remove("backup_test.db3.backup");
{
SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)");
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, \"first\")"));
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, \"second\")"));
SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
SQLite::Backup backup(destDB, "main", srcDB, "main");
int res = backup.executeStep(1); // backup only one page at a time
ASSERT_EQ(SQLite::OK, res);
const int total = backup.getTotalPageCount();
ASSERT_EQ(2, total);
int remaining = backup.getRemainingPageCount();
ASSERT_EQ(1, remaining);
res = backup.executeStep(1); // backup the second and last page
ASSERT_EQ(SQLITE_DONE, res);
remaining = backup.getRemainingPageCount();
ASSERT_EQ(0, remaining);
SQLite::Statement query(destDB, "SELECT * FROM backup_test ORDER BY id ASC");
ASSERT_TRUE(query.executeStep());
EXPECT_EQ(1, query.getColumn(0).getInt());
EXPECT_STREQ("first", query.getColumn(1));
ASSERT_TRUE(query.executeStep());
EXPECT_EQ(2, query.getColumn(0).getInt());
EXPECT_STREQ("second", query.getColumn(1));
}
remove("backup_test.db3");
remove("backup_test.db3.backup");
}
TEST(Backup, executeStepAll)
{
remove("backup_test.db3");
remove("backup_test.db3.backup");
{
SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)");
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, \"first\")"));
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, \"second\")"));
SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
SQLite::Backup backup(destDB, srcDB);
const int res = backup.executeStep(); // uses default argument "-1" => execute all steps at once
ASSERT_EQ(res, SQLITE_DONE);
const int total = backup.getTotalPageCount();
ASSERT_EQ(2, total);
const int remaining = backup.getRemainingPageCount();
ASSERT_EQ(0, remaining);
SQLite::Statement query(destDB, "SELECT * FROM backup_test ORDER BY id ASC");
ASSERT_TRUE(query.executeStep());
EXPECT_EQ(1, query.getColumn(0).getInt());
EXPECT_STREQ("first", query.getColumn(1));
ASSERT_TRUE(query.executeStep());
EXPECT_EQ(2, query.getColumn(0).getInt());
EXPECT_STREQ("second", query.getColumn(1));
}
remove("backup_test.db3");
remove("backup_test.db3.backup");
}
TEST(Backup, executeStepException)
{
remove("backup_test.db3");
remove("backup_test.db3.backup");
{
SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)");
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, \"first\")"));
ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, \"second\")"));
{
SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
(void)destDB;
}
{
SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READONLY);
SQLite::Backup backup(destDB, srcDB);
EXPECT_THROW(backup.executeStep(), SQLite::Exception);
}
}
remove("backup_test.db3");
remove("backup_test.db3.backup");
}

View file

@ -0,0 +1,241 @@
/**
* @file Column_test.cpp
* @ingroup tests
* @brief Test of a SQLiteCpp Column.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/Column.h>
#include <sqlite3.h> // for sqlite3_int64
#include <gtest/gtest.h>
#include <cstdio>
#include <stdint.h>
TEST(Column, basis)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(SQLite::OK, db.getErrorCode());
EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode());
// Create a new table
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL, binary BLOB, empty TEXT)"));
EXPECT_TRUE(db.tableExists("test"));
EXPECT_TRUE(db.tableExists(std::string("test")));
EXPECT_EQ(0, db.getLastInsertRowid());
// Create a first row (autoid: 1) with all kind of data and a null value
SQLite::Statement insert(db, "INSERT INTO test VALUES (NULL, \"first\", -123, 0.123, ?, NULL)");
// Bind the blob value to the first parameter of the SQL query
const char buffer[] = {'b', 'l', '\0', 'b'}; // "bl\0b" : 4 char, with a null byte inside
const int size = sizeof(buffer); // size = 4
const void* blob = &buffer;
insert.bind(1, blob, size);
// Execute the one-step query to insert the row
EXPECT_EQ(1, insert.exec());
EXPECT_EQ(1, db.getLastInsertRowid());
EXPECT_EQ(1, db.getTotalChanges());
EXPECT_THROW(insert.exec(), SQLite::Exception); // exec() shall throw as it needs to be reseted
// Compile a SQL query
SQLite::Statement query(db, "SELECT * FROM test");
EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str());
EXPECT_EQ(6, query.getColumnCount ());
query.executeStep();
EXPECT_TRUE (query.hasRow());
EXPECT_FALSE(query.isDone());
// validates every variant of cast operators, and conversions of types
{
const sqlite3_int64 id1 = query.getColumn(0); // operator long long()
const int64_t id2 = query.getColumn(0); // operator long long()
const long long id3 = query.getColumn(0); // operator long long()
const long id4 = query.getColumn(0); // operator long long() or long() depending on compiler/architecture
const char id5 = query.getColumn(0); // operator char()
const short id6 = query.getColumn(0); // operator short()
const unsigned int uint1 = query.getColumn(0); // operator unsigned int()
const uint32_t uint2 = query.getColumn(0); // operator unsigned int()
const unsigned char uint3 = query.getColumn(0); // operator unsigned char()
const unsigned short uint4 = query.getColumn(0); // operator unsigned short()
const char* ptxt = query.getColumn(1); // operator const char*()
const std::string msg = query.getColumn(1); // operator std::string() (or const char* with MSVC)
const int integer = query.getColumn(2); // operator int()
const double real = query.getColumn(3); // operator double()
const void* pblob = query.getColumn(4); // operator void*()
#if !defined(_MSC_VER) || _MSC_VER >= 1900
// This implicit cast should use operator std::string()
// but would fallback to const char* with MSVC 2010-2013 (witch does not work with the NULL char in the middle)
const std::string sblob = query.getColumn(4); // operator std::string()
#endif
const void* pempty = query.getColumn(5); // operator void*()
EXPECT_EQ(1, id1);
EXPECT_EQ(1, id2);
EXPECT_EQ(1, id3);
EXPECT_EQ(1, id4);
EXPECT_EQ(1, id5);
EXPECT_EQ(1, id6);
EXPECT_EQ(1U, uint1);
EXPECT_EQ(1U, uint2);
EXPECT_EQ(1U, uint3);
EXPECT_EQ(1U, uint4);
EXPECT_STREQ("first", ptxt);
EXPECT_EQ("first", msg);
EXPECT_EQ(-123, integer);
EXPECT_EQ(0.123, real);
EXPECT_EQ(0, memcmp("bl\0b", pblob, size));
#if !defined(_MSC_VER) || _MSC_VER >= 1900
EXPECT_EQ((size_t)size, sblob.size());
EXPECT_EQ(0, memcmp("bl\0b", &sblob[0], size));
#endif
EXPECT_EQ(NULL, pempty);
}
// validates every variant of explicit getters
{
int64_t id = query.getColumn(0).getInt64();
const unsigned int uint1 = query.getColumn(0).getUInt();
const uint32_t uint2 = query.getColumn(0).getUInt();
const char* ptxt = query.getColumn(1).getText();
const std::string msg1 = query.getColumn(1).getText();
const std::string msg2 = query.getColumn(1).getString();
const int integer = query.getColumn(2).getInt();
const double real = query.getColumn(3).getDouble();
const void* pblob = query.getColumn(4).getBlob();
const std::string sblob = query.getColumn(4).getString();
EXPECT_EQ(1, id);
EXPECT_EQ(1U, uint1);
EXPECT_EQ(1U, uint2);
EXPECT_STREQ("first", ptxt);
EXPECT_EQ("first", msg1);
EXPECT_EQ("first", msg2);
EXPECT_EQ(-123, integer);
EXPECT_EQ(0.123, real);
EXPECT_EQ(0, memcmp("bl\0b", pblob, 4));
EXPECT_EQ(0, memcmp("bl\0b", &sblob[0], 4));
}
// Validate getBytes(), getType(), isInteger(), isNull()...
EXPECT_EQ(SQLite::INTEGER, query.getColumn(0).getType());
EXPECT_EQ(true, query.getColumn(0).isInteger());
EXPECT_EQ(false, query.getColumn(0).isFloat());
EXPECT_EQ(false, query.getColumn(0).isText());
EXPECT_EQ(false, query.getColumn(0).isBlob());
EXPECT_EQ(false, query.getColumn(0).isNull());
EXPECT_STREQ("1", query.getColumn(0).getText()); // convert to string
EXPECT_EQ(1, query.getColumn(0).getBytes()); // size of the string "1" without the null terminator
EXPECT_EQ(SQLite::TEXT, query.getColumn(1).getType());
EXPECT_EQ(false, query.getColumn(1).isInteger());
EXPECT_EQ(false, query.getColumn(1).isFloat());
EXPECT_EQ(true, query.getColumn(1).isText());
EXPECT_EQ(false, query.getColumn(1).isBlob());
EXPECT_EQ(false, query.getColumn(1).isNull());
EXPECT_STREQ("first", query.getColumn(1).getText()); // convert to string
EXPECT_EQ(5, query.getColumn(1).getBytes()); // size of the string "first"
EXPECT_EQ(SQLite::INTEGER, query.getColumn(2).getType());
EXPECT_EQ(true, query.getColumn(2).isInteger());
EXPECT_EQ(false, query.getColumn(2).isFloat());
EXPECT_EQ(false, query.getColumn(2).isText());
EXPECT_EQ(false, query.getColumn(2).isBlob());
EXPECT_EQ(false, query.getColumn(2).isNull());
EXPECT_STREQ("-123", query.getColumn(2).getText()); // convert to string
EXPECT_EQ(4, query.getColumn(2).getBytes()); // size of the string "-123"
EXPECT_EQ(SQLite::FLOAT, query.getColumn(3).getType());
EXPECT_EQ(false, query.getColumn(3).isInteger());
EXPECT_EQ(true, query.getColumn(3).isFloat());
EXPECT_EQ(false, query.getColumn(3).isText());
EXPECT_EQ(false, query.getColumn(3).isBlob());
EXPECT_EQ(false, query.getColumn(3).isNull());
EXPECT_STREQ("0.123", query.getColumn(3).getText()); // convert to string
EXPECT_EQ(5, query.getColumn(3).getBytes()); // size of the string "0.123"
EXPECT_EQ(SQLite::BLOB, query.getColumn(4).getType());
EXPECT_EQ(false, query.getColumn(4).isInteger());
EXPECT_EQ(false, query.getColumn(4).isFloat());
EXPECT_EQ(false, query.getColumn(4).isText());
EXPECT_EQ(true, query.getColumn(4).isBlob());
EXPECT_EQ(false, query.getColumn(4).isNull());
EXPECT_STREQ("bl\0b", query.getColumn(4).getText()); // convert to string
EXPECT_EQ(4, query.getColumn(4).getBytes()); // size of the blob "bl\0b" with the null char
EXPECT_EQ(SQLite::Null, query.getColumn(5).getType());
EXPECT_EQ(false, query.getColumn(5).isInteger());
EXPECT_EQ(false, query.getColumn(5).isFloat());
EXPECT_EQ(false, query.getColumn(5).isText());
EXPECT_EQ(false, query.getColumn(5).isBlob());
EXPECT_EQ(true, query.getColumn(5).isNull());
EXPECT_STREQ("", query.getColumn(5).getText()); // convert to string
EXPECT_EQ(0, query.getColumn(5).getBytes()); // size of the string "" without the null terminator
// Use intermediate Column objects (this is not the recommended way to use the API)
{
const SQLite::Column id = query.getColumn(0);
EXPECT_EQ(1, id.getInt64());
const SQLite::Column msg = query.getColumn(1);
EXPECT_EQ("first", msg.getString());
const SQLite::Column integer = query.getColumn(2);
EXPECT_EQ(-123, integer.getInt());
const SQLite::Column dbl = query.getColumn(3);
EXPECT_EQ(0.123, dbl.getDouble());
}
}
TEST(Column, getName)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT)"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\")"));
// Compile a SQL query, using the "id" column name as-is, but aliasing the "msg" column with new name "value"
SQLite::Statement query(db, "SELECT id, msg as value FROM test");
query.executeStep();
// Show how to get the aliased names of the result columns.
const std::string name0 = query.getColumn(0).getName();
const std::string name1 = query.getColumn(1).getName();
EXPECT_EQ("id", name0);
EXPECT_EQ("value", name1);
#ifdef SQLITE_ENABLE_COLUMN_METADATA
// Show how to get origin names of the table columns from which theses result columns come from.
// Requires the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro to be
// also defined at compile times of the SQLite library itself.
const std::string oname0 = query.getColumn(0).getOriginName();
const std::string oname1 = query.getColumn(1).getOriginName();
EXPECT_EQ("id", oname0);
EXPECT_EQ("msg", oname1);
#endif
}
TEST(Column, stream)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(0, db.exec("CREATE TABLE test (msg TEXT)"));
SQLite::Statement insert(db, "INSERT INTO test VALUES (?)");
// content to test
const char str_[] = "stringwith\0embedded";
std::string str(str_, sizeof(str_)-1);
insert.bind(1, str);
// Execute the one-step query to insert the row
EXPECT_EQ(1, insert.exec());
EXPECT_EQ(1, db.getTotalChanges());
SQLite::Statement query(db, "SELECT * FROM test");
query.executeStep();
std::stringstream ss;
ss << query.getColumn(0);
std::string content = ss.str();
EXPECT_EQ(content, str);
}

View file

@ -0,0 +1,512 @@
/**
* @file Database_test.cpp
* @ingroup tests
* @brief Test of a SQLiteCpp Database.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Database.h>
#include <sqlite3.h> // for SQLITE_ERROR and SQLITE_VERSION_NUMBER
#include <gtest/gtest.h>
#include <cstdio>
#include <fstream>
#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
{
/// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
{
// TODO: unit test that this assertion callback get called (already tested manually)
std::cout << "assertion_failed(" << apFile << ", " << apLine << ", " << apFunc << ", " << apExpr << ", " << apMsg << ")\n";
}
}
#endif
TEST(SQLiteCpp, version)
{
EXPECT_STREQ(SQLITE_VERSION, SQLite::VERSION);
EXPECT_EQ (SQLITE_VERSION_NUMBER, SQLite::VERSION_NUMBER);
EXPECT_STREQ(SQLITE_VERSION, SQLite::getLibVersion());
EXPECT_EQ (SQLITE_VERSION_NUMBER, SQLite::getLibVersionNumber());
}
TEST(Database, ctorExecCreateDropExist)
{
remove("test.db3");
{
// Try to open a non-existing database
std::string filename = "test.db3";
EXPECT_THROW(SQLite::Database not_found(filename), SQLite::Exception);
// Create a new database
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_STREQ("test.db3", db.getFilename().c_str());
EXPECT_FALSE(db.tableExists("test"));
EXPECT_FALSE(db.tableExists(std::string("test")));
EXPECT_EQ(0, db.getLastInsertRowid());
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"));
EXPECT_TRUE(db.tableExists("test"));
EXPECT_TRUE(db.tableExists(std::string("test")));
EXPECT_EQ(0, db.getLastInsertRowid());
EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test"));
EXPECT_FALSE(db.tableExists("test"));
EXPECT_FALSE(db.tableExists(std::string("test")));
EXPECT_EQ(0, db.getLastInsertRowid());
} // Close DB test.db3
remove("test.db3");
}
#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600)
SQLite::Database DatabaseBuilder(const char* apName)
{
return SQLite::Database(apName, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
}
TEST(Database, moveConstructor)
{
remove("test.db3");
{
// Create a new database, using the move constructor
SQLite::Database db = DatabaseBuilder("test.db3");
EXPECT_FALSE(db.tableExists("test"));
EXPECT_TRUE(db.getHandle() != NULL);
SQLite::Database moved = std::move(db);
EXPECT_TRUE(db.getHandle() == NULL);
EXPECT_TRUE(moved.getHandle() != NULL);
EXPECT_FALSE(moved.tableExists("test"));
} // Close DB test.db3
remove("test.db3");
}
#endif
TEST(Database, createCloseReopen)
{
remove("test.db3");
{
// Try to open the non-existing database
EXPECT_THROW(SQLite::Database not_found("test.db3"), SQLite::Exception);
// Create a new database
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_FALSE(db.tableExists("test"));
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_TRUE(db.tableExists("test"));
} // Close DB test.db3
{
// Reopen the database file
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_TRUE(db.tableExists("test"));
} // Close DB test.db3
remove("test.db3");
}
TEST(Database, inMemory)
{
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
EXPECT_FALSE(db.tableExists("test"));
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_TRUE(db.tableExists("test"));
// Create a new database: not shared with the above db
SQLite::Database db2(":memory:");
EXPECT_FALSE(db2.tableExists("test"));
} // Close an destroy DBs
{
// Create a new database: no more "test" table
SQLite::Database db(":memory:");
EXPECT_FALSE(db.tableExists("test"));
} // Close an destroy DB
}
TEST(Database, backup)
{
// Create a new in-memory database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
EXPECT_FALSE(db.tableExists("test"));
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_TRUE(db.tableExists("test"));
// Export the data into a file
remove("backup.db3");
EXPECT_NO_THROW(db.backup("backup.db3", SQLite::Database::Save));
// Trash the table
db.exec("DROP TABLE test;");
EXPECT_FALSE(db.tableExists("test"));
// Import the data back from the file
EXPECT_NO_THROW(db.backup("backup.db3", SQLite::Database::Load));
remove("backup.db3");
EXPECT_TRUE(db.tableExists("test"));
}
#if SQLITE_VERSION_NUMBER >= 3007015 // SQLite v3.7.15 is first version with PRAGMA busy_timeout
TEST(Database, busyTimeout)
{
{
// Create a new database with default timeout of 0ms
SQLite::Database db(":memory:");
// Busy timeout default to 0ms: any contention between threads or process leads to SQLITE_BUSY error
EXPECT_EQ(0, db.execAndGet("PRAGMA busy_timeout").getInt());
// Set a non null busy timeout: any contention between threads will leads to as much retry as possible during the time
db.setBusyTimeout(5000);
EXPECT_EQ(5000, db.execAndGet("PRAGMA busy_timeout").getInt());
// Reset timeout to 0
db.setBusyTimeout(0);
EXPECT_EQ(0, db.execAndGet("PRAGMA busy_timeout").getInt());
}
{
// Create a new database with a non null busy timeout
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE, 5000);
EXPECT_EQ(5000, db.execAndGet("PRAGMA busy_timeout").getInt());
// Reset timeout to null
db.setBusyTimeout(0);
EXPECT_EQ(0, db.execAndGet("PRAGMA busy_timeout").getInt());
}
{
// Create a new database with a non null busy timeout
const std::string memory = ":memory:";
SQLite::Database db(memory, SQLite::OPEN_READWRITE, 5000);
EXPECT_EQ(5000, db.execAndGet("PRAGMA busy_timeout").getInt());
// Reset timeout to null
db.setBusyTimeout(0);
EXPECT_EQ(0, db.execAndGet("PRAGMA busy_timeout").getInt());
}
}
#endif // SQLITE_VERSION_NUMBER >= 3007015
TEST(Database, exec)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
// Create a new table with an explicit "id" column aliasing the underlying rowid
// NOTE: here exec() returns 0 only because it is the first statements since database connexion,
// but its return is an undefined value for "CREATE TABLE" statements.
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_EQ(0, db.getLastInsertRowid());
EXPECT_EQ(0, db.getTotalChanges());
// first row : insert the "first" text value into new row of id 1
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\")"));
EXPECT_EQ(1, db.getLastInsertRowid());
EXPECT_EQ(1, db.getTotalChanges());
// second row : insert the "second" text value into new row of id 2
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"second\")"));
EXPECT_EQ(2, db.getLastInsertRowid());
EXPECT_EQ(2, db.getTotalChanges());
// third row : insert the "third" text value into new row of id 3
const std::string insert("INSERT INTO test VALUES (NULL, \"third\")");
EXPECT_EQ(1, db.exec(insert));
EXPECT_EQ(3, db.getLastInsertRowid());
EXPECT_EQ(3, db.getTotalChanges());
// update the second row : update text value to "second_updated"
EXPECT_EQ(1, db.exec("UPDATE test SET value=\"second-updated\" WHERE id='2'"));
EXPECT_EQ(3, db.getLastInsertRowid()); // last inserted row ID is still 3
EXPECT_EQ(4, db.getTotalChanges());
// delete the third row
EXPECT_EQ(1, db.exec("DELETE FROM test WHERE id='3'"));
EXPECT_EQ(3, db.getLastInsertRowid());
EXPECT_EQ(5, db.getTotalChanges());
// drop the whole table, ie the two remaining columns
// NOTE: here exec() returns 1, like the last time, as it is an undefined value for "DROP TABLE" statements
db.exec("DROP TABLE IF EXISTS test");
EXPECT_FALSE(db.tableExists("test"));
EXPECT_EQ(5, db.getTotalChanges());
// Re-Create the same table
// NOTE: here exec() returns 1, like the last time, as it is an undefined value for "CREATE TABLE" statements
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_EQ(5, db.getTotalChanges());
// insert two rows with two *different* statements => returns only 1, ie. for the second INSERT statement
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\");INSERT INTO test VALUES (NULL, \"second\");"));
EXPECT_EQ(2, db.getLastInsertRowid());
EXPECT_EQ(7, db.getTotalChanges());
#if (SQLITE_VERSION_NUMBER >= 3007011)
// insert two rows with only one statement (starting with SQLite 3.7.11) => returns 2
EXPECT_EQ(2, db.exec("INSERT INTO test VALUES (NULL, \"third\"), (NULL, \"fourth\");"));
EXPECT_EQ(4, db.getLastInsertRowid());
EXPECT_EQ(9, db.getTotalChanges());
#endif
}
TEST(Database, execAndGet)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
// Create a new table with an explicit "id" column aliasing the underlying rowid
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT, weight INTEGER)");
// insert a few rows
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 3)"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"second\", 5)"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"third\", 7)"));
// Get a single value result with an easy to use shortcut
EXPECT_STREQ("second", db.execAndGet("SELECT value FROM test WHERE id=2"));
EXPECT_STREQ("third", db.execAndGet("SELECT value FROM test WHERE weight=7"));
const std::string query("SELECT weight FROM test WHERE value=\"first\"");
EXPECT_EQ(3, db.execAndGet(query).getInt());
}
TEST(Database, execException)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
EXPECT_EQ(SQLite::OK, db.getErrorCode());
EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode());
// exception with SQL error: "no such table"
EXPECT_THROW(db.exec("INSERT INTO test VALUES (NULL, \"first\", 3)"), SQLite::Exception);
EXPECT_EQ(SQLITE_ERROR, db.getErrorCode());
EXPECT_EQ(SQLITE_ERROR, db.getExtendedErrorCode());
EXPECT_STREQ("no such table: test", db.getErrorMsg());
// Create a new table
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT, weight INTEGER)");
EXPECT_EQ(SQLite::OK, db.getErrorCode());
EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode());
EXPECT_STREQ("not an error", db.getErrorMsg());
// exception with SQL error: "table test has 3 columns but 2 values were supplied"
EXPECT_THROW(db.exec("INSERT INTO test VALUES (NULL, 3)"), SQLite::Exception);
EXPECT_EQ(SQLITE_ERROR, db.getErrorCode());
EXPECT_EQ(SQLITE_ERROR, db.getExtendedErrorCode());
EXPECT_STREQ("table test has 3 columns but 2 values were supplied", db.getErrorMsg());
// exception with SQL error: "No row to get a column from"
EXPECT_THROW(db.execAndGet("SELECT weight FROM test WHERE value=\"first\""), SQLite::Exception);
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 3)"));
// exception with SQL error: "No row to get a column from"
EXPECT_THROW(db.execAndGet("SELECT weight FROM test WHERE value=\"second\""), SQLite::Exception);
// Add a row with more values than columns in the table: "table test has 3 columns but 4 values were supplied"
EXPECT_THROW(db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)"), SQLite::Exception);
EXPECT_EQ(SQLITE_ERROR, db.getErrorCode());
EXPECT_EQ(SQLITE_ERROR, db.getExtendedErrorCode());
EXPECT_STREQ("table test has 3 columns but 4 values were supplied", db.getErrorMsg());
}
// From https://stackoverflow.com/a/8283265/1163698 How can I create a user-defined function in SQLite?
static void firstchar(sqlite3_context *context, int argc, sqlite3_value **argv)
{
if (argc == 1)
{
const unsigned char *text = sqlite3_value_text(argv[0]);
if (text && text[0])
{
char result[2];
result[0] = text[0]; result[1] = '\0';
sqlite3_result_text(context, result, -1, SQLITE_TRANSIENT);
return;
}
}
sqlite3_result_null(context);
}
TEST(Database, createFunction)
{
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\")"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"second\")"));
// exception with SQL error: "no such function: firstchar"
EXPECT_THROW(db.exec("SELECT firstchar(value) FROM test WHERE id=1"), SQLite::Exception);
db.createFunction("firstchar", 1, true, nullptr, &firstchar, nullptr, nullptr, nullptr);
EXPECT_EQ(1, db.exec("SELECT firstchar(value) FROM test WHERE id=1"));
}
TEST(Database, loadExtension)
{
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
// Try to load a non-existing extension (no dynamic library found)
EXPECT_THROW(db.loadExtension("non-existing-extension", "entry-point"), SQLite::Exception);
// TODO: test a proper extension
}
TEST(Database, getHeaderInfo)
{
remove("test.db3");
{
// Call without passing a database file name
EXPECT_THROW(SQLite::Database::getHeaderInfo(""),SQLite::Exception);
// Call with a non-existent database
EXPECT_THROW(SQLite::Database::getHeaderInfo("test.db3"), SQLite::Exception);
// Simulate an incomplete header by writing garbage to a file
{
const unsigned char badData[] = "garbage...";
const char* pBadData = reinterpret_cast<const char*>(&badData[0]);
remove("short.db3");
std::ofstream corruptDb;
corruptDb.open("short.db3", std::ios::app | std::ios::binary);
corruptDb.write(pBadData, sizeof(badData));
corruptDb.close();
EXPECT_THROW(SQLite::Database::getHeaderInfo("short.db3"), SQLite::Exception);
remove("short.db3");
}
// Simulate a corrupt header by writing garbage to a file
{
const unsigned char badData[100] = "garbage...";
const char* pBadData = reinterpret_cast<const char*>(&badData[0]);
remove("corrupt.db3");
std::ofstream corruptDb;
corruptDb.open("corrupt.db3", std::ios::app | std::ios::binary);
corruptDb.write(pBadData, sizeof(badData));
corruptDb.close();
EXPECT_THROW(SQLite::Database::getHeaderInfo("corrupt.db3"), SQLite::Exception);
remove("corrupt.db3");
}
// Create a new database
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
// Set assorted SQLite header values using associated PRAGMA
db.exec("PRAGMA main.user_version = 12345");
db.exec("PRAGMA main.application_id = 2468");
// Parse header fields from test database
const SQLite::Header h = db.getHeaderInfo();
//Test header values explicitly set via PRAGMA statements
EXPECT_EQ(h.userVersion, 12345);
EXPECT_EQ(h.applicationId, 2468);
//Test header values with expected default values
EXPECT_EQ(h.pageSizeBytes, 4096);
EXPECT_EQ(h.fileFormatWriteVersion,1);
EXPECT_EQ(h.fileFormatReadVersion,1);
EXPECT_EQ(h.reservedSpaceBytes,0);
EXPECT_EQ(h.maxEmbeddedPayloadFrac, 64);
EXPECT_EQ(h.minEmbeddedPayloadFrac, 32);
EXPECT_EQ(h.leafPayloadFrac, 32);
EXPECT_EQ(h.fileChangeCounter, 3);
EXPECT_EQ(h.databaseSizePages, 2);
EXPECT_EQ(h.firstFreelistTrunkPage, 0);
EXPECT_EQ(h.totalFreelistPages, 0);
EXPECT_EQ(h.schemaCookie, 1);
EXPECT_EQ(h.schemaFormatNumber, 4);
EXPECT_EQ(h.defaultPageCacheSizeBytes, 0);
EXPECT_EQ(h.largestBTreePageNumber, 0);
EXPECT_EQ(h.databaseTextEncoding, 1);
EXPECT_EQ(h.incrementalVaccumMode, 0);
EXPECT_EQ(h.versionValidFor, 3);
EXPECT_EQ(h.sqliteVersion, SQLITE_VERSION_NUMBER);
}
remove("test.db3");
}
#ifdef SQLITE_HAS_CODEC
TEST(Database, encryptAndDecrypt)
{
remove("test.db3");
{
// Try to open the non-existing database
EXPECT_THROW(SQLite::Database not_found("test.db3"), SQLite::Exception);
EXPECT_THROW(SQLite::Database::isUnencrypted("test.db3"), SQLite::Exception);
EXPECT_THROW(SQLite::Database::isUnencrypted(""), SQLite::Exception);
// Create a new database
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
EXPECT_FALSE(db.tableExists("test"));
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_TRUE(db.tableExists("test"));
} // Close DB test.db3
{
// Reopen the database file and encrypt it
EXPECT_TRUE(SQLite::Database::isUnencrypted("test.db3"));
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE);
// Encrypt the database
db.rekey("123secret");
} // Close DB test.db3
{
// Reopen the database file and try to use it
EXPECT_FALSE(SQLite::Database::isUnencrypted("test.db3"));
SQLite::Database db("test.db3", SQLite::OPEN_READONLY);
EXPECT_THROW(db.tableExists("test"), SQLite::Exception);
db.key("123secret");
EXPECT_TRUE(db.tableExists("test"));
} // Close DB test.db3
{
// Reopen the database file and decrypt it
EXPECT_FALSE(SQLite::Database::isUnencrypted("test.db3"));
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE);
// Decrypt the database
db.key("123secret");
db.rekey("");
} // Close DB test.db3
{
// Reopen the database file and use it
EXPECT_TRUE(SQLite::Database::isUnencrypted("test.db3"));
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE);
EXPECT_TRUE(db.tableExists("test"));
} // Close DB test.db3
remove("test.db3");
}
#else // SQLITE_HAS_CODEC
TEST(Database, encryptAndDecrypt)
{
remove("test.db3");
{
// Try to open the non-existing database
EXPECT_THROW(SQLite::Database not_found("test.db3"), SQLite::Exception);
EXPECT_THROW(SQLite::Database::isUnencrypted("test.db3"), SQLite::Exception);
EXPECT_THROW(SQLite::Database::isUnencrypted(""), SQLite::Exception);
// Create a new database
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
EXPECT_FALSE(db.tableExists("test"));
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_TRUE(db.tableExists("test"));
} // Close DB test.db3
{
// Reopen the database file and encrypt it
EXPECT_TRUE(SQLite::Database::isUnencrypted("test.db3"));
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE);
// Encrypt the database
EXPECT_THROW(db.key("123secret"), SQLite::Exception);
EXPECT_THROW(db.rekey("123secret"), SQLite::Exception);
} // Close DB test.db3
remove("test.db3");
}
#endif // SQLITE_HAS_CODEC

View file

@ -0,0 +1,89 @@
/**
* @file Transaction_test.cpp
* @ingroup tests
* @brief Test of a SQLite Transaction.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Exception.h>
#include <gtest/gtest.h>
#include <string>
TEST(Exception, copy)
{
const SQLite::Exception ex1("some error", 2);
const SQLite::Exception ex2 = ex1;
EXPECT_STREQ(ex1.what(), ex2.what());
EXPECT_EQ(ex1.getErrorCode(), ex2.getErrorCode());
EXPECT_EQ(ex1.getExtendedErrorCode(), ex2.getExtendedErrorCode());
}
// see http://eel.is/c++draft/exception#2 or http://www.cplusplus.com/reference/exception/exception/operator=/
// an assignment operator is expected to be avaiable
TEST(Exception, assignment)
{
const char message[] = "some error";
const SQLite::Exception ex1(message, 1);
SQLite::Exception ex2("another error", 2);
ex2 = ex1;
EXPECT_STREQ(ex2.what(), message);
EXPECT_EQ(ex2.getErrorCode(), 1);
EXPECT_EQ(ex2.getExtendedErrorCode(), -1);
EXPECT_STREQ(ex2.getErrorStr(), "SQL logic error");
}
TEST(Exception, throw_catch)
{
const char message[] = "some error";
try
{
throw SQLite::Exception(message);
}
catch (const std::runtime_error& ex)
{
EXPECT_STREQ(ex.what(), message);
}
}
TEST(Exception, constructor)
{
const char msg1[] = "some error";
std::string msg2 = "another error";
{
const SQLite::Exception ex(msg1);
EXPECT_STREQ(ex.what(), msg1);
EXPECT_EQ(ex.getErrorCode(), -1);
EXPECT_EQ(ex.getExtendedErrorCode(), -1);
EXPECT_STREQ("unknown error", ex.getErrorStr());
}
{
const SQLite::Exception ex(msg2);
EXPECT_STREQ(ex.what(), msg2.c_str());
EXPECT_EQ(ex.getErrorCode(), -1);
EXPECT_EQ(ex.getExtendedErrorCode(), -1);
EXPECT_STREQ("unknown error", ex.getErrorStr());
}
{
const SQLite::Exception ex(msg1, 1);
EXPECT_STREQ(ex.what(), msg1);
EXPECT_EQ(ex.getErrorCode(), 1);
EXPECT_EQ(ex.getExtendedErrorCode(), -1);
EXPECT_STREQ(ex.getErrorStr(), "SQL logic error");
}
{
const SQLite::Exception ex(msg2, 2);
EXPECT_STREQ(ex.what(), msg2.c_str());
EXPECT_EQ(ex.getErrorCode(), 2);
EXPECT_EQ(ex.getExtendedErrorCode(), -1);
EXPECT_STREQ(ex.getErrorStr(), "unknown error");
}
}

View file

@ -0,0 +1,54 @@
/**
* @file VariadicBind_test.cpp
* @ingroup tests
* @brief Test of variadic bind
*
* Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de)
* Copyright (c) 2019-2020 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)
*/
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/ExecuteMany.h>
#include <gtest/gtest.h>
#include <cstdio>
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
TEST(ExecuteMany, invalid)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test"));
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default')"));
EXPECT_TRUE(db.tableExists("test"));
{
execute_many(db, "INSERT INTO test VALUES (?, ?)",
1,
std::make_tuple(2),
std::make_tuple(3, "three")
);
}
// make sure the content is as expected
{
SQLite::Statement query(db, std::string{"SELECT id, value FROM test ORDER BY id"});
std::vector<std::pair<int, std::string> > results;
while (query.executeStep())
{
const int id = query.getColumn(0);
std::string value = query.getColumn(1);
results.emplace_back( id, std::move(value) );
}
EXPECT_EQ(std::size_t(3), results.size());
EXPECT_EQ(std::make_pair(1,std::string{""}), results.at(0));
EXPECT_EQ(std::make_pair(2,std::string{""}), results.at(1));
EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2));
}
}
#endif // c++14

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,104 @@
/**
* @file Transaction_test.cpp
* @ingroup tests
* @brief Test of a SQLite Transaction.
*
* Copyright (c) 2012-2020 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)
*/
#include <SQLiteCpp/Transaction.h>
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/Exception.h>
#include <gtest/gtest.h>
#include <cstdio>
TEST(Transaction, commitRollback)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(SQLite::OK, db.getErrorCode());
{
// Begin transaction
SQLite::Transaction transaction(db);
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"));
EXPECT_EQ(SQLite::OK, db.getErrorCode());
// Insert a first value
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\")"));
EXPECT_EQ(1, db.getLastInsertRowid());
// Commit transaction
transaction.commit();
// Commit again throw an exception
EXPECT_THROW(transaction.commit(), SQLite::Exception);
}
// Auto rollback if no commit() before the end of scope
{
// Begin transaction
SQLite::Transaction transaction(db);
// Insert a second value (that will be rollbacked)
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"third\")"));
EXPECT_EQ(2, db.getLastInsertRowid());
// end of scope: automatic rollback
}
// Auto rollback of a transaction on error/exception
try
{
// Begin transaction
SQLite::Transaction transaction(db);
// Insert a second value (that will be rollbacked)
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"second\")"));
EXPECT_EQ(2, db.getLastInsertRowid());
// Execute with an error => exception with auto-rollback
db.exec("DesiredSyntaxError to raise an exception to rollback the transaction");
GTEST_FATAL_FAILURE_("we should never get there");
transaction.commit(); // We should never get there
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
// expected error, see above
}
// Double rollback with a manual command before the end of scope
{
// Begin transaction
SQLite::Transaction transaction(db);
// Insert a second value (that will be rollbacked)
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"third\")"));
EXPECT_EQ(2, db.getLastInsertRowid());
// Execute a manual rollback (no real use case I can think of, so no rollback() method)
db.exec("ROLLBACK");
// end of scope: the automatic rollback should not raise an error because it is harmless
}
// Check the results (expect only one row of result, as all other one have been rollbacked)
SQLite::Statement query(db, "SELECT * FROM test");
int nbRows = 0;
while (query.executeStep())
{
nbRows++;
EXPECT_EQ(1, query.getColumn(0).getInt());
EXPECT_STREQ("first", query.getColumn(1).getText());
}
EXPECT_EQ(1, nbRows);
}

View file

@ -0,0 +1,109 @@
/**
* @file VariadicBind_test.cpp
* @ingroup tests
* @brief Test of variadic bind
*
* Copyright (c) 2016 Paul Dreik (github@pauldreik.se)
* Copyright (c) 2016-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
* Copyright (c) 2019 Maximilian Bachmann (github@maxbachmann)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/VariadicBind.h>
#include <gtest/gtest.h>
#include <cstdio>
#if (__cplusplus >= 201103L) || ( defined(_MSC_VER) && (_MSC_VER >= 1800) ) // c++11: Visual Studio 2013
TEST(VariadicBind, invalid)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test"));
EXPECT_EQ(0,
db.exec(
"CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') "));
EXPECT_EQ(0,
db.exec(
"CREATE TABLE test2 (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') "));
EXPECT_TRUE(db.tableExists("test"));
EXPECT_TRUE(db.tableExists("test2"));
{
SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)");
// bind one argument less than expected - should be fine.
// the unspecified argument should be set to null, not the default.
SQLite::bind(query, 1);
EXPECT_EQ(1, query.exec());
query.reset();
// bind all arguments - should work just fine
SQLite::bind(query, 2, "two");
EXPECT_EQ(1, query.exec());
query.reset();
// bind too many arguments - should throw.
EXPECT_THROW(SQLite::bind(query, 3, "three", 0), SQLite::Exception);
EXPECT_EQ(1, query.exec());
}
// make sure the content is as expected
{
SQLite::Statement query(db, std::string{"SELECT id, value FROM test ORDER BY id"});
std::vector<std::pair<int, std::string> > results;
while (query.executeStep())
{
const int id = query.getColumn(0);
std::string value = query.getColumn(1);
results.emplace_back( id, std::move(value) );
}
EXPECT_EQ(std::size_t(3), results.size());
EXPECT_EQ(std::make_pair(1,std::string{""}), results.at(0));
EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1));
EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2));
}
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
{
SQLite::Statement query(db, "INSERT INTO test2 VALUES (?, ?)");
// bind one argument less than expected - should be fine.
// the unspecified argument should be set to null, not the default.
SQLite::bind(query, std::make_tuple(1));
EXPECT_EQ(1, query.exec());
query.reset();
// bind all arguments - should work just fine
SQLite::bind(query, std::make_tuple(2, "two"));
EXPECT_EQ(1, query.exec());
query.reset();
// bind too many arguments - should throw.
EXPECT_THROW(SQLite::bind(query, std::make_tuple(3, "three", 0)), SQLite::Exception);
EXPECT_EQ(1, query.exec());
}
// make sure the content is as expected
{
SQLite::Statement query(db, std::string{"SELECT id, value FROM test2 ORDER BY id"});
std::vector<std::pair<int, std::string> > results;
while (query.executeStep())
{
const int id = query.getColumn(0);
std::string value = query.getColumn(1);
results.emplace_back( id, std::move(value) );
}
EXPECT_EQ(std::size_t(3), results.size());
EXPECT_EQ(std::make_pair(1,std::string{""}), results.at(0));
EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1));
EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2));
}
#endif // c++14
}
#endif // c++11