From 15b1a95942518c84f8161c59820ea5d0e49a4e54 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 27 Apr 2012 11:36:43 +0200 Subject: Add support for NULL pointers to objects with composite object ids --- odb/common.cxx | 1 + odb/common.hxx | 7 ++ odb/context.cxx | 15 ++++ odb/context.hxx | 9 +- odb/makefile | 5 ++ odb/relational/header.hxx | 15 ++++ odb/relational/inline.hxx | 173 ++++++++++++++++++++++++++++++++++++++- odb/relational/model.hxx | 2 +- odb/relational/mssql/inline.cxx | 43 ++++++++++ odb/relational/mysql/inline.cxx | 43 ++++++++++ odb/relational/oracle/inline.cxx | 43 ++++++++++ odb/relational/pgsql/inline.cxx | 43 ++++++++++ odb/relational/source.cxx | 1 - odb/relational/source.hxx | 41 +++++----- odb/relational/sqlite/inline.cxx | 43 ++++++++++ 15 files changed, 456 insertions(+), 28 deletions(-) create mode 100644 odb/relational/mssql/inline.cxx create mode 100644 odb/relational/mysql/inline.cxx create mode 100644 odb/relational/oracle/inline.cxx create mode 100644 odb/relational/pgsql/inline.cxx create mode 100644 odb/relational/sqlite/inline.cxx diff --git a/odb/common.cxx b/odb/common.cxx index 28b80cb..0edcb0d 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -338,6 +338,7 @@ traverse (semantics::data_member& m, root_ = &m; root_id_ = (kp.empty () ? context::id (m) : kp == "id"); root_op_ = (c != 0); + root_null_ = context::null (m, kp); key_prefix_ = kp; default_name_ = dn; diff --git a/odb/common.hxx b/odb/common.hxx index bc45d00..082d78f 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -310,10 +310,17 @@ protected: member_path_, key_prefix_, (root_ != 0 && (root_id_ || root_op_))); } + bool + null () const + { + return (root_ != 0 && root_null_) || context::null (member_path_); + } + private: semantics::data_member* root_; // Root member if traversing from a member. bool root_id_; // True if traversing root as object id. bool root_op_; // True if traversing root as object pointer. + bool root_null_; // True if root is null-able. private: void diff --git a/odb/context.cxx b/odb/context.cxx index f699f38..693ada4 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -296,6 +296,21 @@ readonly (semantics::data_member& m) } bool context:: +null (data_member_path const& mp) +{ + // Outer members can override the null-ability of the inner ones. So + // start from the most outer member. + // + for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i) + { + if (null (**i)) + return true; + } + + return false; +} + +bool context:: null (semantics::data_member& m) { semantics::type& t (utype (m)); diff --git a/odb/context.hxx b/odb/context.hxx index 2bb91cb..0bb58ba 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -425,12 +425,15 @@ public: return c.count ("readonly"); } + // Null-able. // - // - bool + static bool + null (data_member_path const&); + + static bool null (semantics::data_member&); - bool + static bool null (semantics::data_member&, string const& key_prefix); // Optimistic concurrency. diff --git a/odb/makefile b/odb/makefile index d048065..743ad0b 100644 --- a/odb/makefile +++ b/odb/makefile @@ -44,6 +44,7 @@ cxx_ptun += \ relational/mssql/common.cxx \ relational/mssql/context.cxx \ relational/mssql/header.cxx \ +relational/mssql/inline.cxx \ relational/mssql/source.cxx \ relational/mssql/model.cxx \ relational/mssql/schema.cxx @@ -54,6 +55,7 @@ cxx_ptun += \ relational/mysql/common.cxx \ relational/mysql/context.cxx \ relational/mysql/header.cxx \ +relational/mysql/inline.cxx \ relational/mysql/source.cxx \ relational/mysql/model.cxx \ relational/mysql/schema.cxx @@ -64,6 +66,7 @@ cxx_ptun += \ relational/oracle/common.cxx \ relational/oracle/context.cxx \ relational/oracle/header.cxx \ +relational/oracle/inline.cxx \ relational/oracle/source.cxx \ relational/oracle/model.cxx \ relational/oracle/schema.cxx @@ -74,6 +77,7 @@ cxx_ptun += \ relational/pgsql/common.cxx \ relational/pgsql/context.cxx \ relational/pgsql/header.cxx \ +relational/pgsql/inline.cxx \ relational/pgsql/source.cxx \ relational/pgsql/model.cxx \ relational/pgsql/schema.cxx @@ -84,6 +88,7 @@ cxx_ptun += \ relational/sqlite/common.cxx \ relational/sqlite/context.cxx \ relational/sqlite/header.cxx \ +relational/sqlite/inline.cxx \ relational/sqlite/source.cxx \ relational/sqlite/model.cxx \ relational/sqlite/schema.cxx diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 3d3189e..09a2d39 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -1187,6 +1187,21 @@ namespace relational << "init (value_type&, const image_type&, database*);" << endl; + if (!has_a (c, test_container)) + { + // get_null (image) + // + os << "static bool" << endl + << "get_null (const image_type&);" + << endl; + + // set_null (image) + // + os << "static void" << endl + << "set_null (image_type&, " << db << "::statement_kind);" + << endl; + } + os << "};"; } diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index 0c585b8..add4741 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -75,23 +75,145 @@ namespace relational }; // + // get/set null (composite value only) + // + + struct null_member: virtual member_base + { + typedef null_member base; + + null_member (bool get) + : member_base (string (), 0, string (), string ()), get_ (get) + { + } + + protected: + bool get_; + }; + + template + struct null_member_impl: null_member, virtual member_base_impl + { + typedef null_member_impl base_impl; + + null_member_impl (base const& x): base (x) {} + + typedef typename member_base_impl::member_info member_info; + + virtual bool + pre (member_info& mi) + { + // If the whole value type is readonly, then set will never be + // called with sk == statement_update. + // + if (!get_ && !readonly (*context::top_object)) + { + semantics::class_* c; + + if (readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c))) + os << "if (sk == statement_insert)" << endl; + } + + return true; + } + + virtual void + traverse_composite (member_info& mi) + { + string traits ("composite_value_traits< " + mi.fq_type () + " >"); + + if (get_) + os << "r = r && " << traits << "::get_null (" << + "i." << mi.var << "value);"; + else + os << traits << "::set_null (i." << mi.var << "value, sk);"; + } + }; + + struct null_base: traversal::class_, virtual context + { + typedef null_base base; + + null_base (bool get): get_ (get) {} + + virtual void + traverse (type& c) + { + // Ignore transient bases. + // + if (!composite (c)) + return; + + string traits ("composite_value_traits< " + class_fq_name (c) + " >"); + + // If the derived value type is readonly, then set will never be + // called with sk == statement_update. + // + if (!get_ && readonly (c) && !readonly (*context::top_object)) + os << "if (sk == statement_insert)" << endl; + + if (get_) + os << "r = r && " << traits << "::get_null (i);"; + else + os << traits << "::set_null (i, sk);"; + } + + protected: + bool get_; + }; + + // // struct class_: traversal::class_, virtual context { typedef class_ base; + class_ () + : get_null_base_ (true), + get_null_member_ (true), + set_null_base_ (false), + set_null_member_ (false) + { + init (); + } + + class_ (class_ const&) + : root_context (), //@@ -Wextra + context (), + get_null_base_ (true), + get_null_member_ (true), + set_null_base_ (false), + set_null_member_ (false) + { + init (); + } + + void + init () + { + get_null_base_inherits_ >> get_null_base_; + get_null_member_names_ >> get_null_member_; + + set_null_base_inherits_ >> set_null_base_; + set_null_member_names_ >> set_null_member_; + } + virtual void traverse (type& c) { if (class_file (c) != unit.file ()) return; + context::top_object = context::cur_object = &c; + if (object (c)) traverse_object (c); if (view (c)) traverse_view (c); else if (composite (c)) traverse_composite (c); + + context::top_object = context::cur_object = 0; } virtual void @@ -504,12 +626,61 @@ namespace relational } virtual void - traverse_composite (type&) + traverse_composite (type& c) { + string const& type (class_fq_name (c)); + string traits ("access::composite_value_traits< " + type + " >"); + + os << "// " << class_name (c) << endl + << "//" << endl + << endl; + + if (!has_a (c, test_container)) + { + // get_null (image) + // + os << "inline" << endl + << "bool " << traits << "::" << endl + << "get_null (const image_type& i)" + << "{" + << "bool r (true);"; + + inherits (c, get_null_base_inherits_); + names (c, get_null_member_names_); + + os << "return r;" + << "}"; + + // set_null (image) + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "set_null (image_type& i, " << db << "::statement_kind sk)" + << "{" + << "ODB_POTENTIALLY_UNUSED (sk);" + << endl + << "using namespace " << db << ";" + << endl; + + inherits (c, set_null_base_inherits_); + names (c, set_null_member_names_); + + os << "}"; + } } private: instance callback_calls_; + + instance get_null_base_; + traversal::inherits get_null_base_inherits_; + instance get_null_member_; + traversal::names get_null_member_names_; + + instance set_null_base_; + traversal::inherits set_null_base_inherits_; + instance set_null_member_; + traversal::names set_null_member_names_; }; struct include: virtual context diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index afaa90d..d41b701 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -102,7 +102,7 @@ namespace relational traverse_column (semantics::data_member& m, string const& name, bool) { bool id (object_columns_base::id ()); // Id or part of. - bool null (!id && context::null (m, key_prefix_)); + bool null (!id && object_columns_base::null ()); string col_id (id_prefix_ + (key_prefix_.empty () ? m.name () : key_prefix_)); diff --git a/odb/relational/mssql/inline.cxx b/odb/relational/mssql/inline.cxx new file mode 100644 index 0000000..d49fbfa --- /dev/null +++ b/odb/relational/mssql/inline.cxx @@ -0,0 +1,43 @@ +// file : odb/relational/mssql/inline.cxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace mssql + { + namespace inline_ + { + namespace relational = relational::inline_; + + struct null_member: relational::null_member_impl, + member_base + { + null_member (base const& x) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) + { + } + + virtual void + traverse_simple (member_info& mi) + { + if (get_) + os << "r = r && i." << mi.var << "size_ind == SQL_NULL_DATA;"; + else + os << "i." << mi.var << "size_ind = SQL_NULL_DATA;"; + } + }; + entry null_member_; + } + } +} diff --git a/odb/relational/mysql/inline.cxx b/odb/relational/mysql/inline.cxx new file mode 100644 index 0000000..f9a02a9 --- /dev/null +++ b/odb/relational/mysql/inline.cxx @@ -0,0 +1,43 @@ +// file : odb/relational/mysql/inline.cxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace mysql + { + namespace inline_ + { + namespace relational = relational::inline_; + + struct null_member: relational::null_member_impl, + member_base + { + null_member (base const& x) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) + { + } + + virtual void + traverse_simple (member_info& mi) + { + if (get_) + os << "r = r && i." << mi.var << "null;"; + else + os << "i." << mi.var << "null = 1;"; + } + }; + entry null_member_; + } + } +} diff --git a/odb/relational/oracle/inline.cxx b/odb/relational/oracle/inline.cxx new file mode 100644 index 0000000..5f69fa8 --- /dev/null +++ b/odb/relational/oracle/inline.cxx @@ -0,0 +1,43 @@ +// file : odb/relational/oracle/inline.cxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace oracle + { + namespace inline_ + { + namespace relational = relational::inline_; + + struct null_member: relational::null_member_impl, + member_base + { + null_member (base const& x) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) + { + } + + virtual void + traverse_simple (member_info& mi) + { + if (get_) + os << "r = r && i." << mi.var << "indicator == -1;"; + else + os << "i." << mi.var << "indicator = -1;"; + } + }; + entry null_member_; + } + } +} diff --git a/odb/relational/pgsql/inline.cxx b/odb/relational/pgsql/inline.cxx new file mode 100644 index 0000000..ece021a --- /dev/null +++ b/odb/relational/pgsql/inline.cxx @@ -0,0 +1,43 @@ +// file : odb/relational/pgsql/inline.cxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace pgsql + { + namespace inline_ + { + namespace relational = relational::inline_; + + struct null_member: relational::null_member_impl, + member_base + { + null_member (base const& x) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) + { + } + + virtual void + traverse_simple (member_info& mi) + { + if (get_) + os << "r = r && i." << mi.var << "null;"; + else + os << "i." << mi.var << "null = true;"; + } + }; + entry null_member_; + } + } +} diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 5bf14f1..eb909ce 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -79,7 +79,6 @@ traverse_object (type& c) if (containers) { - containers = true; size_t scn (has_a (c, test_straight_container)); if (scn != 0) diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index bbea0a7..594274a 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -1266,10 +1266,10 @@ namespace relational os << "}" << "else" << endl; - // @@ Composite value currently cannot be NULL. - // - if (!null (mi.m, key_prefix_) || composite (mi.t)) + if (!null (mi.m, key_prefix_)) os << "throw null_pointer ();"; + else if (composite (mi.t)) + os << traits << "::set_null (i." << mi.var << "value, sk);"; else set_null (mi); } @@ -1456,22 +1456,23 @@ namespace relational " > ptr_traits;" << endl; - // @@ Composite value currently cannot be NULL. - // - if (!comp) - { - os << "if ("; + os << "if ("; + + if (comp) + os << "composite_value_traits< " + type + " >::get_null (i." << + mi.var << "value)"; + else get_null (mi); - os << ")" << endl; - if (!null (mi.m, key_prefix_) ) - os << "throw null_pointer ();"; - else - os << member << " = ptr_traits::pointer_type ();"; + os << ")" << endl; - os << "else" - << "{"; - } + if (!null (mi.m, key_prefix_) ) + os << "throw null_pointer ();"; + else + os << member << " = ptr_traits::pointer_type ();"; + + os << "else" + << "{"; os << type << " id;"; @@ -1542,12 +1543,8 @@ namespace relational } } - // @@ Composite value currently cannot be NULL. - // - if (!composite (mi.t)) - os << "}"; - - os << "}"; + os << "}" + << "}"; } } diff --git a/odb/relational/sqlite/inline.cxx b/odb/relational/sqlite/inline.cxx new file mode 100644 index 0000000..f4dc377 --- /dev/null +++ b/odb/relational/sqlite/inline.cxx @@ -0,0 +1,43 @@ +// file : odb/relational/sqlite/inline.cxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace sqlite + { + namespace inline_ + { + namespace relational = relational::inline_; + + struct null_member: relational::null_member_impl, + member_base + { + null_member (base const& x) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) + { + } + + virtual void + traverse_simple (member_info& mi) + { + if (get_) + os << "r = r && i." << mi.var << "null;"; + else + os << "i." << mi.var << "null = true;"; + } + }; + entry null_member_; + } + } +} -- cgit v1.1