From 5ea55acce8ae8bc0361696ed2ff413eefaf89a8e Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Tue, 12 Jul 2011 18:42:17 +0200 Subject: Add PostgreSQL types and truncation tests --- pgsql/makefile | 4 +- pgsql/truncation/driver.cxx | 163 ++++++++++++++++++++++++++++++++++ pgsql/truncation/makefile | 106 ++++++++++++++++++++++ pgsql/truncation/test.hxx | 49 +++++++++++ pgsql/truncation/test.std | 0 pgsql/types/driver.cxx | 90 +++++++++++++++++++ pgsql/types/makefile | 112 ++++++++++++++++++++++++ pgsql/types/test.hxx | 158 +++++++++++++++++++++++++++++++++ pgsql/types/test.std | 0 pgsql/types/traits.hxx | 208 ++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 889 insertions(+), 1 deletion(-) create mode 100644 pgsql/truncation/driver.cxx create mode 100644 pgsql/truncation/makefile create mode 100644 pgsql/truncation/test.hxx create mode 100644 pgsql/truncation/test.std create mode 100644 pgsql/types/driver.cxx create mode 100644 pgsql/types/makefile create mode 100644 pgsql/types/test.hxx create mode 100644 pgsql/types/test.std create mode 100644 pgsql/types/traits.hxx diff --git a/pgsql/makefile b/pgsql/makefile index 34ac15f..1fee285 100644 --- a/pgsql/makefile +++ b/pgsql/makefile @@ -7,7 +7,9 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make tests := \ template \ -native +native \ +truncation \ +types default := $(out_base)/ dist := $(out_base)/.dist diff --git a/pgsql/truncation/driver.cxx b/pgsql/truncation/driver.cxx new file mode 100644 index 0000000..bbfa99e --- /dev/null +++ b/pgsql/truncation/driver.cxx @@ -0,0 +1,163 @@ +// file : pgsql/truncation/driver.cxx +// author : Boris Kolpackov +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// Test insufficient buffer/truncation handling. +// + +#include // std::auto_ptr +#include +#include + +#include +#include + +#include + +#include "test.hxx" +#include "test-odb.hxx" + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + // The default pre-allocated buffer is 512 bytes long. + // + string long_str (640, 'c'); // This will get the buffer to 1024 + string longer_str (1025, 'b'); + + try + { + // Test basic operations. + // + { + auto_ptr db (create_database (argc, argv)); + + // Run persist/load so that the initial bindings are established + // (version == 0). + // + { + object1 o (1); + o.str_ = "test string"; + + transaction t (db->begin ()); + db->persist (o); + db->load (1, o); + t.commit (); + } + + { + object2 o (2); + o.str_ = "test string"; + + transaction t (db->begin ()); + db->persist (o); + db->load (2, o); + t.commit (); + } + + // Store/load the long string which should trigger buffer growth. + // + { + object1 o (3); + o.str_ = long_str; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr o (db->load (3)); + assert (o->str_ == long_str); + t.commit (); + } + + // Store/load longer string. + // + { + object1 o (3); + o.str_ = longer_str; + + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr o (db->load (3)); + assert (o->str_ == longer_str); + t.commit (); + } + } + + // Test query. + // + { + typedef odb::query query; + typedef odb::result result; + + auto_ptr db (create_database (argc, argv)); + + // Run persist/query so that the initial bindings are established + // (version == 0). + // + { + object1 o (20); + o.str_ = "test string"; + + transaction t (db->begin ()); + db->persist (o); + o.id_++; + db->persist (o); + o.id_++; + db->persist (o); + + result r (db->query (query::id == 20)); + assert (r.begin ()->id_ == 20); + t.commit (); + } + + // Test buffer growth with cached result. + // + { + object1 o; + + transaction t (db->begin ()); + + result r (db->query (query::id >= 20)); + result::iterator i (r.begin ()); + + o.id_ = i->id_; + o.str_ = long_str; + + // This forces buffer growth in the middle of result iteration. + // + db->update (o); + + ++i; + assert (i->str_ == "test string"); + + o.id_ = i->id_; + o.str_ = longer_str; + db->update (o); + + ++i; + assert (i->str_ == "test string"); + + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/pgsql/truncation/makefile b/pgsql/truncation/makefile new file mode 100644 index 0000000..a94639d --- /dev/null +++ b/pgsql/truncation/makefile @@ -0,0 +1,106 @@ +# file : pgsql/truncation/makefile +# author : Constantin Michael +# copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +# license : GNU GPL v2; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make + +cxx_tun := driver.cxx +odb_hdr := test.hxx +cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(odb_hdr:.hxx=-odb.o)) +cxx_od := $(cxx_obj:.o=.o.d) + +common.l := $(out_root)/libcommon/common/common.l +common.l.cpp-options := $(out_root)/libcommon/common/common.l.cpp-options + +driver := $(out_base)/driver +dist := $(out_base)/.dist +test := $(out_base)/.test +clean := $(out_base)/.clean + +# Import. +# +$(call import,\ + $(scf_root)/import/odb/stub.make,\ + odb: odb,odb-rules: odb_rules) + +# Build. +# +$(driver): $(cxx_obj) $(common.l) +$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base) -I$(src_base) +$(cxx_obj) $(cxx_od): $(common.l.cpp-options) + +genf := $(addprefix $(odb_hdr:.hxx=-odb),.hxx .ixx .cxx) $(odb_hdr:.hxx=.sql) +gen := $(addprefix $(out_base)/,$(genf)) + +$(gen): $(odb) +$(gen): odb := $(odb) +$(gen) $(dist): export odb_options += --database pgsql --generate-query \ +--generate-schema +$(gen): cpp_options := -I$(src_base) +$(gen): $(common.l.cpp-options) + +$(call include-dep,$(cxx_od),$(cxx_obj),$(gen)) + +# Alias for default target. +# +$(out_base)/: $(driver) + +# Dist +# +$(dist): sources := $(cxx_tun) +$(dist): headers := $(odb_hdr) +$(dist): data_dist := test.std +$(dist): export name := $(subst /,-,$(subst $(src_root)/pgsql/,,$(src_base))) +$(dist): export extra_dist := $(data_dist) $(name)-vc9.vcproj \ +$(name)-vc10.vcxproj $(name)-vc10.vcxproj.filters +$(dist): + $(call dist-data,$(sources) $(headers) $(data_dist)) + $(call meta-automake,../template/Makefile.am) + $(call meta-vc9proj,../template/template-vc9.vcproj,$(name)-vc9.vcproj) + $(call meta-vc10proj,../template/template-vc10.vcxproj,$(name)-vc10.vcxproj) + +# Test. +# +$(test): $(driver) $(src_base)/test.std + $(call schema) + $(call message,test $<,$< --options-file $(dcf_root)/db.options \ +>$(out_base)/test.out) + $(call message,,diff -u $(src_base)/test.std $(out_base)/test.out) + $(call message,,rm -f $(out_base)/test.out) + +# Clean. +# +$(clean): \ + $(driver).o.clean \ + $(addsuffix .cxx.clean,$(cxx_obj)) \ + $(addsuffix .cxx.clean,$(cxx_od)) \ + $(addprefix $(out_base)/,$(odb_hdr:.hxx=-odb.cxx.hxx.clean)) + $(call message,,rm -f $(out_base)/test.out) + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver $(genf) +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + +# How to. +# +$(call include,$(bld_root)/dist.make) +$(call include,$(bld_root)/meta/vc9proj.make) +$(call include,$(bld_root)/meta/vc10proj.make) +$(call include,$(bld_root)/meta/automake.make) + +$(call include,$(odb_rules)) +$(call include,$(bld_root)/cxx/cxx-d.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/o-e.make) + +# Dependencies. +# +$(call import,$(src_root)/libcommon/makefile) diff --git a/pgsql/truncation/test.hxx b/pgsql/truncation/test.hxx new file mode 100644 index 0000000..0cd2852 --- /dev/null +++ b/pgsql/truncation/test.hxx @@ -0,0 +1,49 @@ +// file : mysql/truncation/test.hxx +// author : Boris Kolpackov +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include +#include + +#pragma db object table ("test") +struct object1 +{ + object1 (unsigned long id) + : id_ (id) + { + } + + object1 () + { + } + + #pragma db id + unsigned long id_; + + std::string str_; +}; + +#pragma db object table ("test") +struct object2 +{ + object2 (unsigned long id) + : id_ (id) + { + } + + object2 () + { + } + + #pragma db id + unsigned long id_; + + std::string str_; +}; + +#endif // TEST_HXX diff --git a/pgsql/truncation/test.std b/pgsql/truncation/test.std new file mode 100644 index 0000000..e69de29 diff --git a/pgsql/types/driver.cxx b/pgsql/types/driver.cxx new file mode 100644 index 0000000..5c5145a --- /dev/null +++ b/pgsql/types/driver.cxx @@ -0,0 +1,90 @@ +// file : pgsql/types/driver.cxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// Test PostgreSQL type conversion. +// + +#include // std::auto_ptr +#include +#include + +#include +#include + +#include + +#include "test.hxx" +#include "test-odb.hxx" + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + auto_ptr db (create_database (argc, argv)); + + object o (1); + + o.bool_ = true; + o.short_ = 12345; + o.int_ = -123456; + o.long_long_ = 123456; + + o.float_ = 1.123F; + o.float8_ = 1.123; + o.double_ = 1.123; + // o.numeric_ = "123.456"; + + o.date_ = 4015; + o.time_ = 48180000000; + o.timestamp_ = 346896000; + + 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; + o.bytea_ = long_buf; + o.varbit_ = buffer (varbit_buf, 8); + + o.bit_.a = 0; + o.bit_.b = 1; + o.bit_.c = 0; + o.bit_.d = 1; + + o.enum_ = green; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + auto_ptr o1 (db->load (1)); + t.commit (); + + assert (o == *o1); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/pgsql/types/makefile b/pgsql/types/makefile new file mode 100644 index 0000000..88fdb4a --- /dev/null +++ b/pgsql/types/makefile @@ -0,0 +1,112 @@ +# file : pgsql/types/makefile +# author : Constantin Michael +# copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +# license : GNU GPL v2; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make + +cxx_tun := driver.cxx +odb_hdr := test.hxx +cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(odb_hdr:.hxx=-odb.o)) +cxx_od := $(cxx_obj:.o=.o.d) + +common.l := $(out_root)/libcommon/common/common.l +common.l.cpp-options := $(out_root)/libcommon/common/common.l.cpp-options + +driver := $(out_base)/driver +dist := $(out_base)/.dist +test := $(out_base)/.test +clean := $(out_base)/.clean + +# Import. +# +$(call import,\ + $(scf_root)/import/odb/stub.make,\ + odb: odb,odb-rules: odb_rules) + +$(call import,\ + $(scf_root)/import/libodb-pgsql/stub.make,\ + l: odb_pgsql.l,cpp-options: odb_pgsql.l.cpp-options) + +# Build. +# +$(driver): $(cxx_obj) $(odb_pgsql.l) $(common.l) +$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base) -I$(src_base) +$(cxx_obj) $(cxx_od): $(odb_pgsql.l.cpp-options) $(common.l.cpp-options) + +genf := $(addprefix $(odb_hdr:.hxx=-odb),.hxx .ixx .cxx) $(odb_hdr:.hxx=.sql) +gen := $(addprefix $(out_base)/,$(genf)) + +$(gen): $(odb) +$(gen): odb := $(odb) +$(gen) $(dist): export odb_options += --database pgsql --generate-schema \ +--generate-query --cxx-prologue '\#include "traits.hxx"' + +$(gen): cpp_options := -I$(src_base) +$(gen): $(common.l.cpp-options) + +$(call include-dep,$(cxx_od),$(cxx_obj),$(gen)) + +# Alias for default target. +# +$(out_base)/: $(driver) + +# Dist +# +$(dist): sources := $(cxx_tun) +$(dist): headers := $(odb_hdr) +$(dist): export extra_headers := traits.hxx +$(dist): data_dist := test.std +$(dist): export name := $(subst /,-,$(subst $(src_root)/pgsql/,,$(src_base))) +$(dist): export extra_dist := $(data_dist) $(name)-vc9.vcproj \ +$(name)-vc10.vcxproj $(name)-vc10.vcxproj.filters +$(dist): + $(call dist-data,$(sources) $(headers) $(extra_headers) $(data_dist)) + $(call meta-automake,../template/Makefile.am) + $(call meta-vc9proj,../template/template-vc9.vcproj,$(name)-vc9.vcproj) + $(call meta-vc10proj,../template/template-vc10.vcxproj,$(name)-vc10.vcxproj) + +# Test. +# +$(test): $(driver) $(src_base)/test.std + $(call schema) + $(call message,test $<,$< --options-file $(dcf_root)/db.options \ +>$(out_base)/test.out) + $(call message,,diff -u $(src_base)/test.std $(out_base)/test.out) + $(call message,,rm -f $(out_base)/test.out) + +# Clean. +# +$(clean): \ + $(driver).o.clean \ + $(addsuffix .cxx.clean,$(cxx_obj)) \ + $(addsuffix .cxx.clean,$(cxx_od)) \ + $(addprefix $(out_base)/,$(odb_hdr:.hxx=-odb.cxx.hxx.clean)) + $(call message,,rm -f $(out_base)/test.out) + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver $(genf) +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + +# How to. +# +$(call include,$(bld_root)/dist.make) +$(call include,$(bld_root)/meta/vc9proj.make) +$(call include,$(bld_root)/meta/vc10proj.make) +$(call include,$(bld_root)/meta/automake.make) + +$(call include,$(odb_rules)) +$(call include,$(bld_root)/cxx/cxx-d.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/o-e.make) + +# Dependencies. +# +$(call import,$(src_root)/libcommon/makefile) diff --git a/pgsql/types/test.hxx b/pgsql/types/test.hxx new file mode 100644 index 0000000..39de674 --- /dev/null +++ b/pgsql/types/test.hxx @@ -0,0 +1,158 @@ +// file : pgsql/types/test.hxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include +#include +#include // std::auto_ptr +#include // std::memcmp + +#include + +#include + +struct bitfield +{ + unsigned int a: 1; + unsigned int b: 1; + unsigned int c: 1; + unsigned int d: 1; +}; + +inline bool +operator== (bitfield x, bitfield y) +{ + return + x.a == y.a && + x.b == y.b && + x.c == y.c && + x.d == y.d; +} + +#pragma db value(bitfield) type ("BIT(4) NOT NULL") + +typedef std::auto_ptr string_ptr; + +enum color {red, green, blue}; + +#pragma db object +struct object +{ + object (unsigned long id) + : id_ (id) + { + } + + object () + { + } + + #pragma db id + unsigned long id_; + + // Integral types. + // + #pragma db type ("BOOL NOT NULL") + bool bool_; + + #pragma db type ("SMALLINT NOT NULL") + short short_; + + #pragma db type ("INT NOT NULL") + int int_; + + #pragma db type ("BIGINT NOT NULL") + long long long_long_; + + // Float types. + // + #pragma db type ("REAL NOT NULL") + float float_; + + #pragma db type ("FLOAT(32) NOT NULL") + double float8_; + + #pragma db type ("DOUBLE PRECISION NOT NULL") + double double_; + + // #pragma db type ("NUMERIC(6,3) NOT NULL") + // std::string numeric_; + + // Data-time types. + // + #pragma db type ("DATE NOT NULL") + int date_; + + #pragma db type ("TIME NOT NULL") + long long time_; + + #pragma db type ("TIMESTAMP NOT NULL") + long long timestamp_; + + // String and binary types. + // + #pragma db type ("CHAR(128) NOT NULL") + std::string char_; + + #pragma db type ("VARCHAR(256) NOT NULL") + std::string varchar_; + + #pragma db type ("TEXT NOT NULL") + std::string text_; + + #pragma db type ("BYTEA NOT NULL") + buffer bytea_; + + #pragma db type ("VARBIT(1024) NOT NULL") + buffer varbit_; + + // #pragma db type ("BIT(4) NOT NULL") - assigned by #pragma db value + bitfield bit_; + + // Other types. + // + #pragma db type ("UUID NOT NULL") + unsigned char uuid_[16]; + + // Test ENUM representation. + // + color enum_; + + // Test NULL value. + // + #pragma db type ("TEXT") + string_ptr null_; + + bool + operator== (const object& y) const + { + return + id_ == y.id_ && + bool_ == y.bool_ && + short_ == y.short_ && + int_ == y.int_ && + long_long_ == y.long_long_ && + float_ == y.float_ && + float8_ == y.float8_ && + double_ == y.double_ && + // numeric__ == y.numeric_ && + date_ == y.date_ && + time_ == y.time_ && + timestamp_ == y.timestamp_ && + char_ == y.char_ && + varchar_ == y.varchar_ && + text_ == y.text_ && + bytea_ == y.bytea_ && + bit_ == y.bit_ && + varbit_ == y.varbit_ && + memcmp (uuid_, y.uuid_, 16) == 0 && + enum_ == y.enum_ && + ((null_.get () == 0 && y.null_.get () == 0) || *null_ == *y.null_); + } +}; + +#endif // TEST_HXX diff --git a/pgsql/types/test.std b/pgsql/types/test.std new file mode 100644 index 0000000..e69de29 diff --git a/pgsql/types/traits.hxx b/pgsql/types/traits.hxx new file mode 100644 index 0000000..b7734b5 --- /dev/null +++ b/pgsql/types/traits.hxx @@ -0,0 +1,208 @@ +// file : pgsql/types/traits.hxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TRAITS_HXX +#define TRAITS_HXX + +#include // std::memcpy, std::memset + +#include +#include + +#include "test.hxx" // date_time, buffer, string_ptr + +namespace odb +{ + namespace pgsql + { + template <> + class value_traits + { + public: + typedef buffer value_type; + typedef buffer query_type; + typedef details::buffer image_type; + + static void + set_value (buffer& v, + const details::buffer& b, + std::size_t n, + bool is_null) + { + if (!is_null) + v.assign (b.data (), n); + else + v.assign (0, 0); + } + + static void + set_image (details::buffer& b, + std::size_t& n, + bool& is_null, + const buffer& v) + { + is_null = false; + n = v.size (); + + if (n > b.capacity ()) + b.capacity (n); + + if (n != 0) + std::memcpy (b.data (), v.data (), n); + } + }; + + template <> + class value_traits + { + public: + typedef bitfield value_type; + typedef bitfield query_type; + typedef unsigned char* image_type; + + static void + set_value (bitfield& v, + const unsigned char* i, + std::size_t, + bool is_null) + { + if (!is_null) + { + unsigned char const* d = i + 4; + + v.a = *d >> 4 & 1; + v.b = (*d >> 5) & 1; + v.c = (*d >> 6) & 1; + v.d = (*d >> 7) & 1; + } + else + v.a = v.b = v.c = v.d = 0; + } + + static void + set_image (unsigned char* i, + std::size_t, + std::size_t& n, + bool& is_null, + bitfield v) + { + is_null = false; + n = 5; + + *reinterpret_cast (i) = details::endian_traits::hton (4); + *(i + 4) = v.a << 4 | (v.b << 5) | (v.c << 6) | (v.d << 7); + } + }; + + template <> + class value_traits + { + public: + typedef buffer value_type; + typedef buffer query_type; + typedef details::ubuffer image_type; + + static void + set_value (buffer& v, + const details::ubuffer& b, + std::size_t n, + bool is_null) + { + if (!is_null) + v.assign (b.data () + 4, n - 4); + else + v.assign (0, 0); + } + + static void + set_image (details::ubuffer& b, + std::size_t& n, + bool& is_null, + const buffer& v) + { + is_null = false; + n = v.size () + 4; + + if (n > b.capacity ()) + b.capacity (n); + + int bit_len = static_cast (v.size () * 8); + + *reinterpret_cast (b.data ()) = + details::endian_traits::hton (bit_len); + + if (bit_len != 0) + std::memcpy (b.data () + 4, v.data (), v.size ()); + } + }; + + template <> + class value_traits + { + public: + typedef unsigned char* value_type; + typedef details::ubuffer query_type; + typedef unsigned char* image_type; + + static void + set_value (unsigned char v[16], + unsigned char const* i, + bool is_null) + { + if (!is_null) + std::memcpy (v, i, 16); + else + std::memset (v, 0, 16); + } + + static void + set_image (unsigned char* i, bool& is_null, unsigned char const v[16]) + { + is_null = false; + std::memcpy (i, v, 16); + } + }; + + template <> + class value_traits + { + public: + typedef string_ptr value_type; + typedef std::string query_type; + typedef details::buffer image_type; + + static void + set_value (string_ptr& v, + const details::buffer& b, + std::size_t n, + bool is_null) + { + v.reset (is_null ? 0 : new std::string (b.data (), n)); + } + + static void + set_image (details::buffer& b, + std::size_t& n, + bool& is_null, + const string_ptr& v) + { + is_null = v.get () == 0; + + if (!is_null) + { + n = v->size (); + + if (n > b.capacity ()) + b.capacity (n); + + if (n != 0) + std::memcpy (b.data (), v->c_str (), n); + } + } + }; + } +} + +#endif // TRAITS_HXX -- cgit v1.1