aboutsummaryrefslogtreecommitdiff
path: root/pgsql/types
diff options
context:
space:
mode:
authorConstantin Michael <constantin@codesynthesis.com>2011-07-14 17:11:01 +0200
committerConstantin Michael <constantin@codesynthesis.com>2011-07-14 23:12:28 +0200
commite67830fa25b5cf42428d32b544b115fda44383a3 (patch)
treed8082a82f9fd05fdfa8d5d7a1353e9091e68eec2 /pgsql/types
parent92c6c45e7d3d95ef6241502824239a28a38dd7c2 (diff)
Test VARBIT bit length is handled correctly
Diffstat (limited to 'pgsql/types')
-rw-r--r--pgsql/types/driver.cxx10
-rw-r--r--pgsql/types/test.hxx39
-rw-r--r--pgsql/types/traits.hxx47
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);
}
};