yandex / ozo

OZO is a C++17 Boost.Asio based header-only library for asyncronous communication with PostgreSQL DBMS.
PostgreSQL License
226 stars 45 forks source link

How to choose C++ type for Postgres built-in types? #290

Open croja opened 3 years ago

croja commented 3 years ago

Which C++ type should I use if I want to read column of type "inet"? Is it as simple as using std::string for "text"? Which actions must be performed to be able to read column value into variable of type boost::asio::ip::network_v4?

croja commented 3 years ago
#pragma once

#include <ozo/pg/definitions.h>
#include <ozo/io/send.h>
#include <ozo/io/recv.h>

#include <boost/asio.hpp>

namespace ozo {

// NOT TESTED!!!
template <>
struct send_impl<boost::asio::ip::network_v4> {
    template <typename OidMap>
    static ostream& apply(ostream& out, const OidMap&, const boost::asio::ip::network_v4& in) {
        std::uint8_t size_of_mask = sizeof(std::uint16_t);
        std::uint16_t mask = in.netmask().to_uint();
        write(out, size_of_mask);
        write(out, htons(mask));

        std::uint8_t size_of_address = sizeof(std::uint32_t);
        std::uint32_t address = in.address().to_uint();
        write(out, size_of_address);
        return write(out, address);
    }
};

template <>
struct recv_impl<boost::asio::ip::network_v4> {
    template <typename OidMap>
    static istream& apply(istream& in, size_type, const OidMap&, boost::asio::ip::network_v4& out) {
        std::uint8_t size_of_mask = 0;
        read(in, size_of_mask);
        assert(size_of_mask == sizeof(std::uint16_t));

        std::uint16_t mask = 0;
        read(in, mask);
        mask = ntohs(mask);

        std::uint8_t size_of_address = 0;
        read(in, size_of_address);
        assert(size_of_address == sizeof(std::uint32_t));

        std::uint32_t address = 0;
        read(in, address);

        out = boost::asio::ip::network_v4(boost::asio::ip::address_v4(address), mask);

        return in;
    }
};

}

OZO_PG_BIND_TYPE(boost::asio::ip::network_v4, "inet")

Found some examples in source code and tried to implement support for "inet". recv_impl seems to work (binary representation explored experimentally, not sure where I can find it's description)

Unfortunately I don't know how to test send_impl, failed to write INSERT query using boost::asio::ip::network_v4 variable.

thed636 commented 3 years ago

Hi!

Thanks for trying to implement the IO. Yes, this is very close to the proper implementation. A good description for the binary representation may be found here. Since the type has a dynamic size of the binary representation, the ozo::size_of_impl specialization is needed too. The ozo::size_of should return a size of binary representation for the type. So this is the reason for send failure.

I spent some time last night implementing a more general solution. So feel free to use your solution for a while until the out-of-the-box solution is on the way.

croja commented 3 years ago

Thank you very much! It is great.

p.s. About send - I meant I don't know how to construct INSERT query passing network_v4 variables. All INSERT queries I found in sources contain hardcoded values. Maybe you can provide example how to construct insert query using variables?

I tried concatenate variable (analogous to SELECT example from documentation), but this doesn't compile:

using namespace boost::asio;
const ip::network_v4 net(ip::make_address_v4("127.0.0.0"), 8);
const auto query_insert = "INSERT INTO tbl (network) VALUES ('"_SQL  + net + "') RETURNING network"_SQL;

Maybe there other ways to trigger send_impl::apply() execution?

thed636 commented 3 years ago

Could you please share the compilation error?

Also, in the case of inet type of the network column, you do not need to quote variables, since they are transferred via the binary protocol, and all the type information being preserved.

croja commented 3 years ago

Sorry! As you said, compilation error is related to missing ozo::size_of. I had to check it again more carefully after your response. Implemented size_of and error is gone.

p.s. Thank you for the library! I use boost::asio hard, and for me it is really enjoying to asynchronously access Postgres with little to no efforts, reusing io_context from my applications.