diff options
author | Constantin Michael <constantin@codesynthesis.com> | 2011-07-14 17:11:01 +0200 |
---|---|---|
committer | Constantin Michael <constantin@codesynthesis.com> | 2011-07-14 23:12:28 +0200 |
commit | e67830fa25b5cf42428d32b544b115fda44383a3 (patch) | |
tree | d8082a82f9fd05fdfa8d5d7a1353e9091e68eec2 | |
parent | 92c6c45e7d3d95ef6241502824239a28a38dd7c2 (diff) |
Test VARBIT bit length is handled correctly
-rw-r--r-- | pgsql/types/driver.cxx | 10 | ||||
-rw-r--r-- | pgsql/types/test.hxx | 39 | ||||
-rw-r--r-- | 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 <string> #include <memory> // std::auto_ptr #include <cstring> // std::memcmp +#include <cstddef> // std::size_t #include <odb/core.hxx> @@ -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<std::string> 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 <cassert> #include <cstring> // std::memcpy, std::memset #include <odb/pgsql/traits.hxx> #include <odb/pgsql/details/endian-traits.hxx> -#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<buffer, id_varbit> + class value_traits<varbit, id_varbit> { 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<std::size_t> ( + details::endian_traits::ntoh ( + *reinterpret_cast<const int*> (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<int> (v.size () * 8); + // PostgreSQL requires all trailing bits of a varbit image + // be zero. + // + std::memset (b.data (), 0, b.capacity ()); *reinterpret_cast<int*> (b.data ()) = - details::endian_traits::hton (bit_len); + details::endian_traits::hton (static_cast<int> (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); } }; |