sql types
parent
25ec286d05
commit
ab8abd93f1
|
@ -14,6 +14,9 @@ set(SQLITE_ORM_ENABLE_CXX_17 ON)
|
||||||
add_subdirectory(libs/blt)
|
add_subdirectory(libs/blt)
|
||||||
add_subdirectory(libs/DPP-10.0.29)
|
add_subdirectory(libs/DPP-10.0.29)
|
||||||
add_subdirectory(libs/sqlite_orm-1.8.2)
|
add_subdirectory(libs/sqlite_orm-1.8.2)
|
||||||
|
add_subdirectory(libs/SQLiteCpp-3.3.1)
|
||||||
|
|
||||||
|
find_package(CURL)
|
||||||
|
|
||||||
include_directories(include/)
|
include_directories(include/)
|
||||||
file(GLOB_RECURSE PROJECT_BUILD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
|
file(GLOB_RECURSE PROJECT_BUILD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
|
||||||
|
@ -26,6 +29,8 @@ target_link_options(discord_bot PUBLIC -Wall -Wpedantic -Wno-comment)
|
||||||
target_link_libraries(discord_bot PUBLIC BLT)
|
target_link_libraries(discord_bot PUBLIC BLT)
|
||||||
target_link_libraries(discord_bot PUBLIC dpp)
|
target_link_libraries(discord_bot PUBLIC dpp)
|
||||||
target_link_libraries(discord_bot PUBLIC sqlite_orm)
|
target_link_libraries(discord_bot PUBLIC sqlite_orm)
|
||||||
|
target_link_libraries(discord_bot PUBLIC curl)
|
||||||
|
target_link_libraries(discord_bot PUBLIC SQLiteCpp sqlite3 pthread dl)
|
||||||
|
|
||||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||||
target_compile_options(discord_bot PRIVATE -fsanitize=address)
|
target_compile_options(discord_bot PRIVATE -fsanitize=address)
|
||||||
|
|
|
@ -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 "${plugin_state_location}/${specs_file}"'" 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 "${plugin_state_location}/specs.cpp"'" 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 "${plugin_state_location}/specs.c"'" 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 "${plugin_state_location}/${specs_file}"'" 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 "${plugin_state_location}/specs.cpp"'" 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 "${plugin_state_location}/specs.c"'" 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 "${plugin_state_location}/${specs_file}"'" 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 "${plugin_state_location}/specs.cpp"'" 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 "${plugin_state_location}/specs.c"'" 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 "${plugin_state_location}/${specs_file}"'" 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 "${plugin_state_location}/specs.cpp"'" 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 "${plugin_state_location}/specs.c"'" 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 "${plugin_state_location}/${specs_file}"'" 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 "${plugin_state_location}/specs.cpp"'" 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 "${plugin_state_location}/specs.c"'" command="sh" useDefault="true"/>
|
||||||
|
<parser enabled="true"/>
|
||||||
|
</scannerInfoProvider>
|
||||||
|
</profile>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||||
|
</cproject>
|
|
@ -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
|
|
@ -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+
|
|
@ -0,0 +1,62 @@
|
||||||
|
name: CMake build
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: ${{ matrix.config.name }}
|
||||||
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
name: "Windows Latest MSVC",
|
||||||
|
os: windows-latest,
|
||||||
|
generator: "Visual Studio 17 2022",
|
||||||
|
build_type: "Debug",
|
||||||
|
cc: "cl", cxx: "cl",
|
||||||
|
extra_path: "",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "Windows Latest MinGW",
|
||||||
|
os: windows-latest,
|
||||||
|
generator: "MinGW Makefiles",
|
||||||
|
build_type: "Debug",
|
||||||
|
cc: "gcc", cxx: "g++",
|
||||||
|
extra_path: "C:\\ProgramData\\chocolatey\\lib\\mingw\\tools\\install\\mingw64\\bin",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "Ubuntu Latest GCC",
|
||||||
|
os: ubuntu-latest,
|
||||||
|
generator: "Unix Makefiles",
|
||||||
|
build_type: "Debug",
|
||||||
|
cc: "gcc", cxx: "g++",
|
||||||
|
extra_path: "",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "macOS Latest Clang",
|
||||||
|
os: macos-latest,
|
||||||
|
generator: "Unix Makefiles",
|
||||||
|
build_type: "Debug",
|
||||||
|
cc: "clang", cxx: "clang++",
|
||||||
|
extra_path: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout ${{ github.ref_name }}
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- run: git submodule update --init --recursive
|
||||||
|
- name: set extra GITHUB_PATH ${{ matrix.config.extra_path }} (for MinGW)
|
||||||
|
shell: bash
|
||||||
|
run: echo "${{ matrix.config.extra_path }}" >> $GITHUB_PATH
|
||||||
|
- name: set env CXX=${{ matrix.config.cxx }}
|
||||||
|
shell: cmake -P {0}
|
||||||
|
run: |
|
||||||
|
set(ENV{CC} ${{ matrix.config.cc }})
|
||||||
|
set(ENV{CXX} ${{ matrix.config.cxx }})
|
||||||
|
- run: mkdir build
|
||||||
|
- run: cmake -G "${{ matrix.config.generator }}" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -DBUILD_SHARED_LIBS=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_RUN_CPPCHECK=OFF -DSQLITECPP_RUN_CPPLINT=OFF ..
|
||||||
|
working-directory: build
|
||||||
|
- run: cmake --build build --config ${{ matrix.config.build_type }}
|
||||||
|
- run: ctest --verbose --output-on-failure --test-dir build
|
|
@ -0,0 +1,43 @@
|
||||||
|
name: CMake build of example in subdirectory
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
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: "macOS Latest Clang",
|
||||||
|
os: macos-latest,
|
||||||
|
build_type: "Debug", cc: "clang", cxx: "clang++"
|
||||||
|
}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- 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}}
|
|
@ -0,0 +1,80 @@
|
||||||
|
name: Meson build
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: (Meson) ${{ matrix.config.name }}
|
||||||
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
name: "Windows Latest MSVC",
|
||||||
|
os: windows-latest,
|
||||||
|
cc: "cl", cxx: "cl",
|
||||||
|
extra_path: "",
|
||||||
|
requires_msvc: true,
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "Windows Latest MinGW",
|
||||||
|
os: windows-latest,
|
||||||
|
cc: "gcc", cxx: "g++",
|
||||||
|
extra_path: "C:\\ProgramData\\chocolatey\\lib\\mingw\\tools\\install\\mingw64\\bin",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "Windows Latest Clang",
|
||||||
|
os: windows-latest,
|
||||||
|
cc: "clang", cxx: "clang++", c_ld: "lld-link", cxx_ld: "lld-link",
|
||||||
|
extra_path: "",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "Ubuntu Latest GCC",
|
||||||
|
os: ubuntu-latest,
|
||||||
|
cc: "gcc", cxx: "g++",
|
||||||
|
extra_path: ""
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "Ubuntu Latest Clang",
|
||||||
|
os: ubuntu-latest,
|
||||||
|
cc: "clang", cxx: "clang++", c_ld: "lld", cxx_ld: "lld",
|
||||||
|
extra_path: ""
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "macOS Latest Clang",
|
||||||
|
os: macos-latest,
|
||||||
|
cc: "clang", cxx: "clang++",
|
||||||
|
extra_path: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
# use msvc-dev-cmd to setup the environment for MSVC if needed
|
||||||
|
- name: setup MSVC
|
||||||
|
if: matrix.config.requires_msvc
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
- name: extra_path
|
||||||
|
shell: bash
|
||||||
|
run: echo "${{matrix.config.extra_path}}" >> $GITHUB_PATH
|
||||||
|
- name: install prerequisites
|
||||||
|
run: |
|
||||||
|
# asuming that python and pip are already installed
|
||||||
|
pip3 install meson ninja
|
||||||
|
- name: setup meson project
|
||||||
|
env: # set proper compilers and linkers for meson
|
||||||
|
CC: ${{matrix.config.cc}}
|
||||||
|
CXX: ${{matrix.config.cxx}}
|
||||||
|
C_LD: ${{matrix.config.c_ld}}
|
||||||
|
CXX_LD: ${{matrix.config.cxx_ld}}
|
||||||
|
run: |
|
||||||
|
# setup the build directory with tests and examples enabled
|
||||||
|
meson setup builddir -DSQLITECPP_BUILD_TESTS=true -DSQLITECPP_BUILD_EXAMPLES=true --force-fallback-for=sqlite3
|
||||||
|
- name: build meson project
|
||||||
|
run: |
|
||||||
|
# build the project
|
||||||
|
meson compile -C builddir
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
# run the tests
|
||||||
|
meson test -C builddir
|
|
@ -0,0 +1,34 @@
|
||||||
|
Debug
|
||||||
|
Release
|
||||||
|
build
|
||||||
|
*.a
|
||||||
|
|
||||||
|
# ignore clangd cache directory
|
||||||
|
.cache
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
.vsconfig
|
||||||
|
*.sln
|
||||||
|
*.ncb
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*sdf
|
||||||
|
*.vc*
|
||||||
|
*~
|
||||||
|
doc
|
||||||
|
core
|
||||||
|
*ipch
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
# do not track Visual Studio CMake settings
|
||||||
|
CMakeSettings.json
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
*.dir
|
||||||
|
Testing
|
||||||
|
Win32
|
||||||
|
|
||||||
|
SQLiteCpp_example1
|
||||||
|
SQLiteCpp_tests
|
||||||
|
|
||||||
|
!FindSQLiteCpp.cmake
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "googletest"]
|
||||||
|
path = googletest
|
||||||
|
url = https://github.com/google/googletest.git
|
|
@ -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>
|
|
@ -0,0 +1,136 @@
|
||||||
|
# Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
|
||||||
|
language: cpp
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
jobs:
|
||||||
|
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
|
|
@ -0,0 +1,265 @@
|
||||||
|
2012 Mar 30
|
||||||
|
- Start of a new thin C++ SQLite wrapper
|
||||||
|
|
||||||
|
2012 Apr 2
|
||||||
|
- The wrapper is functional
|
||||||
|
- Added documentation and examples
|
||||||
|
- Publication on GitHub
|
||||||
|
|
||||||
|
Version 0.1.0 - 2012 Apr 4
|
||||||
|
- 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 - 2012 Apr 11
|
||||||
|
- Added getLastInsertId() and setBusyTimout()
|
||||||
|
- Added bind() by name methods
|
||||||
|
|
||||||
|
Version 0.3.0 - 2012 Apr 16
|
||||||
|
- Added an easy wrapper Database::execAngGet()
|
||||||
|
|
||||||
|
Version 0.4.0 - 2012 Apr 23
|
||||||
|
- 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 - 2013 March 9
|
||||||
|
- 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 - 2013 April 7
|
||||||
|
- Added Column::getName()
|
||||||
|
|
||||||
|
Version 0.6.0 - 2013 November 22
|
||||||
|
- Renamed Column::getName() to Column::getOriginName()
|
||||||
|
- Added Column::getName()
|
||||||
|
|
||||||
|
Version 0.7.0 - 2014 January 9
|
||||||
|
- Added Database::createFunction()
|
||||||
|
- Added std::string version of existing APIs
|
||||||
|
- Improved CMake with more build options and Doxygen auto-detection
|
||||||
|
|
||||||
|
Version 0.8.0 - 2014 February 26
|
||||||
|
- 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 - 2015 May 3
|
||||||
|
- 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 - 2015 May 18
|
||||||
|
- Fixed valgrind error on Database destructor
|
||||||
|
- Added Database::loadExtension
|
||||||
|
|
||||||
|
Version 1.2.0 - 2015 September 9
|
||||||
|
- 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 - 2015 November 1
|
||||||
|
- Fixed build with Visual Studio 2015
|
||||||
|
- Further improvements to README
|
||||||
|
- Added Backup class
|
||||||
|
|
||||||
|
Version 1.3.1 - 2016 February 10
|
||||||
|
- 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 - 2016 July 25
|
||||||
|
- 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 - 2017 July 18
|
||||||
|
- 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 - 2017 Sept 19
|
||||||
|
- 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 - 2019 March 3
|
||||||
|
- 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 - 2019 August 25
|
||||||
|
- 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 - 2019 December 31
|
||||||
|
- 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 - 2020 January 31
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
Version 3.1.0 - 2020 August 11
|
||||||
|
- Update SQLite3 from 3.30.1 to 3.32.3 (2020-06-18)
|
||||||
|
- #274 Install both cmake files into same lib directory from tcraigtyler
|
||||||
|
- #275 Add a method on Statement to get the declared type of a column. from daniel-schmidt
|
||||||
|
- #284 Add SQLITE_OPEN_FULLMUTEX flag from rwrx
|
||||||
|
- #286 Add CMake option to toggle stack protection from chrisdalke
|
||||||
|
- #287 Fixed installation on other than Ubuntu distributions from xvitaly
|
||||||
|
- #288 Allow building of sqlite JSON1 extension when building internal sqlite library from zxey
|
||||||
|
|
||||||
|
Version 3.1.1 - 2020 August 19
|
||||||
|
- #292 Fix compilation if using SQLITE_HAS_CODEC from sum01
|
||||||
|
- #293 Remove FindSQLiteCpp.cmake from sum01
|
||||||
|
|
||||||
|
Version 3.2.0 - 2022 Septembre 18
|
||||||
|
- #300 #316 #362 #368 Updated SQLite3 from 3.32.3 to 3.39.3 (2022-09-05)
|
||||||
|
- #236 Disable explicit setting of MSVC runtime from BioDataAnalysis/emmenlau
|
||||||
|
- #308 Fix build warning due to string truncation from stauffer-garmin
|
||||||
|
- #311 Add Database::tryExec() from kcowolf
|
||||||
|
- #313 [CMake] Add SQLITECPP_INCLUDE_SCRIPT option from past-due
|
||||||
|
- #314 Add Database constructor for filesystem::path (#296) from ptrks
|
||||||
|
- #295 Compile internal SQLite library with -ffunction-sections from smichaku
|
||||||
|
- #299 Added Savepoint support from catalogm
|
||||||
|
- #333 Added Database and Statement getChanges()
|
||||||
|
- #305 Add other constants that work with sqlite3_open_v2 from LuAPi/more-flags
|
||||||
|
- #333 Added Database and Statement method getChanges() from SRombauts/get-changes
|
||||||
|
- #334 fix link for HAS_CODEC from linux-fan-dave/master
|
||||||
|
- #338 fix load extension from paulo-coutinho/fix-load-extension
|
||||||
|
- #335 from jagerman/older-macos-avoid-std-filesystem
|
||||||
|
- #337 Add catkin configuration from ardabbour/master
|
||||||
|
- #339 Allow specifying transaction behaviors DEFERRED, IMMEDIATE, and EXCLUSIVE from jjenkins278/transaction_behavior
|
||||||
|
- #340 add HTML keywords and properly link up the links in docs/README.md from phoebe-leong/patch-1
|
||||||
|
- #341 Install the package.xml file from ardabbour/patch-1
|
||||||
|
- #352 add basic meson support from ninjaoflight/meson-support
|
||||||
|
- #349 Refactoring of Statement and Column classes from Kacperos155/refactoring-Statement&Column
|
||||||
|
- #359 Fix compilation issues earlier than iOS 13
|
||||||
|
- #354 Windows improved support (meson) from ninjaoflight/windows-migration
|
||||||
|
- #361 Fix Statement unit test using long from SRombauts/fix-statement-unit-tests-long-long-type
|
||||||
|
- #346 Add compatible definition for std::experimental::filesystem from guoh27/master
|
||||||
|
- #364 Removal of remaining long APIs from SRombauts/convert-remaining-long-types
|
||||||
|
- #366 Add vcpkg installation instructions from FrankXie05/vcpkg-instructions
|
||||||
|
- #360 Small improvements and code cleaning from Kacperos155/small_improvements
|
||||||
|
|
||||||
|
Versions 3.2.1 - 2022 Decembre 12
|
||||||
|
- #383 Update SQLite from 3.39.3 to 3.40.0 (2022-11-16) from SRombauts/update-sqlite-340
|
||||||
|
- #370 Don't link anymore with Visual Studio's static runtime by default from SRombauts/dont-enforce-static-linking
|
||||||
|
- #371 from SRombauts/appveyor-vs-2022
|
||||||
|
- #277 from cuberite/cmake-scoping
|
||||||
|
- #374 Update googletest from vuhailongkl97/master
|
||||||
|
- #377 Some documentation fixes from cbielow/fix_doc
|
||||||
|
- #380 [Meson] fixes for meson project from ninjaoflight/windows-support
|
||||||
|
- #387 Ensure that TEXT column is UTF-8 encoded before using sqlite3_column_blob() from dougnazar
|
||||||
|
- #385 disable SQLITECPP_USE_STACK_PROTECTION when on MinGW from SRombauts/mingw-disable-stack-protection
|
||||||
|
- #386 [meson] Update SQLite from 3.39.3 to 3.40.0 from ninjaoflight/sqlite-meson-update
|
||||||
|
- #389 [meson] add missing compile options from ninjaoflight/meson-fixes
|
||||||
|
|
||||||
|
Version 3.3.0 - 2023 May 24
|
||||||
|
- #393 Fix preprocessor issues from jowr/fix_preprocessor_issues
|
||||||
|
- #394 check if SQLITE_OPEN_NOFOLLOW is defined from ninjaoflight/macos-11-fix
|
||||||
|
- #391 meson project changes based on wrap submission review from ninjaoflight/meson-macos-fix
|
||||||
|
- #390 fix incorrect work of savepoint from spoyler/save_point Sébastien Rombauts 12/15/2022 01:12 PM
|
||||||
|
- #396 Rename Savepoint RollbackTo() and fix class comments and formatting from SRombauts/rename-savepoint-rollback-to
|
||||||
|
- #384 Add Mingw GitHub actions from SRombauts/mingw-github-actions
|
||||||
|
- #397 Add a Transaction::rollback() method from SRombauts/add-transaction-rollback
|
||||||
|
- #395 add meson usage guide from ninjaoflight/meson-readme-guide
|
||||||
|
- #401 Fix meson installation from dougnazar/fix_meson_install
|
||||||
|
- #400 CMakr/meson Lint corrections from ninjaoflight/lint-corrections
|
||||||
|
- #404 Add documentation for prepared statements in transactions from ewarchul/query_transactions_example
|
||||||
|
- #399 add disable option for sqlite3_expanded_sql from ninjaoflight/optional-sqlite3_expanded_sql
|
||||||
|
- #408 correct executable name in meson from ninjaoflight/patch-2
|
||||||
|
- #407 Create Meson CI from ninjaoflight/patch-1
|
||||||
|
- #409 Update package.xml from poshul/patch-1
|
||||||
|
- #410 use checkout@v3 in CMake CI from ninjaoflight/fix-nodejs-warnings
|
||||||
|
- #406 DLL export/import using BUILD_SHARED_LIBS from pierre-aimi/dllexport_import
|
||||||
|
- #415 Remove mismatched else condition in CMakeLists.txt from Timmmm/patch-1
|
||||||
|
- #413 Fix compiler warnings from ninjaoflight/fix-visibility-warning
|
||||||
|
- #423 Update SQLite from 3.40.0 to 3.42.0 (2023-05-16) from SRombauts/update-sqlite
|
||||||
|
|
||||||
|
Version 3.3.1 - 2023 Aug 27
|
||||||
|
|
||||||
|
- #428 Add CMake option SQLITE_ENABLE_DBSTAT_VTAB and SQLITE_ENABLE_RTREE from SRombauts/cmake-sqlite-enable-dbstat-vtab
|
||||||
|
- #434 Define SQLITECPP_COMPILE_DLL as PUBLIC from calumr/fix-dll-import
|
||||||
|
- #439 Update CMake minimum version to 3.5 to get rid of a new deprecation warning with CMake 3.27 from SRombauts/cmake-update-minimum-version
|
||||||
|
- #441 Cleanup of the Github "build" workflow from SRombauts/github-actions-improvements
|
||||||
|
- Update usage of SQLITECPP_USE_STATIC_RUNTIME (#438)
|
||||||
|
- Don't build the googlemock subproject, only the main googletest library
|
||||||
|
- Declare BUILD_SHARED_LIBS option for discoverability (#440)
|
||||||
|
- Set -DBUILD_SHARED_LIBS=ON by default on scripts and CI/CD (#442)
|
||||||
|
- Update SQLite from 3.42.0 to 3.43.0 (2023-08-24) (#443)
|
||||||
|
- Rename the original build.yml to cmake.yml vs meson.yml (#444)
|
|
@ -0,0 +1,502 @@
|
||||||
|
# Main CMake file for compiling the library itself, examples and tests.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
#
|
||||||
|
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
# or copy at http://opensource.org/licenses/MIT)
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # custom CMake modules like FindSQLiteCpp
|
||||||
|
project(SQLiteCpp VERSION 3.3.1)
|
||||||
|
|
||||||
|
# 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}")
|
||||||
|
|
||||||
|
option(SQLITECPP_BUILD_TESTS "Build and run tests." OFF)
|
||||||
|
|
||||||
|
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make it prominent
|
||||||
|
option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
# 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 internal googletest
|
||||||
|
option(SQLITECPP_USE_STATIC_RUNTIME "Use MSVC static runtime (default for internal googletest)." FALSE)
|
||||||
|
if (SQLITECPP_USE_STATIC_RUNTIME)
|
||||||
|
message(STATUS "Linking against multithread static C++ runtime")
|
||||||
|
# inspired from Zlib licensed glfw https://github.com/glfw/glfw/blob/master/CMakeLists.txt
|
||||||
|
foreach (flag CMAKE_C_FLAGS
|
||||||
|
CMAKE_C_FLAGS_DEBUG
|
||||||
|
CMAKE_C_FLAGS_RELEASE
|
||||||
|
CMAKE_C_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_CXX_FLAGS
|
||||||
|
CMAKE_CXX_FLAGS_DEBUG
|
||||||
|
CMAKE_CXX_FLAGS_RELEASE
|
||||||
|
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||||
|
|
||||||
|
string(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
|
||||||
|
string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
|
||||||
|
|
||||||
|
endforeach()
|
||||||
|
else (SQLITECPP_USE_STATIC_RUNTIME)
|
||||||
|
if (SQLITECPP_BUILD_TESTS)
|
||||||
|
message(STATUS "Force googletest to link against dynamic C++ runtime")
|
||||||
|
set(gtest_force_shared_crt ON CACHE BOOL "Use shared (DLL) run-time lib even when Google Test is built as static lib.")
|
||||||
|
endif (SQLITECPP_BUILD_TESTS)
|
||||||
|
endif (SQLITECPP_USE_STATIC_RUNTIME)
|
||||||
|
# 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) # Unix/macOS/MinGW
|
||||||
|
set(CPPLINT_ARG_OUTPUT "--output=eclipse")
|
||||||
|
set(CPPCHECK_ARG_TEMPLATE "--template=gcc")
|
||||||
|
# Useful compile flags and extra warnings
|
||||||
|
if (NOT MINGW)
|
||||||
|
# Stack protection is not supported on MinGW-W64 on Windows.
|
||||||
|
# Allow this flag to be turned off.
|
||||||
|
option(SQLITECPP_USE_STACK_PROTECTION "USE Stack Protection hardening." ON)
|
||||||
|
if (SQLITECPP_USE_STACK_PROTECTION)
|
||||||
|
message (STATUS "Using Stack Protection hardening")
|
||||||
|
add_compile_options(-fstack-protector)
|
||||||
|
endif (SQLITECPP_USE_STACK_PROTECTION)
|
||||||
|
endif (NOT MINGW)
|
||||||
|
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 ()
|
||||||
|
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/Savepoint.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/Savepoint.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/Savepoint_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})
|
||||||
|
|
||||||
|
option(SQLITECPP_INCLUDE_SCRIPT "Include config & script files." ON)
|
||||||
|
if (SQLITECPP_INCLUDE_SCRIPT)
|
||||||
|
# list of config & script files of the library
|
||||||
|
set(SQLITECPP_SCRIPT
|
||||||
|
.editorconfig
|
||||||
|
.gitbugtraq
|
||||||
|
.github/workflows/cmake.yml
|
||||||
|
.github/workflows/cmake_subdir_example.yml
|
||||||
|
.github/workflows/meson.yml
|
||||||
|
.gitignore
|
||||||
|
.gitmodules
|
||||||
|
.travis.yml
|
||||||
|
appveyor.yml
|
||||||
|
build.bat
|
||||||
|
build.sh
|
||||||
|
cpplint.py
|
||||||
|
Doxyfile
|
||||||
|
cmake/FindSQLite3.cmake
|
||||||
|
cmake/SQLiteCppConfig.cmake.in
|
||||||
|
)
|
||||||
|
source_group(scripts FILES ${SQLITECPP_SCRIPT})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 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 (BUILD_SHARED_LIBS)
|
||||||
|
if (WIN32)
|
||||||
|
message(STATUS "Build shared libraries (DLLs).")
|
||||||
|
target_compile_definitions(SQLiteCpp PUBLIC "SQLITECPP_COMPILE_DLL")
|
||||||
|
target_compile_definitions(SQLiteCpp PRIVATE "SQLITECPP_DLL_EXPORT")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(SQLITE_OMIT_LOAD_EXTENSION "Enable omit load extension" OFF)
|
||||||
|
if (SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
# Enable the user definition of load_extension().
|
||||||
|
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
endif (SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
|
||||||
|
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")
|
||||||
|
option(SQLITE_ENABLE_RTREE "Enable RTree extension when building internal sqlite3 library." OFF)
|
||||||
|
option(SQLITE_ENABLE_DBSTAT_VTAB "Enable DBSTAT read-only eponymous virtual table extension when building internal sqlite3 library." OFF)
|
||||||
|
# build the SQLite3 C library (for ease of use/compatibility) versus Linux sqlite3-dev package
|
||||||
|
add_subdirectory(sqlite3)
|
||||||
|
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3)
|
||||||
|
else (SQLITECPP_INTERNAL_SQLITE)
|
||||||
|
# When using the SQLite codec, we need to link against the sqlcipher lib & include <sqlcipher/sqlite3.h>
|
||||||
|
# So this gets the lib & header, and links/includes everything
|
||||||
|
if(SQLITE_HAS_CODEC)
|
||||||
|
# Make PkgConfig optional since Windows doesn't usually have it installed.
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
# IMPORTED_TARGET was added in 3.6.3
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 3.6.3)
|
||||||
|
pkg_check_modules(sqlcipher REQUIRED sqlcipher)
|
||||||
|
# Only used in Database.cpp so PRIVATE to hide from end-user
|
||||||
|
# Since we can't use IMPORTED_TARGET on this older Cmake version, manually link libs & includes
|
||||||
|
target_link_libraries(SQLiteCpp PRIVATE ${sqlcipher_LIBRARIES})
|
||||||
|
target_include_directories(SQLiteCpp PRIVATE ${sqlcipher_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
pkg_check_modules(sqlcipher REQUIRED IMPORTED_TARGET sqlcipher)
|
||||||
|
# Only used in Database.cpp so PRIVATE to hide from end-user
|
||||||
|
target_link_libraries(SQLiteCpp PRIVATE PkgConfig::sqlcipher)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Since we aren't using pkgconf here, find it manually
|
||||||
|
find_library(sqlcipher_LIBRARY "sqlcipher")
|
||||||
|
find_path(sqlcipher_INCLUDE_DIR "sqlcipher/sqlite3.h"
|
||||||
|
PATH_SUFFIXES
|
||||||
|
"include"
|
||||||
|
"includes"
|
||||||
|
)
|
||||||
|
# Hides it from the GUI
|
||||||
|
mark_as_advanced(sqlcipher_LIBRARY sqlcipher_INCLUDE_DIR)
|
||||||
|
if(NOT sqlcipher_INCLUDE_DIR)
|
||||||
|
message(FATAL_ERROR "${PROJECT_NAME} requires the \"<sqlcipher/sqlite3.h>\" header to use the codec functionality but it wasn't found.")
|
||||||
|
elseif(NOT sqlcipher_LIBRARY)
|
||||||
|
message(FATAL_ERROR "${PROJECT_NAME} requires the sqlcipher library to use the codec functionality but it wasn't found.")
|
||||||
|
endif()
|
||||||
|
# Only used in Database.cpp so PRIVATE to hide from end-user
|
||||||
|
target_include_directories(SQLiteCpp PRIVATE "${sqlcipher_INCLUDE_DIR}/sqlcipher")
|
||||||
|
target_link_libraries(SQLiteCpp PRIVATE ${sqlcipher_LIBRARY})
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
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()
|
||||||
|
endif (SQLITECPP_INTERNAL_SQLITE)
|
||||||
|
|
||||||
|
## disable the optional support for std::filesystem (C++17)
|
||||||
|
option(SQLITECPP_DISABLE_STD_FILESYSTEM "Disable the use of std::filesystem in SQLiteCpp." OFF)
|
||||||
|
if (SQLITECPP_DISABLE_STD_FILESYSTEM)
|
||||||
|
message (STATUS "Disabling std::filesystem support")
|
||||||
|
target_compile_definitions(SQLiteCpp PUBLIC SQLITECPP_DISABLE_STD_FILESYSTEM)
|
||||||
|
endif (SQLITECPP_DISABLE_STD_FILESYSTEM)
|
||||||
|
|
||||||
|
## disable the optional support for sqlite3_expanded_sql (from sqlite3 3.14.0)
|
||||||
|
option(SQLITECPP_DISABLE_EXPANDED_SQL "Disable the use of sqlite3_expanded_sql in SQLiteCpp." OFF)
|
||||||
|
if (SQLITECPP_DISABLE_EXPANDED_SQL)
|
||||||
|
message (STATUS "Disabling sqlite3_expanded_sql support")
|
||||||
|
target_compile_definitions(SQLiteCpp PUBLIC SQLITECPP_DISABLE_EXPANDED_SQL)
|
||||||
|
endif (SQLITECPP_DISABLE_EXPANDED_SQL)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
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})
|
||||||
|
install(FILES ${PROJECT_SOURCE_DIR}/package.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/${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 ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
||||||
|
install(FILES
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
if (SQLITECPP_BUILD_TESTS)
|
||||||
|
# add the unit test executable
|
||||||
|
add_executable(SQLiteCpp_tests ${SQLITECPP_TESTS})
|
||||||
|
target_link_libraries(SQLiteCpp_tests SQLiteCpp)
|
||||||
|
|
||||||
|
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 ()
|
||||||
|
# don't build the googlemock subproject, only the main googletest library
|
||||||
|
set(BUILD_GMOCK OFF CACHE BOOL "Builds the googlemock subproject")
|
||||||
|
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 bin/SQLiteCpp_tests)
|
||||||
|
|
||||||
|
if (SQLITECPP_BUILD_EXAMPLES)
|
||||||
|
# does the example1 runs successfully?
|
||||||
|
add_test(Example1Run bin/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)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2023 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.
|
|
@ -0,0 +1,462 @@
|
||||||
|
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 lean and easy to use C++ SQLite3 wrapper.
|
||||||
|
|
||||||
|
<!--Keywords: sqlite, sqlite3, C, library, wrapper C++-->
|
||||||
|
<meta name="keywords" content="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-2023 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](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](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 and Github Actions)
|
||||||
|
- Windows 10, and Windows Server 2012 R2, Windows Server 2016, Windows Server 2022 (AppVeyor and Github Actions)
|
||||||
|
- MacOS 10.11 and 11.7 (Travis CI and Github Actions)
|
||||||
|
- Valgrind memcheck tool
|
||||||
|
|
||||||
|
And the following IDEs/Compilers
|
||||||
|
- GCC 4.8.4, 5.3.0, 7.1.1 and latest eg 9.4 (C++11, C++14, C++17)
|
||||||
|
- Clang 5 and 7 (Travis CI)
|
||||||
|
- AppleClang 8, 9 and 13 (Travis CI and Github Actions)
|
||||||
|
- Xcode 8 & 9 (Travis CI)
|
||||||
|
- Visual Studio Community/Entreprise 2022, 2019, 2017, and 2015 (AppVeyor and Github Actions)
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installing SQLiteCpp (vcpkg)
|
||||||
|
|
||||||
|
Alternatively, you can build and install SQLiteCpp using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
|
||||||
|
|
||||||
|
```bash or powershell
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
cd vcpkg
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
./vcpkg integrate install
|
||||||
|
./vcpkg install sqlitecpp
|
||||||
|
```
|
||||||
|
|
||||||
|
The SQLiteCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||||
|
|
||||||
|
|
||||||
|
#### Using SQLiteCpp on a system-wide installation
|
||||||
|
|
||||||
|
If you installed this package to your system, a `SQLiteCppConfig.cmake` file will be generated & installed to your system.
|
||||||
|
This file lets you link against the SQLiteCpp library for use in your Cmake project.
|
||||||
|
|
||||||
|
Here's an example of using this in your CMakeLists.txt
|
||||||
|
```cmake
|
||||||
|
# You can optionally define a minimum version in this call
|
||||||
|
find_package(SQLiteCpp REQUIRED)
|
||||||
|
# For this example, lets say you created an target with add_executable (or add_library) called "my_target"
|
||||||
|
# You can optionally declare PUBLIC or PRIVATE linkage here, depending on your needs.
|
||||||
|
target_link_libraries(my_target PRIVATE SQLiteCpp)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Building with meson
|
||||||
|
|
||||||
|
You can build SQLiteCpp with [meson](https://mesonbuild.com/) using the provided meson project.
|
||||||
|
|
||||||
|
you can install meson using pip: `pip install meson` however you may need to install ninja and other dependencies depending on your platform as an compiler toolchain
|
||||||
|
|
||||||
|
Arch Linux:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# install clang (compiler toolchain) and ninja (recommended build system)
|
||||||
|
sudo pacman -Syu clang ninja
|
||||||
|
# install python and pip (required for meson)
|
||||||
|
sudo pacman -Syu python python-pip
|
||||||
|
# install meson
|
||||||
|
pip install meson
|
||||||
|
```
|
||||||
|
|
||||||
|
Ubuntu:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# install gcc(compiler toolchain) and ninja (recommended build system)
|
||||||
|
sudo apt install build-essential ninja-build
|
||||||
|
# install python and pip (required for meson)
|
||||||
|
sudo apt install python3 python3-pip
|
||||||
|
# install meson
|
||||||
|
pip install meson
|
||||||
|
```
|
||||||
|
|
||||||
|
for example you can build the library using the default options with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# setup the build directory
|
||||||
|
meson setup builddir
|
||||||
|
# build sqlitecpp
|
||||||
|
meson compile -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
or if you wish to build with tests and examples:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# setup the build directory with tests and examples enabled
|
||||||
|
meson setup builddir -DSQLITECPP_BUILD_TESTS=true -DSQLITECPP_BUILD_EXAMPLES=true
|
||||||
|
# build sqlitecpp
|
||||||
|
meson compile -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using SQLiteCpp as subproject in meson
|
||||||
|
|
||||||
|
please check the examples in the examples folder for usage of SQLiteCpp as a subproject in meson, as for the wrap file you can use the one provided in the subprojects folder called `SQLiteCpp.wrap`
|
||||||
|
|
||||||
|
> keep in mind that even that this wrap should be up to date, it is recommended to check the latest version of SQLiteCpp and update the wrap file accordingly
|
||||||
|
|
||||||
|
#### System SQLiteCpp support under meson
|
||||||
|
|
||||||
|
additionally meson can detect and use the bundled sqlitecpp library included on your system if available, for example with vcpkg you would need to set the `PKG_CONFIG_PATH` environment variable to the vcpkg directory before running meson setup, and if applies the corresponding `PKG-CONFIG` executable to the path.
|
||||||
|
|
||||||
|
#### Building the Doxygen/html documentation
|
||||||
|
|
||||||
|
Make sure you have Dogygen installed and configure CMake using the `SQLITECPP_RUN_DOXYGEN=ON` flag:
|
||||||
|
|
||||||
|
```
|
||||||
|
cmake -DSQLITECPP_RUN_DOXYGEN=ON <MORE ARGUMENTS_HERE>
|
||||||
|
```
|
||||||
|
|
||||||
|
and then execute the `SQLiteCpp_doxygen` target (or build all targets, see above).
|
||||||
|
The documentation will be generated in the 'doc' subfolder of the source tree.
|
||||||
|
|
||||||
|
#### 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](http://www.sqlite.org/compile.html#enable_column_metadata)).
|
||||||
|
You can:
|
||||||
|
- either recompile the sqlite3 library provided by your distribution yourself (seek help online)
|
||||||
|
- or turn off the `option(SQLITE_ENABLE_COLUMN_METADATA "Enable Column::getColumnOriginName(). Require support from sqlite3 library." ON)` in [CMakeFiles.txt](CMakeFiles.txt) (or other build system scripts)
|
||||||
|
- or turn on the `option(SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ON)` in [CMakeFiles.txt](CMakeFiles.txt)
|
||||||
|
|
||||||
|
### 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://travis-ci.org/SRombauts/SQLiteCpp)
|
||||||
|
- [https://ci.appveyor.com/project/SbastienRombauts/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](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").
|
||||||
|
|
||||||
|
### Valgrind memcheck
|
||||||
|
|
||||||
|
Run valgrind to search for memory leaks in your application, the SQLiteCpp wrapper, or the sqlite3 library.
|
||||||
|
Execute the following command under Unix like OS (Linux, MacOS or WSL2/Ubuntu under Windows Subsystem for Linux):
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose build/SQLiteCpp_example1
|
||||||
|
```
|
||||||
|
|
||||||
|
or uncoment the line at the end of [build.sh](build.sh)
|
||||||
|
|
||||||
|
## 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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The third sample shows how to manage a prepared statement with a transaction:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
|
||||||
|
|
||||||
|
db.exec("DROP TABLE IF EXISTS test");
|
||||||
|
|
||||||
|
db.exec("CREATE TABLE test (value INTEGER)");
|
||||||
|
|
||||||
|
// Begin transaction
|
||||||
|
SQLite::Transaction transaction(db);
|
||||||
|
|
||||||
|
// Prepare query
|
||||||
|
SQLite::Statement query {db, "INSERT INTO test (value) VALUES (?)"};
|
||||||
|
|
||||||
|
// Collection to save in database
|
||||||
|
std::vector<int> values{1, 2, 3};
|
||||||
|
|
||||||
|
for (const auto& v: values)
|
||||||
|
{
|
||||||
|
query.bind(1, v);
|
||||||
|
query.exec();
|
||||||
|
query.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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](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](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
|
|
@ -0,0 +1,25 @@
|
||||||
|
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:
|
||||||
|
- 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 ?
|
|
@ -0,0 +1 @@
|
||||||
|
theme: jekyll-theme-slate
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
|
||||||
|
# build format
|
||||||
|
version: "{build}"
|
||||||
|
|
||||||
|
# scripts that run after cloning repository
|
||||||
|
install:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
|
image:
|
||||||
|
# Reduce the number of images to test on AppVeyor, to speed up the build
|
||||||
|
- Visual Studio 2022
|
||||||
|
# - 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%)
|
||||||
|
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2022" (set generator="Visual Studio 17 2022" -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
|
|
@ -0,0 +1,26 @@
|
||||||
|
@REM Copyright (c) 2012-2023 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
|
||||||
|
REM -DPYTHON_EXECUTABLE=D:\workspace\Corvus\UnrealEngine\Engine\Binaries\ThirdParty\Python\Win64\python.exe
|
||||||
|
cmake -DBUILD_SHARED_LIBS=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_RUN_CPPLINT=OFF ..
|
||||||
|
@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 ..
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Copyright (c) 2012-2023 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, depending on CC/CXX envvar)
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSQLITECPP_USE_ASAN=ON -DSQLITECPP_USE_GCOV=OFF -DBUILD_SHARED_LIBS=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON ..
|
||||||
|
|
||||||
|
# Build (ie 'make')
|
||||||
|
cmake --build .
|
||||||
|
|
||||||
|
# Build and run unit-tests (ie 'make test')
|
||||||
|
ctest --output-on-failure
|
||||||
|
|
||||||
|
# And with Valgrind
|
||||||
|
echo "Note: uncomment to run valgrind memcheck"
|
||||||
|
#valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./SQLiteCpp_example1
|
||||||
|
#valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./SQLiteCpp_tests
|
|
@ -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()
|
|
@ -0,0 +1,13 @@
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
if(NOT @SQLITECPP_INTERNAL_SQLITE@)
|
||||||
|
find_dependency(SQLite3 REQUIRED)
|
||||||
|
endif()
|
||||||
|
if(@UNIX@)
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@)
|
||||||
|
find_dependency(Threads REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||||
|
check_required_components("@PROJECT_NAME@")
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,359 @@
|
||||||
|
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 lean and easy to use C++ SQLite3 wrapper.
|
||||||
|
|
||||||
|
<!--Keywords: sqlite, sqlite3, C, library, wrapper C++-->
|
||||||
|
<meta name="keywords" content="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-2023 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](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](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 and Github Actions)
|
||||||
|
- Windows 10, and Windows Server 2012 R2, Windows Server 2016, Windows Server 2022 (AppVeyor and Github Actions)
|
||||||
|
- MacOS 10.11 and 11.7 (Travis CI and Github Actions)
|
||||||
|
- Valgrind memcheck tool
|
||||||
|
|
||||||
|
And the following IDEs/Compilers
|
||||||
|
- GCC 4.8.4, 5.3.0, 7.1.1 and latest eg 9.4 (C++11, C++14, C++17)
|
||||||
|
- Clang 5 and 7 (Travis CI)
|
||||||
|
- AppleClang 8, 9 and 13 (Travis CI and Github Actions)
|
||||||
|
- Xcode 8 & 9 (Travis CI)
|
||||||
|
- Visual Studio Community/Entreprise 2022, 2019, 2017, and 2015 (AppVeyor and Github Actions)
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installing SQLiteCpp (vcpkg)
|
||||||
|
|
||||||
|
Alternatively, you can build and install SQLiteCpp using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
|
||||||
|
|
||||||
|
```bash or powershell
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
cd vcpkg
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
./vcpkg integrate install
|
||||||
|
./vcpkg install sqlitecpp
|
||||||
|
```
|
||||||
|
|
||||||
|
The SQLiteCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||||
|
|
||||||
|
|
||||||
|
#### Using SQLiteCpp on a system-wide installation
|
||||||
|
|
||||||
|
If you installed this package to your system, a `SQLiteCppConfig.cmake` file will be generated & installed to your system.
|
||||||
|
This file lets you link against the SQLiteCpp library for use in your Cmake project.
|
||||||
|
|
||||||
|
Here's an example of using this in your CMakeLists.txt
|
||||||
|
```cmake
|
||||||
|
# You can optionally define a minimum version in this call
|
||||||
|
find_package(SQLiteCpp REQUIRED)
|
||||||
|
# For this example, lets say you created an target with add_executable (or add_library) called "my_target"
|
||||||
|
# You can optionally declare PUBLIC or PRIVATE linkage here, depending on your needs.
|
||||||
|
target_link_libraries(my_target PRIVATE SQLiteCpp)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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](http://www.sqlite.org/compile.html#enable_column_metadata)).
|
||||||
|
You can:
|
||||||
|
- either recompile the sqlite3 library provided by your distribution yourself (seek help online)
|
||||||
|
- or turn off the `option(SQLITE_ENABLE_COLUMN_METADATA "Enable Column::getColumnOriginName(). Require support from sqlite3 library." ON)` in [CMakeFiles.txt](CMakeFiles.txt) (or other build system scripts)
|
||||||
|
- or turn on the `option(SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ON)` in [CMakeFiles.txt](CMakeFiles.txt)
|
||||||
|
|
||||||
|
### 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://travis-ci.org/SRombauts/SQLiteCpp)
|
||||||
|
- [https://ci.appveyor.com/project/SbastienRombauts/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](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").
|
||||||
|
|
||||||
|
### Valgrind memcheck
|
||||||
|
|
||||||
|
Run valgrind to search for memory leaks in your application, the SQLiteCpp wrapper, or the sqlite3 library.
|
||||||
|
Execute the following command under Unix like OS (Linux, MacOS or WSL2/Ubuntu under Windows Subsystem for Linux):
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose build/SQLiteCpp_example1
|
||||||
|
```
|
||||||
|
|
||||||
|
or uncoment the line at the end of [build.sh](build.sh)
|
||||||
|
|
||||||
|
## 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](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](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
|
|
@ -0,0 +1,6 @@
|
||||||
|
examples/example1 - main example
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
SQLiteCpp_Example demonstrates how to use SQLiteCpp as a subdirectory of a CMake project.
|
||||||
|
|
||||||
|
See also examples/example2 on how to use SQLiteCpp as a subdirectory of a CMake project
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,507 @@
|
||||||
|
/**
|
||||||
|
* @file main.cpp
|
||||||
|
* @brief A few short examples in a row.
|
||||||
|
*
|
||||||
|
* Demonstrates how-to use the SQLite++ wrapper
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 path to this example's directory (including the final separator '/' or '\')
|
||||||
|
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 (SQLite logo as a 12581 bytes PNG file)
|
||||||
|
static const int sizeof_logo_png = 12581;
|
||||||
|
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 read-only 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 read-only 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 read-only 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 assignment 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 read-only 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;
|
||||||
|
|
||||||
|
nb = db.getTotalChanges();
|
||||||
|
std::cout << "Nb of total changes since connection: " << 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)");
|
||||||
|
|
||||||
|
// A) insert the logo.png image into the db as a blob
|
||||||
|
FILE* fp = fopen(filename_logo_png.c_str(), "rb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
char buffer[16*1024];
|
||||||
|
static_assert(sizeof(buffer) > sizeof_logo_png, "Buffer is smaller than the size of the file to read");
|
||||||
|
void* blob = &buffer;
|
||||||
|
const int size = static_cast<int>(fread(blob, 1, 16*1024, fp));
|
||||||
|
buffer[size] = '\0';
|
||||||
|
SQLITECPP_ASSERT(size == sizeof_logo_png, "unexpected fread return value"); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
||||||
|
fclose(fp);
|
||||||
|
std::cout << filename_logo_png << " file size=" << size << " bytes\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
|
||||||
|
}
|
||||||
|
|
||||||
|
// B) select the blob from the db and write it to disk into a "out.png" image file
|
||||||
|
fp = fopen("out.png", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
SQLite::Statement query(db, "SELECT * FROM test");
|
||||||
|
std::cout << "SELECT * FROM test :\n";
|
||||||
|
if (query.executeStep())
|
||||||
|
{
|
||||||
|
SQLite::Column colBlob = query.getColumn(1);
|
||||||
|
const void* const blob = colBlob.getBlob();
|
||||||
|
const size_t size = colBlob.getBytes();
|
||||||
|
std::cout << "row (" << query.getColumn(0) << ", size=" << size << " bytes)\n";
|
||||||
|
size_t sizew = fwrite(blob, 1, size, fp);
|
||||||
|
SQLITECPP_ASSERT(sizew == size, "fwrite failed"); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
// NOTE: here the blob is still held in memory, until the Statement is finalized at the end of the scope
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
example1_sources = files(
|
||||||
|
'main.cpp'
|
||||||
|
)
|
||||||
|
|
||||||
|
example1_args = []
|
||||||
|
|
||||||
|
## under windows define _CRT_SECURE_NO_WARNINGS
|
||||||
|
if host_machine.system() == 'windows'
|
||||||
|
example1_args += ['-D_CRT_SECURE_NO_WARNINGS']
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
sqlitecpp_demo1_exe = executable('SQLITECPP_sample_demo1',
|
||||||
|
sqlitecpp_sample1_srcs,
|
||||||
|
dependencies: sqlitecpp_dep,
|
||||||
|
# inherit the default options from sqlitecpp
|
||||||
|
override_options: sqlitecpp_opts,
|
||||||
|
cpp_args: example1_args,)
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Example CMake file for compiling & linking a project with the the SQLiteCpp wrapper
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
#
|
||||||
|
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
# or copy at http://opensource.org/licenses/MIT)
|
||||||
|
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)
|
||||||
|
set(SQLITECPP_USE_STATIC_RUNTIME 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)
|
|
@ -0,0 +1,8 @@
|
||||||
|
examples/example2 - SQLiteCpp_Example
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
SQLiteCpp_Example demonstrates how to use SQLiteCpp as a subdirectory of a CMake project.
|
||||||
|
|
||||||
|
See https://github.com/SRombauts/SQLiteCpp_Example
|
||||||
|
|
||||||
|
See also examples/example1 for the main example on how to use SQLiteCpp in a C++ project
|
|
@ -0,0 +1,21 @@
|
||||||
|
@REM Copyright (c) 2012-2023 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 ..
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Copyright (c) 2012-2023 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 .
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
example2_srcs = files(
|
||||||
|
'src/main.cpp'
|
||||||
|
)
|
||||||
|
|
||||||
|
# if running on windows define _CRT_SECURE_NO_WARNINGS
|
||||||
|
example2_args = []
|
||||||
|
|
||||||
|
|
||||||
|
sqlitecpp_demo2_exe = executable('SQLITECPP_sample_demo2',
|
||||||
|
sqlitecpp_sample2_srcs,
|
||||||
|
dependencies: sqlitecpp_dep,
|
||||||
|
# inherit the default options from sqlitecpp
|
||||||
|
override_options: sqlitecpp_opts,
|
||||||
|
cpp_args: example2_args)
|
|
@ -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-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
subdir('example1')
|
||||||
|
subdir('example2')
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* @file Assertion.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Definition of the SQLITECPP_ASSERT() macro.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <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 int apLine, const char* apFunc,
|
||||||
|
const char* apExpr, const char* apMsg);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define __func__ __FUNCTION__
|
||||||
|
#endif
|
||||||
|
// call the assert handler provided by user code
|
||||||
|
#define SQLITECPP_ASSERT(expression, message) \
|
||||||
|
if (!(expression)) SQLite::assertion_failed(__FILE__, __LINE__, __func__, #expression, message)
|
||||||
|
|
||||||
|
} // namespace SQLite
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// if no assert handler provided by user code, use standard assert()
|
||||||
|
// (note: in release mode assert() does nothing)
|
||||||
|
#define SQLITECPP_ASSERT(expression, message) assert(expression && message)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,131 @@
|
||||||
|
/**
|
||||||
|
* @file Backup.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Backup is used to backup a database file in a safe and online way.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com)
|
||||||
|
* Copyright (c) 2015-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// 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 SQLITECPP_API Backup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initialize a SQLite Backup object.
|
||||||
|
*
|
||||||
|
* Initialize a SQLite Backup object for the source database and destination database.
|
||||||
|
* The database name is "main" for the main database, "temp" for the temporary database,
|
||||||
|
* or the name specified after the AS keyword in an ATTACH statement for an attached database.
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Backup object is NOT constructed.
|
||||||
|
*
|
||||||
|
* @param[in] aDestDatabase Destination database connection
|
||||||
|
* @param[in] apDestDatabaseName Destination database name
|
||||||
|
* @param[in] aSrcDatabase Source database connection
|
||||||
|
* @param[in] apSrcDatabaseName Source database name
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Backup(Database& aDestDatabase,
|
||||||
|
const char* apDestDatabaseName,
|
||||||
|
Database& aSrcDatabase,
|
||||||
|
const char* apSrcDatabaseName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a SQLite Backup object.
|
||||||
|
*
|
||||||
|
* Initialize a SQLite Backup object for source database and destination database.
|
||||||
|
* The database name is "main" for the main database, "temp" for the temporary database,
|
||||||
|
* or the name specified after the AS keyword in an ATTACH statement for an attached database.
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Backup object is NOT constructed.
|
||||||
|
*
|
||||||
|
* @param[in] aDestDatabase Destination database connection
|
||||||
|
* @param[in] aDestDatabaseName Destination database name
|
||||||
|
* @param[in] aSrcDatabase Source database connection
|
||||||
|
* @param[in] aSrcDatabaseName Source database name
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Backup(Database& aDestDatabase,
|
||||||
|
const std::string& aDestDatabaseName,
|
||||||
|
Database& aSrcDatabase,
|
||||||
|
const std::string& aSrcDatabaseName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a SQLite Backup object for main databases.
|
||||||
|
*
|
||||||
|
* Initialize a SQLite Backup object for source database and destination database.
|
||||||
|
* Backup the main databases between the source and the destination.
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Backup object is NOT constructed.
|
||||||
|
*
|
||||||
|
* @param[in] aDestDatabase Destination database connection
|
||||||
|
* @param[in] aSrcDatabase Source database connection
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Backup(Database& aDestDatabase,
|
||||||
|
Database& aSrcDatabase);
|
||||||
|
|
||||||
|
// Backup is non-copyable
|
||||||
|
Backup(const Backup&) = delete;
|
||||||
|
Backup& operator=(const Backup&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a step of backup with a given number of source pages to be copied
|
||||||
|
*
|
||||||
|
* Exception is thrown when SQLITE_IOERR_XXX, SQLITE_NOMEM, or SQLITE_READONLY is returned
|
||||||
|
* in sqlite3_backup_step(). These errors are considered fatal, so there is no point
|
||||||
|
* in retrying the call to executeStep().
|
||||||
|
*
|
||||||
|
* @param[in] aNumPage The number of source pages to be copied, with a negative value meaning all remaining source pages
|
||||||
|
*
|
||||||
|
* @return SQLITE_OK/SQLITE_DONE/SQLITE_BUSY/SQLITE_LOCKED
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
int executeStep(const int aNumPage = -1);
|
||||||
|
|
||||||
|
/// Return the number of source pages still to be backed up as of the most recent call to executeStep().
|
||||||
|
int getRemainingPageCount() const;
|
||||||
|
|
||||||
|
/// Return the total number of pages in the source database as of the most recent call to executeStep().
|
||||||
|
int getTotalPageCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Deleter functor to use with smart pointers to close the SQLite database backup in an RAII fashion.
|
||||||
|
struct Deleter
|
||||||
|
{
|
||||||
|
void operator()(sqlite3_backup* apBackup);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<sqlite3_backup, Deleter> mpSQLiteBackup; ///< Pointer to SQLite Database Backup Handle
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,267 @@
|
||||||
|
/**
|
||||||
|
* @file Column.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#include <SQLiteCpp/Statement.h>
|
||||||
|
#include <SQLiteCpp/Exception.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Forward declarations to avoid inclusion of <sqlite3.h> in a header
|
||||||
|
struct sqlite3_stmt;
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
SQLITECPP_API extern const int INTEGER; ///< SQLITE_INTEGER
|
||||||
|
SQLITECPP_API extern const int FLOAT; ///< SQLITE_FLOAT
|
||||||
|
SQLITECPP_API extern const int TEXT; ///< SQLITE_TEXT
|
||||||
|
SQLITECPP_API extern const int BLOB; ///< SQLITE_BLOB
|
||||||
|
SQLITECPP_API extern const int Null; ///< SQLITE_NULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encapsulation of a Column in a row of the result pointed by the prepared Statement.
|
||||||
|
*
|
||||||
|
* A Column is a particular field of SQLite data in the current row of result
|
||||||
|
* of the Statement : it points to a single cell.
|
||||||
|
*
|
||||||
|
* Its value can be expressed as a text, and, when applicable, as a numeric
|
||||||
|
* (integer or floating point) or a binary blob.
|
||||||
|
*
|
||||||
|
* Thread-safety: a Column object shall not be shared by multiple threads, because :
|
||||||
|
* 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads
|
||||||
|
* provided that no single database connection is used simultaneously in two or more threads."
|
||||||
|
* 2) the SQLite "Serialized" mode is not supported by SQLiteC++,
|
||||||
|
* because of the way it shares the underling SQLite precompiled statement
|
||||||
|
* in a custom shared pointer (See the inner class "Statement::Ptr").
|
||||||
|
*/
|
||||||
|
class SQLITECPP_API Column
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Encapsulation of a Column in a Row of the result.
|
||||||
|
*
|
||||||
|
* @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object.
|
||||||
|
* @param[in] aIndex Index of the column in the row of result, starting at 0
|
||||||
|
*/
|
||||||
|
explicit Column(const Statement::TStatementPtr& aStmtPtr, int aIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the named assigned to this result column (potentially aliased)
|
||||||
|
*
|
||||||
|
* @see getOriginName() to get original column name (not aliased)
|
||||||
|
*/
|
||||||
|
const char* getName() const noexcept;
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the table column name that is the origin of this result column
|
||||||
|
*
|
||||||
|
* Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro :
|
||||||
|
* - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance),
|
||||||
|
* - and also when compiling this wrapper.
|
||||||
|
*/
|
||||||
|
const char* getOriginName() const noexcept;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Return the integer value of the column.
|
||||||
|
int32_t getInt() const noexcept;
|
||||||
|
/// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits).
|
||||||
|
uint32_t getUInt() const noexcept;
|
||||||
|
/// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits).
|
||||||
|
int64_t getInt64() const noexcept;
|
||||||
|
/// Return the double (64bits float) value of the column
|
||||||
|
double getDouble() const noexcept;
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the text value (NULL terminated string) of the column.
|
||||||
|
*
|
||||||
|
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
|
||||||
|
* thus you must copy it before using it beyond its scope (to a std::string for instance).
|
||||||
|
*/
|
||||||
|
const char* getText(const char* apDefaultValue = "") const noexcept;
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the binary blob value of the column.
|
||||||
|
*
|
||||||
|
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
|
||||||
|
* thus you must copy it before using it beyond its scope (to a std::string for instance).
|
||||||
|
*/
|
||||||
|
const void* getBlob() const noexcept;
|
||||||
|
/**
|
||||||
|
* @brief Return a std::string for a TEXT or BLOB column.
|
||||||
|
*
|
||||||
|
* Note this correctly handles strings that contain null bytes.
|
||||||
|
*/
|
||||||
|
std::string getString() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the type of the value of the column using sqlite3_column_type()
|
||||||
|
*
|
||||||
|
* Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null.
|
||||||
|
* This type may change from one row to the next, since
|
||||||
|
* SQLite stores data types dynamically for each value and not per column.
|
||||||
|
* Use Statement::getColumnDeclaredType() to retrieve the declared column type from a SELECT statement.
|
||||||
|
*
|
||||||
|
* @warning After a type conversion (by a call to a getXxx on a Column of a Yyy type),
|
||||||
|
* the value returned by sqlite3_column_type() is undefined.
|
||||||
|
*/
|
||||||
|
int getType() const noexcept;
|
||||||
|
|
||||||
|
/// Test if the column is an integer type value (meaningful only before any conversion)
|
||||||
|
bool isInteger() const noexcept
|
||||||
|
{
|
||||||
|
return (SQLite::INTEGER == getType());
|
||||||
|
}
|
||||||
|
/// Test if the column is a floating point type value (meaningful only before any conversion)
|
||||||
|
bool isFloat() const noexcept
|
||||||
|
{
|
||||||
|
return (SQLite::FLOAT == getType());
|
||||||
|
}
|
||||||
|
/// Test if the column is a text type value (meaningful only before any conversion)
|
||||||
|
bool isText() const noexcept
|
||||||
|
{
|
||||||
|
return (SQLite::TEXT == getType());
|
||||||
|
}
|
||||||
|
/// Test if the column is a binary blob type value (meaningful only before any conversion)
|
||||||
|
bool isBlob() const noexcept
|
||||||
|
{
|
||||||
|
return (SQLite::BLOB == getType());
|
||||||
|
}
|
||||||
|
/// Test if the column is NULL (meaningful only before any conversion)
|
||||||
|
bool isNull() const noexcept
|
||||||
|
{
|
||||||
|
return (SQLite::Null == getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the number of bytes used by the text (or blob) value of the column
|
||||||
|
*
|
||||||
|
* Return either :
|
||||||
|
* - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator
|
||||||
|
* - size in bytes of the string representation of the numerical value (integer or double)
|
||||||
|
* - size in bytes of the binary blob returned by getBlob()
|
||||||
|
* - 0 for a NULL value
|
||||||
|
*/
|
||||||
|
int getBytes() const noexcept;
|
||||||
|
|
||||||
|
/// Alias returning the number of bytes used by the text (or blob) value of the column
|
||||||
|
int size() const noexcept
|
||||||
|
{
|
||||||
|
return getBytes ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inline cast operators to basic types
|
||||||
|
operator char() const
|
||||||
|
{
|
||||||
|
return static_cast<char>(getInt());
|
||||||
|
}
|
||||||
|
operator int8_t() const
|
||||||
|
{
|
||||||
|
return static_cast<int8_t>(getInt());
|
||||||
|
}
|
||||||
|
operator uint8_t() const
|
||||||
|
{
|
||||||
|
return static_cast<uint8_t>(getInt());
|
||||||
|
}
|
||||||
|
operator int16_t() const
|
||||||
|
{
|
||||||
|
return static_cast<int16_t>(getInt());
|
||||||
|
}
|
||||||
|
operator uint16_t() const
|
||||||
|
{
|
||||||
|
return static_cast<uint16_t>(getInt());
|
||||||
|
}
|
||||||
|
operator int32_t() const
|
||||||
|
{
|
||||||
|
return getInt();
|
||||||
|
}
|
||||||
|
operator uint32_t() const
|
||||||
|
{
|
||||||
|
return getUInt();
|
||||||
|
}
|
||||||
|
operator int64_t() const
|
||||||
|
{
|
||||||
|
return getInt64();
|
||||||
|
}
|
||||||
|
operator double() const
|
||||||
|
{
|
||||||
|
return getDouble();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Inline cast operator to char*
|
||||||
|
*
|
||||||
|
* @see getText
|
||||||
|
*/
|
||||||
|
operator const char*() const
|
||||||
|
{
|
||||||
|
return getText();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Inline cast operator to void*
|
||||||
|
*
|
||||||
|
* @see getBlob
|
||||||
|
*/
|
||||||
|
operator const void*() const
|
||||||
|
{
|
||||||
|
return getBlob();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inline cast operator to std::string
|
||||||
|
*
|
||||||
|
* Handles BLOB or TEXT, which may contain null bytes within
|
||||||
|
*
|
||||||
|
* @see getString
|
||||||
|
*/
|
||||||
|
operator std::string() const
|
||||||
|
{
|
||||||
|
return getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object
|
||||||
|
int mIndex; ///< Index of the column in the row of result, starting at 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Standard std::ostream text inserter
|
||||||
|
*
|
||||||
|
* Insert the text value of the Column object, using getText(), into the provided stream.
|
||||||
|
*
|
||||||
|
* @param[in] aStream Stream to use
|
||||||
|
* @param[in] aColumn Column object to insert into the provided stream
|
||||||
|
*
|
||||||
|
* @return Reference to the stream used
|
||||||
|
*/
|
||||||
|
SQLITECPP_API std::ostream& operator<<(std::ostream& aStream, const Column& aColumn);
|
||||||
|
|
||||||
|
#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015
|
||||||
|
|
||||||
|
// Create an instance of T from the first N columns, see declaration in Statement.h for full details
|
||||||
|
template<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(mpPreparedStatement, Is)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,624 @@
|
||||||
|
/**
|
||||||
|
* @file Database.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Management of a SQLite Database Connection.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#include <SQLiteCpp/Column.h>
|
||||||
|
|
||||||
|
// c++17: MinGW GCC version > 8
|
||||||
|
// c++17: Visual Studio 2017 version 15.7
|
||||||
|
// c++17: macOS unless targetting compatibility with macOS < 10.15
|
||||||
|
#ifndef SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||||
|
#if __GNUC__ > 8 // MinGW requires GCC version > 8 for std::filesystem
|
||||||
|
#define SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
|
||||||
|
// macOS clang won't let us touch std::filesystem if we're targetting earlier than 10.15
|
||||||
|
#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_13_0) && \
|
||||||
|
__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
|
||||||
|
// build for iOS clang won't let us touch std::filesystem if we're targetting earlier than iOS 13
|
||||||
|
#else
|
||||||
|
#define SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
#elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
|
||||||
|
#define SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// disable the support if the required header is not available
|
||||||
|
#ifdef __has_include
|
||||||
|
#if !__has_include(<filesystem>)
|
||||||
|
#undef SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
#if !__has_include(<experimental/filesystem>)
|
||||||
|
#undef SQLITECPP_HAVE_EXPERIMENTAL_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++17 allow to disable std::filesystem support
|
||||||
|
#ifdef SQLITECPP_DISABLE_STD_FILESYSTEM
|
||||||
|
#undef SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#undef SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#include <filesystem>
|
||||||
|
#endif // c++17 and a suitable compiler
|
||||||
|
|
||||||
|
#else // SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM
|
||||||
|
|
||||||
|
#define SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
namespace std {
|
||||||
|
namespace filesystem = experimental::filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM
|
||||||
|
|
||||||
|
#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.
|
||||||
|
SQLITECPP_API extern const int OPEN_READONLY; // SQLITE_OPEN_READONLY
|
||||||
|
/// The database is opened for reading and writing if possible, or reading only if the file is write protected
|
||||||
|
/// by the operating system. In either case the database must already exist, otherwise an error is returned.
|
||||||
|
SQLITECPP_API extern const int OPEN_READWRITE; // SQLITE_OPEN_READWRITE
|
||||||
|
/// With OPEN_READWRITE: The database is opened for reading and writing, and is created if it does not already exist.
|
||||||
|
SQLITECPP_API extern const int OPEN_CREATE; // SQLITE_OPEN_CREATE
|
||||||
|
/// Enable URI filename interpretation, parsed according to RFC 3986 (ex. "file:data.db?mode=ro&cache=private")
|
||||||
|
SQLITECPP_API extern const int OPEN_URI; // SQLITE_OPEN_URI
|
||||||
|
/// Open in memory database
|
||||||
|
SQLITECPP_API extern const int OPEN_MEMORY; // SQLITE_OPEN_MEMORY
|
||||||
|
/// Open database in multi-thread threading mode
|
||||||
|
SQLITECPP_API extern const int OPEN_NOMUTEX; // SQLITE_OPEN_NOMUTEX
|
||||||
|
/// Open database with thread-safety in serialized threading mode
|
||||||
|
SQLITECPP_API extern const int OPEN_FULLMUTEX; // SQLITE_OPEN_FULLMUTEX
|
||||||
|
/// Open database with shared cache enabled
|
||||||
|
SQLITECPP_API extern const int OPEN_SHAREDCACHE; // SQLITE_OPEN_SHAREDCACHE
|
||||||
|
/// Open database with shared cache disabled
|
||||||
|
SQLITECPP_API extern const int OPEN_PRIVATECACHE; // SQLITE_OPEN_PRIVATECACHE
|
||||||
|
/// Database filename is not allowed to be a symbolic link (Note: only since SQlite 3.31.0 from 2020-01-22)
|
||||||
|
SQLITECPP_API extern const int OPEN_NOFOLLOW; // SQLITE_OPEN_NOFOLLOW
|
||||||
|
|
||||||
|
|
||||||
|
SQLITECPP_API extern const int OK; ///< SQLITE_OK (used by check() bellow)
|
||||||
|
|
||||||
|
SQLITECPP_API extern const char* const VERSION; ///< SQLITE_VERSION string from sqlite3.h used at compile time
|
||||||
|
SQLITECPP_API extern const int VERSION_NUMBER; ///< SQLITE_VERSION_NUMBER from sqlite3.h used at compile time
|
||||||
|
|
||||||
|
/// Return SQLite version string using runtime call to the compiled library
|
||||||
|
SQLITECPP_API const char* getLibVersion() noexcept;
|
||||||
|
/// Return SQLite version number using runtime call to the compiled library
|
||||||
|
SQLITECPP_API int getLibVersionNumber() noexcept;
|
||||||
|
|
||||||
|
// Public structure for representing all fields contained within the SQLite header.
|
||||||
|
// Official documentation for fields: https://www.sqlite.org/fileformat.html#the_database_header
|
||||||
|
struct Header {
|
||||||
|
unsigned char headerStr[16];
|
||||||
|
unsigned int pageSizeBytes;
|
||||||
|
unsigned char fileFormatWriteVersion;
|
||||||
|
unsigned char fileFormatReadVersion;
|
||||||
|
unsigned char reservedSpaceBytes;
|
||||||
|
unsigned char maxEmbeddedPayloadFrac;
|
||||||
|
unsigned char minEmbeddedPayloadFrac;
|
||||||
|
unsigned char leafPayloadFrac;
|
||||||
|
unsigned long fileChangeCounter;
|
||||||
|
unsigned long databaseSizePages;
|
||||||
|
unsigned long firstFreelistTrunkPage;
|
||||||
|
unsigned long totalFreelistPages;
|
||||||
|
unsigned long schemaCookie;
|
||||||
|
unsigned long schemaFormatNumber;
|
||||||
|
unsigned long defaultPageCacheSizeBytes;
|
||||||
|
unsigned long largestBTreePageNumber;
|
||||||
|
unsigned long databaseTextEncoding;
|
||||||
|
unsigned long userVersion;
|
||||||
|
unsigned long incrementalVaccumMode;
|
||||||
|
unsigned long applicationId;
|
||||||
|
unsigned long versionValidFor;
|
||||||
|
unsigned long sqliteVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RAII management of a SQLite Database Connection.
|
||||||
|
*
|
||||||
|
* A Database object manage a list of all SQLite Statements associated with the
|
||||||
|
* underlying SQLite 3 database connection.
|
||||||
|
*
|
||||||
|
* Resource Acquisition Is Initialization (RAII) means that the Database Connection
|
||||||
|
* is opened in the constructor and closed in the destructor, so that there is
|
||||||
|
* no need to worry about memory management or the validity of the underlying SQLite Connection.
|
||||||
|
*
|
||||||
|
* Thread-safety: a Database object shall not be shared by multiple threads, because :
|
||||||
|
* 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads
|
||||||
|
* provided that no single database connection is used simultaneously in two or more threads."
|
||||||
|
* 2) the SQLite "Serialized" mode is not supported by SQLiteC++,
|
||||||
|
* because of the way it shares the underling SQLite precompiled statement
|
||||||
|
* in a custom shared pointer (See the inner class "Statement::Ptr").
|
||||||
|
*/
|
||||||
|
class SQLITECPP_API Database
|
||||||
|
{
|
||||||
|
friend class Statement; // Give Statement constructor access to the mSQLitePtr Connection Handle
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Open the provided database UTF-8 filename.
|
||||||
|
*
|
||||||
|
* Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior
|
||||||
|
* of the old sqlite3_open() function (READWRITE+CREATE).
|
||||||
|
* This makes sense if you want to use it on a readonly filesystem
|
||||||
|
* or to prevent creation of a void file when a required file is missing.
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Database object is NOT constructed.
|
||||||
|
*
|
||||||
|
* @param[in] apFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter)
|
||||||
|
* @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE...
|
||||||
|
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout())
|
||||||
|
* @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Database(const char* apFilename,
|
||||||
|
const int aFlags = SQLite::OPEN_READONLY,
|
||||||
|
const int aBusyTimeoutMs = 0,
|
||||||
|
const char* apVfs = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open the provided database UTF-8 filename.
|
||||||
|
*
|
||||||
|
* Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior
|
||||||
|
* of the old sqlite3_open() function (READWRITE+CREATE).
|
||||||
|
* This makes sense if you want to use it on a readonly filesystem
|
||||||
|
* or to prevent creation of a void file when a required file is missing.
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Database object is NOT constructed.
|
||||||
|
*
|
||||||
|
* @param[in] aFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter)
|
||||||
|
* @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE...
|
||||||
|
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout())
|
||||||
|
* @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Database(const std::string& aFilename,
|
||||||
|
const int aFlags = SQLite::OPEN_READONLY,
|
||||||
|
const int aBusyTimeoutMs = 0,
|
||||||
|
const std::string& aVfs = "") :
|
||||||
|
Database(aFilename.c_str(), aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open the provided database std::filesystem::path.
|
||||||
|
*
|
||||||
|
* @note This feature requires std=C++17
|
||||||
|
*
|
||||||
|
* Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior
|
||||||
|
* of the old sqlite3_open() function (READWRITE+CREATE).
|
||||||
|
* This makes sense if you want to use it on a readonly filesystem
|
||||||
|
* or to prevent creation of a void file when a required file is missing.
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Database object is NOT constructed.
|
||||||
|
*
|
||||||
|
* @param[in] apFilename Path/uri to the database file ("filename" sqlite3 parameter)
|
||||||
|
* @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE...
|
||||||
|
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout())
|
||||||
|
* @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Database(const std::filesystem::path& apFilename,
|
||||||
|
const int aFlags = SQLite::OPEN_READONLY,
|
||||||
|
const int aBusyTimeoutMs = 0,
|
||||||
|
const std::string& aVfs = "") :
|
||||||
|
Database(reinterpret_cast<const char*>(apFilename.u8string().c_str()),
|
||||||
|
aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // have std::filesystem
|
||||||
|
|
||||||
|
// Database is non-copyable
|
||||||
|
Database(const Database&) = delete;
|
||||||
|
Database& operator=(const Database&) = delete;
|
||||||
|
|
||||||
|
// Database is movable
|
||||||
|
Database(Database&& aDatabase) = default;
|
||||||
|
Database& operator=(Database&& aDatabase) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close the SQLite database connection.
|
||||||
|
*
|
||||||
|
* All SQLite statements must have been finalized before,
|
||||||
|
* so all Statement objects must have been unregistered.
|
||||||
|
*
|
||||||
|
* @warning assert in case of error
|
||||||
|
*/
|
||||||
|
~Database() = default;
|
||||||
|
|
||||||
|
// Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion.
|
||||||
|
struct Deleter
|
||||||
|
{
|
||||||
|
SQLITECPP_API void operator()(sqlite3* apSQLite);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||||
|
*
|
||||||
|
* This is useful in multithreaded program to handle case where a table is locked for writing by a thread.
|
||||||
|
* Any other thread cannot access the table and will receive a SQLITE_BUSY error:
|
||||||
|
* setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error.
|
||||||
|
* Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;".
|
||||||
|
* Default busy timeout is 0ms.
|
||||||
|
*
|
||||||
|
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void setBusyTimeout(const int aBusyTimeoutMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shortcut to execute one or multiple statements without results. Return the number of changes.
|
||||||
|
*
|
||||||
|
* This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||||
|
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||||
|
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||||
|
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||||
|
*
|
||||||
|
* @see Database::tryExec() to execute, returning the sqlite result code
|
||||||
|
* @see Statement::exec() to handle precompiled statements (for better performances) without results
|
||||||
|
* @see Statement::executeStep() to handle "SELECT" queries with results
|
||||||
|
*
|
||||||
|
* @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements
|
||||||
|
*
|
||||||
|
* @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements)
|
||||||
|
* @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
int exec(const char* apQueries);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shortcut to execute one or multiple statements without results.
|
||||||
|
*
|
||||||
|
* This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||||
|
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||||
|
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||||
|
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||||
|
*
|
||||||
|
* @see Database::tryExec() to execute, returning the sqlite result code
|
||||||
|
* @see Statement::exec() to handle precompiled statements (for better performances) without results
|
||||||
|
* @see Statement::executeStep() to handle "SELECT" queries with results
|
||||||
|
*
|
||||||
|
* @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements
|
||||||
|
*
|
||||||
|
* @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements)
|
||||||
|
* @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
int exec(const std::string& aQueries)
|
||||||
|
{
|
||||||
|
return exec(aQueries.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to execute one or multiple statements, returning the sqlite result code.
|
||||||
|
*
|
||||||
|
* This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||||
|
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||||
|
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||||
|
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||||
|
*
|
||||||
|
* @see exec() to execute, returning number of rows modified
|
||||||
|
*
|
||||||
|
* @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements
|
||||||
|
*
|
||||||
|
* @return the sqlite result code.
|
||||||
|
*/
|
||||||
|
int tryExec(const char* apQueries) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to execute one or multiple statements, returning the sqlite result code.
|
||||||
|
*
|
||||||
|
* This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||||
|
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||||
|
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||||
|
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||||
|
*
|
||||||
|
* @see exec() to execute, returning number of rows modified
|
||||||
|
*
|
||||||
|
* @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements
|
||||||
|
*
|
||||||
|
* @return the sqlite result code.
|
||||||
|
*/
|
||||||
|
int tryExec(const std::string& aQueries) noexcept
|
||||||
|
{
|
||||||
|
return tryExec(aQueries.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shortcut to execute a one step query and fetch the first column of the result.
|
||||||
|
*
|
||||||
|
* This is a shortcut to execute a simple statement with a single result.
|
||||||
|
* This should be used only for non reusable queries (else you should use a Statement with bind()).
|
||||||
|
* This should be used only for queries with expected results (else an exception is fired).
|
||||||
|
*
|
||||||
|
* @warning WARNING: Be very careful with this dangerous method: you have to
|
||||||
|
* make a COPY OF THE result, else it will be destroy before the next line
|
||||||
|
* (when the underlying temporary Statement and Column objects are destroyed)
|
||||||
|
*
|
||||||
|
* @see also Statement class for handling queries with multiple results
|
||||||
|
*
|
||||||
|
* @param[in] apQuery an UTF-8 encoded SQL query
|
||||||
|
*
|
||||||
|
* @return a temporary Column object with the first column of result.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Column execAndGet(const char* apQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shortcut to execute a one step query and fetch the first column of the result.
|
||||||
|
*
|
||||||
|
* This is a shortcut to execute a simple statement with a single result.
|
||||||
|
* This should be used only for non reusable queries (else you should use a Statement with bind()).
|
||||||
|
* This should be used only for queries with expected results (else an exception is fired).
|
||||||
|
*
|
||||||
|
* @warning WARNING: Be very careful with this dangerous method: you have to
|
||||||
|
* make a COPY OF THE result, else it will be destroy before the next line
|
||||||
|
* (when the underlying temporary Statement and Column objects are destroyed)
|
||||||
|
*
|
||||||
|
* @see also Statement class for handling queries with multiple results
|
||||||
|
*
|
||||||
|
* @param[in] aQuery an UTF-8 encoded SQL query
|
||||||
|
*
|
||||||
|
* @return a temporary Column object with the first column of result.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
Column execAndGet(const std::string& aQuery)
|
||||||
|
{
|
||||||
|
return execAndGet(aQuery.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shortcut to test if a table exists.
|
||||||
|
*
|
||||||
|
* Table names are case sensitive.
|
||||||
|
*
|
||||||
|
* @param[in] apTableName an UTF-8 encoded case sensitive Table name
|
||||||
|
*
|
||||||
|
* @return true if the table exists.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
bool tableExists(const char* apTableName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shortcut to test if a table exists.
|
||||||
|
*
|
||||||
|
* Table names are case sensitive.
|
||||||
|
*
|
||||||
|
* @param[in] aTableName an UTF-8 encoded case sensitive Table name
|
||||||
|
*
|
||||||
|
* @return true if the table exists.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
bool tableExists(const std::string& aTableName) const
|
||||||
|
{
|
||||||
|
return tableExists(aTableName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the rowid of the most recent successful INSERT into the database from the current connection.
|
||||||
|
*
|
||||||
|
* Each entry in an SQLite table always has a unique 64-bit signed integer key called the rowid.
|
||||||
|
* If the table has a column of type INTEGER PRIMARY KEY, then it is an alias for the rowid.
|
||||||
|
*
|
||||||
|
* @return Rowid of the most recent successful INSERT into the database, or 0 if there was none.
|
||||||
|
*/
|
||||||
|
int64_t getLastInsertRowid() const noexcept;
|
||||||
|
|
||||||
|
/// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
|
||||||
|
int getChanges() const noexcept;
|
||||||
|
|
||||||
|
/// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection (not DROP table).
|
||||||
|
int getTotalChanges() const noexcept;
|
||||||
|
|
||||||
|
/// Return the numeric result code for the most recent failed API call (if any).
|
||||||
|
int getErrorCode() const noexcept;
|
||||||
|
/// Return the extended numeric result code for the most recent failed API call (if any).
|
||||||
|
int getExtendedErrorCode() const noexcept;
|
||||||
|
/// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
|
||||||
|
const char* getErrorMsg() const noexcept;
|
||||||
|
|
||||||
|
/// Return the filename used to open the database.
|
||||||
|
const std::string& getFilename() const noexcept
|
||||||
|
{
|
||||||
|
return mFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return raw pointer to SQLite Database Connection Handle.
|
||||||
|
*
|
||||||
|
* This is often needed to mix this wrapper with other libraries or for advance usage not supported by SQLiteCpp.
|
||||||
|
*/
|
||||||
|
sqlite3* getHandle() const noexcept
|
||||||
|
{
|
||||||
|
return mSQLitePtr.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create or redefine a SQL function or aggregate in the sqlite database.
|
||||||
|
*
|
||||||
|
* This is the equivalent of the sqlite3_create_function_v2 command.
|
||||||
|
* @see http://www.sqlite.org/c3ref/create_function.html
|
||||||
|
*
|
||||||
|
* @note UTF-8 text encoding assumed.
|
||||||
|
*
|
||||||
|
* @param[in] apFuncName Name of the SQL function to be created or redefined
|
||||||
|
* @param[in] aNbArg Number of arguments in the function
|
||||||
|
* @param[in] abDeterministic Optimize for deterministic functions (most are). A random number generator is not.
|
||||||
|
* @param[in] apApp Arbitrary pointer of user data, accessible with sqlite3_user_data().
|
||||||
|
* @param[in] apFunc Pointer to a C-function to implement a scalar SQL function (apStep & apFinal nullptr)
|
||||||
|
* @param[in] apStep Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr)
|
||||||
|
* @param[in] apFinal Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr)
|
||||||
|
* @param[in] apDestroy If not nullptr, then it is the destructor for the application data pointer.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void createFunction(const char* apFuncName,
|
||||||
|
int aNbArg,
|
||||||
|
bool abDeterministic,
|
||||||
|
void* apApp,
|
||||||
|
void (*apFunc)(sqlite3_context *, int, sqlite3_value **),
|
||||||
|
void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr,
|
||||||
|
void (*apFinal)(sqlite3_context *) = nullptr, // NOLINT(readability/casting)
|
||||||
|
void (*apDestroy)(void *) = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a module into the current sqlite database instance.
|
||||||
|
*
|
||||||
|
* This is the equivalent of the sqlite3_load_extension call, but additionally enables
|
||||||
|
* module loading support prior to loading the requested module.
|
||||||
|
*
|
||||||
|
* @see http://www.sqlite.org/c3ref/load_extension.html
|
||||||
|
*
|
||||||
|
* @note UTF-8 text encoding assumed.
|
||||||
|
*
|
||||||
|
* @param[in] apExtensionName Name of the shared library containing extension
|
||||||
|
* @param[in] apEntryPointName Name of the entry point (nullptr to let sqlite work it out)
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void loadExtension(const char* apExtensionName, const char* apEntryPointName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the key for the current sqlite database instance.
|
||||||
|
*
|
||||||
|
* This is the equivalent of the sqlite3_key call and should thus be called
|
||||||
|
* directly after opening the database.
|
||||||
|
* Open encrypted database -> call db.key("secret") -> database ready
|
||||||
|
*
|
||||||
|
* @param[in] aKey Key to decode/encode the database
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void key(const std::string& aKey) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the key for the current sqlite database instance.
|
||||||
|
*
|
||||||
|
* This is the equivalent of the sqlite3_rekey call and should thus be called
|
||||||
|
* after the database has been opened with a valid key. To decrypt a
|
||||||
|
* database, call this method with an empty string.
|
||||||
|
* Open normal database -> call db.rekey("secret") -> encrypted database, database ready
|
||||||
|
* Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready
|
||||||
|
* Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready
|
||||||
|
*
|
||||||
|
* @param[in] aNewKey New key to encode the database
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void rekey(const std::string& aNewKey) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test if a file contains an unencrypted database.
|
||||||
|
*
|
||||||
|
* This is a simple test that reads the first bytes of a database file and
|
||||||
|
* compares them to the standard header for unencrypted databases. If the
|
||||||
|
* header does not match the standard string, we assume that we have an
|
||||||
|
* encrypted file.
|
||||||
|
*
|
||||||
|
* @param[in] aFilename path/uri to a file
|
||||||
|
*
|
||||||
|
* @return true if the database has the standard header.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
static bool isUnencrypted(const std::string& aFilename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse SQLite header data from a database file.
|
||||||
|
*
|
||||||
|
* This function reads the first 100 bytes of a SQLite database file
|
||||||
|
* and reconstructs groups of individual bytes into the associated fields
|
||||||
|
* in a Header object.
|
||||||
|
*
|
||||||
|
* @param[in] aFilename path/uri to a file
|
||||||
|
*
|
||||||
|
* @return Header object containing file data
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
static Header getHeaderInfo(const std::string& aFilename);
|
||||||
|
|
||||||
|
// Parse SQLite header data from a database file.
|
||||||
|
Header getHeaderInfo() const
|
||||||
|
{
|
||||||
|
return getHeaderInfo(mFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BackupType for the backup() method
|
||||||
|
*/
|
||||||
|
enum BackupType { Save, Load };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load or save the database content.
|
||||||
|
*
|
||||||
|
* This function is used to load the contents of a database file on disk
|
||||||
|
* into the "main" database of open database connection, or to save the current
|
||||||
|
* contents of the database into a database file on disk.
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void backup(const char* apFilename, BackupType aType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message
|
||||||
|
*/
|
||||||
|
void check(const int aRet) const
|
||||||
|
{
|
||||||
|
if (SQLite::OK != aRet)
|
||||||
|
{
|
||||||
|
throw SQLite::Exception(getHandle(), aRet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TODO: perhaps switch to having Statement sharing a pointer to the Connexion
|
||||||
|
std::unique_ptr<sqlite3, Deleter> mSQLitePtr; ///< Pointer to SQLite Database Connection Handle
|
||||||
|
std::string mFilename; ///< UTF-8 filename used to open the database
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,91 @@
|
||||||
|
/**
|
||||||
|
* @file Exception.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Encapsulation of the error message from SQLite3 on a std::runtime_error.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#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 SQLITECPP_API Exception : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Encapsulation of the error message from SQLite3, based on std::runtime_error.
|
||||||
|
*
|
||||||
|
* @param[in] aErrorMessage The string message describing the SQLite error
|
||||||
|
* @param[in] ret Return value from function call that failed.
|
||||||
|
*/
|
||||||
|
Exception(const char* aErrorMessage, int ret);
|
||||||
|
|
||||||
|
Exception(const std::string& aErrorMessage, int ret) :
|
||||||
|
Exception(aErrorMessage.c_str(), ret)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encapsulation of the error message from SQLite3, based on std::runtime_error.
|
||||||
|
*
|
||||||
|
* @param[in] aErrorMessage The string message describing the SQLite error
|
||||||
|
*/
|
||||||
|
explicit Exception(const char* aErrorMessage) :
|
||||||
|
Exception(aErrorMessage, -1) // 0 would be SQLITE_OK, which doesn't make sense
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit Exception(const std::string& aErrorMessage) :
|
||||||
|
Exception(aErrorMessage.c_str(), -1) // 0 would be SQLITE_OK, which doesn't make sense
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encapsulation of the error message from SQLite3, based on std::runtime_error.
|
||||||
|
*
|
||||||
|
* @param[in] apSQLite The SQLite object, to obtain detailed error messages from.
|
||||||
|
*/
|
||||||
|
explicit Exception(sqlite3* apSQLite);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encapsulation of the error message from SQLite3, based on std::runtime_error.
|
||||||
|
*
|
||||||
|
* @param[in] apSQLite The SQLite object, to obtain detailed error messages from.
|
||||||
|
* @param[in] ret Return value from function call that failed.
|
||||||
|
*/
|
||||||
|
Exception(sqlite3* apSQLite, int ret);
|
||||||
|
|
||||||
|
/// Return the result code (if any, otherwise -1).
|
||||||
|
int getErrorCode() const noexcept
|
||||||
|
{
|
||||||
|
return mErrcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the extended numeric result code (if any, otherwise -1).
|
||||||
|
int getExtendedErrorCode() const noexcept
|
||||||
|
{
|
||||||
|
return mExtendedErrcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a string, solely based on the error code
|
||||||
|
const char* getErrorStr() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int mErrcode; ///< Error code value
|
||||||
|
int mExtendedErrcode; ///< Detailed error code if any
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,91 @@
|
||||||
|
/**
|
||||||
|
* @file ExecuteMany.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Convenience function to execute a Statement with multiple Parameter sets
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de)
|
||||||
|
* Copyright (c) 2019-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
|
||||||
|
|
||||||
|
#include <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
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* @file SQLiteCpp.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files.
|
||||||
|
*
|
||||||
|
* Include this main header file in your project to gain access to all functionality provided by the wrapper.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @defgroup SQLiteCpp SQLiteC++
|
||||||
|
* @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
// Include useful headers of SQLiteC++
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#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.03.01" // 3.3.1
|
||||||
|
#define SQLITECPP_VERSION_NUMBER 3003001 // 3.3.1
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* @file SQLiteCppExport.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief File with macros needed in the generation of Windows DLLs
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #define SQLITECPP_COMPILE_DLL to compile a DLL under Windows
|
||||||
|
* #define SQLITECPP_EXPORT to export symbols when creating the DLL, otherwise it defaults to importing symbols
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Windows DLL export/import */
|
||||||
|
#if defined(_WIN32)&& !defined(__GNUC__) && defined(SQLITECPP_COMPILE_DLL)
|
||||||
|
#if SQLITECPP_DLL_EXPORT
|
||||||
|
#define SQLITECPP_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define SQLITECPP_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if __GNUC__ >= 4
|
||||||
|
#define SQLITECPP_API __attribute__ ((visibility ("default")))
|
||||||
|
#else
|
||||||
|
#define SQLITECPP_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WIN32) && defined(SQLITECPP_COMPILE_DLL)
|
||||||
|
#pragma warning( disable : 4251 )
|
||||||
|
#pragma warning( disable : 4275 )
|
||||||
|
#endif
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* @file Savepoint.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief A Savepoint is a way to group multiple SQL statements into an atomic
|
||||||
|
* secured operation. Similar to a transaction while allowing child savepoints.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@gmail.com)
|
||||||
|
* Copyright (c) 2020-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or
|
||||||
|
* copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#include <SQLiteCpp/Exception.h>
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class Database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RAII encapsulation of a SQLite Savepoint.
|
||||||
|
*
|
||||||
|
* SAVEPOINTs are a method of creating Transactions, similar to BEGIN and COMMIT,
|
||||||
|
* except that the SAVEPOINT and RELEASE commands are named and may be nested..
|
||||||
|
*
|
||||||
|
* Resource Acquisition Is Initialization (RAII) means that the Savepoint
|
||||||
|
* begins in the constructor and is rolled back in the destructor (unless committed before), so that there is
|
||||||
|
* no need to worry about memory management or the validity of the underlying SQLite Connection.
|
||||||
|
*
|
||||||
|
* This method also offers big performances improvements compared to
|
||||||
|
* individually executed statements.
|
||||||
|
*
|
||||||
|
* Caveats:
|
||||||
|
*
|
||||||
|
* 1) Calling COMMIT or committing a parent transaction or RELEASE on a parent
|
||||||
|
* savepoint will cause this savepoint to be released.
|
||||||
|
*
|
||||||
|
* 2) Calling ROLLBACK TO or rolling back a parent savepoint will cause this
|
||||||
|
* savepoint to be rolled back.
|
||||||
|
*
|
||||||
|
* 3) This savepoint is not saved to the database until this and all savepoints
|
||||||
|
* or transaction in the savepoint stack have been released or committed.
|
||||||
|
*
|
||||||
|
* See also: https://sqlite.org/lang_savepoint.html
|
||||||
|
*
|
||||||
|
* Thread-safety: a Savepoint object shall not be shared by multiple threads, because:
|
||||||
|
* 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads
|
||||||
|
* provided that no single database connection is used simultaneously in two or more threads."
|
||||||
|
* 2) the SQLite "Serialized" mode is not supported by SQLiteC++,
|
||||||
|
* because of the way it shares the underling SQLite precompiled statement
|
||||||
|
* in a custom shared pointer (See the inner class "Statement::Ptr").
|
||||||
|
*/
|
||||||
|
class SQLITECPP_API Savepoint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Begins the SQLite savepoint
|
||||||
|
*
|
||||||
|
* @param[in] aDatabase the SQLite Database Connection
|
||||||
|
* @param[in] aName the name of the Savepoint
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Savepoint is NOT
|
||||||
|
* initiated.
|
||||||
|
*/
|
||||||
|
Savepoint(Database& aDatabase, const std::string& aName);
|
||||||
|
|
||||||
|
// Savepoint is non-copyable
|
||||||
|
Savepoint(const Savepoint&) = delete;
|
||||||
|
Savepoint& operator=(const Savepoint&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Safely rollback the savepoint if it has not been committed.
|
||||||
|
*/
|
||||||
|
~Savepoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Commit and release the savepoint.
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rollback to the savepoint, but don't release it.
|
||||||
|
*/
|
||||||
|
void rollbackTo();
|
||||||
|
// @deprecated same as rollbackTo();
|
||||||
|
void rollback() { rollbackTo(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Database& mDatabase; ///< Reference to the SQLite Database Connection
|
||||||
|
std::string msName; ///< Name of the Savepoint
|
||||||
|
bool mbReleased = false; ///< True when release has been called
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,711 @@
|
||||||
|
/**
|
||||||
|
* @file Statement.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#include <SQLiteCpp/Exception.h>
|
||||||
|
#include <SQLiteCpp/Utils.h> // SQLITECPP_PURE_FUNC
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Forward declarations to avoid inclusion of <sqlite3.h> in a header
|
||||||
|
struct sqlite3;
|
||||||
|
struct sqlite3_stmt;
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class Database;
|
||||||
|
class Column;
|
||||||
|
|
||||||
|
SQLITECPP_API extern const int OK; ///< SQLITE_OK
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RAII encapsulation of a prepared SQLite Statement.
|
||||||
|
*
|
||||||
|
* A Statement is a compiled SQL query ready to be executed step by step
|
||||||
|
* to provide results one row at a time.
|
||||||
|
*
|
||||||
|
* Resource Acquisition Is Initialization (RAII) means that the Statement
|
||||||
|
* is compiled in the constructor and finalized in the destructor, so that there is
|
||||||
|
* no need to worry about memory management or the validity of the underlying SQLite Statement.
|
||||||
|
*
|
||||||
|
* Thread-safety: a Statement object shall not be shared by multiple threads, because :
|
||||||
|
* 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads
|
||||||
|
* provided that no single database connection is used simultaneously in two or more threads."
|
||||||
|
* 2) the SQLite "Serialized" mode is not supported by SQLiteC++,
|
||||||
|
* because of the way it shares the underling SQLite precompiled statement
|
||||||
|
* in a custom shared pointer (See the inner class "Statement::Ptr").
|
||||||
|
*/
|
||||||
|
class SQLITECPP_API Statement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Compile and register the SQL query for the provided SQLite Database Connection
|
||||||
|
*
|
||||||
|
* @param[in] aDatabase the SQLite Database Connection
|
||||||
|
* @param[in] apQuery an UTF-8 encoded query string
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Statement object is NOT constructed.
|
||||||
|
*/
|
||||||
|
Statement(const Database& aDatabase, const char* apQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compile and register the SQL query for the provided SQLite Database Connection
|
||||||
|
*
|
||||||
|
* @param[in] aDatabase the SQLite Database Connection
|
||||||
|
* @param[in] aQuery an UTF-8 encoded query string
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Statement object is NOT constructed.
|
||||||
|
*/
|
||||||
|
Statement(const Database& aDatabase, const std::string& aQuery) :
|
||||||
|
Statement(aDatabase, aQuery.c_str())
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Statement is non-copyable
|
||||||
|
Statement(const Statement&) = delete;
|
||||||
|
Statement& operator=(const Statement&) = delete;
|
||||||
|
|
||||||
|
// TODO: Change Statement move constructor to default
|
||||||
|
Statement(Statement&& aStatement) noexcept;
|
||||||
|
Statement& operator=(Statement&& aStatement) noexcept = default;
|
||||||
|
|
||||||
|
/// Finalize and unregister the SQL query from the SQLite Database Connection.
|
||||||
|
/// The finalization will be done by the destructor of the last shared pointer
|
||||||
|
~Statement() = default;
|
||||||
|
|
||||||
|
/// Reset the statement to make it ready for a new execution by calling sqlite3_reset.
|
||||||
|
/// Throws an exception on error.
|
||||||
|
/// Call this function before any news calls to bind() if the statement was already executed before.
|
||||||
|
/// Calling reset() does not clear the bindings (see clearBindings()).
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/// Reset the statement. Returns the sqlite result code instead of throwing an exception on error.
|
||||||
|
int tryReset() noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears away all the bindings of a prepared statement.
|
||||||
|
*
|
||||||
|
* Contrary to the intuition of many, reset() does not reset the bindings on a prepared statement.
|
||||||
|
* Use this routine to reset all parameters to NULL.
|
||||||
|
*/
|
||||||
|
void clearBindings(); // throw(SQLite::Exception)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Bind a value to a parameter of the SQL statement,
|
||||||
|
// in the form "?" (unnamed), "?NNN", ":VVV", "@VVV" or "$VVV".
|
||||||
|
//
|
||||||
|
// Can use the parameter index, starting from "1", to the higher NNN value,
|
||||||
|
// or the complete parameter name "?NNN", ":VVV", "@VVV" or "$VVV"
|
||||||
|
// (prefixed with the corresponding sign "?", ":", "@" or "$")
|
||||||
|
//
|
||||||
|
// Note that for text and blob values, the SQLITE_TRANSIENT flag is used,
|
||||||
|
// which tell the sqlite library to make its own copy of the data before the bind() call returns.
|
||||||
|
// This choice is done to prevent any common misuses, like passing a pointer to a
|
||||||
|
// dynamic allocated and temporary variable (a std::string for instance).
|
||||||
|
// This is under-optimized for static data (a static text define in code)
|
||||||
|
// as well as for dynamic allocated buffer which could be transfer to sqlite
|
||||||
|
// instead of being copied.
|
||||||
|
// => if you know what you are doing, use bindNoCopy() instead of bind()
|
||||||
|
|
||||||
|
SQLITECPP_PURE_FUNC
|
||||||
|
int getIndex(const char * const apName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const int32_t aValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const uint32_t aValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const int64_t aValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const double aValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const std::string& aValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const char* apValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex, const void* apValue, const int aSize);
|
||||||
|
/**
|
||||||
|
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1).
|
||||||
|
*
|
||||||
|
* The string can contain null characters as it is binded using its size.
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const int aIndex, const std::string& aValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* Main usage is with null-terminated literal text (aka in code static strings)
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const int aIndex, const char* apValue);
|
||||||
|
/**
|
||||||
|
* @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const int aIndex, const void* apValue, const int aSize);
|
||||||
|
/**
|
||||||
|
* @brief Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @see clearBindings() to set all bound parameters to NULL.
|
||||||
|
*/
|
||||||
|
void bind(const int aIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bind an int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const int32_t aValue)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a 32bits unsigned int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const uint32_t aValue)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a 64bits int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const int64_t aValue)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a double (64bits float) value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const double aValue)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const std::string& aValue)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const char* apValue)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), apValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const char* apName, const void* apValue, const int aSize)
|
||||||
|
{
|
||||||
|
bind(getIndex(apName), apValue, aSize);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* The string can contain null characters as it is binded using its size.
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const char* apName, const std::string& aValue)
|
||||||
|
{
|
||||||
|
bindNoCopy(getIndex(apName), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* Main usage is with null-terminated literal text (aka in code static strings)
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const char* apName, const char* apValue)
|
||||||
|
{
|
||||||
|
bindNoCopy(getIndex(apName), apValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const char* apName, const void* apValue, const int aSize)
|
||||||
|
{
|
||||||
|
bindNoCopy(getIndex(apName), apValue, aSize);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a NULL value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @see clearBindings() to set all bound parameters to NULL.
|
||||||
|
*/
|
||||||
|
void bind(const char* apName) // bind NULL value
|
||||||
|
{
|
||||||
|
bind(getIndex(apName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bind an int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const int32_t aValue)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a 32bits unsigned int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const uint32_t aValue)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a 64bits int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const int64_t aValue)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a double (64bits float) value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const double aValue)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const std::string& aValue)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const char* apValue)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), apValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName, const void* apValue, const int aSize)
|
||||||
|
{
|
||||||
|
bind(aName.c_str(), apValue, aSize);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* The string can contain null characters as it is binded using its size.
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const std::string& aName, const std::string& aValue)
|
||||||
|
{
|
||||||
|
bindNoCopy(aName.c_str(), aValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* Main usage is with null-terminated literal text (aka in code static strings)
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const std::string& aName, const char* apValue)
|
||||||
|
{
|
||||||
|
bindNoCopy(aName.c_str(), apValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
|
||||||
|
*/
|
||||||
|
void bindNoCopy(const std::string& aName, const void* apValue, const int aSize)
|
||||||
|
{
|
||||||
|
bindNoCopy(aName.c_str(), apValue, aSize);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Bind a NULL value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||||
|
*
|
||||||
|
* @see clearBindings() to set all bound parameters to NULL.
|
||||||
|
*/
|
||||||
|
void bind(const std::string& aName) // bind NULL value
|
||||||
|
{
|
||||||
|
bind(aName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a step of the prepared query to fetch one row of results.
|
||||||
|
*
|
||||||
|
* While true is returned, a row of results is available, and can be accessed
|
||||||
|
* through the getColumn() method
|
||||||
|
*
|
||||||
|
* @see exec() execute a one-step prepared statement with no expected result
|
||||||
|
* @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
|
||||||
|
* @see Database::exec() is a shortcut to execute one or multiple statements without results
|
||||||
|
*
|
||||||
|
* @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it
|
||||||
|
* then you have to call executeStep() again to fetch more rows until the query is finished
|
||||||
|
* - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result
|
||||||
|
* (case of a query with no result, or after N rows fetched successfully)
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
bool executeStep();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @see exec() execute a one-step prepared statement with no expected result
|
||||||
|
* @see executeStep() execute a step of the prepared query to fetch one row of results
|
||||||
|
* @see Database::exec() is a shortcut to execute one or multiple statements without results
|
||||||
|
*
|
||||||
|
* @return the sqlite result code.
|
||||||
|
*/
|
||||||
|
int tryExecuteStep() noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a one-step query with no expected result, and return the number of changes.
|
||||||
|
*
|
||||||
|
* This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||||
|
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||||
|
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||||
|
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||||
|
*
|
||||||
|
* It is similar to Database::exec(), but using a precompiled statement, it adds :
|
||||||
|
* - the ability to bind() arguments to it (best way to insert data),
|
||||||
|
* - reusing it allows for better performances (efficient for multiple insertion).
|
||||||
|
*
|
||||||
|
* @see executeStep() execute a step of the prepared query to fetch one row of results
|
||||||
|
* @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
|
||||||
|
* @see Database::exec() is a shortcut to execute one or multiple statements without results
|
||||||
|
*
|
||||||
|
* @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE)
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error, or if row of results are returned while they are not expected!
|
||||||
|
*/
|
||||||
|
int exec();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a copy of the column data specified by its index
|
||||||
|
*
|
||||||
|
* Can be used to access the data of the current row of result when applicable,
|
||||||
|
* while the executeStep() method returns true.
|
||||||
|
*
|
||||||
|
* Throw an exception if there is no row to return a Column from:
|
||||||
|
* - if provided index is out of bound
|
||||||
|
* - before any executeStep() call
|
||||||
|
* - after the last executeStep() returned false
|
||||||
|
* - after a reset() call
|
||||||
|
*
|
||||||
|
* Throw an exception if the specified index is out of the [0, getColumnCount()) range.
|
||||||
|
*
|
||||||
|
* @param[in] aIndex Index of the column, starting at 0
|
||||||
|
*
|
||||||
|
* @note This method is not const, reflecting the fact that the returned Column object will
|
||||||
|
* share the ownership of the underlying sqlite3_stmt.
|
||||||
|
*
|
||||||
|
* @warning The resulting Column object must not be memorized "as-is".
|
||||||
|
* Is is only a wrapper around the current result row, so it is only valid
|
||||||
|
* while the row from the Statement remains valid, that is only until next executeStep() call.
|
||||||
|
* Thus, you should instead extract immediately its data (getInt(), getText()...)
|
||||||
|
* and use or copy this data for any later usage.
|
||||||
|
*/
|
||||||
|
Column getColumn(const int aIndex) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a copy of the column data specified by its column name (less efficient than using an index)
|
||||||
|
*
|
||||||
|
* Can be used to access the data of the current row of result when applicable,
|
||||||
|
* while the executeStep() method returns true.
|
||||||
|
*
|
||||||
|
* Throw an exception if there is no row to return a Column from :
|
||||||
|
* - if provided name is not one of the aliased column names
|
||||||
|
* - before any executeStep() call
|
||||||
|
* - after the last executeStep() returned false
|
||||||
|
* - after a reset() call
|
||||||
|
*
|
||||||
|
* Throw an exception if the specified name is not an on of the aliased name of the columns in the result.
|
||||||
|
*
|
||||||
|
* @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name)
|
||||||
|
*
|
||||||
|
* @note Uses a map of column names to indexes, build on first call.
|
||||||
|
*
|
||||||
|
* @note This method is not const, reflecting the fact that the returned Column object will
|
||||||
|
* share the ownership of the underlying sqlite3_stmt.
|
||||||
|
*
|
||||||
|
* @warning The resulting Column object must not be memorized "as-is".
|
||||||
|
* Is is only a wrapper around the current result row, so it is only valid
|
||||||
|
* while the row from the Statement remains valid, that is only until next executeStep() call.
|
||||||
|
* Thus, you should instead extract immediately its data (getInt(), getText()...)
|
||||||
|
* and use or copy this data for any later usage.
|
||||||
|
*
|
||||||
|
* Throw an exception if the specified name is not one of the aliased name of the columns in the result.
|
||||||
|
*/
|
||||||
|
Column getColumn(const char* apName) const;
|
||||||
|
|
||||||
|
#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015
|
||||||
|
/**
|
||||||
|
* @brief Return an instance of T constructed from copies of the first N columns
|
||||||
|
*
|
||||||
|
* Can be used to access the data of the current row of result when applicable,
|
||||||
|
* while the executeStep() method returns true.
|
||||||
|
*
|
||||||
|
* Throw an exception if there is no row to return a Column from:
|
||||||
|
* - if provided column count is out of bound
|
||||||
|
* - before any executeStep() call
|
||||||
|
* - after the last executeStep() returned false
|
||||||
|
* - after a reset() call
|
||||||
|
*
|
||||||
|
* Throw an exception if the specified column count is out of the [0, getColumnCount()) range.
|
||||||
|
*
|
||||||
|
* @tparam T Object type to construct
|
||||||
|
* @tparam N Number of columns
|
||||||
|
*
|
||||||
|
* @note Requires std=C++14
|
||||||
|
*/
|
||||||
|
template<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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the declared type of the specified result column for a SELECT statement.
|
||||||
|
*
|
||||||
|
* This is the type given at creation of the column and not the actual data type.
|
||||||
|
* SQLite stores data types dynamically for each value and not per column.
|
||||||
|
*
|
||||||
|
* @param[in] aIndex Index of the column in the range [0, getColumnCount()).
|
||||||
|
*
|
||||||
|
* Throw an exception if the type can't be determined because:
|
||||||
|
* - the specified index is out of the [0, getColumnCount()) range
|
||||||
|
* - the statement is not a SELECT query
|
||||||
|
* - the column at aIndex is not a table column but an expression or subquery
|
||||||
|
*/
|
||||||
|
const char * getColumnDeclaredType(const int aIndex) const;
|
||||||
|
|
||||||
|
|
||||||
|
/// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
|
||||||
|
int getChanges() const noexcept;
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// Return the UTF-8 SQL Query.
|
||||||
|
const std::string& getQuery() const
|
||||||
|
{
|
||||||
|
return mQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
|
||||||
|
std::string getExpandedSQL() const;
|
||||||
|
|
||||||
|
/// Return the number of columns in the result set returned by the prepared statement
|
||||||
|
int getColumnCount() const
|
||||||
|
{
|
||||||
|
return mColumnCount;
|
||||||
|
}
|
||||||
|
/// true when a row has been fetched with executeStep()
|
||||||
|
bool hasRow() const
|
||||||
|
{
|
||||||
|
return mbHasRow;
|
||||||
|
}
|
||||||
|
/// true when the last executeStep() had no more row to fetch
|
||||||
|
bool isDone() const
|
||||||
|
{
|
||||||
|
return mbDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of bind parameters in the statement
|
||||||
|
int getBindParameterCount() const noexcept;
|
||||||
|
|
||||||
|
/// Return the numeric result code for the most recent failed API call (if any).
|
||||||
|
int getErrorCode() const noexcept;
|
||||||
|
/// Return the extended numeric result code for the most recent failed API call (if any).
|
||||||
|
int getExtendedErrorCode() const noexcept;
|
||||||
|
/// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
|
||||||
|
const char* getErrorMsg() const noexcept;
|
||||||
|
|
||||||
|
/// Shared pointer to SQLite Prepared Statement Object
|
||||||
|
using TStatementPtr = std::shared_ptr<sqlite3_stmt>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message
|
||||||
|
*
|
||||||
|
* @param[in] aRet SQLite return code to test against the SQLITE_OK expected value
|
||||||
|
*/
|
||||||
|
void check(const int aRet) const
|
||||||
|
{
|
||||||
|
if (SQLite::OK != aRet)
|
||||||
|
{
|
||||||
|
throw SQLite::Exception(mpSQLite, aRet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception.
|
||||||
|
*/
|
||||||
|
void checkRow() const
|
||||||
|
{
|
||||||
|
if (false == mbHasRow)
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("No row to get a column from. executeStep() was not called, or returned false.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if there is a Column index is in the range of columns in the result.
|
||||||
|
*/
|
||||||
|
void checkIndex(const int aIndex) const
|
||||||
|
{
|
||||||
|
if ((aIndex < 0) || (aIndex >= mColumnCount))
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Column index out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepare statement object.
|
||||||
|
*
|
||||||
|
* @return Shared pointer to prepared statement object
|
||||||
|
*/
|
||||||
|
TStatementPtr prepareStatement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a prepared statement object.
|
||||||
|
*
|
||||||
|
* Throw an exception if the statement object was not prepared.
|
||||||
|
* @return raw pointer to Prepared Statement Object
|
||||||
|
*/
|
||||||
|
sqlite3_stmt* getPreparedStatement() const;
|
||||||
|
|
||||||
|
std::string mQuery; //!< UTF-8 SQL Query
|
||||||
|
sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle
|
||||||
|
TStatementPtr mpPreparedStatement; //!< Shared Pointer to the prepared SQLite Statement Object
|
||||||
|
int mColumnCount = 0; //!< Number of columns in the result of the prepared statement
|
||||||
|
bool mbHasRow = false; //!< true when a row has been fetched with executeStep()
|
||||||
|
bool mbDone = false; //!< true when the last executeStep() had no more row to fetch
|
||||||
|
|
||||||
|
/// Map of columns index by name (mutable so getColumnIndex can be const)
|
||||||
|
mutable std::map<std::string, int> mColumnNames;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* @file Transaction.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief A Transaction is way to group multiple SQL statements into an atomic secured operation.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SQLiteCpp/SQLiteCppExport.h>
|
||||||
|
#include <SQLiteCpp/Exception.h>
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class Database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transaction behaviors when opening an SQLite transaction.
|
||||||
|
* Names correspond directly to the behavior.
|
||||||
|
*/
|
||||||
|
enum class TransactionBehavior {
|
||||||
|
DEFERRED,
|
||||||
|
IMMEDIATE,
|
||||||
|
EXCLUSIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RAII encapsulation of a SQLite Transaction.
|
||||||
|
*
|
||||||
|
* A Transaction is a way to group multiple SQL statements into an atomic secured operation;
|
||||||
|
* either it succeeds, with all the changes committed to the database file,
|
||||||
|
* or if it fails, all the changes are rolled back to the initial state.
|
||||||
|
*
|
||||||
|
* Resource Acquisition Is Initialization (RAII) means that the Transaction
|
||||||
|
* begins in the constructor and is rolled back in the destructor (unless committed before), so that there is
|
||||||
|
* no need to worry about memory management or the validity of the underlying SQLite Connection.
|
||||||
|
*
|
||||||
|
* This method also offers big performances improvements compared to individually executed statements.
|
||||||
|
*
|
||||||
|
* Thread-safety: a Transaction object shall not be shared by multiple threads, because :
|
||||||
|
* 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads
|
||||||
|
* provided that no single database connection is used simultaneously in two or more threads."
|
||||||
|
* 2) the SQLite "Serialized" mode is not supported by SQLiteC++,
|
||||||
|
* because of the way it shares the underling SQLite precompiled statement
|
||||||
|
* in a custom shared pointer (See the inner class "Statement::Ptr").
|
||||||
|
*/
|
||||||
|
class SQLITECPP_API Transaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Begins the SQLite transaction using the default transaction behavior.
|
||||||
|
*
|
||||||
|
* @param[in] aDatabase the SQLite Database Connection
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Transaction is NOT initiated.
|
||||||
|
*/
|
||||||
|
explicit Transaction(Database& aDatabase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Begins the SQLite transaction with the specified behavior.
|
||||||
|
*
|
||||||
|
* @param[in] aDatabase the SQLite Database Connection
|
||||||
|
* @param[in] behavior the requested transaction behavior
|
||||||
|
*
|
||||||
|
* Exception is thrown in case of error, then the Transaction is NOT initiated.
|
||||||
|
*/
|
||||||
|
explicit Transaction(Database& aDatabase, TransactionBehavior behavior);
|
||||||
|
|
||||||
|
// Transaction is non-copyable
|
||||||
|
Transaction(const Transaction&) = delete;
|
||||||
|
Transaction& operator=(const Transaction&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Safely rollback the transaction if it has not been committed.
|
||||||
|
*/
|
||||||
|
~Transaction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Commit the transaction.
|
||||||
|
*/
|
||||||
|
void commit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rollback the transaction
|
||||||
|
*/
|
||||||
|
void rollback();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Database& mDatabase; ///< Reference to the SQLite Database Connection
|
||||||
|
bool mbCommited = false; ///< True when commit has been called
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* @file Utils.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Definition of the SQLITECPP_PURE_FUNC macro.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// macro taken from https://github.com/nemequ/hedley/blob/master/hedley.h that was in public domain at this time
|
||||||
|
#if defined(__GNUC__) || defined(__GNUG__) || defined(__clang__) ||\
|
||||||
|
(defined(__INTEL_COMPILER) && __INTEL_COMPILER > 1600) ||\
|
||||||
|
(defined(__ARMCC_VERSION) && __ARMCC_VERSION > 4010000) ||\
|
||||||
|
(\
|
||||||
|
defined(__TI_COMPILER_VERSION__) && (\
|
||||||
|
__TI_COMPILER_VERSION__ > 8003000 ||\
|
||||||
|
(__TI_COMPILER_VERSION__ > 7003000 && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\
|
||||||
|
)\
|
||||||
|
)
|
||||||
|
#if defined(__has_attribute)
|
||||||
|
#if !defined(SQLITECPP_PURE_FUNC) && __has_attribute(pure)
|
||||||
|
#define SQLITECPP_PURE_FUNC __attribute__((pure))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if !defined(SQLITECPP_PURE_FUNC)
|
||||||
|
#define SQLITECPP_PURE_FUNC
|
||||||
|
#endif
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* @file VariadicBind.h
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Convenience function for Statement::bind(...)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Paul Dreik (github@pauldreik.se)
|
||||||
|
* Copyright (c) 2016-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
* Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <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
|
|
@ -0,0 +1,308 @@
|
||||||
|
project(
|
||||||
|
'SQLiteCpp', 'cpp',
|
||||||
|
# SQLiteCpp requires C++11 support
|
||||||
|
default_options: ['cpp_std=c++11', 'warning_level=3'],
|
||||||
|
license: 'MIT',
|
||||||
|
version: '3.3.1',
|
||||||
|
)
|
||||||
|
|
||||||
|
cxx = meson.get_compiler('cpp')
|
||||||
|
|
||||||
|
## at best we might try to test if this code compiles
|
||||||
|
## testing for compilers or platforms is not reliable enough
|
||||||
|
## example: native clang on windows or mingw in windows
|
||||||
|
unix_like_code = '''
|
||||||
|
#if defined(unix) || defined(__unix__) || defined(__unix)
|
||||||
|
// do nothing
|
||||||
|
#else
|
||||||
|
# error "Non Unix-like OS"
|
||||||
|
#endif
|
||||||
|
'''
|
||||||
|
unix_like = cxx.compiles(unix_like_code, name : 'unix like environment')
|
||||||
|
|
||||||
|
mingw_64_env_code = '''
|
||||||
|
#if defined(__MINGW64__)
|
||||||
|
// do nothing
|
||||||
|
#else
|
||||||
|
# error "Non MinGW-W64 environment"
|
||||||
|
#endif
|
||||||
|
'''
|
||||||
|
mingw_64_env = cxx.compiles(mingw_64_env_code, name : 'MinGW-W64 environment')
|
||||||
|
|
||||||
|
thread_dep = dependency('threads')
|
||||||
|
# sqlite3 support
|
||||||
|
sqlite3_dep = dependency(
|
||||||
|
'sqlite3',
|
||||||
|
fallback: ['sqlite3', 'sqlite3_dep']
|
||||||
|
)
|
||||||
|
|
||||||
|
sqlitecpp_incl = [
|
||||||
|
include_directories('include')
|
||||||
|
]
|
||||||
|
sqlitecpp_srcs = files(
|
||||||
|
'src/Backup.cpp',
|
||||||
|
'src/Column.cpp',
|
||||||
|
'src/Database.cpp',
|
||||||
|
'src/Exception.cpp',
|
||||||
|
'src/Savepoint.cpp',
|
||||||
|
'src/Statement.cpp',
|
||||||
|
'src/Transaction.cpp',
|
||||||
|
)
|
||||||
|
sqlitecpp_args = cxx.get_supported_arguments(
|
||||||
|
# included in meson by default
|
||||||
|
# -Wall
|
||||||
|
# included when warning_level=3
|
||||||
|
#'-Wextra',
|
||||||
|
#'-Wpedantic',
|
||||||
|
'-Wswitch-enum',
|
||||||
|
'-Wshadow',
|
||||||
|
'-Wno-long-long',
|
||||||
|
'-Wno-attributes',
|
||||||
|
)
|
||||||
|
sqlitecpp_link = []
|
||||||
|
sqlitecpp_deps = [
|
||||||
|
sqlite3_dep,
|
||||||
|
thread_dep,
|
||||||
|
]
|
||||||
|
## used to override the default sqlitecpp options like cpp standard
|
||||||
|
sqlitecpp_opts = []
|
||||||
|
|
||||||
|
## used to set required macros when using sqlitecpp
|
||||||
|
sqlitecpp_dep_args = []
|
||||||
|
|
||||||
|
## tests
|
||||||
|
|
||||||
|
sqlitecpp_test_srcs = files(
|
||||||
|
'tests/Column_test.cpp',
|
||||||
|
'tests/Database_test.cpp',
|
||||||
|
'tests/Savepoint_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',
|
||||||
|
)
|
||||||
|
sqlitecpp_test_args = []
|
||||||
|
|
||||||
|
## samples
|
||||||
|
|
||||||
|
sqlitecpp_sample1_srcs = files(
|
||||||
|
'examples/example1/main.cpp',
|
||||||
|
)
|
||||||
|
sqlitecpp_sample2_srcs = files(
|
||||||
|
'examples/example2/src/main.cpp',
|
||||||
|
)
|
||||||
|
|
||||||
|
## using MSVC headers requires c++14, if not will show an error on xstddef as:
|
||||||
|
## 'auto' return without trailing return type; deduced return types are a C++14 extension
|
||||||
|
if host_machine.system() == 'windows'
|
||||||
|
message('[WINDOWS] using c++14 standard')
|
||||||
|
sqlitecpp_opts += [
|
||||||
|
'cpp_std=c++14',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
# Options relative to SQLite and SQLiteC++ functions
|
||||||
|
|
||||||
|
if get_option('SQLITE_ENABLE_COLUMN_METADATA')
|
||||||
|
sqlitecpp_args += [
|
||||||
|
'-DSQLITE_ENABLE_COLUMN_METADATA',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('SQLITE_ENABLE_ASSERT_HANDLER')
|
||||||
|
sqlitecpp_args += [
|
||||||
|
'-DSQLITE_ENABLE_ASSERT_HANDLER',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('SQLITE_HAS_CODEC')
|
||||||
|
sqlitecpp_args += [
|
||||||
|
'-DSQLITE_HAS_CODEC',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('SQLITE_USE_LEGACY_STRUCT')
|
||||||
|
sqlitecpp_args += [
|
||||||
|
'-DSQLITE_USE_LEGACY_STRUCT',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
## C++17 disable the support for std::filesystem (by default off)
|
||||||
|
if get_option('SQLITECPP_DISABLE_STD_FILESYSTEM')
|
||||||
|
sqlitecpp_cxx_flags += ['-DSQLITECPP_DISABLE_STD_FILESYSTEM']
|
||||||
|
endif
|
||||||
|
|
||||||
|
## get the user option for the SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL
|
||||||
|
disable_sqlitecpp_expanded_sql = get_option('SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL')
|
||||||
|
|
||||||
|
## Disable the use of sqlite3_expanded_sql (from sqlite3 3.14.0)
|
||||||
|
if disable_sqlitecpp_expanded_sql
|
||||||
|
sqlitecpp_args += ['-DSQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL']
|
||||||
|
endif
|
||||||
|
|
||||||
|
## stack protection hardening
|
||||||
|
if get_option('SQLITECPP_USE_STACK_PROTECTION')
|
||||||
|
## if is on MinGW-W64 give a warning that is not supported
|
||||||
|
if mingw_64_env
|
||||||
|
message('warning: SQLiteCpp does not support stack protection on MinGW-W64')
|
||||||
|
message('warning: this could lead to a crash on the application')
|
||||||
|
message('warning: you can disable this warning by setting SQLITECPP_USE_STACK_PROTECTOR to false')
|
||||||
|
## check if it is supported by the compiler
|
||||||
|
elif cxx.has_argument('-fstack-protector')
|
||||||
|
sqlitecpp_args += ['-fstack-protector']
|
||||||
|
## if not supported give a warning
|
||||||
|
else
|
||||||
|
message('warning: SQLiteCpp does not have stack protection support in this compiler')
|
||||||
|
message('warning: this argument will be ignored')
|
||||||
|
message('warning: you can disable this warning by setting SQLITECPP_USE_STACK_PROTECTOR to false')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
## enable ommit load extension
|
||||||
|
if get_option('SQLITE_OMIT_LOAD_EXTENSION')
|
||||||
|
sqlitecpp_args += ['-DSQLITE_OMIT_LOAD_EXTENSION']
|
||||||
|
## check if running on OSX
|
||||||
|
elif host_machine.system() == 'darwin' and sqlite3_dep.found()
|
||||||
|
## check if sqlite3 is the one bundled with OSX
|
||||||
|
if sqlite3_dep.type_name() != 'internal'
|
||||||
|
message('warning: Detected non-internal SQLite3 in OSX, check if it supports load extension')
|
||||||
|
sqlite3_load_extension_support = cxx.links(
|
||||||
|
'''
|
||||||
|
#include <sqlite3.h>
|
||||||
|
int main() {
|
||||||
|
sqlite3_enable_load_extension(0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
''',
|
||||||
|
name: 'sqlite3_load_extension',
|
||||||
|
dependencies: [sqlite3_dep])
|
||||||
|
if not sqlite3_load_extension_support
|
||||||
|
message('warning: Detected bundled SQLite3 in OSX, but it does not support load extension')
|
||||||
|
message('warning: SQLiteCpp will be built without load extension support')
|
||||||
|
message('warning: You can disable this warning by setting SQLITE_OMIT_LOAD_EXTENSION to false')
|
||||||
|
sqlitecpp_args += ['-DSQLITE_OMIT_LOAD_EXTENSION']
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if unix_like
|
||||||
|
sqlitecpp_args += [
|
||||||
|
# -fPIC is included by default in meson
|
||||||
|
# 'fPIC',
|
||||||
|
]
|
||||||
|
# add dl dependency
|
||||||
|
libdl_dep = cxx.find_library('dl')
|
||||||
|
sqlitecpp_deps += [
|
||||||
|
libdl_dep,
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('b_coverage')
|
||||||
|
# Prevent the compiler from removing the unused inline functions so that they get tracked as "non-covered"
|
||||||
|
sqlitecpp_args += [
|
||||||
|
'-fkeep-inline-functions',
|
||||||
|
'-fkeep-static-functions',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
sqlitecpp_static_args = sqlitecpp_args
|
||||||
|
sqlitecpp_static_dep_args = sqlitecpp_dep_args
|
||||||
|
|
||||||
|
# if windows and shared library
|
||||||
|
if host_machine.system() == 'windows' and get_option('default_library') == 'shared'
|
||||||
|
# compile with SQLITECPP_COMPILE_DLL and SQLITECPP_DLL_EXPORT=1
|
||||||
|
sqlitecpp_args += [
|
||||||
|
'-DSQLITECPP_COMPILE_DLL',
|
||||||
|
'-DSQLITECPP_DLL_EXPORT',
|
||||||
|
]
|
||||||
|
sqlitecpp_dep_args += [
|
||||||
|
# we just need to define SQLITECPP_COMPILE_DLL
|
||||||
|
'-DSQLITECPP_COMPILE_DLL',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
libsqlitecpp = library(
|
||||||
|
'sqlitecpp',
|
||||||
|
sqlitecpp_srcs,
|
||||||
|
include_directories: sqlitecpp_incl,
|
||||||
|
cpp_args: sqlitecpp_args,
|
||||||
|
dependencies: sqlitecpp_deps,
|
||||||
|
# override the default options
|
||||||
|
override_options: sqlitecpp_opts,
|
||||||
|
install: true,
|
||||||
|
# API version for SQLiteCpp shared library.
|
||||||
|
version: '0',)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if get_option('SQLITECPP_BUILD_TESTS')
|
||||||
|
# for the unit tests we need to link against a static version of SQLiteCpp
|
||||||
|
if get_option('default_library') == 'static'
|
||||||
|
# we do not need to recomplile the library
|
||||||
|
libsqlitecpp_static = libsqlitecpp
|
||||||
|
else
|
||||||
|
libsqlitecpp_static = static_library(
|
||||||
|
'sqlitecpp_static',
|
||||||
|
sqlitecpp_srcs,
|
||||||
|
include_directories: sqlitecpp_incl,
|
||||||
|
cpp_args: sqlitecpp_static_args,
|
||||||
|
dependencies: sqlitecpp_deps,
|
||||||
|
# override the default options
|
||||||
|
override_options: sqlitecpp_opts,)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
install_subdir(
|
||||||
|
'include/SQLiteCpp',
|
||||||
|
install_dir: get_option('includedir'))
|
||||||
|
|
||||||
|
sqlitecpp_dep = declare_dependency(
|
||||||
|
include_directories: sqlitecpp_incl,
|
||||||
|
link_with: libsqlitecpp,
|
||||||
|
compile_args: sqlitecpp_dep_args,
|
||||||
|
)
|
||||||
|
if get_option('SQLITECPP_BUILD_TESTS')
|
||||||
|
## make the dependency static so the unit tests can link against it
|
||||||
|
## (mainly for windows as the symbols are not exported by default)
|
||||||
|
sqlitecpp_static_dep = declare_dependency(
|
||||||
|
include_directories: sqlitecpp_incl,
|
||||||
|
link_with: libsqlitecpp_static,
|
||||||
|
compile_args: sqlitecpp_static_dep_args,
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('SQLITECPP_BUILD_TESTS')
|
||||||
|
gtest_dep = dependency(
|
||||||
|
'gtest',
|
||||||
|
main : true,
|
||||||
|
fallback: ['gtest', 'gtest_main_dep'])
|
||||||
|
sqlitecpp_test_dependencies = [
|
||||||
|
gtest_dep,
|
||||||
|
sqlitecpp_dep,
|
||||||
|
sqlite3_dep,
|
||||||
|
]
|
||||||
|
|
||||||
|
testexe = executable('testexe', sqlitecpp_test_srcs,
|
||||||
|
dependencies: sqlitecpp_test_dependencies,
|
||||||
|
cpp_args: sqlitecpp_test_args,
|
||||||
|
# override the default options
|
||||||
|
override_options: sqlitecpp_opts,)
|
||||||
|
|
||||||
|
test_args = []
|
||||||
|
|
||||||
|
test('sqlitecpp unit tests', testexe, args: test_args)
|
||||||
|
endif
|
||||||
|
if get_option('SQLITECPP_BUILD_EXAMPLES')
|
||||||
|
subdir('examples')
|
||||||
|
endif
|
||||||
|
|
||||||
|
pkgconfig = import('pkgconfig')
|
||||||
|
pkgconfig.generate(
|
||||||
|
libsqlitecpp,
|
||||||
|
description: 'a smart and easy to use C++ SQLite3 wrapper.',
|
||||||
|
version: meson.project_version(),
|
||||||
|
)
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Options relative to SQLite and SQLiteC++ functions
|
||||||
|
## 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).
|
||||||
|
option('SQLITE_ENABLE_COLUMN_METADATA', type: 'boolean', value: false, description: 'Enable Column::getColumnOriginName(). Require support from sqlite3 library.')
|
||||||
|
## Enable the user definition of a assertion_failed() handler (default to false, easier to handler for beginners).
|
||||||
|
option('SQLITE_ENABLE_ASSERT_HANDLER', type: 'boolean', value: false, description: 'Enable the user definition of a assertion_failed() handler.')
|
||||||
|
## 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.
|
||||||
|
option('SQLITE_HAS_CODEC', type: 'boolean', value: false, description: 'Enable database encryption API. Not available in the public release of SQLite.')
|
||||||
|
## Force forward declaration of legacy struct sqlite3_value (pre SQLite 3.19)
|
||||||
|
option('SQLITE_USE_LEGACY_STRUCT', type: 'boolean', value: false, description: 'Fallback to forward declaration of legacy struct sqlite3_value (pre SQLite 3.19)')
|
||||||
|
## Enable ommit load extension
|
||||||
|
option('SQLITE_OMIT_LOAD_EXTENSION', type: 'boolean', value: false, description: 'Enable ommit load extension.')
|
||||||
|
## Disable the support for std::filesystem (C++17)
|
||||||
|
option('SQLITECPP_DISABLE_STD_FILESYSTEM', type: 'boolean', value: false, description: 'Disable the support for std::filesystem (C++17)')
|
||||||
|
## Disable the support for sqlite3_expanded_sql (since SQLite 3.14.0)
|
||||||
|
option('SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL', type: 'boolean', value: false, description: 'Disable the support for sqlite3_expanded_sql (since SQLite 3.14.0)')
|
||||||
|
## Stack protection is not supported on MinGW-W64 on Windows, allow this flag to be turned off.
|
||||||
|
option('SQLITECPP_USE_STACK_PROTECTION', type: 'boolean', value: true, description: 'Enable stack protection for MySQL.')
|
||||||
|
## Enable build for the tests of SQLiteC++
|
||||||
|
option('SQLITECPP_BUILD_TESTS', type: 'boolean', value: false, description: 'Build SQLiteC++ unit tests.')
|
||||||
|
## Build the examples of SQLiteC++
|
||||||
|
option('SQLITECPP_BUILD_EXAMPLES', type: 'boolean', value: false, description: 'Build SQLiteC++ examples.')
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>SQLiteCpp</name>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
<description>A smart and easy to use C++ SQLite3 wrapper.</description>
|
||||||
|
|
||||||
|
<maintainer email="sebastien.rombauts@gmail.com">Sébastien Rombauts</maintainer>
|
||||||
|
|
||||||
|
<license>MIT</license>
|
||||||
|
|
||||||
|
<author email="sebastien.rombauts@gmail.com">Sébastien Rombauts</author>
|
||||||
|
|
||||||
|
<buildtool_depend>cmake</buildtool_depend>
|
||||||
|
|
||||||
|
<depend>libsqlite3-dev</depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>cmake</build_type>
|
||||||
|
</export>
|
||||||
|
|
||||||
|
</package>
|
|
@ -0,0 +1,73 @@
|
||||||
|
# CMake file for compiling the sqlite3 static library under Windows (for ease of use)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
#
|
||||||
|
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
# or copy at http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
# add sources of the "sqlite3" static library
|
||||||
|
add_library(sqlite3
|
||||||
|
sqlite3.c
|
||||||
|
sqlite3.h
|
||||||
|
)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
if (BUILD_SHARED_LIBS)
|
||||||
|
add_definitions("-DSQLITE_API=__declspec(dllexport)")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(SQLite::SQLite3 ALIAS sqlite3)
|
||||||
|
|
||||||
|
target_include_directories(sqlite3
|
||||||
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
$<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 (SQLITE_ENABLE_RTREE)
|
||||||
|
# Enable RTree extension when building sqlite3
|
||||||
|
# See more here: https://sqlite.org/rtree.html
|
||||||
|
target_compile_definitions(sqlite3 PUBLIC SQLITE_ENABLE_RTREE)
|
||||||
|
message(STATUS "Compile sqlite3 with SQLITE_ENABLE_RTREE")
|
||||||
|
endif (SQLITE_ENABLE_RTREE)
|
||||||
|
|
||||||
|
if (SQLITE_ENABLE_DBSTAT_VTAB)
|
||||||
|
# Enable DBSTAT extension when building sqlite3
|
||||||
|
# See more here: https://www.sqlite.org/dbstat.html
|
||||||
|
target_compile_definitions(sqlite3 PUBLIC SQLITE_ENABLE_DBSTAT_VTAB)
|
||||||
|
message(STATUS "Compile sqlite3 with SQLITE_ENABLE_DBSTAT_VTAB")
|
||||||
|
endif (SQLITE_ENABLE_DBSTAT_VTAB)
|
||||||
|
|
||||||
|
if (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
|
||||||
|
set_target_properties(sqlite3 PROPERTIES COMPILE_FLAGS "-fPIC")
|
||||||
|
|
||||||
|
# Put each function in its own section to allow the linker garbage
|
||||||
|
# collection to remove unused section and produced a smaller
|
||||||
|
# statically-lined executables.
|
||||||
|
target_compile_options(sqlite3 PRIVATE "-ffunction-sections")
|
||||||
|
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)
|
|
@ -0,0 +1,16 @@
|
||||||
|
sqlite3
|
||||||
|
-------
|
||||||
|
|
||||||
|
Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
|
||||||
|
"sqlite3.c" and "sqlite3.h" files from sqlite-amalgamation-3430000.zip (SQLite 3.43.0 2023-08-24)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#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.reset(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")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.get(), 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() const
|
||||||
|
{
|
||||||
|
return sqlite3_backup_remaining(mpSQLiteBackup.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the number of total source pages to be copied in this backup process
|
||||||
|
int Backup::getTotalPageCount() const
|
||||||
|
{
|
||||||
|
return sqlite3_backup_pagecount(mpSQLiteBackup.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release resource for SQLite database backup
|
||||||
|
void SQLite::Backup::Deleter::operator()(sqlite3_backup* apBackup)
|
||||||
|
{
|
||||||
|
if (apBackup)
|
||||||
|
{
|
||||||
|
sqlite3_backup_finish(apBackup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,123 @@
|
||||||
|
/**
|
||||||
|
* @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-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#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(const Statement::TStatementPtr& aStmtPtr, int aIndex) :
|
||||||
|
mStmtPtr(aStmtPtr),
|
||||||
|
mIndex(aIndex)
|
||||||
|
{
|
||||||
|
if (!aStmtPtr)
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Statement was destroyed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the named assigned to this result column (potentially aliased)
|
||||||
|
const char* Column::getName() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_column_name(mStmtPtr.get(), 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.get(), mIndex);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Return the integer value of the column specified by its index starting at 0
|
||||||
|
int32_t Column::getInt() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_column_int(mStmtPtr.get(), mIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the unsigned integer value of the column specified by its index starting at 0
|
||||||
|
uint32_t Column::getUInt() const noexcept
|
||||||
|
{
|
||||||
|
return static_cast<unsigned>(getInt64());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the 64bits integer value of the column specified by its index starting at 0
|
||||||
|
int64_t Column::getInt64() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_column_int64(mStmtPtr.get(), 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.get(), 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
|
||||||
|
{
|
||||||
|
auto pText = reinterpret_cast<const char*>(sqlite3_column_text(mStmtPtr.get(), 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.get(), 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
|
||||||
|
// however, we need to call sqlite3_column_bytes() to ensure correct format. It's a noop on a BLOB
|
||||||
|
// or a TEXT value with the correct encoding (UTF-8). Otherwise it'll do a conversion to TEXT (UTF-8).
|
||||||
|
(void)sqlite3_column_bytes(mStmtPtr.get(), mIndex);
|
||||||
|
auto data = static_cast<const char *>(sqlite3_column_blob(mStmtPtr.get(), 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.get(), mIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the type of the value of the column
|
||||||
|
int Column::getType() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_column_type(mStmtPtr.get(), mIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of bytes used by the text value of the column
|
||||||
|
int Column::getBytes() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_column_bytes(mStmtPtr.get(), mIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard std::ostream inserter
|
||||||
|
std::ostream& operator<<(std::ostream& aStream, const Column& aColumn)
|
||||||
|
{
|
||||||
|
aStream.write(aColumn.getText(), aColumn.getBytes());
|
||||||
|
return aStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,439 @@
|
||||||
|
/**
|
||||||
|
* @file Database.cpp
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Management of a SQLite Database Connection.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#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 OK = SQLITE_OK;
|
||||||
|
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 OPEN_MEMORY = SQLITE_OPEN_MEMORY;
|
||||||
|
const int OPEN_NOMUTEX = SQLITE_OPEN_NOMUTEX;
|
||||||
|
const int OPEN_FULLMUTEX = SQLITE_OPEN_FULLMUTEX;
|
||||||
|
const int OPEN_SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE;
|
||||||
|
const int OPEN_PRIVATECACHE = SQLITE_OPEN_PRIVATECACHE;
|
||||||
|
// check if sqlite version is >= 3.31.0 and SQLITE_OPEN_NOFOLLOW is defined
|
||||||
|
#if SQLITE_VERSION_NUMBER >= 3031000 && defined(SQLITE_OPEN_NOFOLLOW)
|
||||||
|
const int OPEN_NOFOLLOW = SQLITE_OPEN_NOFOLLOW;
|
||||||
|
#else
|
||||||
|
const int OPEN_NOFOLLOW = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char* const 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||||
|
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...).
|
||||||
|
// Return the number of changes.
|
||||||
|
int Database::exec(const char* apQueries)
|
||||||
|
{
|
||||||
|
const int ret = tryExec(apQueries);
|
||||||
|
check(ret);
|
||||||
|
|
||||||
|
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only)
|
||||||
|
return sqlite3_changes(getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Database::tryExec(const char* apQueries) noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_exec(getHandle(), apQueries, nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) const
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
int64_t Database::getLastInsertRowid() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_last_insert_rowid(getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
|
||||||
|
int Database::getChanges() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_changes(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
|
||||||
|
memcpy(pHeaderStr, pBuf, 16);
|
||||||
|
pHeaderStr[15] = '\0';
|
||||||
|
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 == BackupType::Save ? *this : otherDatabase);
|
||||||
|
Database& dest = (aType == BackupType::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
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* @file Exception.cpp
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief Encapsulation of the error message from SQLite3 on a std::runtime_error.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#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
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* @file Savepoint.cpp
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief A Savepoint is a way to group multiple SQL statements into an atomic
|
||||||
|
* secured operation. Similar to a transaction while allowing child savepoints.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@gmail.com)
|
||||||
|
* Copyright (c) 2020-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or
|
||||||
|
* copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SQLiteCpp/Assertion.h>
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
#include <SQLiteCpp/Savepoint.h>
|
||||||
|
#include <SQLiteCpp/Statement.h>
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
// Begins the SQLite savepoint
|
||||||
|
Savepoint::Savepoint(Database& aDatabase, const std::string& aName)
|
||||||
|
: mDatabase(aDatabase), msName(aName)
|
||||||
|
{
|
||||||
|
// workaround because you cannot bind to SAVEPOINT
|
||||||
|
// escape name for use in query
|
||||||
|
Statement stmt(mDatabase, "SELECT quote(?)");
|
||||||
|
stmt.bind(1, msName);
|
||||||
|
stmt.executeStep();
|
||||||
|
msName = stmt.getColumn(0).getText();
|
||||||
|
|
||||||
|
mDatabase.exec(std::string("SAVEPOINT ") + msName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely rollback the savepoint if it has not been committed.
|
||||||
|
Savepoint::~Savepoint()
|
||||||
|
{
|
||||||
|
if (!mbReleased)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rollback();
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
catch (SQLite::Exception&)
|
||||||
|
{
|
||||||
|
// Never throw an exception in a destructor: error if already released,
|
||||||
|
// but no harm is caused by this.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the savepoint and commit
|
||||||
|
void Savepoint::release()
|
||||||
|
{
|
||||||
|
if (!mbReleased)
|
||||||
|
{
|
||||||
|
mDatabase.exec(std::string("RELEASE SAVEPOINT ") + msName);
|
||||||
|
mbReleased = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Savepoint already released.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rollback to the savepoint, but don't release it
|
||||||
|
void Savepoint::rollbackTo()
|
||||||
|
{
|
||||||
|
if (!mbReleased)
|
||||||
|
{
|
||||||
|
mDatabase.exec(std::string("ROLLBACK TO SAVEPOINT ") + msName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Savepoint already released.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,385 @@
|
||||||
|
/**
|
||||||
|
* @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-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#include <SQLiteCpp/Statement.h>
|
||||||
|
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
#include <SQLiteCpp/Column.h>
|
||||||
|
#include <SQLiteCpp/Assertion.h>
|
||||||
|
#include <SQLiteCpp/Exception.h>
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
// check for if SQLite3 version >= 3.14.0
|
||||||
|
#if SQLITE_VERSION_NUMBER < 3014000
|
||||||
|
#warning "SQLite3 version is less than 3.14.0, so expanded SQL is not available"
|
||||||
|
#warning "To use expanded SQL, please upgrade to SQLite3 version 3.14.0 or later"
|
||||||
|
#warning "If you want to disable this warning, define SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL"
|
||||||
|
#warning "or use the specific project option in your build system"
|
||||||
|
#warning "disabling expanded SQL support"
|
||||||
|
#define SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
Statement::Statement(const Database& aDatabase, const char* apQuery) :
|
||||||
|
mQuery(apQuery),
|
||||||
|
mpSQLite(aDatabase.getHandle()),
|
||||||
|
mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship)
|
||||||
|
{
|
||||||
|
mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Statement(Statement&& aStatement) noexcept :
|
||||||
|
mQuery(std::move(aStatement.mQuery)),
|
||||||
|
mpSQLite(aStatement.mpSQLite),
|
||||||
|
mpPreparedStatement(std::move(aStatement.mpPreparedStatement)),
|
||||||
|
mColumnCount(aStatement.mColumnCount),
|
||||||
|
mbHasRow(aStatement.mbHasRow),
|
||||||
|
mbDone(aStatement.mbDone),
|
||||||
|
mColumnNames(std::move(aStatement.mColumnNames))
|
||||||
|
{
|
||||||
|
aStatement.mpSQLite = nullptr;
|
||||||
|
aStatement.mColumnCount = 0;
|
||||||
|
aStatement.mbHasRow = false;
|
||||||
|
aStatement.mbDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the statement to make it ready for a new execution (see also #clearBindings() below)
|
||||||
|
void Statement::reset()
|
||||||
|
{
|
||||||
|
const int ret = tryReset();
|
||||||
|
check(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Statement::tryReset() noexcept
|
||||||
|
{
|
||||||
|
mbHasRow = false;
|
||||||
|
mbDone = false;
|
||||||
|
return sqlite3_reset(mpPreparedStatement.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears away all the bindings of a prepared statement (can be associated with #reset() above).
|
||||||
|
void Statement::clearBindings()
|
||||||
|
{
|
||||||
|
const int ret = sqlite3_clear_bindings(getPreparedStatement());
|
||||||
|
check(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Statement::getIndex(const char * const apName) const
|
||||||
|
{
|
||||||
|
return sqlite3_bind_parameter_index(getPreparedStatement(), apName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind an 32bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||||
|
void Statement::bind(const int aIndex, const int32_t aValue)
|
||||||
|
{
|
||||||
|
const int ret = sqlite3_bind_int(getPreparedStatement(), 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 uint32_t aValue)
|
||||||
|
{
|
||||||
|
const int ret = sqlite3_bind_int64(getPreparedStatement(), 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 int64_t aValue)
|
||||||
|
{
|
||||||
|
const int ret = sqlite3_bind_int64(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(mpSQLite))
|
||||||
|
{
|
||||||
|
throw SQLite::Exception(mpSQLite, 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, and return the number of changes.
|
||||||
|
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(mpSQLite))
|
||||||
|
{
|
||||||
|
throw SQLite::Exception(mpSQLite, 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(mpSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Statement::tryExecuteStep() noexcept
|
||||||
|
{
|
||||||
|
if (mbDone)
|
||||||
|
{
|
||||||
|
return SQLITE_MISUSE; // Statement needs to be reseted !
|
||||||
|
}
|
||||||
|
|
||||||
|
const int ret = sqlite3_step(mpPreparedStatement.get());
|
||||||
|
if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it
|
||||||
|
{
|
||||||
|
mbHasRow = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mbHasRow = false;
|
||||||
|
mbDone = SQLITE_DONE == ret; // check if the query has finished executing
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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) const
|
||||||
|
{
|
||||||
|
checkRow();
|
||||||
|
checkIndex(aIndex);
|
||||||
|
|
||||||
|
// Share the Statement Object handle with the new Column created
|
||||||
|
return Column(mpPreparedStatement, 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) const
|
||||||
|
{
|
||||||
|
checkRow();
|
||||||
|
const int index = getColumnIndex(apName);
|
||||||
|
|
||||||
|
// Share the Statement Object handle with the new Column created
|
||||||
|
return Column(mpPreparedStatement, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the column is NULL
|
||||||
|
bool Statement::isColumnNull(const int aIndex) const
|
||||||
|
{
|
||||||
|
checkRow();
|
||||||
|
checkIndex(aIndex);
|
||||||
|
return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), aIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Statement::isColumnNull(const char* apName) const
|
||||||
|
{
|
||||||
|
checkRow();
|
||||||
|
const int index = getColumnIndex(apName);
|
||||||
|
return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), i);
|
||||||
|
mColumnNames[pName] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto iIndex = mColumnNames.find(apName);
|
||||||
|
if (iIndex == mColumnNames.end())
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Unknown column name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return iIndex->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Statement::getColumnDeclaredType(const int aIndex) const
|
||||||
|
{
|
||||||
|
checkIndex(aIndex);
|
||||||
|
const char * result = sqlite3_column_decltype(getPreparedStatement(), aIndex);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Could not determine declared column type.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
|
||||||
|
int Statement::getChanges() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_changes(mpSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Statement::getBindParameterCount() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_bind_parameter_count(mpPreparedStatement.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the numeric result code for the most recent failed API call (if any).
|
||||||
|
int Statement::getErrorCode() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_errcode(mpSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the extended numeric result code for the most recent failed API call (if any).
|
||||||
|
int Statement::getExtendedErrorCode() const noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_extended_errcode(mpSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(mpSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
|
||||||
|
std::string Statement::getExpandedSQL() const {
|
||||||
|
#ifdef SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL
|
||||||
|
throw SQLite::Exception("this version of SQLiteCpp does not support expanded SQL");
|
||||||
|
#else
|
||||||
|
char* expanded = sqlite3_expanded_sql(getPreparedStatement());
|
||||||
|
std::string expandedString(expanded);
|
||||||
|
sqlite3_free(expanded);
|
||||||
|
return expandedString;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Prepare SQLite statement object and return shared pointer to this object
|
||||||
|
Statement::TStatementPtr Statement::prepareStatement()
|
||||||
|
{
|
||||||
|
sqlite3_stmt* statement;
|
||||||
|
const int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), static_cast<int>(mQuery.size()), &statement, nullptr);
|
||||||
|
if (SQLITE_OK != ret)
|
||||||
|
{
|
||||||
|
throw SQLite::Exception(mpSQLite, ret);
|
||||||
|
}
|
||||||
|
return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt)
|
||||||
|
{
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return prepered statement object or throw
|
||||||
|
sqlite3_stmt* Statement::getPreparedStatement() const
|
||||||
|
{
|
||||||
|
sqlite3_stmt* ret = mpPreparedStatement.get();
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
throw SQLite::Exception("Statement was not prepared.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,92 @@
|
||||||
|
/**
|
||||||
|
* @file Transaction.cpp
|
||||||
|
* @ingroup SQLiteCpp
|
||||||
|
* @brief A Transaction is way to group multiple SQL statements into an atomic secured operation.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
#include <SQLiteCpp/Transaction.h>
|
||||||
|
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
#include <SQLiteCpp/Assertion.h>
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
namespace SQLite
|
||||||
|
{
|
||||||
|
|
||||||
|
// Begins the SQLite transaction
|
||||||
|
Transaction::Transaction(Database& aDatabase, TransactionBehavior behavior) :
|
||||||
|
mDatabase(aDatabase)
|
||||||
|
{
|
||||||
|
const char *stmt;
|
||||||
|
switch (behavior) {
|
||||||
|
case TransactionBehavior::DEFERRED:
|
||||||
|
stmt = "BEGIN DEFERRED";
|
||||||
|
break;
|
||||||
|
case TransactionBehavior::IMMEDIATE:
|
||||||
|
stmt = "BEGIN IMMEDIATE";
|
||||||
|
break;
|
||||||
|
case TransactionBehavior::EXCLUSIVE:
|
||||||
|
stmt = "BEGIN EXCLUSIVE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw SQLite::Exception("invalid/unknown transaction behavior", SQLITE_ERROR);
|
||||||
|
}
|
||||||
|
mDatabase.exec(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begins the SQLite transaction
|
||||||
|
Transaction::Transaction(Database &aDatabase) :
|
||||||
|
mDatabase(aDatabase)
|
||||||
|
{
|
||||||
|
mDatabase.exec("BEGIN TRANSACTION");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely rollback the transaction if it has not been committed.
|
||||||
|
Transaction::~Transaction()
|
||||||
|
{
|
||||||
|
if (false == mbCommited)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mDatabase.exec("ROLLBACK TRANSACTION");
|
||||||
|
}
|
||||||
|
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 TRANSACTION");
|
||||||
|
mbCommited = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Transaction already committed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rollback the transaction
|
||||||
|
void Transaction::rollback()
|
||||||
|
{
|
||||||
|
if (false == mbCommited)
|
||||||
|
{
|
||||||
|
mDatabase.exec("ROLLBACK TRANSACTION");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw SQLite::Exception("Transaction already committed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SQLite
|
|
@ -0,0 +1,5 @@
|
||||||
|
#ignore everything here
|
||||||
|
*
|
||||||
|
# but not the wrap files and the .gitignore
|
||||||
|
!*.wrap
|
||||||
|
!*.gitignore
|
|
@ -0,0 +1,15 @@
|
||||||
|
[wrap-file]
|
||||||
|
directory = googletest-release-1.12.1
|
||||||
|
source_url = https://github.com/google/googletest/archive/release-1.12.1.tar.gz
|
||||||
|
source_filename = gtest-1.12.1.tar.gz
|
||||||
|
source_hash = 81964fe578e9bd7c94dfdb09c8e4d6e6759e19967e397dbea48d1c10e45d0df2
|
||||||
|
patch_filename = gtest_1.12.1-1_patch.zip
|
||||||
|
patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.12.1-1/get_patch
|
||||||
|
patch_hash = 75143f11e174952bc768699fde3176511fe8e33b25dc6f6347d89e41648e99cf
|
||||||
|
wrapdb_version = 1.12.1-1
|
||||||
|
|
||||||
|
[provide]
|
||||||
|
gtest = gtest_dep
|
||||||
|
gtest_main = gtest_main_dep
|
||||||
|
gmock = gmock_dep
|
||||||
|
gmock_main = gmock_main_dep
|
|
@ -0,0 +1,12 @@
|
||||||
|
[wrap-file]
|
||||||
|
directory = sqlite-amalgamation-3400000
|
||||||
|
source_url = https://sqlite.org/2022/sqlite-amalgamation-3400000.zip
|
||||||
|
source_filename = sqlite-amalgamation-3400000.zip
|
||||||
|
source_hash = 7c23eb51409315738c930a222cf7cd41518ae5823c41e60a81b93a07070ef22a
|
||||||
|
patch_filename = sqlite3_3.40.0-1_patch.zip
|
||||||
|
patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.40.0-1/get_patch
|
||||||
|
patch_hash = 7780fd0908a57ffbf718d88b5930c7b551993352a9156d5a33573f6ea01768c2
|
||||||
|
wrapdb_version = 3.40.0-1
|
||||||
|
|
||||||
|
[provide]
|
||||||
|
sqlite3 = sqlite3_dep
|
|
@ -0,0 +1,8 @@
|
||||||
|
[wrap-file]
|
||||||
|
directory = SQLiteCpp-3.3.0
|
||||||
|
source_url = https://github.com/SRombauts/SQLiteCpp/archive/refs/tags/3.3.0.zip
|
||||||
|
source_filename = sqlitecpp-3.3.0.zip
|
||||||
|
source_hash = 4fa61167484bad7220c4582f2d97475da46f364ff3b1b11078fec18db6bdfe33
|
||||||
|
|
||||||
|
[provide]
|
||||||
|
sqlitecpp = sqlitecpp_dep
|
|
@ -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");
|
||||||
|
}
|
|
@ -0,0 +1,299 @@
|
||||||
|
/**
|
||||||
|
* @file Column_test.cpp
|
||||||
|
* @ingroup tests
|
||||||
|
* @brief Test of a SQLiteCpp Column.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
#include <SQLiteCpp/Statement.h>
|
||||||
|
#include <SQLiteCpp/Column.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
static void test_column_basis(bool utf16)
|
||||||
|
{
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
if (utf16)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(0, db.exec("PRAGMA encoding = 'UTF-16';"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.getChanges());
|
||||||
|
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 int64_t id1 = query.getColumn(0); // operator int64_t()
|
||||||
|
const int32_t id2 = query.getColumn(0); // operator int32_t()
|
||||||
|
const int id3 = query.getColumn(0); // operator int32_t()
|
||||||
|
const int16_t id4 = query.getColumn(0); // operator int32_t()
|
||||||
|
const short id5 = query.getColumn(0); // operator int32_t()
|
||||||
|
const int8_t id6 = query.getColumn(0); // operator int32_t()
|
||||||
|
const char id7 = query.getColumn(0); // operator int32_t()
|
||||||
|
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(1, id7);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
query.reset();
|
||||||
|
query.executeStep();
|
||||||
|
|
||||||
|
// 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 std::string msg1 = query.getColumn(1).getString();
|
||||||
|
const char* ptxt = query.getColumn(1).getText();
|
||||||
|
const std::string msg2 = query.getColumn(1).getText();
|
||||||
|
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 TEXT via text func
|
||||||
|
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).getString().c_str()); // convert to TEXT via string func
|
||||||
|
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_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
|
||||||
|
|
||||||
|
query.reset();
|
||||||
|
query.executeStep();
|
||||||
|
|
||||||
|
// 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, basis)
|
||||||
|
{
|
||||||
|
test_column_basis(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Column, basis16)
|
||||||
|
{
|
||||||
|
test_column_basis(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.getChanges());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Column, shared_ptr)
|
||||||
|
{
|
||||||
|
// 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(R"(INSERT INTO test VALUES (42, "fortytwo"))"));
|
||||||
|
const char* query_str = "SELECT id, msg FROM test";
|
||||||
|
|
||||||
|
std::unique_ptr<SQLite::Statement> query{ new SQLite::Statement(db, query_str) };
|
||||||
|
query->executeStep();
|
||||||
|
|
||||||
|
auto column0 = query->getColumn(0);
|
||||||
|
auto column1 = query->getColumn(1);
|
||||||
|
query.reset();
|
||||||
|
|
||||||
|
EXPECT_EQ(42, column0.getInt());
|
||||||
|
EXPECT_STREQ("fortytwo", column1.getText());
|
||||||
|
|
||||||
|
query.reset(new SQLite::Statement(db, query_str));
|
||||||
|
query->executeStep();
|
||||||
|
column0 = query->getColumn(0);
|
||||||
|
EXPECT_EQ(true, column0.isInteger());
|
||||||
|
query->executeStep(); // query is done
|
||||||
|
|
||||||
|
// Undefined behavior
|
||||||
|
// auto x = column0.getInt();
|
||||||
|
|
||||||
|
query.reset();
|
||||||
|
|
||||||
|
// Undefined behavior
|
||||||
|
// auto x = column0.getInt();
|
||||||
|
// bool isInt = column0.isInteger();
|
||||||
|
|
||||||
|
EXPECT_STREQ("id", column0.getName());
|
||||||
|
}
|
|
@ -0,0 +1,640 @@
|
||||||
|
/**
|
||||||
|
* @file Database_test.cpp
|
||||||
|
* @ingroup tests
|
||||||
|
* @brief Test of a SQLiteCpp Database.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||||
|
*
|
||||||
|
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||||
|
* or copy at http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
|
||||||
|
#include <sqlite3.h> // for SQLITE_ERROR and SQLITE_VERSION_NUMBER
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#ifdef SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
#include <filesystem>
|
||||||
|
#endif // c++17
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#ifdef SQLITECPP_INTERNAL_SQLITE
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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 using a string or a std::filesystem::path if using c++17 and a
|
||||||
|
// compatible compiler
|
||||||
|
#ifdef SQLITECPP_HAVE_STD_FILESYSTEM
|
||||||
|
SQLite::Database db(std::filesystem::path("test.db3"), SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
|
||||||
|
#else
|
||||||
|
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
|
||||||
|
#endif // have std::filesystem
|
||||||
|
|
||||||
|
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.getChanges());
|
||||||
|
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.getChanges());
|
||||||
|
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(1, db.getChanges());
|
||||||
|
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(1, db.getChanges());
|
||||||
|
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(1, db.getChanges());
|
||||||
|
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(1, db.getChanges());
|
||||||
|
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(1, db.getChanges());
|
||||||
|
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(2, db.getChanges());
|
||||||
|
EXPECT_EQ(4, db.getLastInsertRowid());
|
||||||
|
EXPECT_EQ(9, db.getTotalChanges());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Database, tryExec)
|
||||||
|
{
|
||||||
|
// Create a new database
|
||||||
|
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
|
||||||
|
|
||||||
|
// Create a new table with an explicit "id" column aliasing the underlying rowid
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"));
|
||||||
|
EXPECT_EQ(0, db.getChanges());
|
||||||
|
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(SQLite::OK, db.tryExec("INSERT INTO test VALUES (NULL, \"first\")"));
|
||||||
|
EXPECT_EQ(1, db.getChanges());
|
||||||
|
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(SQLite::OK, db.tryExec("INSERT INTO test VALUES (NULL, \"second\")"));
|
||||||
|
EXPECT_EQ(1, db.getChanges());
|
||||||
|
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(SQLite::OK, db.tryExec(insert));
|
||||||
|
EXPECT_EQ(1, db.getChanges());
|
||||||
|
EXPECT_EQ(3, db.getLastInsertRowid());
|
||||||
|
EXPECT_EQ(3, db.getTotalChanges());
|
||||||
|
|
||||||
|
// update the second row : update text value to "second_updated"
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("UPDATE test SET value=\"second-updated\" WHERE id='2'"));
|
||||||
|
EXPECT_EQ(1, db.getChanges());
|
||||||
|
EXPECT_EQ(3, db.getLastInsertRowid()); // last inserted row ID is still 3
|
||||||
|
EXPECT_EQ(4, db.getTotalChanges());
|
||||||
|
|
||||||
|
// delete the third row
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("DELETE FROM test WHERE id='3'"));
|
||||||
|
EXPECT_EQ(1, db.getChanges());
|
||||||
|
EXPECT_EQ(3, db.getLastInsertRowid());
|
||||||
|
EXPECT_EQ(5, db.getTotalChanges());
|
||||||
|
|
||||||
|
// drop the whole table, ie the two remaining columns
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("DROP TABLE IF EXISTS test"));
|
||||||
|
EXPECT_FALSE(db.tableExists("test"));
|
||||||
|
EXPECT_EQ(5, db.getTotalChanges());
|
||||||
|
|
||||||
|
// Re-Create the same table
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"));
|
||||||
|
EXPECT_EQ(5, db.getTotalChanges());
|
||||||
|
|
||||||
|
// insert two rows with two *different* statements => only 1 change, ie. for the second INSERT statement
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("INSERT INTO test VALUES (NULL, \"first\");INSERT INTO test VALUES (NULL, \"second\");"));
|
||||||
|
EXPECT_EQ(1, db.getChanges());
|
||||||
|
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)
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("INSERT INTO test VALUES (NULL, \"third\"), (NULL, \"fourth\");"));
|
||||||
|
EXPECT_EQ(2, db.getChanges());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Database, tryExecError)
|
||||||
|
{
|
||||||
|
// Create a new database
|
||||||
|
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
|
||||||
|
EXPECT_EQ(SQLite::OK, db.getErrorCode());
|
||||||
|
EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode());
|
||||||
|
|
||||||
|
// Insert into nonexistent table: "no such table"
|
||||||
|
EXPECT_EQ(SQLITE_ERROR, db.tryExec("INSERT INTO test VALUES (NULL, \"first\", 3)"));
|
||||||
|
EXPECT_EQ(SQLITE_ERROR, db.getErrorCode());
|
||||||
|
EXPECT_EQ(SQLITE_ERROR, db.getExtendedErrorCode());
|
||||||
|
EXPECT_STREQ("no such table: test", db.getErrorMsg());
|
||||||
|
|
||||||
|
// Create a new table
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("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());
|
||||||
|
|
||||||
|
// Add a row with fewer values than columns in the table: "table test has 3 columns but 2 values were supplied"
|
||||||
|
EXPECT_EQ(SQLITE_ERROR, db.tryExec("INSERT INTO test VALUES (NULL, 3)"));
|
||||||
|
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());
|
||||||
|
|
||||||
|
// Add a row with more values than columns in the table: "table test has 3 columns but 4 values were supplied"
|
||||||
|
EXPECT_EQ(SQLITE_ERROR, db.tryExec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)"));
|
||||||
|
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());
|
||||||
|
|
||||||
|
// Create a first row
|
||||||
|
EXPECT_EQ(SQLite::OK, db.tryExec("INSERT INTO test VALUES (NULL, \"first\", 3)"));
|
||||||
|
EXPECT_EQ(1, db.getLastInsertRowid());
|
||||||
|
|
||||||
|
// Try to insert a new row with the same PRIMARY KEY: "UNIQUE constraint failed: test.id"
|
||||||
|
EXPECT_EQ(SQLITE_CONSTRAINT, db.tryExec("INSERT INTO test VALUES (1, \"impossible\", 456)"));
|
||||||
|
EXPECT_EQ(SQLITE_CONSTRAINT, db.getErrorCode());
|
||||||
|
EXPECT_EQ(SQLITE_CONSTRAINT_PRIMARYKEY, db.getExtendedErrorCode());
|
||||||
|
EXPECT_STREQ("UNIQUE constraint failed: test.id", 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
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* @file Savepoint_test.cpp
|
||||||
|
* @ingroup tests
|
||||||
|
* @brief Test of a SQLite Savepoint.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@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/Exception.h>
|
||||||
|
#include <SQLiteCpp/Savepoint.h>
|
||||||
|
#include <SQLiteCpp/Statement.h>
|
||||||
|
#include <SQLiteCpp/Transaction.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
TEST(Savepoint, commitRollback)
|
||||||
|
{
|
||||||
|
// Create a new database
|
||||||
|
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
||||||
|
EXPECT_EQ(SQLite::OK, db.getErrorCode());
|
||||||
|
|
||||||
|
{
|
||||||
|
// Begin savepoint
|
||||||
|
SQLite::Savepoint savepoint(db, "sp1");
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// release savepoint
|
||||||
|
savepoint.release();
|
||||||
|
|
||||||
|
// Commit again throw an exception
|
||||||
|
EXPECT_THROW(savepoint.release(), SQLite::Exception);
|
||||||
|
EXPECT_THROW(savepoint.rollback(), SQLite::Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto rollback if no release() before the end of scope
|
||||||
|
{
|
||||||
|
// Begin savepoint
|
||||||
|
SQLite::Savepoint savepoint(db, "sp2");
|
||||||
|
|
||||||
|
// 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 savepoint
|
||||||
|
SQLite::Savepoint savepoint(db, "sp3");
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
savepoint.release(); // 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 savepoint
|
||||||
|
SQLite::Savepoint savepoint(db, "sp4");
|
||||||
|
|
||||||
|
// 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
|
||||||
|
savepoint.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);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure transactions with different types are well-formed
|
||||||
|
{
|
||||||
|
for (auto behavior : {
|
||||||
|
SQLite::TransactionBehavior::DEFERRED,
|
||||||
|
SQLite::TransactionBehavior::IMMEDIATE,
|
||||||
|
SQLite::TransactionBehavior::EXCLUSIVE })
|
||||||
|
{
|
||||||
|
SQLite::Transaction transaction(db, behavior);
|
||||||
|
transaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_THROW(SQLite::Transaction(db, static_cast<SQLite::TransactionBehavior>(-1)), 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
|
||||||
|
transaction.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);
|
||||||
|
}
|
|
@ -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
|
254
src/main.cpp
254
src/main.cpp
|
@ -6,21 +6,10 @@
|
||||||
#include <blt/parse/argparse.h>
|
#include <blt/parse/argparse.h>
|
||||||
#include <sqlite_orm/sqlite_orm.h>
|
#include <sqlite_orm/sqlite_orm.h>
|
||||||
#include "blt/std/types.h"
|
#include "blt/std/types.h"
|
||||||
|
#include "blt/std/utility.h"
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
struct message_t
|
namespace sql = sqlite_orm;
|
||||||
{
|
|
||||||
blt::u64 messageID;
|
|
||||||
blt::u64 channelID;
|
|
||||||
blt::u64 userID;
|
|
||||||
std::string content;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct attachment_t
|
|
||||||
{
|
|
||||||
blt::u64 messageID;
|
|
||||||
blt::u64 channelID;
|
|
||||||
std::string url;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct user_info_t
|
struct user_info_t
|
||||||
{
|
{
|
||||||
|
@ -30,82 +19,236 @@ struct user_info_t
|
||||||
std::string server_name;
|
std::string server_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto make_user_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("users",
|
||||||
|
sql::make_column("userID", &user_info_t::userID, sql::primary_key()),
|
||||||
|
sql::make_column("username", &user_info_t::username),
|
||||||
|
sql::make_column("global_nickname", &user_info_t::global_nickname),
|
||||||
|
sql::make_column("server_name", &user_info_t::server_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
using user_table_t = decltype(make_user_table());
|
||||||
|
|
||||||
|
struct user_history_t
|
||||||
|
{
|
||||||
|
blt::u64 userID;
|
||||||
|
blt::u64 time_changed;
|
||||||
|
std::string old_username;
|
||||||
|
std::string old_global_nickname;
|
||||||
|
std::string old_server_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto make_user_history_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("user_history",
|
||||||
|
sql::make_column("userID", &user_history_t::userID, sql::primary_key()),
|
||||||
|
sql::make_column("time_changed", &user_history_t::time_changed, sql::primary_key()),
|
||||||
|
sql::make_column("old_username", &user_history_t::old_username),
|
||||||
|
sql::make_column("old_global_nickname", &user_history_t::old_global_nickname),
|
||||||
|
sql::make_column("old_server_name", &user_history_t::old_server_name),
|
||||||
|
sql::foreign_key(&user_history_t::userID).references(&user_info_t::userID));
|
||||||
|
}
|
||||||
|
|
||||||
|
using user_history_table_t = decltype(make_user_history_table());
|
||||||
|
|
||||||
struct channel_info_t
|
struct channel_info_t
|
||||||
{
|
{
|
||||||
blt::u64 channelID;
|
blt::u64 channelID;
|
||||||
blt::u64 changeTime;
|
|
||||||
std::string channel_name;
|
std::string channel_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct message_edits_t
|
auto make_channel_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("channels",
|
||||||
|
sql::make_column("channelID", &channel_info_t::channelID, sql::primary_key()),
|
||||||
|
sql::make_column("channel_name", &channel_info_t::channel_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
using channel_table_t = decltype(make_channel_table());
|
||||||
|
|
||||||
|
struct channel_history_t
|
||||||
|
{
|
||||||
|
blt::u64 channelID;
|
||||||
|
blt::u64 time_changed;
|
||||||
|
std::string old_channel_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto make_channel_history_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("channel_history",
|
||||||
|
sql::make_column("channelID", &channel_history_t::channelID, sql::primary_key()),
|
||||||
|
sql::make_column("time_changed", &channel_history_t::time_changed, sql::primary_key()),
|
||||||
|
sql::make_column("old_channel_name", &channel_history_t::old_channel_name),
|
||||||
|
sql::foreign_key(&channel_history_t::channelID).references(&channel_info_t::channelID));
|
||||||
|
}
|
||||||
|
|
||||||
|
using channel_history_table_t = decltype(make_channel_history_table());
|
||||||
|
|
||||||
|
struct message_t
|
||||||
{
|
{
|
||||||
blt::u64 messageID;
|
blt::u64 messageID;
|
||||||
blt::u64 channelID;
|
blt::u64 channelID;
|
||||||
blt::u64 userID;
|
blt::u64 userID;
|
||||||
|
std::string content;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto make_message_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("messages",
|
||||||
|
sql::make_column("messageID", &message_t::messageID, sql::primary_key()),
|
||||||
|
sql::make_column("channelID", &message_t::channelID),
|
||||||
|
sql::make_column("userID", &message_t::userID),
|
||||||
|
sql::make_column("content", &message_t::content),
|
||||||
|
sql::foreign_key(&message_t::channelID).references(&channel_info_t::channelID),
|
||||||
|
sql::foreign_key(&message_t::userID).references(&user_info_t::userID));
|
||||||
|
}
|
||||||
|
|
||||||
|
using message_table_t = decltype(make_message_table());
|
||||||
|
|
||||||
|
struct attachment_t
|
||||||
|
{
|
||||||
|
blt::u64 messageID;
|
||||||
|
std::string url;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto make_attachment_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("attachments",
|
||||||
|
sql::make_column("messageID", &attachment_t::messageID, sql::primary_key()),
|
||||||
|
sql::make_column("url", &attachment_t::url, sql::primary_key()),
|
||||||
|
sql::foreign_key(&attachment_t::messageID).references(&message_t::messageID));
|
||||||
|
}
|
||||||
|
|
||||||
|
using attachment_table_t = decltype(make_attachment_table());
|
||||||
|
|
||||||
|
struct message_edits_t
|
||||||
|
{
|
||||||
|
blt::u64 messageID;
|
||||||
|
std::string old_content;
|
||||||
std::string new_content;
|
std::string new_content;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto make_message_edits_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("message_edits",
|
||||||
|
sql::make_column("messageID", &message_edits_t::messageID, sql::primary_key()),
|
||||||
|
sql::make_column("old_content", &message_edits_t::old_content, sql::primary_key()),
|
||||||
|
sql::make_column("new_content", &message_edits_t::new_content, sql::primary_key()),
|
||||||
|
sql::foreign_key(&message_edits_t::messageID).references(&message_t::messageID));
|
||||||
|
}
|
||||||
|
|
||||||
|
using message_edits_table_t = decltype(make_message_edits_table());
|
||||||
|
|
||||||
struct message_deletes_t
|
struct message_deletes_t
|
||||||
{
|
{
|
||||||
blt::u64 messageID;
|
blt::u64 messageID;
|
||||||
blt::u64 channelID;
|
blt::u64 channelID;
|
||||||
|
std::string content;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto make_message_deletes_table()
|
||||||
|
{
|
||||||
|
return sql::make_table("message_deletes",
|
||||||
|
sql::make_column("messageID", &message_deletes_t::messageID, sql::primary_key()),
|
||||||
|
sql::make_column("channelID", &message_deletes_t::channelID, sql::primary_key()),
|
||||||
|
sql::make_column("content", &message_deletes_t::content),
|
||||||
|
sql::foreign_key(&message_deletes_t::messageID).references(&message_t::messageID),
|
||||||
|
sql::foreign_key(&message_deletes_t::channelID).references(&channel_info_t::channelID));
|
||||||
|
}
|
||||||
|
|
||||||
|
using message_deletes_table_t = decltype(make_message_deletes_table());
|
||||||
|
|
||||||
|
auto make_database(std::string path)
|
||||||
|
{
|
||||||
|
return sql::make_storage(std::move(path), make_user_table(), make_user_history_table(), make_channel_table(), make_channel_history_table(),
|
||||||
|
make_message_table(), make_attachment_table(), make_message_edits_table(), make_message_deletes_table());
|
||||||
|
}
|
||||||
|
|
||||||
|
using database_type = decltype(make_database(""));
|
||||||
|
|
||||||
struct db_obj
|
struct db_obj
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
blt::u64 guildID;
|
blt::u64 guildID;
|
||||||
public:
|
database_type db;
|
||||||
std::vector<message_t> messages;
|
|
||||||
std::vector<user_info_t> user_data;
|
|
||||||
std::vector<channel_info_t> channel_data;
|
|
||||||
std::vector<message_edits_t> message_edits;
|
|
||||||
std::vector<message_deletes_t> message_deletes;
|
|
||||||
std::vector<attachment_t> attachments;
|
|
||||||
public:
|
|
||||||
explicit db_obj(blt::u64 guildID): guildID(guildID)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void dump()
|
void ensure_channel_exists()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_for_updates(dpp::cluster& bot)
|
void ensure_user_exists()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit db_obj(blt::u64 guildID, const std::string& path): guildID(guildID), db(make_database(path + "/" + std::to_string(guildID) + "/"))
|
||||||
|
{
|
||||||
|
db.sync_schema();
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const user_info_t& edited)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const user_history_t& edited)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const channel_info_t& channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const channel_history_t& channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const message_t& message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const attachment_t& attachment)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const message_edits_t& edited)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit(const message_deletes_t& deleted)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace db
|
|
||||||
{
|
|
||||||
void sync_databases()
|
|
||||||
{
|
|
||||||
using namespace sqlite_orm;
|
|
||||||
auto storage = make_storage(":memory:",
|
|
||||||
make_table("messages", make_column("id", &message_t::userID, primary_key())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blt::hashmap_t<blt::u64, db_obj> databases;
|
blt::hashmap_t<blt::u64, db_obj> databases;
|
||||||
|
std::string path;
|
||||||
|
|
||||||
db_obj& get(blt::u64 id)
|
db_obj& get(blt::u64 id)
|
||||||
{
|
{
|
||||||
if (databases.find(id) == databases.end())
|
if (databases.find(id) == databases.end())
|
||||||
databases.insert({id, db_obj{id}});
|
databases.insert({id, db_obj{id, path}});
|
||||||
return databases.at(id);
|
return databases.at(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char** argv)
|
int main(int argc, const char** argv)
|
||||||
{
|
{
|
||||||
using namespace sqlite_orm;
|
|
||||||
|
|
||||||
blt::arg_parse parser;
|
blt::arg_parse parser;
|
||||||
parser.addArgument(blt::arg_builder("-t", "--token").setAction(blt::arg_action_t::STORE).setHelp("The discord bot token").build());
|
parser.addArgument(blt::arg_builder("-t", "--token").setAction(blt::arg_action_t::STORE).setHelp("The discord bot token").build());
|
||||||
parser.addArgument(blt::arg_builder("-p", "--path").setAction(blt::arg_action_t::STORE).setHelp("Path to store the archive data").build());
|
parser.addArgument(blt::arg_builder("-p", "--path").setAction(blt::arg_action_t::STORE).setHelp("Path to store the archive data").build());
|
||||||
|
|
||||||
auto args = parser.parse_args(argc, argv);
|
auto args = parser.parse_args(argc, argv);
|
||||||
|
path = args.get<std::string>("path");
|
||||||
|
|
||||||
dpp::cluster bot(args.get<std::string>("token"), dpp::i_default_intents | dpp::i_message_content | dpp::i_all_intents);
|
dpp::cluster bot(args.get<std::string>("token"), dpp::i_default_intents | dpp::i_message_content | dpp::i_all_intents);
|
||||||
|
|
||||||
|
@ -126,6 +269,8 @@ int main(int argc, const char** argv)
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.on_message_update([&bot](const dpp::message_update_t& event) {
|
bot.on_message_update([&bot](const dpp::message_update_t& event) {
|
||||||
|
auto& storage = get(event.msg.guild_id);
|
||||||
|
|
||||||
BLT_INFO("%ld (from user %ld in channel %ld ['%s']) -> '%s'", event.msg.id, event.msg.author.id, event.msg.channel_id,
|
BLT_INFO("%ld (from user %ld in channel %ld ['%s']) -> '%s'", event.msg.id, event.msg.author.id, event.msg.channel_id,
|
||||||
event.msg.author.username.c_str(), event.msg.content.c_str());
|
event.msg.author.username.c_str(), event.msg.content.c_str());
|
||||||
});
|
});
|
||||||
|
@ -133,21 +278,22 @@ int main(int argc, const char** argv)
|
||||||
bot.on_message_create([&bot](const dpp::message_create_t& event) {
|
bot.on_message_create([&bot](const dpp::message_create_t& event) {
|
||||||
if (event.msg.id == bot.me.id)
|
if (event.msg.id == bot.me.id)
|
||||||
return;
|
return;
|
||||||
if (blt::string::contains(event.msg.content, "/dump"))
|
if (blt::string::starts_with(event.msg.content, "!dump"))
|
||||||
{
|
{
|
||||||
for (auto g : databases)
|
|
||||||
g.second.dump();
|
|
||||||
}
|
|
||||||
auto& storage = get(event.msg.guild_id);
|
|
||||||
storage.messages.push_back({
|
|
||||||
event.msg.id,
|
|
||||||
event.msg.channel_id,
|
|
||||||
event.msg.author.id,
|
|
||||||
event.msg.content
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const dpp::attachment& attach : event.msg.attachments)
|
}
|
||||||
storage.attachments.push_back({event.msg.id, event.msg.channel_id, attach.url});
|
// auto& storage = get(event.msg.guild_id);
|
||||||
|
// storage.messages.push_back({
|
||||||
|
// event.msg.id,
|
||||||
|
// event.msg.channel_id,
|
||||||
|
// event.msg.author.id,
|
||||||
|
// event.msg.content
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// for (const dpp::attachment& attach : event.msg.attachments)
|
||||||
|
// {
|
||||||
|
// storage.attachments.push_back({event.msg.id, attach.url});
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.start(dpp::st_wait);
|
bot.start(dpp::st_wait);
|
||||||
|
|
Loading…
Reference in New Issue