抛弃GdCpp*.dll/pdb历史重新建库。libhv和Sqlite的dll保留
This commit is contained in:
52
include/CSQLite/CSQLite.h
Normal file
52
include/CSQLite/CSQLite.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file CSQLite.h
|
||||
* @ingroup CSQLite
|
||||
* @brief CSQLite 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.
|
||||
*
|
||||
CSQLite 是在 SQLiteCpp基础上修改而来,主要区别:
|
||||
|
||||
1. 减少抛异常。太多抛异常应用层代码就不好写了。
|
||||
DataBase类构造时不抛出异常,增加isNull()函数判断数据库打开是否成功。
|
||||
Statement bind类函数增加tryBind不抛异常的函数。
|
||||
|
||||
2. Statement类增加模板函数避免重复代码。
|
||||
|
||||
3. 针对Qt的修改
|
||||
Statement类里面用到的共享指针改成用QSharedPointer实现
|
||||
|
||||
4. 一些暂时没用上的功能还没加进来,用到再说。
|
||||
|
||||
*/
|
||||
/**
|
||||
* @defgroup CSQLite
|
||||
* @brief CSQLite is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Include useful headers of SQLiteC++
|
||||
//#include <SQLiteCpp/Assertion.h>
|
||||
#include "CSQLiteException.h"
|
||||
#include "CSQLiteDB.h"
|
||||
#include "CSQLiteStatement.h"
|
||||
#include "CSQLiteColumn.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 CSQLITE_VERSION "1.00.00" // 1.0.0
|
||||
#define CSQLITE_VERSION_NUMBER 1000000 // 1.0.0
|
||||
335
include/CSQLite/CSQLiteColumn.h
Normal file
335
include/CSQLite/CSQLiteColumn.h
Normal file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* @file CSQLiteColumn.h
|
||||
* @ingroup CSQLite
|
||||
* @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement.
|
||||
*
|
||||
* Copyright (c) 2012-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
*
|
||||
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
* or copy at http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "_Statement.h"
|
||||
#include <climits> // For INT_MAX
|
||||
#include "GdCPP_Exports.h"
|
||||
|
||||
/**
|
||||
* @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 GDCPP_API CSQLiteColumn
|
||||
{
|
||||
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
|
||||
*/
|
||||
CSQLiteColumn(_StatementPtr& aStmtPtr, int aIndex) noexcept: // nothrow
|
||||
mStmtPtr(aStmtPtr),
|
||||
mIndex(aIndex)
|
||||
{
|
||||
}
|
||||
/// Finalize and unregister the SQL query from the SQLite Database Connection.
|
||||
~CSQLiteColumn()
|
||||
{
|
||||
// the finalization will be done by the destructor of the last shared pointer
|
||||
}
|
||||
|
||||
// default copy constructor and assignment operator are perfectly suited :
|
||||
// they copy the Statement::Ptr which in turn increments the reference counter.
|
||||
|
||||
/// Make clang happy by explicitly implementing the copy-constructor:
|
||||
CSQLiteColumn(const CSQLiteColumn & aOther) :
|
||||
mStmtPtr(aOther.mStmtPtr),
|
||||
mIndex(aOther.mIndex)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
return sqlite3_column_name(*mStmtPtr, mIndex);
|
||||
}
|
||||
|
||||
#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 // nothrow
|
||||
{
|
||||
return sqlite3_column_origin_name(mStmtPtr, mIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Return the integer value of the column specified by its index starting at 0
|
||||
inline int getInt() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_int(*mStmtPtr, mIndex);
|
||||
}
|
||||
/// Return the 32bits unsigned integer value of the column specified by its index starting at 0
|
||||
/// (note that SQLite3 does not support unsigned 64bits).
|
||||
inline unsigned getUInt() const noexcept // nothrow
|
||||
{
|
||||
return static_cast<unsigned>(getInt());
|
||||
}
|
||||
/// Return the 64bits integer value of the column specified by its index starting at 0
|
||||
/// (note that SQLite3 does not support unsigned 64bits).
|
||||
inline long long getInt64() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_int64(*mStmtPtr, mIndex);
|
||||
}
|
||||
/// Return the double (64bits float) value of the column
|
||||
inline double getDouble() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_double(*mStmtPtr, mIndex);
|
||||
}
|
||||
/**
|
||||
* @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).
|
||||
*/
|
||||
inline const char* getText(const char* apDefaultValue = "") const noexcept // nothrow
|
||||
{
|
||||
const char* pText = reinterpret_cast<const char*>(sqlite3_column_text(*mStmtPtr, mIndex));
|
||||
return (pText?pText:apDefaultValue);
|
||||
}
|
||||
/**
|
||||
* @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).
|
||||
*/
|
||||
inline const void* getBlob() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_blob(*mStmtPtr, mIndex);
|
||||
}
|
||||
// /**
|
||||
// * @brief Return a std::string for a TEXT or BLOB column.
|
||||
// *
|
||||
// * Note this correctly handles strings that contain null bytes.
|
||||
// */
|
||||
// std::string getString() const;
|
||||
|
||||
/**
|
||||
* @brief Return the type of the value of the column
|
||||
*
|
||||
* Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null.
|
||||
*
|
||||
* @warning After a type conversion (by a call to a getXxx on a Column of a Yyy type),
|
||||
* the value returned by sqlite3_column_type() is undefined.
|
||||
*/
|
||||
inline int getType() const noexcept // nothrow
|
||||
{
|
||||
return sqlite3_column_type(*mStmtPtr, mIndex);
|
||||
}
|
||||
|
||||
|
||||
/// Test if the column is an integer type value (meaningful only before any conversion)
|
||||
inline bool isInteger() const noexcept // nothrow
|
||||
{
|
||||
return (SQLITE_INTEGER == getType());
|
||||
}
|
||||
/// Test if the column is a floating point type value (meaningful only before any conversion)
|
||||
inline bool isFloat() const noexcept // nothrow
|
||||
{
|
||||
return (SQLITE_FLOAT == getType());
|
||||
}
|
||||
/// Test if the column is a text type value (meaningful only before any conversion)
|
||||
inline bool isText() const noexcept // nothrow
|
||||
{
|
||||
return (SQLITE_TEXT == getType());
|
||||
}
|
||||
/// Test if the column is a binary blob type value (meaningful only before any conversion)
|
||||
inline bool isBlob() const noexcept // nothrow
|
||||
{
|
||||
return (SQLITE_BLOB == getType());
|
||||
}
|
||||
/// Test if the column is NULL (meaningful only before any conversion)
|
||||
inline bool isNull() const noexcept // nothrow
|
||||
{
|
||||
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
|
||||
{
|
||||
return sqlite3_column_bytes(*mStmtPtr, mIndex);
|
||||
}
|
||||
|
||||
|
||||
/// Alias returning the number of bytes used by the text (or blob) value of the column
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return getBytes ();
|
||||
}
|
||||
|
||||
/// Inline cast operator to char
|
||||
inline operator char() const
|
||||
{
|
||||
return static_cast<char>(getInt());
|
||||
}
|
||||
/// Inline cast operator to unsigned char
|
||||
inline operator unsigned char() const
|
||||
{
|
||||
return static_cast<unsigned char>(getInt());
|
||||
}
|
||||
/// Inline cast operator to short
|
||||
inline operator short() const
|
||||
{
|
||||
return static_cast<short>(getInt());
|
||||
}
|
||||
/// Inline cast operator to unsigned short
|
||||
inline operator unsigned short() const
|
||||
{
|
||||
return static_cast<unsigned short>(getInt());
|
||||
}
|
||||
|
||||
/// Inline cast operator to int
|
||||
inline operator int() const
|
||||
{
|
||||
return getInt();
|
||||
}
|
||||
/// Inline cast operator to 32bits unsigned integer
|
||||
inline operator unsigned int() const
|
||||
{
|
||||
return getUInt();
|
||||
}
|
||||
#if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW)
|
||||
/// Inline cast operator to 32bits long
|
||||
inline operator long() const
|
||||
{
|
||||
return getInt();
|
||||
}
|
||||
/// Inline cast operator to 32bits unsigned long
|
||||
inline operator unsigned long() const
|
||||
{
|
||||
return getUInt();
|
||||
}
|
||||
#else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS)
|
||||
/// Inline cast operator to 64bits long when the data model of the system is LP64 (Linux 64 bits...)
|
||||
inline operator long() const
|
||||
{
|
||||
return getInt64();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Inline cast operator to 64bits integer
|
||||
inline operator long long() const
|
||||
{
|
||||
return getInt64();
|
||||
}
|
||||
/// Inline cast operator to double
|
||||
inline operator double() const
|
||||
{
|
||||
return getDouble();
|
||||
}
|
||||
/**
|
||||
* @brief Inline cast operator to char*
|
||||
*
|
||||
* @see getText
|
||||
*/
|
||||
inline operator const char*() const
|
||||
{
|
||||
return getText();
|
||||
}
|
||||
/**
|
||||
* @brief Inline cast operator to void*
|
||||
*
|
||||
* @see getBlob
|
||||
*/
|
||||
inline operator const void*() const
|
||||
{
|
||||
return getBlob();
|
||||
}
|
||||
|
||||
//#if !defined(_MSC_VER) || _MSC_VER >= 1900
|
||||
// // 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>}’)
|
||||
// // and also required for Microsoft Visual Studio 2015 and newer
|
||||
// // but is not working under Microsoft Visual Studio 2010, 2012 and 2013
|
||||
// // (error C2440: 'initializing' : cannot convert from 'SQLite::Column' to 'std::basic_string<_Elem,_Traits,_Ax>'
|
||||
// // [...] constructor overload resolution was ambiguous)
|
||||
// // WARNING: without it, trying to access a binary blob with implicit cast to string
|
||||
// // ends up converting it to a C-style char*, damaging the data by truncating it to the first null character!
|
||||
// // (see https://github.com/SRombauts/SQLiteCpp/issues/189 Visual Studio 2013: unit test "Column.basis" failing)
|
||||
// /**
|
||||
// * @brief Inline cast operator to std::string
|
||||
// *
|
||||
// * Handles BLOB or TEXT, which may contain null bytes within
|
||||
// *
|
||||
// * @see getString
|
||||
// */
|
||||
// inline operator std::string() const
|
||||
// {
|
||||
// return getString();
|
||||
// }
|
||||
//#endif
|
||||
|
||||
private:
|
||||
_StatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object
|
||||
int mIndex; ///< Index of the column in the row of result, starting at 0
|
||||
};
|
||||
|
||||
///**
|
||||
// * @brief Standard std::ostream text inserter
|
||||
// *
|
||||
// * Insert the text value of the Column object, using getText(), into the provided stream.
|
||||
// *
|
||||
// * @param[in] aStream Stream to use
|
||||
// * @param[in] aColumn Column object to insert into the provided stream
|
||||
// *
|
||||
// * @return Reference to the stream used
|
||||
// */
|
||||
//std::ostream& operator<<(std::ostream& aStream, const CSQLiteColumn& aColumn);
|
||||
|
||||
//#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900)
|
||||
|
||||
//// Create an instance of T from the first N columns, see declaration in Statement.h for full details
|
||||
//template<typename T, int N>
|
||||
//T Statement::getColumns()
|
||||
//{
|
||||
// checkRow();
|
||||
// checkIndex(N - 1);
|
||||
// return getColumns<T>(std::make_integer_sequence<int, N>{});
|
||||
//}
|
||||
|
||||
//// Helper function called by getColums<typename T, int N>
|
||||
//template<typename T, const int... Is>
|
||||
//T Statement::getColumns(const std::integer_sequence<int, Is...>)
|
||||
//{
|
||||
// return T{Column(mStmtPtr, Is)...};
|
||||
//}
|
||||
|
||||
//#endif
|
||||
224
include/CSQLite/CSQLiteDB.h
Normal file
224
include/CSQLite/CSQLiteDB.h
Normal file
@@ -0,0 +1,224 @@
|
||||
#ifndef CSQLITEDB_H
|
||||
#define CSQLITEDB_H
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "CSQLiteException.h"
|
||||
#include "GdCPP_Exports.h"
|
||||
|
||||
class GDCPP_API CSQLiteDB
|
||||
{
|
||||
friend class CSQLiteStatement;
|
||||
public:
|
||||
/// 构造一个实例,未打开数据库
|
||||
CSQLiteDB() noexcept
|
||||
: dbErrCode(0)
|
||||
, mpSQLite(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open the provided database UTF-8 filename.
|
||||
*
|
||||
* @param[in] apFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter)
|
||||
* @param[in] aFlags flags for sqlite3_open_v2()
|
||||
*
|
||||
* 尽量使用后面的openRW()或openRO()
|
||||
*/
|
||||
int open(const char* apFilename, const int aFlags) noexcept;
|
||||
int open(const std::string &apFilename, const int aFlags) noexcept;
|
||||
|
||||
inline int openRW(const char* apFilename){
|
||||
return open(apFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
}
|
||||
|
||||
inline int openRW(const std::string& apFilename) {
|
||||
return open(apFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
}
|
||||
|
||||
inline int openRW(const std::filesystem::path& apFilename) {
|
||||
return open(apFilename.string(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
}
|
||||
|
||||
inline int openRO(const char* apFilename){
|
||||
return open(apFilename, SQLITE_OPEN_READONLY);
|
||||
}
|
||||
inline int openRO(const std::string& apFilename) {
|
||||
return open(apFilename, SQLITE_OPEN_READONLY);
|
||||
}
|
||||
inline int openRO(const std::filesystem::path& apFilename) {
|
||||
return open(apFilename.string(), SQLITE_OPEN_READONLY);
|
||||
}
|
||||
|
||||
int openInRam(const char* apFilename);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
virtual int close();
|
||||
|
||||
|
||||
virtual ~CSQLiteDB()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
int errcode() {
|
||||
return sqlite3_errcode(mpSQLite);
|
||||
}
|
||||
|
||||
const char* errmsg() {
|
||||
return sqlite3_errmsg(mpSQLite);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CSQLiteExceptionin case of error
|
||||
*/
|
||||
void setBusyTimeout(const int aBusyTimeoutMs);
|
||||
|
||||
/**
|
||||
* @brief Shortcut to execute one or multiple statements without results.
|
||||
*
|
||||
* This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||
*
|
||||
* @see Statement::exec() to handle precompiled statements (for better performances) without results
|
||||
* @see Statement::executeStep() to handle "SELECT" queries with results
|
||||
*
|
||||
* @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements
|
||||
*
|
||||
* @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements)
|
||||
* @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement.
|
||||
*
|
||||
* @throw CSQLiteExceptionin case of error
|
||||
*/
|
||||
int exec(const char* apQueries);
|
||||
|
||||
/**
|
||||
* @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 CSQLiteException in case of error
|
||||
*/
|
||||
bool tableExists(const char* apTableName);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
inline sqlite3* getHandle() const noexcept // nothrow
|
||||
{
|
||||
return mpSQLite;
|
||||
}
|
||||
|
||||
inline bool isOpened()
|
||||
{
|
||||
return mpSQLite!=nullptr;
|
||||
}
|
||||
|
||||
int dbErrCode;
|
||||
std::string dbErrMsg;
|
||||
private:
|
||||
/// @{ Database must be non-copyable
|
||||
CSQLiteDB(const CSQLiteDB&);
|
||||
CSQLiteDB& operator=(const CSQLiteDB&);
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @brief Check if aRet equal SQLITE_OK, else throw a CSQLiteException with the SQLite error message
|
||||
*/
|
||||
inline void check(const int aRet) const
|
||||
{
|
||||
if (SQLITE_OK != aRet)
|
||||
{
|
||||
throw CSQLiteException(mpSQLite, aRet);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
sqlite3* mpSQLite; ///< Pointer to SQLite Database Connection Handle
|
||||
std::string mFilename; ///< UTF-8 filename used to open the database
|
||||
|
||||
public:
|
||||
// 获取数据库文件名
|
||||
const std::string& getFilename() const
|
||||
{
|
||||
return mFilename;
|
||||
}
|
||||
|
||||
// 以下与事务相关
|
||||
// 标记是否启动了事务。true未启动事务
|
||||
bool bInTransaction = false;
|
||||
|
||||
inline bool startTransaction()
|
||||
{
|
||||
ASSERT(!bInTransaction);
|
||||
ASSERT(isOpened());
|
||||
try
|
||||
{
|
||||
exec("BEGIN TRANSACTION");
|
||||
bInTransaction = true;
|
||||
return true;
|
||||
}
|
||||
catch (CSQLiteException&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Rollback()
|
||||
{
|
||||
ASSERT(bInTransaction);
|
||||
ASSERT(isOpened());
|
||||
try
|
||||
{
|
||||
int ret = exec("ROLLBACK");
|
||||
bInTransaction = false;
|
||||
return true;
|
||||
}
|
||||
catch (CSQLiteException&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Commit()
|
||||
{
|
||||
ASSERT(bInTransaction);
|
||||
ASSERT(isOpened());
|
||||
try
|
||||
{
|
||||
int ret = exec("COMMIT");
|
||||
return true;
|
||||
}
|
||||
catch (CSQLiteException&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CSQLITEDB_H
|
||||
102
include/CSQLite/CSQLiteException.h
Normal file
102
include/CSQLite/CSQLiteException.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @file CSQLiteException.h
|
||||
* @ingroup CSQLite
|
||||
* @brief Encapsulation of the error message from SQLite3 on a std::runtime_error.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "GdCPP_Exports.h"
|
||||
|
||||
/**
|
||||
* @brief Encapsulation of the error message from SQLite3, based on std::runtime_error.
|
||||
*/
|
||||
class CSQLiteException : 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
|
||||
*/
|
||||
explicit CSQLiteException(const char* aErrorMessage):
|
||||
std::runtime_error(aErrorMessage),
|
||||
mErrcode(-1), // 0 would be SQLITE_OK, which doesn't make sense
|
||||
mExtendedErrcode(-1)
|
||||
{
|
||||
}
|
||||
explicit CSQLiteException(const std::string& aErrorMessage):
|
||||
std::runtime_error(aErrorMessage),
|
||||
mErrcode(-1), // 0 would be SQLITE_OK, which doesn't make sense
|
||||
mExtendedErrcode(-1)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
CSQLiteException(const char* aErrorMessage, int ret):
|
||||
std::runtime_error(aErrorMessage),
|
||||
mErrcode(ret),
|
||||
mExtendedErrcode(-1)
|
||||
{
|
||||
}
|
||||
CSQLiteException(const std::string& aErrorMessage, int ret):
|
||||
std::runtime_error(aErrorMessage),
|
||||
mErrcode(ret),
|
||||
mExtendedErrcode(-1)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @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 CSQLiteException(sqlite3* apSQLite):
|
||||
std::runtime_error(sqlite3_errmsg(apSQLite)),
|
||||
mErrcode(sqlite3_errcode(apSQLite)),
|
||||
mExtendedErrcode(sqlite3_extended_errcode(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.
|
||||
*/
|
||||
CSQLiteException(sqlite3* apSQLite, int ret):
|
||||
std::runtime_error(sqlite3_errmsg(apSQLite)),
|
||||
mErrcode(ret),
|
||||
mExtendedErrcode(sqlite3_extended_errcode(apSQLite))
|
||||
{
|
||||
}
|
||||
|
||||
/// Return the result code (if any, otherwise -1).
|
||||
inline int getErrorCode() const noexcept // nothrow
|
||||
{
|
||||
return mErrcode;
|
||||
}
|
||||
|
||||
/// Return the extended numeric result code (if any, otherwise -1).
|
||||
inline int getExtendedErrorCode() const noexcept // nothrow
|
||||
{
|
||||
return mExtendedErrcode;
|
||||
}
|
||||
|
||||
/// Return a string, solely based on the error code
|
||||
inline const char* getErrorStr() const noexcept
|
||||
{
|
||||
return sqlite3_errstr(mErrcode);
|
||||
}
|
||||
|
||||
private:
|
||||
int mErrcode; ///< Error code value
|
||||
int mExtendedErrcode; ///< Detailed error code if any
|
||||
};
|
||||
|
||||
734
include/CSQLite/CSQLiteStatement.h
Normal file
734
include/CSQLite/CSQLiteStatement.h
Normal file
@@ -0,0 +1,734 @@
|
||||
/**
|
||||
* @file CSQLiteStatement.h
|
||||
* @ingroup CSQLite
|
||||
* @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result.
|
||||
*
|
||||
修改自SQLiteCPP的Statement类。
|
||||
1. Ptr类改用_Statemen和QSharedPointer<_Statement>实现
|
||||
2. 增加errCode和errMsg保存错误信息
|
||||
3. 增加不同数据类型的tryBind(),不抛出异常,出错生成errCode和errMsg
|
||||
4. Bind类函数改成用模板函数实现index版和列名称版两套函数
|
||||
5. Column相关的函数去掉了checkRow()/checkIndex(aIndex)判断和抛出异常,由调用者确保有记录并且index不超界;
|
||||
6. bind一个u32型数据内部转成int,适用于不需要排序的u32列。如果需要排序,强制转成int64再bind
|
||||
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CSQLiteException.h"
|
||||
#include "CSQLiteColumn.h"
|
||||
#include <climits> // For INT_MAX
|
||||
#include <map>
|
||||
#include "_Statement.h"
|
||||
#include "CSQLiteColumn.h"
|
||||
#include "GdCPP_Exports.h"
|
||||
|
||||
#pragma comment(lib, "sqlite3.lib")
|
||||
|
||||
// Forward declaration
|
||||
class CSQLiteDB;
|
||||
|
||||
/**
|
||||
* @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 GDCPP_API CSQLiteStatement
|
||||
{
|
||||
// friend class CSQLiteColumn; // For access to inner member
|
||||
|
||||
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.
|
||||
*/
|
||||
CSQLiteStatement(CSQLiteDB& 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.
|
||||
*/
|
||||
CSQLiteStatement(CSQLiteDB& aDatabase, const std::string& aQuery);
|
||||
|
||||
/**
|
||||
* @brief Move an SQLite statement.
|
||||
*
|
||||
* @param[in] aStatement Statement to move
|
||||
*/
|
||||
CSQLiteStatement(CSQLiteStatement&& aStatement) noexcept;
|
||||
|
||||
/// Finalize and unregister the SQL query from the SQLite Database Connection.
|
||||
~CSQLiteStatement();
|
||||
|
||||
/// Reset the statement to make it ready for a new execution. Throws an exception on error.
|
||||
inline void reset()
|
||||
{
|
||||
const int ret = tryReset();
|
||||
check(ret);
|
||||
}
|
||||
|
||||
/// Reset the statement. Returns the sqlite result code instead of throwing an exception on error.
|
||||
inline int tryReset() noexcept
|
||||
{
|
||||
mbHasRow = false;
|
||||
mbDone = false;
|
||||
return sqlite3_reset(*mStmtPtr);
|
||||
}
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
inline void clearBindings() // throw(SQLite::Exception)
|
||||
{
|
||||
const int ret = sqlite3_clear_bindings(*mStmtPtr);
|
||||
check(ret);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// 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()
|
||||
|
||||
/**
|
||||
* @brief Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const int aValue)
|
||||
{
|
||||
errCode = sqlite3_bind_int(*mStmtPtr, aIndex, aValue);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with int value ";
|
||||
errMsg += std::to_string(aValue);
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const unsigned aValue)
|
||||
{
|
||||
errCode = sqlite3_bind_int(*mStmtPtr, aIndex, int(aValue));
|
||||
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with uint value ";
|
||||
errMsg += std::to_string(aValue);
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const int64_t aValue)
|
||||
{
|
||||
errCode = sqlite3_bind_int64(*mStmtPtr, aIndex, aValue);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with int64_t value ";
|
||||
errMsg += std::to_string(aValue);
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @brief Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const double aValue)
|
||||
{
|
||||
errCode = sqlite3_bind_double(*mStmtPtr, aIndex, aValue);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with double value ";
|
||||
errMsg += std::to_string(aValue);
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const std::string& aValue)
|
||||
{
|
||||
errCode = sqlite3_bind_text(*mStmtPtr, aIndex, aValue.c_str(),
|
||||
static_cast<int>(aValue.size()), SQLITE_TRANSIENT);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with string ";
|
||||
errMsg += aValue;
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const char* apValue)
|
||||
{
|
||||
errCode = sqlite3_bind_text(*mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with const char * ";
|
||||
errMsg += apValue;
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
inline int tryBind(const int aIndex, const void* apValue, const int aSize)
|
||||
{
|
||||
errCode = sqlite3_bind_blob(*mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bind index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with blob ";
|
||||
// errMsg += std::to_string(aValue);
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
int tryBindNoCopy(const int aIndex, const std::string& aValue)
|
||||
{
|
||||
errCode = sqlite3_bind_text(*mStmtPtr, aIndex, aValue.c_str(),
|
||||
static_cast<int>(aValue.size()), SQLITE_STATIC);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bindNoCopy index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with string ";
|
||||
errMsg += aValue;
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
int tryBindNoCopy(const int aIndex, const char* apValue)
|
||||
{
|
||||
errCode = sqlite3_bind_text(*mStmtPtr, aIndex, apValue, -1, SQLITE_STATIC);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bindNoCopy index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with const char*: ";
|
||||
errMsg += apValue;
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
int tryBindNoCopy(const int aIndex, const void* apValue, const int aSize)
|
||||
{
|
||||
errCode = sqlite3_bind_blob(*mStmtPtr, aIndex, apValue, aSize, SQLITE_STATIC);
|
||||
if (SQLITE_OK != errCode)
|
||||
{
|
||||
errMsg ="Fail to bindNoCopy blob to index ";
|
||||
errMsg += std::to_string(aIndex);
|
||||
errMsg += " with void *, size ";
|
||||
errMsg += std::to_string(aSize);
|
||||
errMsg += ", errCode=";
|
||||
errMsg += std::to_string(errCode);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void bind(const int aIndex, T aValue)
|
||||
{
|
||||
const int ret = tryBind(aIndex, aValue);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
throw CSQLiteException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void bindNoCopy(const int aIndex, T aValue)
|
||||
{
|
||||
const int ret = tryBindNoCopy(aIndex, aValue);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
throw CSQLiteException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void bind(const char* apName, T aValue)
|
||||
{
|
||||
const int index = sqlite3_bind_parameter_index(*mStmtPtr, apName);
|
||||
if(index==0){
|
||||
std::string errstr("sqlite3_bind_parameter_index fail for ");
|
||||
errstr += apName;
|
||||
throw CSQLiteException(errstr);
|
||||
}
|
||||
bind(index, aValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void bindNoCopy(const char* apName, T aValue)
|
||||
{
|
||||
const int index = sqlite3_bind_parameter_index(*mStmtPtr, apName);
|
||||
if(index==0){
|
||||
std::string errstr("sqlite3_bind_parameter_index fail for ");
|
||||
errstr += apName;
|
||||
throw CSQLiteException(errstr);
|
||||
}
|
||||
bind(index, aValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void bind(const std::string& aName, T aValue)
|
||||
{
|
||||
bind(aName.c_str(), aValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
inline void bindNull(const int aIndex)
|
||||
{
|
||||
const int ret = sqlite3_bind_null(*mStmtPtr, aIndex);
|
||||
check(ret);
|
||||
}
|
||||
inline void bindNull(const char* apName)
|
||||
{
|
||||
const int index = sqlite3_bind_parameter_index(*mStmtPtr, apName);
|
||||
if(index==0){
|
||||
std::string errstr("sqlite3_bind_parameter_index fail for ");
|
||||
errstr += apName;
|
||||
throw CSQLiteException(errstr);
|
||||
}
|
||||
bindNull(index);
|
||||
}
|
||||
inline void bindNull(const std::string& aName)
|
||||
{
|
||||
bindNull(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
|
||||
* thru 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 CSQLiteDB::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 CSQLiteDB::exec() is a shortcut to execute one or multiple statements without results
|
||||
*
|
||||
* @return the sqlite result code.
|
||||
*/
|
||||
int tryExecuteStep() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Execute a one-step query with no expected result.
|
||||
*
|
||||
* This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
|
||||
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
|
||||
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
|
||||
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
|
||||
*
|
||||
* It is similar to CSQLiteDB::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 CSQLiteDB::exec() is a shortcut to execute one or multiple statements without results
|
||||
*
|
||||
* @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE)
|
||||
*
|
||||
* @throw SQLite::Exception in case of error, or if row of results are returned !
|
||||
*/
|
||||
int exec();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Return a copy of the column data specified by its index
|
||||
*
|
||||
* Can be used to access the data of the current row of result when applicable,
|
||||
* while the executeStep() method returns true.
|
||||
*
|
||||
* Throw an exception if there is no row to return a Column from:
|
||||
* - if provided index is out of bound
|
||||
* - before any executeStep() call
|
||||
* - after the last executeStep() returned false
|
||||
* - after a reset() call
|
||||
*
|
||||
* Throw an exception if the specified index is out of the [0, getColumnCount()) range.
|
||||
*
|
||||
* @param[in] aIndex Index of the column, starting at 0
|
||||
*
|
||||
* @note This method is not const, reflecting the fact that the returned Column object will
|
||||
* share the ownership of the underlying sqlite3_stmt.
|
||||
*
|
||||
* @warning The resulting Column object must not be memorized "as-is".
|
||||
* Is is only a wrapper around the current result row, so it is only valid
|
||||
* while the row from the Statement remains valid, that is only until next executeStep() call.
|
||||
* Thus, you should instead extract immediately its data (getInt(), getText()...)
|
||||
* and use or copy this data for any later usage.
|
||||
*
|
||||
* 注意调用者必须保证有一行数据,且index在[0, getColumnCount())范围内.
|
||||
* 本类只想做简单的封装,不想到处抛出异常
|
||||
*/
|
||||
inline CSQLiteColumn getColumn(const int aIndex)
|
||||
{
|
||||
// checkRow();
|
||||
// checkIndex(aIndex);
|
||||
|
||||
// Share the Statement Object handle with the new Column created
|
||||
return CSQLiteColumn(mStmtPtr, aIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a copy of the column data specified by its column name (less efficient than using an index)
|
||||
*
|
||||
* Can be used to access the data of the current row of result when applicable,
|
||||
* while the executeStep() method returns true.
|
||||
*
|
||||
* Throw an exception if there is no row to return a Column from :
|
||||
* - if provided name is not one of the aliased column names
|
||||
* - before any executeStep() call
|
||||
* - after the last executeStep() returned false
|
||||
* - after a reset() call
|
||||
*
|
||||
* Throw an exception if the specified name is not an on of the aliased name of the columns in the result.
|
||||
*
|
||||
* @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name)
|
||||
*
|
||||
* @note Uses a map of column names to indexes, build on first call.
|
||||
*
|
||||
* @note This method is not const, reflecting the fact that the returned Column object will
|
||||
* share the ownership of the underlying sqlite3_stmt.
|
||||
*
|
||||
* @warning The resulting Column object must not be memorized "as-is".
|
||||
* Is is only a wrapper around the current result row, so it is only valid
|
||||
* while the row from the Statement remains valid, that is only until next executeStep() call.
|
||||
* Thus, you should instead extract immediately its data (getInt(), getText()...)
|
||||
* and use or copy this data for any later usage.
|
||||
*
|
||||
* Throw an exception if the specified name is not one of the aliased name of the columns in the result.
|
||||
* 注意调用者必须保证有一行数据,且名称是有效的.
|
||||
* 本类只想做简单的封装,不想到处抛出异常
|
||||
*/
|
||||
inline CSQLiteColumn getColumn(const char* apName)
|
||||
{
|
||||
// checkRow();
|
||||
const int index = getColumnIndex(apName); //注意名字不对会抛异常
|
||||
|
||||
// Share the Statement Object handle with the new Column created
|
||||
return CSQLiteColumn(mStmtPtr, index);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900)
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* 注意调用者必须保证有一行数据,且index在[0, getColumnCount())范围内,避免抛出异常
|
||||
*/
|
||||
bool isColumnNull(const int aIndex) const
|
||||
{
|
||||
// checkRow();
|
||||
// checkIndex(aIndex);
|
||||
return (SQLITE_NULL == sqlite3_column_type(*mStmtPtr, aIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* 注意调用者必须保证有一行数据,且列名称是有效的,避免抛出异常
|
||||
*/
|
||||
bool isColumnNull(const char* apName) const
|
||||
{
|
||||
// checkRow();
|
||||
const int index = getColumnIndex(apName); //
|
||||
return (SQLITE_NULL == sqlite3_column_type(*mStmtPtr, index));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the named assigned to the specified result column (potentially aliased)
|
||||
*
|
||||
* @param[in] aIndex Index of the column in the range [0, getColumnCount()).
|
||||
*
|
||||
* @see getColumnOriginName() to get original column name (not aliased)
|
||||
*
|
||||
* Throw an exception if the specified index is out of the [0, getColumnCount()) range.
|
||||
*/
|
||||
const char* getColumnName(const int aIndex) const;
|
||||
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
/**
|
||||
* @brief Return a pointer to the table column name that is the origin of the specified result column
|
||||
*
|
||||
* Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro :
|
||||
* - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance),
|
||||
* - and also when compiling this wrapper.
|
||||
*
|
||||
* Throw an exception if the specified index is out of the [0, getColumnCount()) range.
|
||||
*/
|
||||
const char* getColumnOriginName(const int aIndex) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return the index of the specified (potentially aliased) column name
|
||||
*
|
||||
* @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name)
|
||||
*
|
||||
* @note Uses a map of column names to indexes, build on first call.
|
||||
*
|
||||
* Throw an exception if the specified name is not known.
|
||||
*/
|
||||
int getColumnIndex(const char* apName) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Return the UTF-8 SQL Query.
|
||||
inline const std::string& getQuery() const
|
||||
{
|
||||
return mQuery;
|
||||
}
|
||||
|
||||
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
|
||||
std::string getExpandedSQL();
|
||||
|
||||
/// Return the number of columns in the result set returned by the prepared statement
|
||||
inline int getColumnCount() const
|
||||
{
|
||||
return mColumnCount;
|
||||
}
|
||||
/// true when a row has been fetched with executeStep()
|
||||
inline bool hasRow() const
|
||||
{
|
||||
return mbHasRow;
|
||||
}
|
||||
|
||||
/// true when the last executeStep() had no more row to fetch
|
||||
inline bool isDone() const
|
||||
{
|
||||
return mbDone;
|
||||
}
|
||||
|
||||
inline bool isValid()
|
||||
{
|
||||
return bool(mStmtPtr);
|
||||
}
|
||||
|
||||
/// 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; // nothrow
|
||||
/// Return the extended numeric result code for the most recent failed API call (if any).
|
||||
int getExtendedErrorCode() const noexcept; // nothrow
|
||||
/// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
|
||||
const char* getErrorMsg() const noexcept; // nothrow
|
||||
|
||||
int finalize() noexcept {
|
||||
return sqlite3_finalize(*mStmtPtr);
|
||||
}
|
||||
private:
|
||||
/// @{ CSQLiteStatement must be non-copyable
|
||||
CSQLiteStatement(const CSQLiteStatement&);
|
||||
CSQLiteStatement& operator=(const CSQLiteStatement&);
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
inline void check(const int aRet) const
|
||||
{
|
||||
if (SQLITE_OK != aRet)
|
||||
{
|
||||
throw CSQLiteException(*mStmtPtr, aRet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception.
|
||||
*/
|
||||
inline void checkRow() const
|
||||
{
|
||||
if (false == mbHasRow)
|
||||
{
|
||||
throw CSQLiteException("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 CSQLiteException("Column index out of range.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Map of columns index by name (mutable so getColumnIndex can be const)
|
||||
typedef std::map<std::string, int> TColumnNames;
|
||||
|
||||
private:
|
||||
std::string mQuery; //!< UTF-8 SQL Query
|
||||
_StatementPtr mStmtPtr; //!< Shared Pointer to the prepared SQLite Statement Object
|
||||
int mColumnCount; //!< Number of columns in the result of the prepared statement
|
||||
mutable TColumnNames mColumnNames; //!< Map of columns index by name (mutable so getColumnIndex can be const)
|
||||
bool mbHasRow; //!< true when a row has been fetched with executeStep()
|
||||
bool mbDone; //!< true when the last executeStep() had no more row to fetch
|
||||
|
||||
public:
|
||||
int errCode;
|
||||
std::string errMsg;
|
||||
};
|
||||
|
||||
71
include/CSQLite/CSQLiteTransaction.h
Normal file
71
include/CSQLite/CSQLiteTransaction.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @file Transaction.h
|
||||
* @ingroup SQLiteCpp
|
||||
* @brief A Transaction is way to group multiple SQL statements into an atomic secured operation.
|
||||
*
|
||||
* Copyright (c) 2012-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
*
|
||||
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
* or copy at http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GdCPP_Exports.h"
|
||||
|
||||
// Forward declaration
|
||||
class CSQLiteDB;
|
||||
|
||||
|
||||
/**
|
||||
* @brief RAII encapsulation of a SQLite Transaction.
|
||||
*
|
||||
* A Transaction is a way to group multiple SQL statements into an atomic secured operation;
|
||||
* either it succeeds, with all the changes committed to the database file,
|
||||
* or if it fails, all the changes are rolled back to the initial state.
|
||||
*
|
||||
* Resource Acquisition Is Initialization (RAII) means that the Transaction
|
||||
* begins in the constructor and is rollbacked in the destructor, so that there is
|
||||
* no need to worry about memory management or the validity of the underlying SQLite Connection.
|
||||
*
|
||||
* This method also offers big performances improvements compared to individually executed statements.
|
||||
*
|
||||
* Thread-safety: a Transaction object shall not be shared by multiple threads, because :
|
||||
* 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads
|
||||
* provided that no single database connection is used simultaneously in two or more threads."
|
||||
* 2) the SQLite "Serialized" mode is not supported by SQLiteC++,
|
||||
* because of the way it shares the underling SQLite precompiled statement
|
||||
* in a custom shared pointer (See the inner class "Statement::Ptr").
|
||||
*/
|
||||
class GDCPP_API CSQLiteTransaction
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Begins the SQLite transaction
|
||||
*
|
||||
* @param[in] aDatabase the SQLite Database Connection
|
||||
*
|
||||
* Exception is thrown in case of error, then the Transaction is NOT initiated.
|
||||
*/
|
||||
explicit CSQLiteTransaction(CSQLiteDB& aDatabase, bool startnow=true);
|
||||
|
||||
/**
|
||||
* @brief Safely rollback the transaction if it has not been committed.
|
||||
*/
|
||||
~CSQLiteTransaction();
|
||||
|
||||
/**
|
||||
* @brief Commit the transaction.
|
||||
*/
|
||||
void commit();
|
||||
|
||||
private:
|
||||
// Transaction must be non-copyable
|
||||
CSQLiteTransaction(const CSQLiteTransaction&);
|
||||
CSQLiteTransaction& operator=(const CSQLiteTransaction&);
|
||||
/// @}
|
||||
|
||||
private:
|
||||
CSQLiteDB& mDatabase; ///< Reference to the SQLite Database Connection
|
||||
bool mbCommited; ///< True when commit has been called
|
||||
};
|
||||
|
||||
64
include/CSQLite/_Statement.h
Normal file
64
include/CSQLite/_Statement.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef _CSQLITE_STATEMENT_INNER_H
|
||||
#define _CSQLITE_STATEMENT_INNER_H
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "CSQLiteException.h"
|
||||
#include <memory>
|
||||
|
||||
/// 封装一对数据库指针和Stmt指针,用于一个共享指针_StatementPtr
|
||||
class _Statement
|
||||
{
|
||||
public:
|
||||
_Statement(sqlite3* apSQLite, const char * aQuery)
|
||||
: mpSQLite(apSQLite)
|
||||
, mpStmt(nullptr)
|
||||
{
|
||||
const int ret = sqlite3_prepare_v2(apSQLite, aQuery, -1, &mpStmt, nullptr);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
throw CSQLiteException(apSQLite, ret);
|
||||
}
|
||||
}
|
||||
|
||||
_Statement(sqlite3* apSQLite, const std::string& aQuery)
|
||||
: mpSQLite(apSQLite)
|
||||
, mpStmt(nullptr)
|
||||
{
|
||||
const int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast<int>(aQuery.size()), &mpStmt, nullptr);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
throw CSQLiteException(apSQLite, ret);
|
||||
}
|
||||
}
|
||||
|
||||
~_Statement()
|
||||
{
|
||||
// If count reaches zero, finalize the sqlite3_stmt, as no Statement nor Column objet use it anymore.
|
||||
// No need to check the return code, as it is the same as the last statement evaluation.
|
||||
sqlite3_finalize(mpStmt);
|
||||
mpSQLite = nullptr;
|
||||
mpStmt = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle
|
||||
sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object
|
||||
|
||||
public:
|
||||
/// Inline cast operator returning the pointer to SQLite Database Connection Handle
|
||||
/// for functions like:
|
||||
inline operator sqlite3*() const
|
||||
{
|
||||
return mpSQLite;
|
||||
}
|
||||
|
||||
/// Inline cast operator returning the pointer to SQLite Statement Object
|
||||
/// for function like: sqlite3_column_xxx()
|
||||
inline operator sqlite3_stmt*() const
|
||||
{
|
||||
return mpStmt;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<_Statement> _StatementPtr;
|
||||
#endif // _STATEMENT_H
|
||||
Reference in New Issue
Block a user