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;
}
相关推荐
weixin_458580123 分钟前
如何在 Go 中直接将 AST 编译为可执行二进制文件?
jvm·数据库·python
Highcharts.js2 小时前
Highcharts Grid 中文站正式上线:表格数据处理的全新选择
前端·javascript·数据库·表格数据·highcharts·可视化图表·企业级图表
Elastic 中国社区官方博客5 小时前
Elasticsearch:使用 Agent Builder 的 A2A 实现 - 开发者的圣诞颂歌
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
2301_816660215 小时前
PHP怎么处理Eloquent Attribute Inference属性推断_Laravel从数据自动推导类型【操作】
jvm·数据库·python
自信150413057596 小时前
重生之从0开始学习c++之模板初级
c++·学习
qq_372154236 小时前
Go 中自定义类型与基础类型的显式转换规则详解
jvm·数据库·python
历程里程碑6 小时前
2. Git版本回退全攻略:轻松掌握代码时光机
大数据·c++·git·elasticsearch·搜索引擎·github·全文检索
极客智造7 小时前
深度解析 C++ 类继承与多态:面向对象编程的核心
c++
_下雨天.7 小时前
NoSQL之Redis配置与优化
数据库·redis·nosql
LiAo_1996_Y7 小时前
CSS如何实现文字渐变效果_通过background-clip实现艺术字
jvm·数据库·python