From e67830fa25b5cf42428d32b544b115fda44383a3 Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Thu, 14 Jul 2011 17:11:01 +0200 Subject: Test VARBIT bit length is handled correctly --- pgsql/types/driver.cxx | 10 ++++++---- pgsql/types/test.hxx | 39 ++++++++++++++++++++++++++++++++++++++- pgsql/types/traits.hxx | 47 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/pgsql/types/driver.cxx b/pgsql/types/driver.cxx index 5c5145a..17f48f4 100644 --- a/pgsql/types/driver.cxx +++ b/pgsql/types/driver.cxx @@ -47,15 +47,17 @@ main (int argc, char* argv[]) string short_str (128, 's'); string medium_str (250, 'm'); string long_str (2040, 'l'); - buffer long_buf (long_str.c_str (), long_str.size ()); - - unsigned char varbit_buf[8] = {1, 2, 1, 2, 1, 1, 1, 1}; o.char_ = short_str; o.varchar_ = medium_str; o.text_ = long_str; + + buffer long_buf (long_str.c_str (), long_str.size ()); o.bytea_ = long_buf; - o.varbit_ = buffer (varbit_buf, 8); + + unsigned char varbit_buf[8] = {1, 3, 1, 3, 1, 3, 1, 3}; + o.varbit_.size = 52; + o.varbit_.ubuffer_ = ubuffer (varbit_buf, 8); o.bit_.a = 0; o.bit_.b = 1; diff --git a/pgsql/types/test.hxx b/pgsql/types/test.hxx index 39de674..8b42aa1 100644 --- a/pgsql/types/test.hxx +++ b/pgsql/types/test.hxx @@ -10,6 +10,7 @@ #include #include // std::auto_ptr #include // std::memcmp +#include // std::size_t #include @@ -33,6 +34,42 @@ operator== (bitfield x, bitfield y) x.d == y.d; } +struct varbit +{ + std::size_t size; + ubuffer ubuffer_; + + bool + compare (const varbit& x) const + { + if (size != x.size) + return false; + + std::size_t byte_len = size / 8; + + if (std::memcmp (ubuffer_.data (), x.ubuffer_.data (), byte_len != 0)) + return false; + + std::size_t trailing_bits = size % 8; + + if (trailing_bits != 0) + { + unsigned char mask (0xFFU << (8 - trailing_bits)); + + return (ubuffer_.data ()[byte_len] & mask) == + (x.ubuffer_.data ()[byte_len] & mask); + } + + return true; + } +}; + +inline bool +operator== (const varbit& x, const varbit& y) +{ + return x.compare (y); +} + #pragma db value(bitfield) type ("BIT(4) NOT NULL") typedef std::auto_ptr string_ptr; @@ -108,7 +145,7 @@ struct object buffer bytea_; #pragma db type ("VARBIT(1024) NOT NULL") - buffer varbit_; + varbit varbit_; // #pragma db type ("BIT(4) NOT NULL") - assigned by #pragma db value bitfield bit_; diff --git a/pgsql/types/traits.hxx b/pgsql/types/traits.hxx index b7734b5..20d0445 100644 --- a/pgsql/types/traits.hxx +++ b/pgsql/types/traits.hxx @@ -6,12 +6,13 @@ #ifndef TRAITS_HXX #define TRAITS_HXX +#include #include // std::memcpy, std::memset #include #include -#include "test.hxx" // date_time, buffer, string_ptr +#include "test.hxx" // varbit, buffer, ubuffer, string_ptr namespace odb { @@ -96,45 +97,65 @@ namespace odb } }; + // The first 4 bytes of the image is a signed int specifying the + // number of significant bits contained by the VARBIT. The following + // bytes contain the VARBIT data. + // template <> - class value_traits + class value_traits { public: - typedef buffer value_type; - typedef buffer query_type; + typedef varbit value_type; + typedef varbit query_type; typedef details::ubuffer image_type; static void - set_value (buffer& v, + set_value (varbit& v, const details::ubuffer& b, std::size_t n, bool is_null) { if (!is_null) - v.assign (b.data () + 4, n - 4); + { + v.size = static_cast ( + details::endian_traits::ntoh ( + *reinterpret_cast (b.data ()))); + + std::size_t byte_len = v.size / 8 + v.size % 8 > 0 ? 1 : 0; + assert (n >= byte_len + 4); + + v.ubuffer_.assign (b.data () + 4, byte_len); + } + else - v.assign (0, 0); + { + v.size = 0; + v.ubuffer_.assign (0, 0); + } } static void set_image (details::ubuffer& b, std::size_t& n, bool& is_null, - const buffer& v) + const varbit& v) { is_null = false; - n = v.size () + 4; + n = 4 + v.size / 8 + (v.size % 8 > 0 ? 1 : 0); if (n > b.capacity ()) b.capacity (n); - int bit_len = static_cast (v.size () * 8); + // PostgreSQL requires all trailing bits of a varbit image + // be zero. + // + std::memset (b.data (), 0, b.capacity ()); *reinterpret_cast (b.data ()) = - details::endian_traits::hton (bit_len); + details::endian_traits::hton (static_cast (v.size)); - if (bit_len != 0) - std::memcpy (b.data () + 4, v.data (), v.size ()); + if (v.size != 0) + std::memcpy (b.data () + 4, v.ubuffer_.data (), n - 4); } }; -- cgit v1.1