From 73c98a67ef4ed605cf69e0d212934c4dc1f3eb8e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 19 Jul 2011 13:42:18 +0200 Subject: 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). --- odb/relational/mysql/context.cxx | 10 +---- odb/relational/mysql/context.hxx | 5 +-- odb/relational/mysql/schema.cxx | 18 +++++++- odb/relational/mysql/source.cxx | 4 +- odb/relational/pgsql/context.cxx | 12 +---- odb/relational/pgsql/context.hxx | 5 +-- odb/relational/pgsql/source.cxx | 4 +- odb/relational/schema.hxx | 8 ++++ odb/relational/sqlite/context.cxx | 12 +---- odb/relational/sqlite/context.hxx | 6 +-- odb/relational/sqlite/source.cxx | 4 +- odb/relational/type-processor.cxx | 93 +++++++++++++++++++++++++++++---------- 12 files changed, 110 insertions(+), 71 deletions(-) (limited to 'odb/relational') 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 ()) - { 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 ()) - { 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 ("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 ("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 ("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 ("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. // -- cgit v1.1