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;
}
相关推荐
2401_868534783 小时前
RTOS之RT-Thread & Linux:线程/进程管理与调度核心差异分析
c++·python
神王宝宝 王者小学4 小时前
HBase: 看上去很美
大数据·数据库·hbase
io无心4 小时前
Shardingsphere5分库分表
数据库·mysql
wWYy.5 小时前
Mysql:主键索引 唯一索引 普通索引 前缀索引
数据库·mysql
郝学胜-神的一滴5 小时前
Qt 高级编程 040:按钮悬浮弹出滑块弹窗的完整攻略
开发语言·c++·qt·软件工程·用户界面
宝杰X75 小时前
Android Room3 多平台数据库
android·数据库
sky_8106138 小时前
Oracle ERP 各模块业务管理功能及底层表说明
数据库·oracle
程序喵大人9 小时前
【C++进阶】STL算法与函数对象 - 02 sort为什么需要随机访问迭代器
开发语言·c++·算法
ZhangShao06079 小时前
题解:CF2249B
c++·算法
YMatrix 官方技术社区10 小时前
CittaBase vs. Neo4j :原生图性能实测与混合检索实践
数据库·功能测试·ymatrix