From 48714b88af004230b38a3523d94064137377d306 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Thu, 6 Aug 2026 18:13:52 +0000 Subject: [PATCH] =?UTF-8?q?fix(libsqlite3):=20=E5=A2=9E=E5=8A=A0=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=8C=96=20exec=20=E4=B8=8E=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=BB=91=E5=AE=9A/=E8=AF=BB=E5=8F=96=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增模板 exec(sql, args...) 参数化执行,支持 int / int64_t / double / std::string / const char* / nullptr / std::vector - 扩展 Sqlite3_stmt:bind_int64、bind_double、bind_blob、bind_null、execute - 增加 read_int64、read_double,read_text 补充生命周期注释 - 构造/打开异常携带 sqlite3_errmsg 详情 - 原 exec(sql) 增加硬编码 SQL 警告注释 --- libsqlite3/inc/us/Sqlite3.hpp | 98 ++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/libsqlite3/inc/us/Sqlite3.hpp b/libsqlite3/inc/us/Sqlite3.hpp index e91ca36..8ef59c7 100644 --- a/libsqlite3/inc/us/Sqlite3.hpp +++ b/libsqlite3/inc/us/Sqlite3.hpp @@ -1,6 +1,10 @@ +#include #include #include #include +#include +#include +#include namespace qing { @@ -9,8 +13,9 @@ namespace qing { Sqlite3_stmt(sqlite3 *db, const char *sql) { int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { - std::cerr << "Failed to prepare sql stmt: " << sqlite3_errmsg(db) << std::endl; - throw std::runtime_error("Failed to prepare sql stmt"); + std::string err = sqlite3_errmsg(db); + std::cerr << "Failed to prepare sql stmt: " << err << std::endl; + throw std::runtime_error("Failed to prepare sql stmt: " + err); } } @@ -40,19 +45,45 @@ namespace qing { void bind_int(int idx, int data) { int rc = sqlite3_bind_int(stmt, idx, data); if (rc != SQLITE_OK) { - std::cerr << "Failed to bind an integer to sql stmt." << std::endl; throw std::runtime_error("Failed to bind an integer to sql stmt"); } } + void bind_int64(int idx, int64_t data) { + int rc = sqlite3_bind_int64(stmt, idx, data); + if (rc != SQLITE_OK) { + throw std::runtime_error("Failed to bind an int64 to sql stmt"); + } + } + + void bind_double(int idx, double data) { + int rc = sqlite3_bind_double(stmt, idx, data); + if (rc != SQLITE_OK) { + throw std::runtime_error("Failed to bind a double to sql stmt"); + } + } + void bind_text(int idx, const std::string &data) { int rc = sqlite3_bind_text(stmt, idx, data.c_str(), -1, SQLITE_TRANSIENT); if (rc != SQLITE_OK) { - std::cerr << "Failed to bind a text to sql stmt." << std::endl; throw std::runtime_error("Failed to bind a text to sql stmt"); } } + void bind_blob(int idx, const std::vector& data) { + int rc = sqlite3_bind_blob(stmt, idx, data.data(), static_cast(data.size()), SQLITE_TRANSIENT); + if (rc != SQLITE_OK) { + throw std::runtime_error("Failed to bind a blob to sql stmt"); + } + } + + void bind_null(int idx) { + int rc = sqlite3_bind_null(stmt, idx); + if (rc != SQLITE_OK) { + throw std::runtime_error("Failed to bind null to sql stmt"); + } + } + bool step() { int rc = sqlite3_step(stmt); if (rc != SQLITE_ROW && rc != SQLITE_DONE) { @@ -61,6 +92,15 @@ namespace qing { return rc == SQLITE_ROW && rc != SQLITE_DONE; } + // 执行无结果集语句(INSERT / UPDATE / DELETE / DDL),返回影响行数 + int execute() { + int rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + throw std::runtime_error("Failed to execute statement"); + } + return sqlite3_changes(sqlite3_db_handle(stmt)); + } + /* * int len = sqlite3_column_bytes(stmt, 1); // 获取字节长度(不含结束符) * std::string name(reinterpret_cast(rawName), len); @@ -69,7 +109,16 @@ namespace qing { return sqlite3_column_int(stmt, idx); } + int64_t read_int64(int idx) { + return sqlite3_column_int64(stmt, idx); + } + double read_double(int idx) { + return sqlite3_column_double(stmt, idx); + } + + // 返回指向 statement 内部缓冲区的指针,生命周期与当前 statement 绑定。 + // statement finalize 或 step 后指针可能失效;如需长期保存请复制到 std::string。 const unsigned char *read_text(int idx) { return sqlite3_column_text(stmt, idx); } @@ -84,9 +133,10 @@ namespace qing { Sqlite3(const std::string& filename) { int rc = sqlite3_open(filename.c_str(), &db); if (rc != SQLITE_OK) { - std::cerr << "Failed to open database: " << sqlite3_errmsg(db) << std::endl; + std::string err = sqlite3_errmsg(db); + std::cerr << "Failed to open database: " << err << std::endl; sqlite3_close(db); - throw std::runtime_error("Failed to open sqlite3 database."); + throw std::runtime_error("Failed to open sqlite3 database: " + err); } } @@ -113,11 +163,13 @@ namespace qing { return *this; } + // 仅用于硬编码 SQL;禁止拼接用户输入! bool exec(const std::string& sql) { char *errMsg = nullptr; int rc = sqlite3_exec(db, sql.c_str(), 0, 0, &errMsg); if (rc != SQLITE_OK) { - std::cerr << "Failed to execute at sqlite3: " << errMsg << std::endl; + std::string err = errMsg ? errMsg : "unknown error"; + std::cerr << "Failed to execute at sqlite3: " << err << std::endl; sqlite3_free(errMsg); return false; } @@ -125,11 +177,43 @@ namespace qing { return true; } + // 参数化执行 INSERT / UPDATE / DELETE / DDL,支持 int / int64_t / double / + // std::string / const char* / nullptr / std::vector + template + int exec(const std::string& sql, Args&&... args) { + Sqlite3_stmt s(db, sql.c_str()); + int idx = 1; + (_bind_one(s, idx++, std::forward(args)), ...); + return s.execute(); + } + Sqlite3_stmt stmt(const std::string& sql) { return Sqlite3_stmt(db, sql.c_str()); } private: sqlite3* db = nullptr; + + template + static void _bind_one(Sqlite3_stmt& s, int idx, T&& val) { + using U = std::decay_t; + if constexpr (std::is_same_v) { + s.bind_null(idx); + } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { + s.bind_int(idx, static_cast(val)); + } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { + s.bind_int64(idx, static_cast(val)); + } else if constexpr (std::is_same_v || std::is_same_v) { + s.bind_double(idx, static_cast(val)); + } else if constexpr (std::is_same_v) { + s.bind_text(idx, val); + } else if constexpr (std::is_same_v) { + s.bind_text(idx, std::string(val)); + } else if constexpr (std::is_same_v>) { + s.bind_blob(idx, val); + } else { + static_assert(!std::is_same_v, "unsupported bind type"); + } + } }; }