#pragma once #include #include #include #include #include #include #include enum class CellType { Null, Int64, Double, String, Blob }; struct Cell { CellType type = CellType::Null; int64_t i64 = 0; double dbl = 0.0; std::string str; std::vector blob; }; using Row = std::vector; using ResultSet = std::vector; enum class ParamType { Int, BigInt, Float, Double, NVarChar, Bit, DateTime }; struct Param { ParamType type; std::string str_val; int64_t int_val = 0; double dbl_val = 0.0; bool is_null = false; static Param null_int() { Param p; p.type = ParamType::Int; p.is_null = true; return p; } static Param null_bigint() { Param p; p.type = ParamType::BigInt; p.is_null = true; return p; } static Param null_nvarchar() { Param p; p.type = ParamType::NVarChar; p.is_null = true; return p; } }; class OdbcPool { public: struct Connection { SQLHDBC hdbc = SQL_NULL_HDBC; SQLHSTMT hstmt = SQL_NULL_HSTMT; bool in_use = false; bool in_transaction = false; }; // RAII checkout guard. Returned by checkout(). struct ConnGuard { Connection* conn = nullptr; OdbcPool* pool = nullptr; ConnGuard() = default; ConnGuard(Connection* c, OdbcPool* p) : conn(c), pool(p) {} ~ConnGuard() { if (conn && pool) pool->release(conn); } ConnGuard(const ConnGuard&) = delete; ConnGuard(ConnGuard&& other) noexcept : conn(other.conn), pool(other.pool) { other.conn = nullptr; } ConnGuard& operator=(ConnGuard&& other) noexcept { if (this != &other) { if (conn && pool) pool->release(conn); conn = other.conn; pool = other.pool; other.conn = nullptr; } return *this; } Connection* operator->() const { return conn; } Connection* get() const { return conn; } explicit operator bool() const { return conn != nullptr; } }; ~OdbcPool(); int connect(); void disconnect(); bool execute(const std::string& sql, const std::vector& params, ResultSet& out); bool execute(const std::string& sql, ResultSet& out); int64_t execute_scalar(const std::string& sql, const std::vector& params = {}, int64_t fallback = 0); // Transactional execution on an explicitly checked-out connection. ConnGuard checkout(); bool execute(Connection* conn, const std::string& sql, const std::vector& params, ResultSet& out); bool execute(Connection* conn, const std::string& sql, ResultSet& out); bool begin(Connection* conn); bool commit(Connection* conn); bool rollback(Connection* conn); void release(Connection* c); const std::string& last_error() const { return last_error_; } private: SQLHENV henv_ = SQL_NULL_HENV; std::vector conns_; std::mutex mutex_; std::string last_error_; Connection* checkout_raw(); }; OdbcPool& get_pool();