美文网首页
C++11 封装nanodbc库操作snowflake DB

C++11 封装nanodbc库操作snowflake DB

作者: FredricZhu | 来源:发表于2021-08-18 15:56 被阅读0次

    因为自己写的那个库有些限制。使用起来不大好用。
    然后就想既然snowflake是使用ODBC来调用snowflake的Driver,那能不能直接使用现有的C++ ODBC库来解决增删改查的问题。在github上面搜了一下,果然已经有一个了。
    然后做了一下简单的封装,让他更好用一点。

    本示例运行步骤,
    先到github上面编译安装这个库,
    https://github.com/nanodbc/nanodbc

    地址是上面这个,但是编译安装的时候会出现有一个test文件编不过的情况。
    有两种解决方案,第一种是排除test进行编译安装。这种不推荐。
    第二种方案是修复源码中的错误重新编译。
    因为作者应该是基于Windows进行开发的库,对Mac和Linux的支持不是特别好。
    体现在这里就是类型系统没有做兼容。
    在对应的utility_test.cpp源文件中,加入一个using语句,

    using WCHAR = wchar_t;
    

    然后注释掉两行报错的static_assert宏。
    如图所示,


    image.png

    这样就可以编译通过了。
    但是运行测试的时候全部都会挂掉,不用担心,这是因为你没有安装对应数据库的驱动。
    所以全挂是正常的。

    在cmake ..,
    make
    make install
    安装完成之后,需要把example目录下的example_unicode_utils.h拷贝一份到安装的头文件目录中。
    一般是/usr/local/include/nanodbc/这个目录。
    因为这个文件中有两个字符转码的函数convert,所以我把它重命名成了convert.h。
    如图,

    image.png

    然后就可以操作了。
    操作起来还是挺顺畅的,比我自己写的工具类完善得多。
    增删改查都支持,具体用法可以查看/examples下面的usage.cpp
    我的封装和测试如下,因为是生产数据库,修改之类的就没法直接测了,这里只测试查询。
    程序目录结构,


    image.png

    CMakeLists.txt,使用C++17是因为为了做类型擦除使用了std::any。
    如果不想使用17的话,可以使用boost/any.hpp

    
    cmake_minimum_required(VERSION 2.6)
    project(sf_db_lib_test)
    
    add_definitions(-std=c++17)
    add_definitions(-g)
    
    
    
    find_package(Boost REQUIRED COMPONENTS
        system
        filesystem
        serialization
        program_options
        thread
        )
    
    include_directories(${Boost_INCLUDE_DIRS} /usr/local/include /usr/local/iODBC/include /opt/snowflake/snowflakeodbc/include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../)
    
    LINK_DIRECTORIES(/usr/local/lib /usr/local/iODBC/lib /opt/snowflake/snowflakeodbc/lib/universal)
    
    file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h
        ${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
    foreach( sourcefile ${APP_SOURCES} )
            file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
        
            string(FIND "${filename}"  "test.cpp" "TEMP")
        if( NOT "${TEMP}" STREQUAL "-1" )
            string(REPLACE ".cpp" "" file ${filename})
            add_executable(${file}  ${APP_SOURCES})
            target_link_libraries(${file} ${Boost_LIBRARIES})
            target_link_libraries(${file}  ssl crypto libgtest.a libgtest_main.a libgmock.a iodbc iodbcinst libnanodbc.a pthread)
        endif()
    endforeach( sourcefile ${APP_SOURCES})
    

    sf_db_lib.h

    #ifndef _FREDRIC_SF_DB_LIB_H_
    #define _FREDRIC_SF_DB_LIB_H_
    
    #include "nanodbc/nanodbc.h"
    
    #include <any>
    #include <string>
    
    using db_result = nanodbc::result;
    
    struct sf_connection {
        nanodbc::connection conn_;
    
        sf_connection(const std::string& conn_str);
        db_result exec_raw_query(const std::string& raw_query);
        db_result exec_prpare_statement(const std::string& pre_stmt,
                                        const std::vector<std::any>& params);
        virtual ~sf_connection();
    
       private:
        void bind_params(nanodbc::statement& stmt, const short index,
                         const std::any& param);
    };
    
    #endif
    

    sf_db_lib.cpp

    
    #include "sf_db_lib/sf_db_lib.h"
    
    #include <exception>
    #include <iostream>
    
    #include "nanodbc/convert.h"
    
    using std::cout;
    using std::endl;
    
    sf_connection::sf_connection(const std::string& conn_str) {
        conn_ = nanodbc::connection{convert(conn_str)};
    }
    
    db_result sf_connection::exec_raw_query(const std::string& raw_query) {
        auto res = execute(conn_, NANODBC_TEXT(raw_query));
        return std::move(res);
    }
    
    void sf_connection::bind_params(nanodbc::statement& stmt, const short index,
                                    const std::any& param) {
        if (param.type() == typeid(nullptr)) {
            stmt.bind_null(index);
        } else if (param.type() == typeid(int)) {
            auto val = std::any_cast<int>(param);
            std::vector<std::string> v{std::to_string(val)};
            stmt.bind_strings(index, v);
        } else if (param.type() == typeid(long)) {
            auto val = std::any_cast<long>(param);
            std::vector<std::string> v{std::to_string(val)};
            stmt.bind_strings(index, v);
        } else if (param.type() == typeid(double)) {
            auto val = std::any_cast<double>(param);
            std::vector<std::string> v{std::to_string(val)};
            stmt.bind_strings(index, v);
        } else if (param.type() == typeid(float)) {
            auto val = std::any_cast<float>(param);
            std::vector<std::string> v{std::to_string(val)};
            stmt.bind_strings(index, v);
        } else if (param.type() == typeid(const char*)) {
            auto val = std::any_cast<const char*>(param);
            stmt.bind(index, val);
        } else if (param.type() == typeid(std::string)) {
            auto val = std::any_cast<std::string>(param);
            stmt.bind(index, val.c_str());
        } else {
            throw std::runtime_error("not supported data types!");
        }
    }
    
    db_result sf_connection::exec_prpare_statement(
        const std::string& pre_stmt, const std::vector<std::any>& params) {
        nanodbc::statement statement(conn_);
        std::cout << pre_stmt << std::endl;
        prepare(statement, NANODBC_TEXT(pre_stmt));
        for (short i = 0; i < params.size(); ++i) {
            bind_params(statement, i, params[i]);
        }
        auto res = execute(statement);
        return std::move(res);
    }
    
    sf_connection::~sf_connection() {}
    

    sf_db_lib_test.cpp

    #include "sf_db_lib/sf_db_lib.h"
    
    #include <gtest/gtest.h>
    
    const std::string ConnStr = "dsn=product_odbc;pwd={YOUR_PASSWORD}";
    
    GTEST_TEST(SFDBTests, TestExecQuery) {
        auto conn_str = ConnStr;
        auto raw_query =
            "select  product_key,change_time,EVENT_TYPE_NAME, change_column, "
            "old_value, new_value,meta from "
            "AA_INTELLIGENCE_PRODUCTION.ADL_MASTER.dim_event_service_v1 where  "
            "event_type_name='screenshot_change' and product_key=20600000009072 "
            "order by change_time desc limit 2;";
        sf_connection sf{conn_str};
        auto res = sf.exec_raw_query(raw_query);
        ASSERT_EQ(2, res.affected_rows());
    
        const auto columns = res.columns();
        for (short i = 0; i < columns; ++i) std::cout << res.column_name(i) << "\t";
    
        std::cout << std::endl;
    
        const std::string null_value = "null";
        while (res.next()) {
            for (short col = 0; col < columns; ++col) {
                auto const value = res.get<std::string>(col, null_value);
                std::cout << "(" << value << ")\t";
            }
            std::cout << std::endl;
        }
    }
    
    GTEST_TEST(SFDBTests, TestExecPreStatement) {
        auto conn_str = ConnStr;
        auto pre_query =
            "select  product_key,change_time,EVENT_TYPE_NAME, change_column, "
            "old_value, new_value,meta from "
            "AA_INTELLIGENCE_PRODUCTION.ADL_MASTER.dim_event_service_v1 where  "
            "event_type_name=? and product_key=? and change_time=? order by "
            "change_time desc limit 2;";
        std::vector<std::any> params = {"screenshot_change", 20600000009072,
                                        "2021-06-07"};
        sf_connection sf{conn_str};
    
        auto res = sf.exec_prpare_statement(pre_query, params);
        ASSERT_EQ(1, res.affected_rows());
    
        const auto columns = res.columns();
        for (short i = 0; i < columns; ++i) std::cout << res.column_name(i) << "\t";
    
        std::cout << std::endl;
    
        const std::string null_value = "null";
        while (res.next()) {
            for (short col = 0; col < columns; ++col) {
                auto const value = res.get<std::string>(col, null_value);
                std::cout << "(" << value << ")\t";
            }
            std::cout << std::endl;
        }
    }
    

    程序输出如下,


    image.png

    相关文章

      网友评论

          本文标题:C++11 封装nanodbc库操作snowflake DB

          本文链接:https://www.haomeiwen.com/subject/ehbgbltx.html