From 0d65234bc5c6742721c00360a0e3117d51d89c5f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 3 Jul 2015 18:23:51 +0200 Subject: Implement nested id support Now the 'id' specifier can optionally include the data member path to the id inside the composite value. For example: #pragma db id(first) std::pair p; Note that one somewhat counter-intuitive aspect of this new feature is that the whole member marked with id ('p' in the above example) and not just the actual id member ('p.first' in the above example) is treated as readonly. Such nested id also cannot be automatically assigned (auto specifier). --- common/id/nested/driver.cxx | 265 ++++++++++++++++++++++++++++++++++++++++++++ common/id/nested/makefile | 118 ++++++++++++++++++++ common/id/nested/test.hxx | 218 ++++++++++++++++++++++++++++++++++++ common/id/nested/test.std | 0 4 files changed, 601 insertions(+) create mode 100644 common/id/nested/driver.cxx create mode 100644 common/id/nested/makefile create mode 100644 common/id/nested/test.hxx create mode 100644 common/id/nested/test.std (limited to 'common/id/nested') diff --git a/common/id/nested/driver.cxx b/common/id/nested/driver.cxx new file mode 100644 index 0000000..28dac6a --- /dev/null +++ b/common/id/nested/driver.cxx @@ -0,0 +1,265 @@ +// file : common/id/nested/driver.cxx +// copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// Test nested ids. +// + +#include // std::auto_ptr +#include +#include + +#include +#include +#include + +#include + +#include "test.hxx" +#include "test-odb.hxx" + +using namespace std; +using namespace odb::core; + +struct failed {}; + +int +main (int argc, char* argv[]) +{ + try + { + auto_ptr db (create_database (argc, argv)); + + + // Simple nested id. + // + { + using namespace test1; + + object o1 (1, "a", 3); + o1.v.push_back (123); + + object o2 (4, "b", 6); + o2.v.push_back (234); + + object1 o (new object (10, "abc", 11)); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o.p); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.y)); + auto_ptr p2 (db->load (o2.id.y)); + auto_ptr p (db->load (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + + o1.z++; + o1.v.pop_back (); + o1.v.push_back (234); + + o2.z--; + o2.v.back ()++; + o2.v.push_back (123); + + delete o.p; + o.p = new object (20, "xyz", 11); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + db->persist (o.p); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.y)); + auto_ptr p2 (db->load (o2.id.y)); + auto_ptr p (db->load (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + } + + // Composite nested id. + // + { + using namespace test2; + + object o1 (1, 2, "a", 123); + o1.v.push_back (123); + + object o2 (1, 3, "b", 234); + o2.v.push_back (234); + + object1 o (new object (2, 2, "abc", 123)); + o.p->v.push_back (345); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o.p); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.c)); + auto_ptr p2 (db->load (o2.id.c)); + auto_ptr p (db->load (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + + o1.z++; + o1.v.pop_back (); + o1.v.push_back (234); + + o2.z--; + o2.v.modify_back ()++; + o2.v.push_back (123); + + delete o.p; + o.p = new object (2, 3, "xyz", 234); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + db->persist (o.p); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.c)); + auto_ptr p2 (db->load (o2.id.c)); + auto_ptr p (db->load (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + } + + // Custom/by-value access. + // + { + using namespace test3; + + object o1 (1, "a", 3); + object o2 (4, "b", 6); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.y)); + auto_ptr p2 (db->load (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o1.z++; + o2.z--; + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.y)); + auto_ptr p2 (db->load (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + + // Polymorphic. + // + { + using namespace test4; + + base o1 (1, "a"); + object o2 (2, "b", 1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.y)); + auto_ptr p2 (db->load (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o2.z--; + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id.y)); + auto_ptr p2 (db->load (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/common/id/nested/makefile b/common/id/nested/makefile new file mode 100644 index 0000000..c2e5b0e --- /dev/null +++ b/common/id/nested/makefile @@ -0,0 +1,118 @@ +# file : common/id/nested/makefile +# copyright : Copyright (c) 2009-2015 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 +genf := $(call odb-gen,$(odb_hdr)) +gen := $(addprefix $(out_base)/,$(genf)) +cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.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 + +# 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) + +$(gen): $(odb) +$(gen): odb := $(odb) +$(gen) $(dist): export odb_options += --generate-schema --generate-query \ +--table-prefix t_id_nested_ +$(gen): cpp_options := -I$(src_base) +$(gen): $(common.l.cpp-options) + +ifneq ($(db_id),common) +$(gen): odb_options += --database $(db_id) +else +$(gen): odb_options += --multi-database dynamic +endif + +$(call include-dep,$(cxx_od),$(cxx_obj),$(gen)) + +# Alias for default target. +# +$(out_base)/: $(driver) + +# Dist +# +name := $(subst /,-,$(subst $(src_root)/common/,,$(src_base))) + +$(dist): sources := $(cxx_tun) +$(dist): headers := $(odb_hdr) +$(dist): data_dist := test.std +$(dist): export name := $(name) +$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \ +$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name)) \ +$(call vc12projs,$(name)) +$(dist): + $(call dist-data,$(sources) $(headers) $(data_dist)) + $(call meta-automake,../../template/Makefile.am) + $(call meta-vc8projs,../../template/template,$(name)) + $(call meta-vc9projs,../../template/template,$(name)) + $(call meta-vc10projs,../../template/template,$(name)) + $(call meta-vc11projs,../../template/template,$(name)) + $(call meta-vc12projs,../../template/template,$(name)) + +# Test. +# +ifneq ($(db_id),common) +$(eval $(call test-rule)) +else +$(foreach d,$(databases),$(eval $(call test-rule,$d))) +endif + +# Clean. +# +$(clean): \ + $(driver).o.clean \ + $(addsuffix .cxx.clean,$(cxx_obj)) \ + $(addsuffix .cxx.clean,$(cxx_od)) \ + $(addsuffix .hxx.clean,$(filter %.cxx,$(gen))) + $(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/vc8proj.make) +$(call include,$(bld_root)/meta/vc9proj.make) +$(call include,$(bld_root)/meta/vc10proj.make) +$(call include,$(bld_root)/meta/vc11proj.make) +$(call include,$(bld_root)/meta/vc12proj.make) +$(call include,$(bld_root)/meta/automake.make) + +$(call include,$(bld_root)/cxx/standard.make) # cxx_standard +ifdef cxx_standard +$(gen): odb_options += --std $(cxx_standard) +$(call include,$(odb_rules)) +endif + +$(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/common/id/nested/test.hxx b/common/id/nested/test.hxx new file mode 100644 index 0000000..6449fd4 --- /dev/null +++ b/common/id/nested/test.hxx @@ -0,0 +1,218 @@ +// file : common/id/nested/test.hxx +// copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include +#include + +#include +#include + +// Simple nested id. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db value + struct comp + { + int x; + std::string y; + }; + + #pragma db object + struct object + { + #pragma db id(y) + comp id; + + int z; + std::vector v; + + object () {} + object (int x, std::string y, int z_): z (z_) {id.x = x; id.y = y;} + }; + + inline bool + operator== (object a, object b) + { + return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z && a.v == b.v; + } + + #pragma db object + struct object1 + { + #pragma db id auto + int id; + + object* p; + + object1 (object* p_ = 0): p (p_) {} + ~object1 () {delete p;} + }; + + inline bool + operator== (const object1& a, const object1& b) + { + return a.id == b.id && *a.p == *b.p; + } +} + +// Composite nested id. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db value + struct comp1 + { + int x; + int y; + }; + + #pragma db value + struct comp2 + { + comp1 c; + std::string s; + }; + + #pragma db object + struct object + { + #pragma db id(c) + comp2 id; + + int z; + odb::vector v; + + object () {} + object (int x, int y, std::string s, int z_) + : z (z_) {id.c.x = x; id.c.y = y; id.s = s;} + }; + + inline bool + operator== (object a, object b) + { + return a.id.c.x == b.id.c.x && a.id.c.y == b.id.c.y && + a.id.s == b.id.s && a.z == b.z && a.v == b.v; + } + + #pragma db object + struct object1 + { + #pragma db id auto + int id; + + object* p; + + object1 (object* p_ = 0): p (p_) {} + ~object1 () {delete p;} + }; + + inline bool + operator== (const object1& a, const object1& b) + { + return a.id == b.id && *a.p == *b.p; + } + + // Multiple levels of nesting, just a compile test. + // + #pragma db object + struct object2 + { + #pragma db id(c.x) + comp2 id; + + int z; + }; +} + +// Custom/by-value access. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db value + struct comp + { + int x; + + std::string get_y () const {return y;} + void set_y (std::string v) {y = v;} + + #pragma db get(get_y) set(set_y) + std::string y; + }; + + #pragma db object + struct object + { + comp get_id () const {return id;} + void set_id (comp v) {id = v;} + + #pragma db id(y) get(get_id) set(set_id) + comp id; + + int z; + + object () {} + object (int x, std::string y, int z_): z (z_) {id.x = x; id.y = y;} + }; + + inline bool + operator== (object a, object b) + { + return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z; + } +} + +// Polymorphic. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db value + struct comp + { + int x; + std::string y; + }; + + #pragma db object polymorphic + struct base + { + #pragma db id(y) + comp id; + + virtual ~base () {} + base () {} + base (int x, std::string y) {id.x = x; id.y = y;} + }; + + inline bool + operator== (const base& a, const base& b) + { + return a.id.x == b.id.x && a.id.y == b.id.y; + } + + #pragma db object + struct object: base + { + int z; + + object () {} + object (int x, std::string y, int z_): base (x, y), z (z_) {} + }; + + inline bool + operator== (const object& a, const object& b) + { + return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z; + } +} + +#endif // TEST_HXX diff --git a/common/id/nested/test.std b/common/id/nested/test.std new file mode 100644 index 0000000..e69de29 -- cgit v1.1