SQLite3 C++ usage demo

cpp 复制代码
#include <iostream>
#include <atomic>
#include <iomanip>
#include <functional>

#include "sqlite3/sqlite3.h"

#pragma comment(lib, "sqlite3/sqlite3.lib")

#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

class Sqlite3Database {
	struct PreparedStatement {
		~PreparedStatement() noexcept {
			sqlite3_stmt* stmt = p;
			if (stmt) {
				p = NULL;
				sqlite3_finalize(stmt);
			}
		}
		sqlite3_stmt*					p = NULL;
	};

public:
	~Sqlite3Database() noexcept;
	bool								Open(const char* szDatabaseFilePath) noexcept;
	bool								IsAvailable() noexcept { return NULL != pDB_; }
	std::string							GetDatabaseFilePath() noexcept { return pDatabaseFilePath_; }

public:
	int									ExecuteNonQuery(const char* szSQL, const char** szParams, int nParamCount) noexcept;
	
	template <typename StringIterator>
	int									ExecuteNonQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl) noexcept;

public:
	using								RowHandler = std::function<bool(Sqlite3Database*, sqlite3_stmt*)>;

	template <typename StringIterator>
	int									ExecuteQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl, const RowHandler& rows) noexcept;

private:
	bool								Finalize(sqlite3* pDB) noexcept;
	int									Internal_ExecuteQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*), bool (*cbRow)(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept;
	int									Internal_ExecuteNonQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept;

private:
	std::atomic<sqlite3*>				pDB_ = NULL;
	std::string							pDatabaseFilePath_;
};

Sqlite3Database::~Sqlite3Database() noexcept {
	Finalize(pDB_.exchange(NULL));
}

bool Sqlite3Database::Open(const char* szDatabaseFilePath) noexcept {
	if (NULL == szDatabaseFilePath || *szDatabaseFilePath == '\x0') {
		return false;
	}

	sqlite3* pDB;
	int rc = sqlite3_open(szDatabaseFilePath, &pDB);
	if (rc) {
		return false; // 打开数据库失败
	}

	Finalize(pDB_.exchange(pDB));
	pDatabaseFilePath_ = szDatabaseFilePath;
	return true;
}

bool Sqlite3Database::Finalize(sqlite3* pDB) noexcept {
	if (NULL == pDB) {
		return false;
	}

	if (pDB) {
		sqlite3_close(pDB);
		return true;
	}

	return false;
}

int Sqlite3Database::Internal_ExecuteNonQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept {
	sqlite3* pDB = pDB_;
	if (NULL == pDB) {
		return -1;
	}

	PreparedStatement stStmt;
	if (sqlite3_prepare_v2(pDB, szSQL, -1, &stStmt.p, NULL) != SQLITE_OK) {
		return -1;
	}

	if (!cbFillParams(this, stStmt.p, pState)) {
		return -1;
	}

	if (sqlite3_step(stStmt.p) != SQLITE_DONE) {
		return -1;
	}

	return sqlite3_changes(pDB);
}

int Sqlite3Database::ExecuteNonQuery(const char* szSQL, const char** szParams, int nParamCount) noexcept {
	if (NULL == szSQL || *szSQL == '\x0') {
		return -1;
	}

	if (nParamCount < 0 || !szParams && nParamCount != 0) {
		return -1;
	}

	struct ExecuteNonQueryContext {
		const char**    szParams;
		int				nParamCount;
	} stExecuteNonQueryContext;

	stExecuteNonQueryContext.szParams    = szParams;
	stExecuteNonQueryContext.nParamCount = nParamCount;

	return Internal_ExecuteNonQuery(szSQL, &stExecuteNonQueryContext,
		[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept {
			ExecuteNonQueryContext* pCtx = static_cast<ExecuteNonQueryContext*>(p);
			for (int i = 0; i < pCtx->nParamCount; i++) {
				const char* szParam = pCtx->szParams[i];
				if (!szParam) {
					szParam = "";
				}

				if (sqlite3_bind_text(pStmt, i + 1, szParam, -1, SQLITE_STATIC) != SQLITE_OK) {
					return false;
				}
			}

			return true;
		});
}

template <typename StringIterator>
inline
int Sqlite3Database::ExecuteNonQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl) noexcept {
	if (NULL == szSQL || *szSQL == '\x0') {
		return -1;
	}

	struct ExecuteNonQueryContext {
		StringIterator itTail;
		StringIterator itEndl;
	} stExecuteNonQueryContext{ itParamTail, itParamEndl };

	return Internal_ExecuteNonQuery(szSQL, &stExecuteNonQueryContext,
		[](Sqlite3Database*, sqlite3_stmt* pStmt, void* p) noexcept -> bool {
			auto* pCtx = static_cast<ExecuteNonQueryContext*>(p);
			int iParamIndex = 1;
			for (auto it = pCtx->itTail; it != pCtx->itEndl; ++it) {
				const char* szParam = *it;
				if (!szParam) {
					szParam = "";
				}

				if (sqlite3_bind_text(pStmt, iParamIndex++, szParam, -1, SQLITE_STATIC) != SQLITE_OK) {
					return false;
				}
			}
			return true;
		});
}

int Sqlite3Database::Internal_ExecuteQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*), bool (*cbRow)(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept {
	sqlite3* pDB = pDB_;
	if (NULL == pDB) {
		return -1;
	}

	PreparedStatement stStmt;
	int rc = sqlite3_prepare_v2(pDB, szSQL, -1, &stStmt.p, NULL);
	if (rc != SQLITE_OK) {
		return -1;
	}

	if (!cbFillParams(this, stStmt.p, pState)) {
		return -1;
	}

	int iRowCount = 0;
	while (sqlite3_step(stStmt.p) == SQLITE_ROW) {
		if (!cbRow(this, stStmt.p, pState)) {
			break;   // 用户要求停止
		}

		++iRowCount;
	}

	return iRowCount;   // 返回实际读取的行数
}

template <typename StringIterator>
inline
int Sqlite3Database::ExecuteQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl, const RowHandler& rows) noexcept {
	if (NULL == szSQL || *szSQL == '\x0') {
		return -1;
	}

	if (!rows) {
		return -1;
	}

	struct ExecuteQueryContext {
		StringIterator	itParamTail;
		StringIterator	itParamEndl;
		RowHandler		fnRows;
	} stExecuteQueryContext;

	stExecuteQueryContext.fnRows      = rows;
	stExecuteQueryContext.itParamTail = itParamTail;
	stExecuteQueryContext.itParamEndl = itParamEndl;

	auto fnFillParams = 
		[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept -> bool {
			ExecuteQueryContext* pCtx = static_cast<ExecuteQueryContext*>(p);
			for (int iParamIndex = 1; pCtx->itParamTail != pCtx->itParamTail; ++pCtx->itParamTail) {
				const char* szParam = *pCtx->itParamEndl;
				if (!szParam) {
					szParam = "";
				}

				if (sqlite3_bind_text(pStmt, iParamIndex++, szParam, -1, SQLITE_STATIC) != SQLITE_OK) {
					return false;
				}
			}

			return true;
		};

	auto fnRows = 
		[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept -> bool {
			ExecuteQueryContext* pCtx = static_cast<ExecuteQueryContext*>(p);
			return pCtx->fnRows(pDB, pStmt);
		};
	return Internal_ExecuteQuery(szSQL, &stExecuteQueryContext, fnFillParams, fnRows);
}

// ----------------------------------------------------------------------
// Demo
// ----------------------------------------------------------------------
int main() {
	Sqlite3Database db;
	if (!db.Open("demo.db")) {
		std::cerr << "Failed to open database" << std::endl;
		return 1;
	}

	// Drop old table to ensure clean state
	db.ExecuteNonQuery("DROP TABLE IF EXISTS users;", nullptr, 0);

	// Create table
	const char* createSQL =
		"CREATE TABLE IF NOT EXISTS users ("
		"id INTEGER PRIMARY KEY AUTOINCREMENT,"
		"name TEXT NOT NULL,"
		"age INTEGER"
		");";
	if (db.ExecuteNonQuery(createSQL, nullptr, 0) < 0) {
		std::cerr << "Failed to create table" << std::endl;
		return 1;
	}

	// Insert data
	std::vector<const char*> insertParams = { "Alice", "25" };
	int affected = db.ExecuteNonQuery(
		"INSERT INTO users (name, age) VALUES (?, ?);",
		insertParams.begin(), insertParams.end()
	);
	std::cout << "Rows inserted: " << affected << std::endl;

	insertParams = { "Bob", "30" };
	affected = db.ExecuteNonQuery(
		"INSERT INTO users (name, age) VALUES (?, ?);",
		insertParams.begin(), insertParams.end()
	);
	std::cout << "Rows inserted: " << affected << std::endl;

	// ------------------------------------------------------------------
	// Dynamic printer: automatically detects column names and types
	// ------------------------------------------------------------------
	auto printRowDynamic = [](Sqlite3Database*, sqlite3_stmt* stmt) -> bool {
		static bool headerPrinted = false;
		int colCount = sqlite3_column_count(stmt);

		if (!headerPrinted) {
			for (int i = 0; i < colCount; ++i) {
				const char* colName = sqlite3_column_name(stmt, i);
				std::cout << std::setw(15) << (colName ? colName : "?");
				if (i != colCount - 1) std::cout << " ";
			}
			std::cout << std::endl;
			for (int i = 0; i < colCount; ++i) {
				std::cout << std::setw(15) << "-----------------";
				if (i != colCount - 1) std::cout << " ";
			}
			std::cout << std::endl;
			headerPrinted = true;
		}

		for (int i = 0; i < colCount; ++i) {
			int type = sqlite3_column_type(stmt, i);
			switch (type) {
			case SQLITE_INTEGER:
				std::cout << std::setw(15) << sqlite3_column_int64(stmt, i);
				break;
			case SQLITE_FLOAT:
				std::cout << std::setw(15) << sqlite3_column_double(stmt, i);
				break;
			case SQLITE_TEXT: {
				const char* text = (const char*)sqlite3_column_text(stmt, i);
				std::cout << std::setw(15) << (text ? text : "NULL");
				break;
			}
			case SQLITE_BLOB:
				std::cout << std::setw(15) << "[BLOB]";
				break;
			case SQLITE_NULL:
				std::cout << std::setw(15) << "NULL";
				break;
			default:
				std::cout << std::setw(15) << "?";
				break;
			}
			if (i != colCount - 1) std::cout << " ";
		}
		std::cout << std::endl;
		return true;
	};

	// Query all
	std::cout << "\nAll users (dynamic printer):" << std::endl;
	int rowCount = db.ExecuteQuery(
		"SELECT id, name, age FROM users;",
		std::vector<const char*>{}.begin(),
		std::vector<const char*>{}.end(),
		printRowDynamic
	);
	std::cout << "Query returned " << rowCount << " rows" << std::endl;

	// Conditional query
	std::cout << "\nUsers with age >= 25:" << std::endl;
	std::vector<const char*> queryParams = { "25" };
	rowCount = db.ExecuteQuery(
		"SELECT id, name, age FROM users WHERE age >= ?;",
		queryParams.begin(), queryParams.end(),
		printRowDynamic
	);
	std::cout << "Query returned " << rowCount << " rows" << std::endl;

	// Update
	std::vector<const char*> updateParams = { "26", "Alice" };
	affected = db.ExecuteNonQuery(
		"UPDATE users SET age = ? WHERE name = ?;",
		updateParams.begin(), updateParams.end()
	);
	std::cout << "\nRows updated: " << affected << std::endl;

	// Verify update
	std::cout << "\nAll users after update:" << std::endl;
	db.ExecuteQuery(
		"SELECT id, name, age FROM users;",
		std::vector<const char*>{}.begin(),
		std::vector<const char*>{}.end(),
		printRowDynamic
	);

	// Delete
	std::vector<const char*> deleteParams = { "Bob" };
	affected = db.ExecuteNonQuery(
		"DELETE FROM users WHERE name = ?;",
		deleteParams.begin(), deleteParams.end()
	);
	std::cout << "\nRows deleted: " << affected << std::endl;

	// Final users
	std::cout << "\nRemaining users:" << std::endl;
	db.ExecuteQuery(
		"SELECT id, name, age FROM users;",
		std::vector<const char*>{}.begin(),
		std::vector<const char*>{}.end(),
		printRowDynamic
	);

	return 0;
}
相关推荐
FL4m3Y4n16 分钟前
MySQL缓存策略
数据库·mysql·缓存
wsx_iot17 分钟前
TDengine学习
数据库·学习·tdengine
不吃香菜的小趴菜37 分钟前
mysql数据库打包与导入
数据库·mysql
野犬寒鸦39 分钟前
Redis复习记录day1
服务器·开发语言·数据库·redis·缓存
njidf40 分钟前
实战:用Python开发一个简单的区块链
jvm·数据库·python
小菜鸡桃蛋狗42 分钟前
C++——类和对象(下)
开发语言·c++
Rick19931 小时前
慢SQL优化
数据库·python·sql
IvorySQL1 小时前
速抢!HOW 2026 早鸟票最后 1 天,解锁开源数据库盛会全亮点
数据库·postgresql·开源
crescent_悦1 小时前
C++:Highest Price in Supply Chain
开发语言·c++
feng_you_ying_li1 小时前
底层实现map和set的第一步,AVL树的学习
c++