sqlite3 和 boost asio ssl 服务器 sqlite3_prepare_v2 错误

sqlite3 and boost asio ssl server sqlite3_prepare_v2 error

我使用来自这个 example 的 boost asio ssl 服务器。我已经这样修改了代码

private:
        void do_handshake()
        {
            auto self(shared_from_this());
            socket_.async_handshake(
                boost::asio::ssl::stream_base::server, 
                [this, self](const boost::system::error_code& error)
                {
                    if (!error)
                    {
                        std::cout << "client: " << socket_.lowest_layer().remote_endpoint().address().to_string() << std::endl;
                        read_header();
                    }
                }
            );
        }

        void read_header()
        {
            auto self(shared_from_this());

            buffer_.clear();
            buffer_.resize(protocol::header_size);

            boost::asio::async_read(
                socket_,
                boost::asio::buffer(buffer_),
                boost::bind(
                    &session::read_body,
                    shared_from_this(),
                    boost::asio::placeholders::error
                )
            );
        }

        void read_body(const boost::system::error_code& error)
        {
            auto self(shared_from_this());

            std::size_t body_length = protocol::Protocol::body_length(buffer_);

            
            buffer_.clear();
            buffer_.resize(body_length);

            boost::asio::async_read(
                socket_,
                boost::asio::buffer(buffer_),
                boost::bind(
                    &session::handle_body,
                    shared_from_this(),
                    boost::asio::placeholders::error
                )
            );
        }

        void handle_body(const boost::system::error_code& error)
        {
            sqlite3* db;
            std::string sql{"SELECT * FROM table1 WHERE id = ?"};

            if (sqlite3_open("testdb.db", &db) == SQLITE_OK)
            {
                std::cout << "connection ok" << std::endl;
            }
            else
            {
                std::cout << "connection failed";
            }
            sqlite3_stmt* stmt;
            int res_prepare = sqlite3_prepare_v2(
                db,
                sql.c_str(),
                -1,
                &stmt,
                NULL
            );

            if (res_prepare != SQLITE_OK)
            {
                std::cout << "sqlite3_prepare_v2 returns an error => " << res_prepare << std::endl;
                
            }
            
            std::cout << "parameter count statement = " << sqlite3_bind_parameter_count(stmt) << std::endl;

            ...
            do_write();
        }

如果我启动服务器,连接到它并发送消息,我得到以下输出:

connection ok
sqlite3_prepare_v2 returns an error => 1
parameter count statement = 0

连接正常,但 sqlite3_prepare_v2 returns 错误代码 1 (SQLITE_ERROR)

The SQLITE_ERROR result code is a generic error code that is used when no other more specific error code is available.

我认为错误是 boost::asio 异步函数的结果(也许某些东西超出了范围?)但我不知道如何修复它。

您的数据库不符合预期的架构。也许它甚至不存在于您指定的路径中?

我 运行 使用此代码:

Compiler Explorer

#define SQLITE_DEBUG 1
#define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
#define SQLITE_ENABLE_SELECTTRACE 1
#define SQLITE_ENABLE_WHERETRACE 1
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind/bind.hpp>
#include <boost/endian/arithmetic.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <sqlite3.h>

using boost::asio::ip::tcp;
namespace protocol {
    enum {
        header_size = 4,
    };
    struct Protocol {
        static size_t body_length(std::string const& b)
        {
            boost::endian::big_int32_t n = 0;
            assert(b.size()>=sizeof(n));
            memcpy(&n, b.data(), sizeof(n));
            return n;
        };
    };
} // namespace protocol

class session : public std::enable_shared_from_this<session> {
  public:
    session(boost::asio::ssl::stream<tcp::socket> socket)
        : socket_(std::move(socket))
    {
    }

    void start() { do_handshake(); }

  private:
    void do_handshake()
    {
        auto self(shared_from_this());
        socket_.async_handshake(
            boost::asio::ssl::stream_base::server,
            [this, self](const boost::system::error_code& error) {
                if (!error) {
                    std::cout << "client: "
                              << socket_.lowest_layer()
                                     .remote_endpoint()
                                     .address()
                                     .to_string()
                              << std::endl;
                    read_header();
                }
            });
    }

    void read_header()
    {
        auto self(shared_from_this());

        buffer_.clear();
        buffer_.resize(protocol::header_size);

        boost::asio::async_read(socket_, boost::asio::buffer(buffer_),
                                boost::bind(&session::read_body,
                                            shared_from_this(),
                                            boost::asio::placeholders::error));
    }

    void read_body(const boost::system::error_code& error)
    {
        auto self(shared_from_this());

        std::size_t body_length = protocol::Protocol::body_length(buffer_);

        buffer_.clear();
        buffer_.resize(body_length);

        boost::asio::async_read(socket_, boost::asio::buffer(buffer_),
                                boost::bind(&session::handle_body,
                                            shared_from_this(),
                                            boost::asio::placeholders::error));
    }

    void handle_body(const boost::system::error_code& error)
    {
        sqlite3*    db;
        std::string sql{"SELECT * FROM table1 WHERE id = ?"};

        if (sqlite3_open("testdb.db", &db) == SQLITE_OK) {
            std::cout << "connection ok" << std::endl;
        } else {
            std::cout << "connection failed";
        }
        sqlite3_stmt* stmt;
        int res_prepare = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, NULL);

        if (res_prepare != SQLITE_OK) {
            std::cout << "sqlite3_prepare_v2 returns an error => "
                      << res_prepare << std::endl;
        }

        std::cout << "parameter count statement = "
                  << sqlite3_bind_parameter_count(stmt) << std::endl;

        //...
        //do_write();
    }

    void do_read()
    {
        auto self(shared_from_this());
        socket_.async_read_some(
            boost::asio::buffer(data_),
            [this, self](const boost::system::error_code& ec,
                         std::size_t                      length) {
                if (!ec) {
                    do_write(length);
                }
            });
    }

    void do_write(std::size_t length)
    {
        auto self(shared_from_this());
        boost::asio::async_write(
            socket_, boost::asio::buffer(data_, length),
            [this, self](const boost::system::error_code& ec,
                         std::size_t /*length*/) {
                if (!ec) {
                    do_read();
                }
            });
    }

    boost::asio::ssl::stream<tcp::socket> socket_;
    char                                  data_[1024];
    std::string                           buffer_;
};

class server {
  public:
    server(boost::asio::io_context& io_context, unsigned short port)
        : acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
        , context_(boost::asio::ssl::context::sslv23)
    {
        context_.set_options(boost::asio::ssl::context::default_workarounds |
                             boost::asio::ssl::context::no_sslv2 |
                             boost::asio::ssl::context::single_dh_use);
        context_.set_password_callback(std::bind(&server::get_password, this));
        context_.use_certificate_chain_file("server.pem");
        context_.use_private_key_file("server.pem",
                                      boost::asio::ssl::context::pem);
        context_.use_tmp_dh_file("dh4096.pem");

        do_accept();
    }

  private:
    std::string get_password() const { return "test"; }

    void do_accept()
    {
        acceptor_.async_accept([this](const boost::system::error_code& error,
                                      tcp::socket                      socket) {
            if (!error) {
                std::make_shared<session>(boost::asio::ssl::stream<tcp::socket>(
                                              std::move(socket), context_))
                    ->start();
            }

            do_accept();
        });
    }

    tcp::acceptor             acceptor_;
    boost::asio::ssl::context context_;
};

int main(int argc, char* argv[])
{
    try {
        if (argc != 2) {
            std::cerr << "Usage: server <port>\n";
            return 1;
        }

        boost::asio::io_context io_context;

        using namespace std; // For atoi.
        server s(io_context, atoi(argv[1]));

        io_context.run();
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

我运行服务器用

./sotest 1237

并使用

模拟客户端
printf '\x00\x00\x00\x04abcd' | openssl s_client -connect localhost:1237 

并打印:

  1. testdb.db 使用

    等“正确”创建时
    PRAGMA foreign_keys=OFF;
    BEGIN TRANSACTION;
    CREATE TABLE table1 (id int);
    INSERT INTO table1 VALUES(1);
    INSERT INTO table1 VALUES(2);
    INSERT INTO table1 VALUES(3);
    COMMIT;
    

    输出:

    client: 127.0.0.1
    connection ok
    parameter count statement = 1
    

  2. 当 test.db 被错误创建时,例如:

    PRAGMA foreign_keys=OFF;
    BEGIN TRANSACTION;
    CREATE TABLE table1(oops int);
    COMMIT;
    
    client: 127.0.0.1
    connection ok
    sqlite3_prepare_v2 returns an error => 1
    parameter count statement = 0
    

  3. 当文件test.db根本不存在时,输出是一样的:

    client: 127.0.0.1
    connection ok
    sqlite3_prepare_v2 returns an error => 1
    parameter count statement = 0
    

因此,请检查该文件是否存在(在可执行文件 运行 所在的工作目录中)并且可以访问,并且它包含所需的架构。