diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2011-07-19 13:42:18 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2011-07-19 13:42:18 +0200 |
commit | 73c98a67ef4ed605cf69e0d212934c4dc1f3eb8e (patch) | |
tree | 8645cd8d06f218ff5d945b1b3b94a58a127753c4 /odb | |
parent | 12d0d9bbd0f5b7295c50fd5b223e538afe1abbf7 (diff) |
New design for NULL semantics
Now, instead of being specified as part of the SQL type with the type
pragma, there are separate null and not_null pragmas. The not_null
pragma was used to control NULL-ness of object pointers. Now the two
pragmas are used consistently for object pointers and simple values
(and in the future will work for composite values and containers).
Diffstat (limited to 'odb')
-rw-r--r-- | odb/context.cxx | 64 | ||||
-rw-r--r-- | odb/context.hxx | 43 | ||||
-rw-r--r-- | odb/pragma.cxx | 40 | ||||
-rw-r--r-- | odb/relational/mysql/context.cxx | 10 | ||||
-rw-r--r-- | odb/relational/mysql/context.hxx | 5 | ||||
-rw-r--r-- | odb/relational/mysql/schema.cxx | 18 | ||||
-rw-r--r-- | odb/relational/mysql/source.cxx | 4 | ||||
-rw-r--r-- | odb/relational/pgsql/context.cxx | 12 | ||||
-rw-r--r-- | odb/relational/pgsql/context.hxx | 5 | ||||
-rw-r--r-- | odb/relational/pgsql/source.cxx | 4 | ||||
-rw-r--r-- | odb/relational/schema.hxx | 8 | ||||
-rw-r--r-- | odb/relational/sqlite/context.cxx | 12 | ||||
-rw-r--r-- | odb/relational/sqlite/context.hxx | 6 | ||||
-rw-r--r-- | odb/relational/sqlite/source.cxx | 4 | ||||
-rw-r--r-- | odb/relational/type-processor.cxx | 93 | ||||
-rw-r--r-- | odb/validator.cxx | 83 |
16 files changed, 282 insertions, 129 deletions
diff --git a/odb/context.cxx b/odb/context.cxx index 04761f8..520caea 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -145,6 +145,47 @@ context () context* context::current_; +bool context:: +null (semantics::data_member& m) +{ + semantics::type& t (m.type ()); + + // By default pointers can be null. + // + if (object_pointer (t)) + return m.count ("null") || + (!m.count ("not-null") && + (t.count ("null") || !t.count ("not-null"))); + else + // Everything else by default is not null. + // + return m.count ("null") || + (!m.count ("not-null") && t.count ("null")); +} + +bool context:: +null (semantics::data_member& m, string const& kp) +{ + if (kp.empty ()) + return null (m); + + semantics::type& c (m.type ()); + semantics::type& t (member_type (m, kp)); + + if (object_pointer (t)) + return m.count (kp + "-null") || + (!m.count (kp + "-not-null") && + (c.count (kp + "-null") || + (!c.count (kp + "-not-null") && + (t.count ("null") || !t.count ("not-null"))))); + else + return m.count (kp + "-null") || + (!m.count (kp + "-not-null") && + (c.count (kp + "-null") || + (!c.count (kp + "-not-null") && + t.count ("null")))); +} + string context:: upcase (string const& s) { @@ -192,7 +233,7 @@ comp_value_ (semantics::class_& c) { bool r (true); - //@@ This is bad. Did I add new value pragmas and forgot to + //@@ This is bad. Did we add new value pragmas and forgot to // account for them here? // r = r && c.count ("value"); @@ -206,7 +247,10 @@ comp_value_ (semantics::class_& c) r = r && !c.count ("index-column"); r = r && !c.count ("key-column"); r = r && !c.count ("id-column"); + r = r && !c.count ("null"); r = r && !c.count ("not-null"); + r = r && !c.count ("value-null"); + r = r && !c.count ("value-not-null"); r = r && !c.count ("unordered"); c.set ("composite-value", r); @@ -269,10 +313,7 @@ column_type (semantics::data_member& m, string const& kp) } string context:: -database_type_impl (semantics::type& t, - semantics::names* hint, - semantics::context& ctx, - column_type_flags f) +database_type_impl (semantics::type& t, semantics::names* hint, bool id) { type_map_type::const_iterator end (data_->type_map_.end ()), i (end); @@ -293,16 +334,9 @@ database_type_impl (semantics::type& t, i = data_->type_map_.find (t.fq_name ()); if (i != end) - { - string r (ctx.count ("id") ? i->second.id_type : i->second.type); - - if ((f & ctf_default_null) == 0) - r += " NOT NULL"; - - return r; - } - - return string (); + return id ? i->second.id_type : i->second.type; + else + return string (); } static string diff --git a/odb/context.hxx b/odb/context.hxx index 54f6832..a256866 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -123,6 +123,12 @@ public: return c.abstract () || c.count ("abstract"); } + bool + null (semantics::data_member&); + + bool + null (semantics::data_member&, string const& key_prefix); + // Database names and types. // public: @@ -225,23 +231,6 @@ public: return pointer_kind (p) == pk_weak; } - bool - null_pointer (semantics::data_member& m) - { - return !(m.count ("not-null") || m.type ().count ("not-null")); - } - - bool - null_pointer (semantics::data_member& m, string const& key_prefix) - { - if (key_prefix.empty ()) - return null_pointer (m); - - return !(m.count (key_prefix + "-not-null") || - m.type ().count ("not-null") || - member_type (m, key_prefix).count ("not-null")); - } - semantics::data_member* inverse (semantics::data_member& m) { @@ -441,33 +430,19 @@ public: // Per-database customizable functionality. // protected: - typedef unsigned short column_type_flags; - - static column_type_flags const ctf_none = 0; - - // Default type should be NULL-able. - // - static column_type_flags const ctf_default_null = 0x01; - // Return empty string if there is no mapping. // string - database_type (semantics::type& t, - semantics::names* hint, - semantics::context& c, - column_type_flags f) + database_type (semantics::type& t, semantics::names* hint, bool id) { - return current ().database_type_impl (t, hint, c, f); + return current ().database_type_impl (t, hint, id); } // The default implementation uses the type map (populated by the database- // specific context implementation) to come up with a mapping. // virtual string - database_type_impl (semantics::type&, - semantics::names*, - semantics::context&, - column_type_flags); + database_type_impl (semantics::type&, semantics::names*, bool); public: typedef context root_context; diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 3b8d222..8408489 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -198,9 +198,13 @@ check_decl_type (tree d, string const& name, string const& p, location_t l) return false; } } - else if (p == "not_null") + else if (p == "null" || + p == "not_null" || + p == "value_null" || + p == "value_not_null") { - // Not_null can be used for both members and types (container or pointer). + // Null pragmas can be used for both members and types (values, + // containers, and pointers). // if (tc != FIELD_DECL && !TYPE_P (d)) { @@ -518,9 +522,15 @@ handle_pragma (cpp_reader* reader, tt = pragma_lex (&t); } - else if (p == "not_null") + else if (p == "null" || + p == "not_null" || + p == "value_null" || + p == "value_not_null") { + // null // not_null + // value_null + // value_not_null // // Make sure we've got the correct declaration type. @@ -766,7 +776,10 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) p == "index_type" || p == "key_type" || p == "table" || + p == "null" || p == "not_null" || + p == "value_null" || + p == "value_not_null" || p == "inverse" || p == "unordered" || p == "transient") @@ -903,12 +916,30 @@ handle_pragma_db_table (cpp_reader* reader) } extern "C" void +handle_pragma_db_null (cpp_reader* reader) +{ + handle_pragma_qualifier (reader, "null"); +} + +extern "C" void handle_pragma_db_not_null (cpp_reader* reader) { handle_pragma_qualifier (reader, "not_null"); } extern "C" void +handle_pragma_db_value_null (cpp_reader* reader) +{ + handle_pragma_qualifier (reader, "value_null"); +} + +extern "C" void +handle_pragma_db_value_not_null (cpp_reader* reader) +{ + handle_pragma_qualifier (reader, "value_not_null"); +} + +extern "C" void handle_pragma_db_inverse (cpp_reader* reader) { handle_pragma_qualifier (reader, "inverse"); @@ -945,7 +976,10 @@ register_odb_pragmas (void*, void*) c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype); c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype); c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table); + c_register_pragma_with_expansion ("db", "null", handle_pragma_db_null); c_register_pragma_with_expansion ("db", "not_null", handle_pragma_db_not_null); + c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null); + c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null); c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse); c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered); c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient); diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index e9446c2..431470a 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -234,12 +234,9 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, - semantics::names* hint, - semantics::context& ctx, - column_type_flags f) + database_type_impl (semantics::type& t, semantics::names* hint, bool id) { - string r (base_context::database_type_impl (t, hint, ctx, f)); + string r (base_context::database_type_impl (t, hint, id)); if (!r.empty ()) return r; @@ -290,9 +287,6 @@ namespace relational if (e->unsigned_ ()) r += " UNSIGNED"; } - - if ((f & ctf_default_null) == 0) - r += " NOT NULL"; } return r; diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 019b878..00ebe57 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -97,10 +97,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type&, - semantics::names*, - semantics::context&, - column_type_flags); + database_type_impl (semantics::type&, semantics::names*, bool); public: virtual diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index 0b44c53..3848c75 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -37,11 +37,27 @@ namespace relational } }; - struct object_columns: relational::object_columns + struct object_columns: relational::object_columns, context { object_columns (base const& x): base (x) {} virtual void + null (semantics::data_member& m) + { + if (!context::null (m, prefix_)) + os << " NOT NULL"; + else + { + // MySQL TIMESTAMP is by default NOT NULL. If we want it + // to contain NULL values, we need to explicitly declare + // the column as NULL. + // + if (column_sql_type (m, prefix_).type == sql_type::TIMESTAMP) + os << " NULL"; + } + } + + virtual void constraints (semantics::data_member& m) { base::constraints (m); diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 17bffdc..d3b4129 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -588,7 +588,7 @@ namespace relational { os << "}"; - if (!null_pointer (mi.m, key_prefix_)) + if (!null (mi.m, key_prefix_)) os << "else" << endl << "throw null_pointer ();"; } @@ -786,7 +786,7 @@ namespace relational << endl << "if (i." << mi.var << "null)" << endl; - if (null_pointer (mi.m, key_prefix_)) + if (null (mi.m, key_prefix_)) os << member << " = ptr_traits::pointer_type ();"; else os << "throw null_pointer ();"; diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 5a6b158..6c6c5a0 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -211,12 +211,9 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, - semantics::names* hint, - semantics::context& ctx, - column_type_flags f) + database_type_impl (semantics::type& t, semantics::names* hint, bool id) { - string r (base_context::database_type_impl (t, hint, ctx, f)); + string r (base_context::database_type_impl (t, hint, id)); if (!r.empty ()) return r; @@ -224,13 +221,8 @@ namespace relational using semantics::enum_; if (t.is_a<semantics::enum_> ()) - { r = "INTEGER"; - if ((f & ctf_default_null) == 0) - r += " NOT NULL"; - } - return r; } diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index 66d9318..a4fbc00 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -85,10 +85,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type& t, - semantics::names* hint, - semantics::context& ctx, - column_type_flags f); + database_type_impl (semantics::type& t, semantics::names* hint, bool); public: virtual diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index 0ed6890..3b7664d 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -502,7 +502,7 @@ namespace relational { os << "}"; - if (!null_pointer (mi.m, key_prefix_)) + if (!null (mi.m, key_prefix_)) os << "else" << endl << "throw null_pointer ();"; } @@ -674,7 +674,7 @@ namespace relational << endl << "if (i." << mi.var << "null)" << endl; - if (null_pointer (mi.m, key_prefix_)) + if (null (mi.m, key_prefix_)) os << member << " = ptr_traits::pointer_type ();"; else os << "throw null_pointer ();"; diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index b9243df..9d64276 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -189,6 +189,7 @@ namespace relational os << " " << quote_id (name) << " "; type (m); + null (m); constraints (m); reference (m); @@ -202,6 +203,13 @@ namespace relational } virtual void + null (semantics::data_member& m) + { + if (!context::null (m, prefix_)) + os << " NOT NULL"; + } + + virtual void constraints (semantics::data_member& m) { if (m.count ("id")) diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index b026822..4d00621 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -197,12 +197,9 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, - semantics::names* hint, - semantics::context& ctx, - column_type_flags f) + database_type_impl (semantics::type& t, semantics::names* hint, bool id) { - string r (base_context::database_type_impl (t, hint, ctx, f)); + string r (base_context::database_type_impl (t, hint, id)); if (!r.empty ()) return r; @@ -210,13 +207,8 @@ namespace relational using semantics::enum_; if (t.is_a<semantics::enum_> ()) - { r = "INTEGER"; - if ((f & ctf_default_null) == 0) - r += " NOT NULL"; - } - return r; } diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx index c625069..b5c3d85 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/relational/sqlite/context.hxx @@ -49,10 +49,8 @@ namespace relational protected: virtual string - database_type_impl (semantics::type&, - semantics::names*, - semantics::context&, - column_type_flags); + database_type_impl (semantics::type&, semantics::names*, bool); + public: virtual ~context (); diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 35a3246..6a99fc8 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -321,7 +321,7 @@ namespace relational { os << "}"; - if (!null_pointer (mi.m, key_prefix_)) + if (!null (mi.m, key_prefix_)) os << "else" << endl << "throw null_pointer ();"; } @@ -437,7 +437,7 @@ namespace relational << endl << "if (i." << mi.var << "null)" << endl; - if (null_pointer (mi.m, key_prefix_)) + if (null (mi.m, key_prefix_)) os << member << " = ptr_traits::pointer_type ();"; else os << "throw null_pointer ();"; diff --git a/odb/relational/type-processor.cxx b/odb/relational/type-processor.cxx index f7b0b2a..0f1c1d2 100644 --- a/odb/relational/type-processor.cxx +++ b/odb/relational/type-processor.cxx @@ -131,14 +131,7 @@ namespace relational type = idt.get<string> ("type"); if (type.empty ()) - { - column_type_flags f (ctf_none); - - if (null_pointer (m)) - f |= ctf_default_null; - - type = database_type (idt, id.belongs ().hint (), id, f); - } + type = database_type (idt, id.belongs ().hint (), true); } else { @@ -149,12 +142,22 @@ namespace relational type = t.get<string> ("type"); if (type.empty ()) - type = database_type (t, m.belongs ().hint (), m, ctf_none); + type = database_type (t, m.belongs ().hint (), m.count ("id")); } if (!type.empty ()) { m.set ("column-type", type); + + // Issue a warning if we are relaxing null-ness. + // + if (m.count ("null") && m.type ().count ("not-null")) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " warning: data member declared null while its type is " + << "declared not null" << endl; + } + return; } @@ -221,14 +224,7 @@ namespace relational type = idt.get<string> ("type"); if (type.empty ()) - { - column_type_flags f (ctf_none); - - if (null_pointer (m, prefix)) - f |= ctf_default_null; - - type = database_type (idt, id.belongs ().hint (), id, f); - } + type = database_type (idt, id.belongs ().hint (), true); } else { @@ -236,7 +232,7 @@ namespace relational type = t.get<string> ("type"); if (type.empty ()) - type = database_type (t, hint, m, ctf_none); + type = database_type (t, hint, false); } if (!type.empty ()) @@ -366,6 +362,10 @@ namespace relational t.set ("container-kind", ck); + // Mark id column as not null. + // + t.set ("id-not-null", string ()); + // Get the value type. // try @@ -404,6 +404,33 @@ namespace relational t.set ("value-tree-type", vt); t.set ("value-tree-hint", vh); + // If we have a set container, automatically mark the value + // column as not null. If we already have an explicit null for + // this column, issue an error. + // + if (ck == ck_set) + { + if (t.count ("value-null")) + { + os << t.file () << ":" << t.line () << ":" << t.column () << ":" + << " error: set container cannot contain null values" << endl; + + throw generation_failed (); + } + else + t.set ("value-not-null", string ()); + } + + // Issue a warning if we are relaxing null-ness in the + // container type. + // + if (t.count ("value-null") && vt->count ("not-null")) + { + os << t.file () << ":" << t.line () << ":" << t.column () << ":" + << " warning: container value declared null while its type " + << "is declared not null" << endl; + } + // Get the index type for ordered containers. // if (ck == ck_ordered) @@ -443,6 +470,7 @@ namespace relational t.set ("index-tree-type", it); t.set ("index-tree-hint", ih); + t.set ("index-not-null", string ()); } // Get the key type for maps. @@ -484,6 +512,7 @@ namespace relational t.set ("key-tree-type", kt); t.set ("key-tree-hint", kh); + t.set ("key-not-null", string ()); } } @@ -507,6 +536,28 @@ namespace relational if (ck == ck_ordered && m.count ("value-inverse")) m.set ("unordered", string ()); // Keep compatible with pragma. + // Issue an error if we have a null column in a set container. + // This can only happen if the value is declared as null in + // the member. + // + if (ck == ck_set && m.count ("value-null")) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: set container cannot contain null values" << endl; + + throw generation_failed (); + } + + // Issue a warning if we are relaxing null-ness in the member. + // + if (m.count ("value-null") && + (t.count ("value-not-null") || vt->count ("not-null"))) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " warning: container value declared null while the container " + << "type or value type declares it as not null" << endl; + } + return true; } @@ -729,12 +780,6 @@ namespace relational throw generation_failed (); } - if (m.count ("not-null") && !kp.empty ()) - { - m.remove ("not-null"); - m.set (kp + "-not-null", string ()); // Keep compatible with pragma. - } - // See if this is the inverse side of a bidirectional relationship. // If so, then resolve the member and cache it in the context. // diff --git a/odb/validator.cxx b/odb/validator.cxx index 487d69a..b07f5cd 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -15,6 +15,29 @@ using namespace std; namespace { + // Resolve null overrides. + // + static void + override_null (semantics::node& n, string const& prefix = "") + { + string p (prefix.empty () ? prefix : prefix + '-'); + + if (n.count (p + "null") && n.count (p + "not-null")) + { + if (n.get<location_t> (p + "null-loc") < + n.get<location_t> (p + "not-null-loc")) + { + n.remove (p + "null"); + n.remove (p + "null-loc"); + } + else + { + n.remove (p + "not-null"); + n.remove (p + "not-null-loc"); + } + } + } + struct data_member: traversal::data_member { data_member (bool& valid) @@ -42,6 +65,11 @@ namespace valid_ = false; } + + // Resolve null overrides. + // + override_null (m); + override_null (m, "value"); } bool& valid_; @@ -119,22 +147,44 @@ namespace // // + struct value_type: traversal::type + { + value_type (bool& valid): valid_ (valid) {} + + virtual void + traverse (semantics::type& t) + { + // Resolve null overrides. + // + override_null (t); + override_null (t, "value"); + } + + bool& valid_; + }; + + // + // struct class_: traversal::class_ { - class_ (bool& valid, semantics::unit& unit) - : valid_ (valid), unit_ (unit), member_ (valid) + class_ (bool& valid, semantics::unit& unit, value_type& vt) + : valid_ (valid), unit_ (unit), vt_ (vt), member_ (valid) { *this >> names_ >> member_; } - virtual void traverse (type& c) { if (c.count ("object")) traverse_object (c); - else if (context::comp_value (c)) - traverse_value (c); + else + { + if (context::comp_value (c)) + traverse_value (c); + + vt_.dispatch (c); + } } virtual void @@ -234,8 +284,23 @@ namespace } } else + { c.set ("id-member", id); + // Automatically mark the id member as not null. If we already have + // an explicit null pragma for this member, issue an error. + // + if (id->count ("null")) + { + cerr << id->file () << ":" << id->line () << ":" << id->column () + << ": error: object id member cannot be null" << endl; + + valid_ = false; + } + else + id->set ("not-null", string ()); + } + // Check members. // member_.count_ = 0; @@ -317,6 +382,7 @@ namespace bool& valid_; semantics::unit& unit_; + value_type& vt_; data_member member_; traversal::names names_; @@ -332,16 +398,21 @@ validate (options const&, traversal::unit unit; traversal::defines unit_defines; + traversal::declares unit_declares; traversal::namespace_ ns; - class_ c (valid, u); + value_type vt (valid); + class_ c (valid, u, vt); unit >> unit_defines >> ns; unit_defines >> c; + unit >> unit_declares >> vt; traversal::defines ns_defines; + traversal::declares ns_declares; ns >> ns_defines >> ns; ns_defines >> c; + ns >> ns_declares >> vt; unit.dispatch (u); |