mirror of
https://github.com/cuberite/SQLiteCpp.git
synced 2025-08-04 09:46:02 -04:00
commit
56ec1c8c44
@ -34,13 +34,30 @@ Version 0.5.1 - April 7 2013
|
||||
|
||||
Version 0.6.0 - November 22 2013
|
||||
Renamed Column::getName() to Column::getOriginName()
|
||||
Added a new Column::getName()
|
||||
Added Column::getName()
|
||||
|
||||
Version 0.7.0 - January 9 2014
|
||||
Added a new Database::createFunction() API
|
||||
Added Database::createFunction()
|
||||
Added std::string version of existing APIs
|
||||
Improved CMake with more build options and Doxygen auto-detection
|
||||
|
||||
Version 0.8.0 - Februrary 26 2014
|
||||
Version 0.8.0 - February 26 2014
|
||||
Database constructor support opening a database with a custom VFS (default to NULL)
|
||||
Changed Column::getText() to return empty string "" by default instead of NULL pointer (to handle std::string conversion)
|
||||
|
||||
Version 1.0.0 - May 3 2015
|
||||
Public headers file moved to include/ dir
|
||||
Added support to biicode in CMakeLists.txt
|
||||
Added Unit Tests
|
||||
Added aBusyTimeoutMs parameter to Database() constructors
|
||||
Added Database::getTotalChanges()
|
||||
Added Database::getErrorCode()
|
||||
Added Statement::clearBindings()
|
||||
Added Statement::getColumn(aName)
|
||||
Added Statement::getErrorCode()
|
||||
Added Statement::getColumnName(aIndex)
|
||||
Added Statement::getColumnOriginName(aIndex)
|
||||
|
||||
Version 1.1.0 - May 2015 ?
|
||||
Fix valgrind error on Database destructor
|
||||
Added Database::loadExtension
|
104
CMakeLists.txt
104
CMakeLists.txt
@ -1,47 +1,31 @@
|
||||
if (BIICODE)
|
||||
|
||||
# biicode doesn't process files bigger than 5Mb
|
||||
list(APPEND BII_LIB_SRC sqlite3/sqlite3.c)
|
||||
# Include base block dir
|
||||
ADD_BIICODE_TARGETS()
|
||||
|
||||
# Link target with dl for linux
|
||||
if (UNIX)
|
||||
TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE pthread)
|
||||
if(NOT APPLE)
|
||||
TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE dl)
|
||||
endif()
|
||||
endif()
|
||||
else (BIICODE)
|
||||
|
||||
# Main CMake file for compiling the library itself, examples and tests.
|
||||
#
|
||||
# Copyright (c) 2012-2015 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)
|
||||
if (NOT BIICODE)
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(SQLiteCpp)
|
||||
|
||||
option(SQLITE_ENABLE_COLUMN_METADATA "Enable Column::getName(). Require support from sqlite3 library." OFF)
|
||||
if (SQLITE_ENABLE_COLUMN_METADATA)
|
||||
# Enable the use of SQLite column metadata and Column::getName() method,
|
||||
# Require that the sqlite3 library is also compiled with this flag (default under Debian/Ubuntu, but not on Mac OS X).
|
||||
add_definitions(-DSQLITE_ENABLE_COLUMN_METADATA)
|
||||
endif (SQLITE_ENABLE_COLUMN_METADATA)
|
||||
|
||||
option(SQLITE_ENABLE_ASSERT_HANDLER "Enable the user defintion of a assertion_failed() handler." OFF)
|
||||
if (SQLITE_ENABLE_ASSERT_HANDLER)
|
||||
# Enable the user defintion of a assertion_failed() handler (default to false, easier to handler for begginers).
|
||||
add_definitions(-DSQLITECPP_ENABLE_ASSERT_HANDLER)
|
||||
endif (SQLITE_ENABLE_ASSERT_HANDLER)
|
||||
|
||||
# Define useful variables to handle OS differences:
|
||||
if (WIN32)
|
||||
set(DEV_NULL "NUL")
|
||||
else (WIN32)
|
||||
# build the SQLite3 C library for Windows (for ease of use)
|
||||
set(SQLITECPP_INTERNAL_SQLITE_DEFAULT ON)
|
||||
set(SQLITE_ENABLE_COLUMN_METADATA_DEFAULT ON)
|
||||
else (WIN32) # UNIX
|
||||
set(DEV_NULL "/dev/null")
|
||||
# do not build the SQLite3 C library, but uses the Linux/Mac OS X sqlite3-dev package
|
||||
set(SQLITECPP_INTERNAL_SQLITE_DEFAULT OFF)
|
||||
if (APPLE)
|
||||
set(SQLITE_ENABLE_COLUMN_METADATA_DEFAULT OFF)
|
||||
else (APPLE)
|
||||
set(SQLITE_ENABLE_COLUMN_METADATA_DEFAULT ON)
|
||||
endif (APPLE)
|
||||
endif (WIN32)
|
||||
|
||||
# then Compiler/IDE differences:
|
||||
if (MSVC)
|
||||
set(CPPLINT_ARG_OUTPUT "--output=vs7")
|
||||
@ -69,6 +53,22 @@ set(CPPLINT_ARG_VERBOSE "--verbose=3")
|
||||
set(CPPLINT_ARG_LINELENGTH "--linelength=120")
|
||||
|
||||
|
||||
# Options relative to SQLite and SQLiteC++ functions
|
||||
|
||||
option(SQLITE_ENABLE_COLUMN_METADATA "Enable Column::getName(). Require support from sqlite3 library." ${SQLITE_ENABLE_COLUMN_METADATA_DEFAULT})
|
||||
if (SQLITE_ENABLE_COLUMN_METADATA)
|
||||
# Enable the use of SQLite column metadata and Column::getName() method,
|
||||
# Require that the sqlite3 library is also compiled with this flag (default under Debian/Ubuntu, but not on Mac OS X).
|
||||
add_definitions(-DSQLITE_ENABLE_COLUMN_METADATA)
|
||||
endif (SQLITE_ENABLE_COLUMN_METADATA)
|
||||
|
||||
option(SQLITE_ENABLE_ASSERT_HANDLER "Enable the user defintion of a assertion_failed() handler." OFF)
|
||||
if (SQLITE_ENABLE_ASSERT_HANDLER)
|
||||
# Enable the user defintion of a assertion_failed() handler (default to false, easier to handler for begginers).
|
||||
add_definitions(-DSQLITECPP_ENABLE_ASSERT_HANDLER)
|
||||
endif (SQLITE_ENABLE_ASSERT_HANDLER)
|
||||
|
||||
|
||||
## Core source code ##
|
||||
|
||||
# adding a new file require explicittly modifing the CMakeLists.txt
|
||||
@ -97,6 +97,7 @@ source_group(inc FILES ${SQLITECPP_INC})
|
||||
|
||||
# list of test files of the library
|
||||
set(SQLITECPP_TESTS
|
||||
tests/Column_test.cpp
|
||||
tests/Database_test.cpp
|
||||
tests/Statement_test.cpp
|
||||
)
|
||||
@ -141,30 +142,29 @@ if (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Cla
|
||||
endif (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
|
||||
|
||||
|
||||
# SQLite3 library (Windows only)
|
||||
# SQLite3 library (mostly usefull under Windows)
|
||||
|
||||
option (SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ON)
|
||||
if (WIN32)
|
||||
if (SQLITECPP_INTERNAL_SQLITE)
|
||||
option(SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ${SQLITECPP_INTERNAL_SQLITE_DEFAULT})
|
||||
if (SQLITECPP_INTERNAL_SQLITE)
|
||||
# build the SQLite3 C library for Windows (for ease of use) versus Linux sqlite3-dev package
|
||||
add_subdirectory(sqlite3)
|
||||
include_directories("${PROJECT_SOURCE_DIR}/sqlite3")
|
||||
endif (SQLITECPP_INTERNAL_SQLITE)
|
||||
endif (WIN32)
|
||||
endif (SQLITECPP_INTERNAL_SQLITE)
|
||||
|
||||
|
||||
# Optional additional targets:
|
||||
|
||||
option(SQLITECPP_RUN_CPPLINT "Run cpplint.py tool for Google C++ StyleGuide." ON)
|
||||
if (SQLITECPP_RUN_CPPLINT)
|
||||
find_package(PythonLibs)
|
||||
if (PYTHONLIBS_FOUND)
|
||||
find_package(PythonInterp)
|
||||
if (PYTHONINTERP_FOUND)
|
||||
# add a cpplint target to the "all" target
|
||||
add_custom_target(SQLiteCpp_cpplint
|
||||
ALL
|
||||
COMMAND python ${PROJECT_SOURCE_DIR}/cpplint.py ${CPPLINT_ARG_OUTPUT} ${CPPLINT_ARG_VERBOSE} ${CPPLINT_ARG_LINELENGTH} ${SQLITECPP_SRC} ${SQLITECPP_INC}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/cpplint.py ${CPPLINT_ARG_OUTPUT} ${CPPLINT_ARG_VERBOSE} ${CPPLINT_ARG_LINELENGTH} ${SQLITECPP_SRC} ${SQLITECPP_INC}
|
||||
)
|
||||
endif (PYTHONLIBS_FOUND)
|
||||
message(STATUS "PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}")
|
||||
endif (PYTHONINTERP_FOUND)
|
||||
else (SQLITECPP_RUN_CPPLINT)
|
||||
message(STATUS "SQLITECPP_RUN_CPPLINT OFF")
|
||||
endif (SQLITECPP_RUN_CPPLINT)
|
||||
@ -205,9 +205,9 @@ if (SQLITECPP_BUILD_EXAMPLES)
|
||||
# add the basic example executable
|
||||
add_executable(SQLiteCpp_example1 ${SQLITECPP_EXAMPLES})
|
||||
target_link_libraries(SQLiteCpp_example1 SQLiteCpp sqlite3)
|
||||
else(SQLITECPP_BUILD_EXAMPLES)
|
||||
else (SQLITECPP_BUILD_EXAMPLES)
|
||||
message(STATUS "SQLITECPP_BUILD_EXAMPLES OFF")
|
||||
endif(SQLITECPP_BUILD_EXAMPLES)
|
||||
endif (SQLITECPP_BUILD_EXAMPLES)
|
||||
|
||||
option(SQLITECPP_BUILD_TESTS "Build and run tests." OFF)
|
||||
if (SQLITECPP_BUILD_TESTS)
|
||||
@ -232,9 +232,25 @@ if (SQLITECPP_BUILD_TESTS)
|
||||
if (SQLITECPP_BUILD_EXAMPLES)
|
||||
# does the example1 runs successfully?
|
||||
add_test(Example1Run SQLiteCpp_example1)
|
||||
endif(SQLITECPP_BUILD_EXAMPLES)
|
||||
endif (SQLITECPP_BUILD_EXAMPLES)
|
||||
else (SQLITECPP_BUILD_TESTS)
|
||||
message(STATUS "SQLITECPP_BUILD_TESTS OFF")
|
||||
endif (SQLITECPP_BUILD_TESTS)
|
||||
|
||||
endif (BIICODE)
|
||||
|
||||
else (NOT BIICODE)
|
||||
|
||||
# biicode doesn't process files bigger than 5Mb
|
||||
list(APPEND BII_LIB_SRC sqlite3/sqlite3.c)
|
||||
# Include base block dir
|
||||
ADD_BIICODE_TARGETS()
|
||||
|
||||
# Link target with dl for linux
|
||||
if (UNIX)
|
||||
target_link_libraries(${BII_BLOCK_TARGET} INTERFACE pthread)
|
||||
if (NOT APPLE)
|
||||
target_link_libraries(${BII_BLOCK_TARGET} INTERFACE dl)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
endif (NOT BIICODE)
|
||||
|
@ -1,9 +1,10 @@
|
||||
SQLiteC++
|
||||
---------
|
||||
|
||||
[](https://github.com/SRombauts/SQLiteCpp/releases)
|
||||
[](https://travis-ci.org/SRombauts/SQLiteCpp "Travis CI Linux Build Status")
|
||||
[](https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp "AppVeyor Windows Build status")
|
||||
[](https://www.biicode.com/sqlite/sqlite)
|
||||
[](https://www.biicode.com/sqlite/sqlite)
|
||||
|
||||
|
||||
SQLiteC++ (SQLiteCpp) is a smart and easy to use C++ SQLite3 wrapper.
|
||||
|
30
TODO.txt
30
TODO.txt
@ -1,28 +1,34 @@
|
||||
Add a full googletest suite
|
||||
|
||||
Add a Tutorial: for SQLite newbies
|
||||
Create Github Wiki pages with the README.md and FAQ.txt: Installation, Examples, Tutorial, How to contribute
|
||||
|
||||
Publish a versionned ZIP file in Google Project Mirror
|
||||
Add a Tutorial for SQLite newbies
|
||||
Add a real example (rework current example?)
|
||||
|
||||
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 v0.9.9:
|
||||
- getColumnByName() (issue #23) ? std::map getRow() ?
|
||||
Missing features in v1.1.0:
|
||||
- bind a SQLITE_STATIC value (string/blob)
|
||||
- bind a dynamic value with zerocopy (unlike SQLITE_TRANSIENT) with custom deleter
|
||||
- #24: executemany() like in Python https://docs.python.org/2/library/sqlite3.html#sqlite3.Connection.executemany
|
||||
- #34: Better type for getColumn
|
||||
|
||||
Missing documentation in v0.9.9:
|
||||
Missing documentation in v1.1.0:
|
||||
- explain the noncopyable property for RAII design
|
||||
- comment on returning error code instead of exception that shall not be thrown when exepected (!?)
|
||||
- comment on returning error code instead of exception that shall not be thrown when expected (!?)
|
||||
|
||||
Missing unit tests in v1.0.0:
|
||||
- Binding variants
|
||||
- Create Function
|
||||
- Assert Handler
|
||||
- Load Extension (not practicable, and easy to verify by code review)
|
||||
|
||||
Advanced missing features:
|
||||
- #39: SAVEPOINT https://www.sqlite.org/lang_savepoint.html
|
||||
|
||||
- backup support to/from file/:memory:
|
||||
- Function ?
|
||||
- Add optional usage of experimental sqlite3_trace() function to enable statistics
|
||||
- Agregate ?
|
||||
|
||||
- support for different transaction mode ? NO: too specific
|
||||
- operator<< binding ? NO: redundant with bind()
|
||||
- ATTACH Database ? NO: can already be done by "ATTACH" Statement
|
||||
- Add optional usage of experimental sqlite3_trace() function to enable statistics
|
||||
|
||||
Post an article to CodeProject: Is there a license issue ?
|
3
cpplint.py
vendored
3
cpplint.py
vendored
@ -4784,7 +4784,8 @@ def main():
|
||||
ProcessFile(filename, _cpplint_state.verbose_level)
|
||||
_cpplint_state.PrintErrorCounts()
|
||||
|
||||
sys.exit(_cpplint_state.error_count > 0)
|
||||
# SRombauts: do not break build for cpplint style warnings
|
||||
#sys.exit(_cpplint_state.error_count > 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -2,9 +2,9 @@
|
||||
* @file main.cpp
|
||||
* @brief A few short examples in a row.
|
||||
*
|
||||
* Demonstrate how-to use the SQLite++ wrapper
|
||||
* Demonstrates how-to use the SQLite++ wrapper
|
||||
*
|
||||
* Copyright (c) 2012-2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
* Copyright (c) 2012-2015 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)
|
||||
@ -83,7 +83,11 @@ private:
|
||||
|
||||
int main ()
|
||||
{
|
||||
// Basic example (1/6) :
|
||||
std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl;
|
||||
std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Very basic first example (1/7) :
|
||||
try
|
||||
{
|
||||
// Open a database file in readonly mode
|
||||
@ -91,12 +95,28 @@ int main ()
|
||||
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
|
||||
|
||||
// Test if the 'test' table exists
|
||||
bool bExists = db.tableExists("test");
|
||||
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
|
||||
std::string value = db.execAndGet("SELECT value FROM test WHERE id=2");
|
||||
const std::string value = db.execAndGet("SELECT value FROM test WHERE id=2");
|
||||
std::cout << "execAndGet=" << value.c_str() << std::endl;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "SQLite exception: " << e.what() << std::endl;
|
||||
return EXIT_FAILURE; // unexpected error : exit the example program
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Simple select query - few variations (2/7) :
|
||||
try
|
||||
{
|
||||
// Open a database file in readonly mode
|
||||
SQLite::Database db(filename_example_db3); // SQLITE_OPEN_READONLY
|
||||
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
|
||||
|
||||
///// a) Loop to get values of column by index, using auto cast to variable type
|
||||
|
||||
// Compile a SQL query, containing one parameter (index 1)
|
||||
SQLite::Statement query(db, "SELECT id as test_id, value as test_val, weight as test_weight FROM test WHERE weight > ?");
|
||||
@ -108,46 +128,77 @@ int main ()
|
||||
// Loop to execute the query step by step, to get one a row of results at a time
|
||||
while (query.executeStep())
|
||||
{
|
||||
// Demonstrate how to get some typed column value (and the equivalent explicit call)
|
||||
int id = query.getColumn(0); // = query.getColumn(0).getInt()
|
||||
//const char* pvalue = query.getColumn(1); // = query.getColumn(1).getText()
|
||||
std::string value2 = query.getColumn(1); // = query.getColumn(1).getText()
|
||||
int bytes = query.getColumn(1).getBytes();
|
||||
double weight = query.getColumn(2); // = query.getColumn(2).getInt()
|
||||
// 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";
|
||||
|
||||
static bool bFirst = true;
|
||||
if (bFirst)
|
||||
{
|
||||
// Show how to get the aliased names of the result columns.
|
||||
std::string name0 = query.getColumn(0).getName();
|
||||
std::string name1 = query.getColumn(1).getName();
|
||||
std::string name2 = query.getColumn(2).getName();
|
||||
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.
|
||||
name0 = query.getColumn(0).getOriginName();
|
||||
name1 = query.getColumn(1).getOriginName();
|
||||
name2 = query.getColumn(2).getOriginName();
|
||||
std::cout << "origin table 'test' [\"" << name0.c_str() << "\", \"" << name1.c_str() << "\", \"" << name2.c_str() << "\"]\n";
|
||||
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
|
||||
bFirst = false;
|
||||
// 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";
|
||||
}
|
||||
std::cout << "row (" << id << ", \"" << value2.c_str() << "\" " << bytes << " bytes, " << weight << ")\n";
|
||||
|
||||
///// c) Get columns by name
|
||||
|
||||
// Reset the query to use it again
|
||||
query.reset();
|
||||
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' reseted (" << query.getColumnCount() << " columns in the result)\n";
|
||||
|
||||
// Loop to execute the query step by step, to get one a row of results at a time
|
||||
while (query.executeStep())
|
||||
{
|
||||
// Demonstrates how to get column value by aliased name (not the original table names, see above)
|
||||
const int id = query.getColumn("test_id");
|
||||
const std::string value = query.getColumn("test_val");
|
||||
const double weight = query.getColumn("test_weight");
|
||||
std::cout << "row (" << id << ", \"" << value.c_str() << "\" " << weight << ")\n";
|
||||
}
|
||||
|
||||
///// d) Uses explicit typed getters instead of auto cast operators
|
||||
|
||||
// Reset the query to use it again
|
||||
query.reset();
|
||||
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' reseted (" << query.getColumnCount () << " columns in the result)\n";
|
||||
// Bind the string value "6" to the first parameter of the SQL query
|
||||
query.bind(1, "6");
|
||||
std::cout << "binded with string value \"6\" :\n";
|
||||
|
||||
// Reuses variables: uses assignement operator in the loop instead of constructor with initialization
|
||||
int id = 0;
|
||||
std::string value;
|
||||
double weight = 0.0;
|
||||
while (query.executeStep())
|
||||
{
|
||||
// Demonstrate that inserting column value in a std:ostream is natural
|
||||
std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\", " << query.getColumn(2) << ")\n";
|
||||
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)
|
||||
@ -157,13 +208,13 @@ int main ()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Object Oriented Basic example (2/6) :
|
||||
// Object Oriented Basic example (3/7) :
|
||||
try
|
||||
{
|
||||
// Open the database and compile the query
|
||||
Example example;
|
||||
|
||||
// Demonstrate the way to use the same query with different parameter values
|
||||
// Demonstrates the way to use the same query with different parameter values
|
||||
example.ListGreaterThan(8);
|
||||
example.ListGreaterThan(6);
|
||||
example.ListGreaterThan(2);
|
||||
@ -174,7 +225,7 @@ int main ()
|
||||
return EXIT_FAILURE; // unexpected error : exit the example program
|
||||
}
|
||||
|
||||
// The execAndGet wrapper example (3/6) :
|
||||
// The execAndGet wrapper example (4/7) :
|
||||
try
|
||||
{
|
||||
// Open a database file in readonly mode
|
||||
@ -194,7 +245,7 @@ int main ()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Simple batch queries example (4/6) :
|
||||
// Simple batch queries example (5/7) :
|
||||
try
|
||||
{
|
||||
// Open a database file in create/write mode
|
||||
@ -235,7 +286,7 @@ int main ()
|
||||
remove("test.db3");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// RAII transaction example (5/6) :
|
||||
// RAII transaction example (6/7) :
|
||||
try
|
||||
{
|
||||
// Open a database file in create/write mode
|
||||
@ -303,7 +354,7 @@ int main ()
|
||||
remove("transaction.db3");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Binary blob and in-memory database example (6/6) :
|
||||
// Binary blob and in-memory database example (7/7) :
|
||||
try
|
||||
{
|
||||
// Open a database file in create/write mode
|
||||
|
@ -40,7 +40,7 @@ namespace SQLite
|
||||
#else
|
||||
|
||||
// if no assert handler provided by user code, use standard assert()
|
||||
// (note: in debug mode, assert() does nothing)
|
||||
// (note: in release mode assert() does nothing)
|
||||
#define SQLITECPP_ASSERT(expression, message) assert(expression && message)
|
||||
|
||||
#endif
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @ingroup SQLiteCpp
|
||||
* @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement.
|
||||
*
|
||||
* Copyright (c) 2012-2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
* Copyright (c) 2012-2015 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)
|
||||
@ -62,11 +62,13 @@ public:
|
||||
// they copy the Statement::Ptr which in turn increments the reference counter.
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the named assigned to a result column (potentially aliased)
|
||||
* @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; // nothrow
|
||||
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
/**
|
||||
* @brief Return a pointer to the table column name that is the origin of this result column
|
||||
*
|
||||
@ -184,8 +186,9 @@ public:
|
||||
{
|
||||
return getBlob();
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
// NOTE : the following is required by GCC to cast a Column result in a std::string
|
||||
// NOTE : the following is required by GCC and Clang to cast a Column result in a std::string
|
||||
// (error: conversion from ‘SQLite::Column’ to non-scalar type ‘std::string {aka std::basic_string<char>}’)
|
||||
// but is not working under Microsoft Visual Studio 2010 and 2012
|
||||
// (error C2440: 'initializing' : cannot convert from 'SQLite::Column' to 'std::basic_string<_Elem,_Traits,_Ax>'
|
||||
@ -196,6 +199,16 @@ public:
|
||||
return getText();
|
||||
}
|
||||
#endif
|
||||
// NOTE : the following is required by GCC and Clang to cast a Column result in a long/int64_t
|
||||
/// @brief Inline cast operator to long as 64bits integer
|
||||
inline operator long() const
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
return getInt64();
|
||||
#else
|
||||
return getInt();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Return UTF-8 encoded English language explanation of the most recent error.
|
||||
inline const char* errmsg() const
|
||||
|
@ -55,11 +55,15 @@ public:
|
||||
*
|
||||
* @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 char * apVfs = NULL);
|
||||
Database(const char* apFilename,
|
||||
const int aFlags = SQLITE_OPEN_READONLY,
|
||||
const int aBusyTimeoutMs = 0,
|
||||
const char* apVfs = NULL);
|
||||
|
||||
/**
|
||||
* @brief Open the provided database UTF-8 filename.
|
||||
@ -73,11 +77,15 @@ public:
|
||||
*
|
||||
* @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 std::string& aVfs = "");
|
||||
Database(const std::string& aFilename,
|
||||
const int aFlags = SQLITE_OPEN_READONLY,
|
||||
const int aBusyTimeoutMs = 0,
|
||||
const std::string& aVfs = "");
|
||||
|
||||
/**
|
||||
* @brief Close the SQLite database connection.
|
||||
@ -89,6 +97,21 @@ public:
|
||||
*/
|
||||
virtual ~Database() noexcept; // nothrow
|
||||
|
||||
/**
|
||||
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||
*
|
||||
* This is usefull in multithreaded program to handle case where a table is locked for writting by a thread.
|
||||
* Any other thread cannot access the table and will receive a SQLITE_BUSY error:
|
||||
* setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error.
|
||||
* Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;".
|
||||
* Default busy timeout is 0ms.
|
||||
*
|
||||
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
||||
*
|
||||
* @throw SQLite::Exception in case of error
|
||||
*/
|
||||
void setBusyTimeout(const int aBusyTimeoutMs) noexcept; // nothrow
|
||||
|
||||
/**
|
||||
* @brief Shortcut to execute one or multiple statements without results.
|
||||
*
|
||||
@ -206,16 +229,6 @@ public:
|
||||
return tableExists(aTableName.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||
*
|
||||
* @param[in] aTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
||||
*/
|
||||
inline int setBusyTimeout(int aTimeoutMs) noexcept // nothrow
|
||||
{
|
||||
return sqlite3_busy_timeout(mpSQLite, aTimeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the rowid of the most recent successful INSERT into the database from the current connection.
|
||||
*
|
||||
@ -323,6 +336,25 @@ public:
|
||||
apApp, apFunc, apStep, apFinal, apDestroy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 (NULL to let sqlite work it out)
|
||||
*
|
||||
* @throw SQLite::Exception in case of error
|
||||
*/
|
||||
void loadExtension(const char* apExtensionName,
|
||||
const char *apEntryPointName);
|
||||
|
||||
private:
|
||||
/// @{ Database must be non-copyable
|
||||
Database(const Database&);
|
||||
@ -332,7 +364,13 @@ private:
|
||||
/**
|
||||
* @brief Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message
|
||||
*/
|
||||
void check(const int aRet) const;
|
||||
inline void check(const int aRet) const
|
||||
{
|
||||
if (SQLITE_OK != aRet)
|
||||
{
|
||||
throw SQLite::Exception(sqlite3_errmsg(mpSQLite));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* Include this main header file in your project to gain access to all functionality provided by the wrapper.
|
||||
*
|
||||
* Copyright (c) 2012-2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
* Copyright (c) 2012-2015 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)
|
||||
@ -38,5 +38,5 @@
|
||||
* with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
|
||||
* numbers used in [SQLITECPP_VERSION].
|
||||
*/
|
||||
#define SQLITECPP_VERSION "0.9.9"
|
||||
#define SQLITECPP_VERSION_NUMBER 0009009
|
||||
#define SQLITECPP_VERSION "1.1.0"
|
||||
#define SQLITECPP_VERSION_NUMBER 1001000
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <SQLiteCpp/Exception.h>
|
||||
|
||||
@ -78,8 +79,12 @@ public:
|
||||
|
||||
/**
|
||||
* @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(void); // throw(SQLite::Exception)
|
||||
void clearBindings(); // throw(SQLite::Exception)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Bind a value to a parameter of the SQL statement,
|
||||
@ -274,7 +279,8 @@ public:
|
||||
* 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 :
|
||||
* 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
|
||||
@ -283,8 +289,7 @@ public:
|
||||
*
|
||||
* @param[in] aIndex Index of the column, starting at 0
|
||||
*
|
||||
* @note This method is no more const, starting in v0.5,
|
||||
* which reflects the fact that the returned Column object will
|
||||
* @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".
|
||||
@ -295,15 +300,68 @@ public:
|
||||
*/
|
||||
Column getColumn(const int aIndex);
|
||||
|
||||
/**
|
||||
* @brief Return a copy of the column data specified by its column name (less efficient than using an index)
|
||||
*
|
||||
* Can be used to access the data of the current row of result when applicable,
|
||||
* while the executeStep() method returns true.
|
||||
*
|
||||
* Throw an exception if there is no row to return a Column from :
|
||||
* - if provided name is not one of the aliased column names
|
||||
* - before any executeStep() call
|
||||
* - after the last executeStep() returned false
|
||||
* - after a reset() call
|
||||
*
|
||||
* Throw an exception if the specified name is not an on of the aliased name of the columns in the result.
|
||||
*
|
||||
* @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name)
|
||||
*
|
||||
* @note Uses a map of column names to indexes, build on first call.
|
||||
*
|
||||
* @note This method is not const, reflecting the fact that the returned Column object will
|
||||
* share the ownership of the underlying sqlite3_stmt.
|
||||
*
|
||||
* @warning The resulting Column object must not be memorized "as-is".
|
||||
* Is is only a wrapper around the current result row, so it is only valid
|
||||
* while the row from the Statement remains valid, that is only until next executeStep() call.
|
||||
* Thus, you should instead extract immediately its data (getInt(), getText()...)
|
||||
* and use or copy this data for any later usage.
|
||||
*/
|
||||
Column getColumn(const char* apName);
|
||||
|
||||
/**
|
||||
* @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 Return a pointer to the named assigned to the specified result column (potentially aliased)
|
||||
*
|
||||
* @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 UTF-8 SQL Query.
|
||||
@ -395,12 +453,44 @@ private:
|
||||
*
|
||||
* @param[in] SQLite return code to test against the SQLITE_OK expected value
|
||||
*/
|
||||
void check(const int aRet);
|
||||
inline void check(const int aRet) const
|
||||
{
|
||||
if (SQLITE_OK != aRet)
|
||||
{
|
||||
throw SQLite::Exception(sqlite3_errmsg(mStmtPtr));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if there is a row of result returnes by executeStep(), else throw a SQLite::Exception.
|
||||
*/
|
||||
inline void checkRow() const
|
||||
{
|
||||
if (false == mbOk)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
inline void checkIndex(const int aIndex) const
|
||||
{
|
||||
if ((aIndex < 0) || (aIndex >= mColumnCount))
|
||||
{
|
||||
throw SQLite::Exception("Column index out of range.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, int> TColumnNames;
|
||||
|
||||
private:
|
||||
std::string mQuery; //!< UTF-8 SQL Query
|
||||
Ptr mStmtPtr; //!< Shared Pointer to the prepared SQLite Statement Object
|
||||
int mColumnCount; //!< Number of columns in the result of the prepared statement
|
||||
TColumnNames mColumnNames; //!< Map of columns index by name
|
||||
bool mbOk; //!< true when a row has been fetched with executeStep()
|
||||
bool mbDone; //!< true when the last executeStep() had no more row to fetch
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
# CMake file for compiling the sqlite3 static library under Windows (for ease of use)
|
||||
#
|
||||
# Copyright (c) 2013-2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
# Copyright (c) 2013-2015 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)
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @ingroup SQLiteCpp
|
||||
* @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement.
|
||||
*
|
||||
* Copyright (c) 2012-2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
* Copyright (c) 2012-2015 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)
|
||||
@ -31,14 +31,14 @@ Column::~Column() noexcept // nothrow
|
||||
}
|
||||
|
||||
// Return the named assigned to this result column (potentially aliased)
|
||||
const char * Column::getName() const noexcept // nothrow
|
||||
const char* Column::getName() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_name(mStmtPtr, mIndex);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
// Return the name of the table column that is the origin of this result column
|
||||
const char * Column::getOriginName() const noexcept // nothrow
|
||||
const char* Column::getOriginName() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_origin_name(mStmtPtr, mIndex);
|
||||
}
|
||||
|
@ -26,45 +26,85 @@ namespace SQLite
|
||||
|
||||
|
||||
// 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 char* apVfs /*= NULL*/) :
|
||||
Database::Database(const char* apFilename,
|
||||
const int aFlags /* = SQLITE_OPEN_READONLY*/,
|
||||
const int aBusyTimeoutMs /* = 0 */,
|
||||
const char* apVfs /* = NULL*/) :
|
||||
mpSQLite(NULL),
|
||||
mFilename(apFilename)
|
||||
{
|
||||
int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs);
|
||||
const int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
std::string strerr = sqlite3_errmsg(mpSQLite);
|
||||
sqlite3_close(mpSQLite); // close is required even in case of error on opening
|
||||
throw SQLite::Exception(strerr);
|
||||
}
|
||||
|
||||
if (aBusyTimeoutMs > 0)
|
||||
{
|
||||
setBusyTimeout(aBusyTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
// Open the provided database UTF-8 filename with SQLITE_OPEN_xxx provided flags.
|
||||
Database::Database(const std::string& aFilename, const int aFlags /*= SQLITE_OPEN_READONLY*/, const std::string& aVfs) :
|
||||
Database::Database(const std::string& aFilename,
|
||||
const int aFlags /* = SQLITE_OPEN_READONLY*/,
|
||||
const int aBusyTimeoutMs /* = 0 */,
|
||||
const std::string& aVfs /* = "" */) :
|
||||
mpSQLite(NULL),
|
||||
mFilename(aFilename)
|
||||
{
|
||||
int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str());
|
||||
const int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str());
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
std::string strerr = sqlite3_errmsg(mpSQLite);
|
||||
sqlite3_close(mpSQLite); // close is required even in case of error on opening
|
||||
throw SQLite::Exception(strerr);
|
||||
}
|
||||
|
||||
if (aBusyTimeoutMs > 0)
|
||||
{
|
||||
setBusyTimeout(aBusyTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the SQLite database connection.
|
||||
Database::~Database() noexcept // nothrow
|
||||
{
|
||||
int ret = sqlite3_close(mpSQLite);
|
||||
// Never throw an exception in a destructor
|
||||
SQLITECPP_ASSERT(SQLITE_OK == ret, sqlite3_errmsg(mpSQLite)); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
||||
const int ret = sqlite3_close(mpSQLite);
|
||||
|
||||
// Avoid unreferenced variable warning when build in release mode
|
||||
(void) ret;
|
||||
|
||||
// Only case of error is SQLITE_BUSY: "database is locked" (some statements are not finalized)
|
||||
// Never throw an exception in a destructor :
|
||||
SQLITECPP_ASSERT(SQLITE_OK == ret, "database is locked"); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||
*
|
||||
* This is usefull in multithreaded program to handle case where a table is locked for writting by a thread.
|
||||
* Any other thread cannot access the table and will receive a SQLITE_BUSY error:
|
||||
* setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error.
|
||||
* Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;".
|
||||
* Default busy timeout is 0ms.
|
||||
*
|
||||
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
||||
*
|
||||
* @throw SQLite::Exception in case of error
|
||||
*/
|
||||
void Database::setBusyTimeout(const int aBusyTimeoutMs) noexcept // nothrow
|
||||
{
|
||||
const int ret = sqlite3_busy_timeout(mpSQLite, aBusyTimeoutMs);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...).
|
||||
int Database::exec(const char* apQueries)
|
||||
{
|
||||
int ret = sqlite3_exec(mpSQLite, apQueries, NULL, NULL, NULL);
|
||||
const int ret = sqlite3_exec(mpSQLite, apQueries, NULL, NULL, NULL);
|
||||
check(ret);
|
||||
|
||||
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only)
|
||||
@ -90,19 +130,10 @@ bool Database::tableExists(const char* apTableName)
|
||||
Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?");
|
||||
query.bind(1, apTableName);
|
||||
(void)query.executeStep(); // Cannot return false, as the above query always return a result
|
||||
int Nb = query.getColumn(0);
|
||||
const int Nb = query.getColumn(0);
|
||||
return (1 == Nb);
|
||||
}
|
||||
|
||||
// Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message
|
||||
void Database::check(const int aRet) const
|
||||
{
|
||||
if (SQLITE_OK != aRet)
|
||||
{
|
||||
throw SQLite::Exception(sqlite3_errmsg(mpSQLite));
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
@ -119,11 +150,31 @@ void Database::createFunction(const char* apFuncName,
|
||||
if (abDeterministic) {
|
||||
TextRep = TextRep|SQLITE_DETERMINISTIC;
|
||||
}
|
||||
int ret = sqlite3_create_function_v2(mpSQLite, apFuncName, aNbArg, TextRep,
|
||||
const int ret = sqlite3_create_function_v2(mpSQLite, 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
|
||||
#
|
||||
throw std::runtime_error("sqlite extensions are disabled");
|
||||
#
|
||||
#else
|
||||
#
|
||||
int ret = sqlite3_enable_load_extension(mpSQLite, 1);
|
||||
|
||||
check(ret);
|
||||
|
||||
ret = sqlite3_load_extension(mpSQLite, apExtensionName, apEntryPointName, 0);
|
||||
|
||||
check(ret);
|
||||
#
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace SQLite
|
||||
|
@ -54,67 +54,66 @@ void Statement::reset()
|
||||
{
|
||||
mbOk = false;
|
||||
mbDone = false;
|
||||
int ret = sqlite3_reset(mStmtPtr);
|
||||
const int ret = sqlite3_reset(mStmtPtr);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Clears away all the bindings of a prepared statement.
|
||||
// "Contrary to the intuition of many, [sqlite3_reset()] does not reset
|
||||
// ** the [sqlite3_bind_blob | bindings] on a [prepared statement]."
|
||||
void Statement::clearBindings(void)
|
||||
void Statement::clearBindings()
|
||||
{
|
||||
mbOk = false;
|
||||
mbDone = false;
|
||||
int ret = sqlite3_clear_bindings(mStmtPtr);
|
||||
const int ret = sqlite3_clear_bindings(mStmtPtr);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const int& aValue)
|
||||
{
|
||||
int ret = sqlite3_bind_int(mStmtPtr, aIndex, aValue);
|
||||
const int ret = sqlite3_bind_int(mStmtPtr, aIndex, aValue);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const sqlite3_int64& aValue)
|
||||
{
|
||||
int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue);
|
||||
const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const double& aValue)
|
||||
{
|
||||
int ret = sqlite3_bind_double(mStmtPtr, aIndex, aValue);
|
||||
const int ret = sqlite3_bind_double(mStmtPtr, aIndex, aValue);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const std::string& aValue)
|
||||
{
|
||||
int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(), static_cast<int>(aValue.size()), SQLITE_TRANSIENT);
|
||||
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(),
|
||||
static_cast<int>(aValue.size()), SQLITE_TRANSIENT);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const char* apValue)
|
||||
{
|
||||
int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT);
|
||||
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const void* apValue, const int aSize)
|
||||
{
|
||||
int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT);
|
||||
const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT);
|
||||
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)
|
||||
{
|
||||
int ret = sqlite3_bind_null(mStmtPtr, aIndex);
|
||||
const int ret = sqlite3_bind_null(mStmtPtr, aIndex);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
@ -122,56 +121,57 @@ void Statement::bind(const int aIndex)
|
||||
// Bind an int value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const char* apName, const int& aValue)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_int(mStmtPtr, index, aValue);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_int(mStmtPtr, index, 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 char* apName, const sqlite3_int64& aValue)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_int64(mStmtPtr, index, aValue);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_int64(mStmtPtr, index, 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 char* apName, const double& aValue)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_double(mStmtPtr, index, aValue);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_double(mStmtPtr, index, aValue);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a string value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const char* apName, const std::string& aValue)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_text(mStmtPtr, index, aValue.c_str(), static_cast<int>(aValue.size()), SQLITE_TRANSIENT);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_text(mStmtPtr, index, 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 char* apName, const char* apValue)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_text(mStmtPtr, index, apValue, -1, SQLITE_TRANSIENT);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_text(mStmtPtr, index, 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 char* apName, const void* apValue, const int aSize)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_blob(mStmtPtr, index, apValue, aSize, SQLITE_TRANSIENT);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_blob(mStmtPtr, index, apValue, aSize, SQLITE_TRANSIENT);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
// Bind a NULL value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const char* apName)
|
||||
{
|
||||
int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
int ret = sqlite3_bind_null(mStmtPtr, index);
|
||||
const int index = sqlite3_bind_parameter_index(mStmtPtr, apName);
|
||||
const int ret = sqlite3_bind_null(mStmtPtr, index);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ bool Statement::executeStep()
|
||||
{
|
||||
if (false == mbDone)
|
||||
{
|
||||
int ret = sqlite3_step(mStmtPtr);
|
||||
const int ret = sqlite3_step(mStmtPtr);
|
||||
if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it
|
||||
{
|
||||
mbOk = true;
|
||||
@ -200,7 +200,7 @@ bool Statement::executeStep()
|
||||
}
|
||||
else
|
||||
{
|
||||
throw SQLite::Exception("Statement needs to be reset");
|
||||
throw SQLite::Exception("Statement needs to be reseted.");
|
||||
}
|
||||
|
||||
return mbOk; // true only if one row is accessible by getColumn(N)
|
||||
@ -211,7 +211,7 @@ int Statement::exec()
|
||||
{
|
||||
if (false == mbDone)
|
||||
{
|
||||
int ret = sqlite3_step(mStmtPtr);
|
||||
const int ret = sqlite3_step(mStmtPtr);
|
||||
if (SQLITE_DONE == ret) // the statement has finished executing successfully
|
||||
{
|
||||
mbOk = false;
|
||||
@ -221,7 +221,7 @@ int Statement::exec()
|
||||
{
|
||||
mbOk = false;
|
||||
mbDone = false;
|
||||
throw SQLite::Exception("exec() does not expect results");
|
||||
throw SQLite::Exception("exec() does not expect results. Use executeStep.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -232,7 +232,7 @@ int Statement::exec()
|
||||
}
|
||||
else
|
||||
{
|
||||
throw SQLite::Exception("Statement need to be reseted");
|
||||
throw SQLite::Exception("Statement need to be reseted.");
|
||||
}
|
||||
|
||||
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE)
|
||||
@ -243,43 +243,61 @@ int Statement::exec()
|
||||
// (use the Column copy-constructor)
|
||||
Column Statement::getColumn(const int aIndex)
|
||||
{
|
||||
if (false == mbOk)
|
||||
{
|
||||
throw SQLite::Exception("No row to get a column from");
|
||||
}
|
||||
else if ((aIndex < 0) || (aIndex >= mColumnCount))
|
||||
{
|
||||
throw SQLite::Exception("Column index out of range");
|
||||
}
|
||||
checkRow();
|
||||
checkIndex(aIndex);
|
||||
|
||||
// Share the Statement Object handle with the new Column created
|
||||
return Column(mStmtPtr, aIndex);
|
||||
}
|
||||
|
||||
// Return a copy of the column data specified by its column name starting at 0
|
||||
// (use the Column copy-constructor)
|
||||
Column Statement::getColumn(const char* apName)
|
||||
{
|
||||
checkRow();
|
||||
|
||||
if (mColumnNames.empty())
|
||||
{
|
||||
for (int i = 0; i < mColumnCount; ++i)
|
||||
{
|
||||
const char* pName = sqlite3_column_name(mStmtPtr, i);
|
||||
mColumnNames[pName] = i;
|
||||
}
|
||||
}
|
||||
|
||||
const TColumnNames::const_iterator iIndex = mColumnNames.find(apName);
|
||||
if (iIndex == mColumnNames.end())
|
||||
{
|
||||
throw SQLite::Exception("Unknown column name.");
|
||||
}
|
||||
|
||||
// Share the Statement Object handle with the new Column created
|
||||
return Column(mStmtPtr, (*iIndex).second);
|
||||
}
|
||||
|
||||
// Test if the column is NULL
|
||||
bool Statement::isColumnNull(const int aIndex) const
|
||||
{
|
||||
if (false == mbOk)
|
||||
{
|
||||
throw SQLite::Exception("No row to get a column from");
|
||||
}
|
||||
else if ((aIndex < 0) || (aIndex >= mColumnCount))
|
||||
{
|
||||
throw SQLite::Exception("Column index out of range");
|
||||
}
|
||||
|
||||
checkRow();
|
||||
checkIndex(aIndex);
|
||||
return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, aIndex));
|
||||
}
|
||||
|
||||
// Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message
|
||||
void Statement::check(const int aRet)
|
||||
// Return the named assigned to the specified result column (potentially aliased)
|
||||
const char* Statement::getColumnName(const int aIndex) const
|
||||
{
|
||||
if (SQLITE_OK != aRet)
|
||||
{
|
||||
throw SQLite::Exception(sqlite3_errmsg(mStmtPtr));
|
||||
}
|
||||
checkIndex(aIndex);
|
||||
return sqlite3_column_name(mStmtPtr, aIndex);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
// Return the named assigned to the specified result column (potentially aliased)
|
||||
const char* Statement::getColumnOriginName(const int aIndex) const
|
||||
{
|
||||
checkIndex(aIndex);
|
||||
return sqlite3_column_origin_name(mStmtPtr, aIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Internal class : shared pointer to the sqlite3_stmt SQLite Statement Object
|
||||
@ -296,7 +314,7 @@ Statement::Ptr::Ptr(sqlite3* apSQLite, std::string& aQuery) :
|
||||
mpStmt(NULL),
|
||||
mpRefCount(NULL)
|
||||
{
|
||||
int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast<int>(aQuery.size()), &mpStmt, NULL);
|
||||
const int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast<int>(aQuery.size()), &mpStmt, NULL);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
throw SQLite::Exception(sqlite3_errmsg(mpSQLite));
|
||||
|
@ -38,6 +38,7 @@ Transaction::~Transaction() noexcept // nothrow
|
||||
catch (SQLite::Exception& e)
|
||||
{
|
||||
// Never throw an exception in a destructor
|
||||
(void)e; // warning proof
|
||||
SQLITECPP_ASSERT(false, e.what()); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
||||
}
|
||||
}
|
||||
@ -53,7 +54,7 @@ void Transaction::commit()
|
||||
}
|
||||
else
|
||||
{
|
||||
throw SQLite::Exception("Transaction already commited");
|
||||
throw SQLite::Exception("Transaction already commited.");
|
||||
}
|
||||
}
|
||||
|
||||
|
169
tests/Column_test.cpp
Normal file
169
tests/Column_test.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @file Column_test.cpp
|
||||
* @ingroup tests
|
||||
* @brief Test of a SQLiteCpp Column.
|
||||
*
|
||||
* Copyright (c) 2012-2015 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>
|
||||
|
||||
|
||||
TEST(Column, basis) {
|
||||
// Create a new database
|
||||
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
||||
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
||||
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
||||
|
||||
// Create a new table
|
||||
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL, binary BLOB, empty TEXT)"));
|
||||
EXPECT_TRUE(db.tableExists("test"));
|
||||
EXPECT_TRUE(db.tableExists(std::string("test")));
|
||||
EXPECT_EQ(0, db.getLastInsertRowid());
|
||||
|
||||
// Create a first row (autoid: 1) with all kind of data and a null value
|
||||
SQLite::Statement insert(db, "INSERT INTO test VALUES (NULL, \"first\", 123, 0.123, ?, NULL)");
|
||||
// Bind the blob value to the first parameter of the SQL query
|
||||
char buffer[] = "blob";
|
||||
void* blob = &buffer;
|
||||
int size = sizeof(buffer);
|
||||
insert.bind(1, blob, size);
|
||||
// Execute the one-step query to insert the row
|
||||
EXPECT_EQ(1, insert.exec());
|
||||
|
||||
EXPECT_EQ(1, db.getLastInsertRowid());
|
||||
EXPECT_EQ(1, db.getTotalChanges());
|
||||
|
||||
// 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.isOk());
|
||||
EXPECT_FALSE(query.isDone());
|
||||
|
||||
// validates every variant of cast operators, and conversions of types
|
||||
{
|
||||
sqlite3_int64 id = query.getColumn(0); // operator sqlite3_int64()
|
||||
int64_t id2 = query.getColumn(0); // operator sqlite3_int64() (or long() with GCC 64bits)
|
||||
long id3 = query.getColumn(0); // operator sqlite3_int64() (or long() with GCC 64bits)
|
||||
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*()
|
||||
const void* pempty = query.getColumn(5); // operator void*()
|
||||
EXPECT_EQ(1, id);
|
||||
EXPECT_EQ(1, id2);
|
||||
EXPECT_EQ(1, id3);
|
||||
EXPECT_STREQ("first", ptxt);
|
||||
EXPECT_EQ("first", msg);
|
||||
EXPECT_EQ(123, integer);
|
||||
EXPECT_EQ(0.123, real);
|
||||
EXPECT_EQ(0, memcmp("blob", pblob, size));
|
||||
EXPECT_EQ(NULL, pempty);
|
||||
}
|
||||
|
||||
// validates every variant of explicit getters
|
||||
{
|
||||
int64_t id = query.getColumn(0).getInt64();
|
||||
const char* ptxt = query.getColumn(1).getText();
|
||||
const std::string msg = query.getColumn(1).getText();
|
||||
const int integer = query.getColumn(2).getInt();
|
||||
const double real = query.getColumn(3).getDouble();
|
||||
const void* pblob = query.getColumn(1).getBlob();
|
||||
EXPECT_EQ(1, id);
|
||||
EXPECT_STREQ("first", ptxt);
|
||||
EXPECT_EQ("first", msg);
|
||||
EXPECT_EQ(123, integer);
|
||||
EXPECT_EQ(0.123, real);
|
||||
EXPECT_EQ(0, memcmp("first", pblob, 5));
|
||||
}
|
||||
|
||||
// Validate getBytes(), getType(), isInteger(), isNull()...
|
||||
EXPECT_EQ(SQLITE_INTEGER, query.getColumn(0).getType());
|
||||
EXPECT_EQ(true, query.getColumn(0).isInteger());
|
||||
EXPECT_EQ(false, query.getColumn(0).isFloat());
|
||||
EXPECT_EQ(false, query.getColumn(0).isText());
|
||||
EXPECT_EQ(false, query.getColumn(0).isBlob());
|
||||
EXPECT_EQ(false, query.getColumn(0).isNull());
|
||||
EXPECT_STREQ("1", query.getColumn(0).getText()); // convert to string
|
||||
EXPECT_EQ(1, query.getColumn(0).getBytes()); // size of the string "1" without the null terminator
|
||||
EXPECT_EQ(SQLITE_TEXT, query.getColumn(1).getType());
|
||||
EXPECT_EQ(false, query.getColumn(1).isInteger());
|
||||
EXPECT_EQ(false, query.getColumn(1).isFloat());
|
||||
EXPECT_EQ(true, query.getColumn(1).isText());
|
||||
EXPECT_EQ(false, query.getColumn(1).isBlob());
|
||||
EXPECT_EQ(false, query.getColumn(1).isNull());
|
||||
EXPECT_STREQ("first", query.getColumn(1).getText()); // convert to string
|
||||
EXPECT_EQ(5, query.getColumn(1).getBytes()); // size of the string "first"
|
||||
EXPECT_EQ(SQLITE_INTEGER, query.getColumn(2).getType());
|
||||
EXPECT_EQ(true, query.getColumn(2).isInteger());
|
||||
EXPECT_EQ(false, query.getColumn(2).isFloat());
|
||||
EXPECT_EQ(false, query.getColumn(2).isText());
|
||||
EXPECT_EQ(false, query.getColumn(2).isBlob());
|
||||
EXPECT_EQ(false, query.getColumn(2).isNull());
|
||||
EXPECT_STREQ("123", query.getColumn(2).getText()); // convert to string
|
||||
EXPECT_EQ(3, query.getColumn(2).getBytes()); // size of the string "123"
|
||||
EXPECT_EQ(SQLITE_FLOAT, query.getColumn(3).getType());
|
||||
EXPECT_EQ(false, query.getColumn(3).isInteger());
|
||||
EXPECT_EQ(true, query.getColumn(3).isFloat());
|
||||
EXPECT_EQ(false, query.getColumn(3).isText());
|
||||
EXPECT_EQ(false, query.getColumn(3).isBlob());
|
||||
EXPECT_EQ(false, query.getColumn(3).isNull());
|
||||
EXPECT_STREQ("0.123", query.getColumn(3).getText()); // convert to string
|
||||
EXPECT_EQ(5, query.getColumn(3).getBytes()); // size of the string "0.123"
|
||||
EXPECT_EQ(SQLITE_BLOB, query.getColumn(4).getType());
|
||||
EXPECT_EQ(false, query.getColumn(4).isInteger());
|
||||
EXPECT_EQ(false, query.getColumn(4).isFloat());
|
||||
EXPECT_EQ(false, query.getColumn(4).isText());
|
||||
EXPECT_EQ(true, query.getColumn(4).isBlob());
|
||||
EXPECT_EQ(false, query.getColumn(4).isNull());
|
||||
EXPECT_STREQ("blob", query.getColumn(4).getText()); // convert to string
|
||||
EXPECT_EQ(5, query.getColumn(4).getBytes()); // size of the string "blob" WITH the null terminator (blob)
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -21,7 +21,7 @@ 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 test that this assertion callback get called
|
||||
// TODO: unit test that this assertion callback get called (already tested manually)
|
||||
std::cout << "assertion_failed(" << apFile << ", " << apLine << ", " << apFunc << ", " << apExpr << ", " << apMsg << ")\n";
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,8 @@ TEST(Database, ctorExecCreateDropExist) {
|
||||
remove("test.db3");
|
||||
{
|
||||
// Try to open an unexisting database
|
||||
EXPECT_THROW(SQLite::Database not_found("test.db3"), SQLite::Exception);
|
||||
std::string filename = "test.db3";
|
||||
EXPECT_THROW(SQLite::Database not_found(filename), SQLite::Exception);
|
||||
|
||||
// Create a new database
|
||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
||||
@ -54,11 +55,75 @@ TEST(Database, ctorExecCreateDropExist) {
|
||||
remove("test.db3");
|
||||
}
|
||||
|
||||
TEST(Database, exec) {
|
||||
TEST(Database, createCloseReopen) {
|
||||
remove("test.db3");
|
||||
{
|
||||
// Try to open the unexisting 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);
|
||||
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
|
||||
}
|
||||
|
||||
#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());
|
||||
}
|
||||
}
|
||||
#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,
|
||||
@ -114,16 +179,11 @@ TEST(Database, exec) {
|
||||
EXPECT_EQ(4, db.getLastInsertRowid());
|
||||
EXPECT_EQ(9, db.getTotalChanges());
|
||||
#endif
|
||||
|
||||
} // Close DB test.db3
|
||||
remove("test.db3");
|
||||
}
|
||||
|
||||
TEST(Database, execAndGet) {
|
||||
remove("test.db3");
|
||||
{
|
||||
// Create a new database
|
||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
||||
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)");
|
||||
@ -136,16 +196,12 @@ TEST(Database, execAndGet) {
|
||||
// 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"));
|
||||
EXPECT_EQ(3, (int)db.execAndGet("SELECT weight FROM test WHERE value=\"first\""));
|
||||
} // Close DB test.db3
|
||||
remove("test.db3");
|
||||
EXPECT_EQ(3, db.execAndGet("SELECT weight FROM test WHERE value=\"first\"").getInt());
|
||||
}
|
||||
|
||||
TEST(Database, execException) {
|
||||
remove("test.db3");
|
||||
{
|
||||
// Create a new database
|
||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
||||
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE);
|
||||
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
||||
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
||||
|
||||
@ -170,6 +226,11 @@ TEST(Database, execException) {
|
||||
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);
|
||||
} // Close DB test.db3
|
||||
remove("test.db3");
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// TODO: test Database::createFunction()
|
||||
|
@ -18,10 +18,8 @@
|
||||
|
||||
|
||||
TEST(Statement, invalid) {
|
||||
remove("test.db3");
|
||||
{
|
||||
// Create a new database
|
||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
||||
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
||||
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
||||
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
||||
|
||||
@ -53,7 +51,7 @@ TEST(Statement, invalid) {
|
||||
EXPECT_FALSE(query.isOk());
|
||||
EXPECT_FALSE(query.isDone());
|
||||
|
||||
query.exec();
|
||||
query.executeStep();
|
||||
EXPECT_FALSE(query.isOk());
|
||||
EXPECT_TRUE( query.isDone());
|
||||
query.reset();
|
||||
@ -70,10 +68,81 @@ TEST(Statement, invalid) {
|
||||
EXPECT_EQ(SQLITE_RANGE, db.getErrorCode());
|
||||
EXPECT_EQ(SQLITE_RANGE, db.getExtendedErrorCode());
|
||||
|
||||
query.exec();
|
||||
query.exec(); // exec() instead of executeStep() as there is no result
|
||||
EXPECT_THROW(query.isColumnNull(0), SQLite::Exception);
|
||||
EXPECT_THROW(query.getColumn(0), SQLite::Exception);
|
||||
|
||||
} // Close DB test.db3
|
||||
remove("test.db3");
|
||||
// Add a first row
|
||||
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\")"));
|
||||
EXPECT_EQ(1, db.getLastInsertRowid());
|
||||
EXPECT_EQ(1, db.getTotalChanges());
|
||||
|
||||
query.reset();
|
||||
EXPECT_FALSE(query.isOk());
|
||||
EXPECT_FALSE(query.isDone());
|
||||
|
||||
EXPECT_THROW(query.exec(), SQLite::Exception); // exec() shall throw as it does not expect a result
|
||||
}
|
||||
|
||||
// TODO: test every kind of binding + clearBindings()
|
||||
|
||||
TEST(Statement, getColumnByName) {
|
||||
// Create a new database
|
||||
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
||||
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
||||
|
||||
// Create a new table
|
||||
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL)"));
|
||||
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
||||
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
||||
|
||||
// Create a first row
|
||||
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)"));
|
||||
EXPECT_EQ(1, db.getLastInsertRowid());
|
||||
EXPECT_EQ(1, db.getTotalChanges());
|
||||
|
||||
// Compile a SQL query
|
||||
SQLite::Statement query(db, "SELECT * FROM test");
|
||||
EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str());
|
||||
EXPECT_EQ(4, query.getColumnCount());
|
||||
query.executeStep();
|
||||
EXPECT_TRUE (query.isOk());
|
||||
EXPECT_FALSE(query.isDone());
|
||||
|
||||
// Look for unexisting columns
|
||||
EXPECT_THROW(query.getColumn("unknown"), SQLite::Exception);
|
||||
EXPECT_THROW(query.getColumn(""), SQLite::Exception);
|
||||
|
||||
const std::string msg = query.getColumn("msg");
|
||||
const int integer = query.getColumn("int");
|
||||
const double real = query.getColumn("double");
|
||||
EXPECT_EQ("first", msg);
|
||||
EXPECT_EQ(123, integer);
|
||||
EXPECT_EQ(0.123, real);
|
||||
}
|
||||
|
||||
TEST(Statement, 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)"));
|
||||
|
||||
// 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();
|
||||
|
||||
const std::string name0 = query.getColumnName(0);
|
||||
const std::string name1 = query.getColumnName(1);
|
||||
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.getColumnOriginName(0);
|
||||
const std::string oname1 = query.getColumnOriginName(1);
|
||||
EXPECT_EQ("id", oname0);
|
||||
EXPECT_EQ("msg", oname1);
|
||||
#endif
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user