aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-07-22 14:49:29 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-07-22 17:57:00 +0200
commit81ea37904e4959414b53b225b4b5e56e1b561bdc (patch)
tree6ae70d4adb9c0c3ed57679873de8061d5b9c5b19
parent246b1557bf84082cc72ec2cbe089262b21c8bc97 (diff)
Add pragma for setting type's or member's default value
New pragma: default. New test: default.
-rw-r--r--odb/context.cxx9
-rw-r--r--odb/gcc.hxx1
-rw-r--r--odb/pragma.cxx138
-rw-r--r--odb/relational/mysql/schema.cxx79
-rw-r--r--odb/relational/pgsql/schema.cxx37
-rw-r--r--odb/relational/schema.cxx122
-rw-r--r--odb/relational/schema.hxx57
-rw-r--r--odb/relational/sqlite/schema.cxx26
-rw-r--r--odb/validator.cxx12
9 files changed, 474 insertions, 7 deletions
diff --git a/odb/context.cxx b/odb/context.cxx
index 0227763..39a01f7 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -247,15 +247,16 @@ 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 ("default");
+ 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 ("options");
r = r && !c.count ("value-options");
r = r && !c.count ("index-options");
r = r && !c.count ("key-options");
r = r && !c.count ("id-options");
- 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);
diff --git a/odb/gcc.hxx b/odb/gcc.hxx
index feb69ba..7967b74 100644
--- a/odb/gcc.hxx
+++ b/odb/gcc.hxx
@@ -27,6 +27,7 @@ extern "C"
#include <coretypes.h>
#include <tree.h>
#include <intl.h>
+#include <real.h>
#include <tm.h>
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index bf1b1d1..a77cf48 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -183,6 +183,17 @@ check_decl_type (tree d, string const& name, string const& p, location_t l)
return false;
}
}
+ else if (p == "default")
+ {
+ // Default can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !TYPE_P (d))
+ {
+ error (l) << "name '" << name << "' in db pragma '" << p << "' does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
else if (p == "value_column" ||
p == "index_column" ||
p == "key_column" ||
@@ -598,6 +609,125 @@ handle_pragma (cpp_reader* reader,
tt = pragma_lex (&t);
}
+ else if (p == "default")
+ {
+ // default () (<empty>)
+ // default (null) (n)
+ // default (true|false) (t|f)
+ // default ([+|-]<number>) (-|+)
+ // default ("string") (s)
+ // default (<enumerator>) (e)
+ //
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl != 0 && !check_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (pragma_lex (&t) != CPP_OPEN_PAREN)
+ {
+ error () << "'(' expected after db pragma '" << p << "'" << endl;
+ return;
+ }
+
+ tt = pragma_lex (&t);
+
+ // Encode the kind of value we have in the first letter of
+ // the string.
+ //
+ switch (tt)
+ {
+ case CPP_CLOSE_PAREN:
+ {
+ // Default value override.
+ //
+ break;
+ }
+ case CPP_STRING:
+ {
+ val = "s";
+ val += TREE_STRING_POINTER (t);
+ tt = pragma_lex (&t);
+ break;
+ }
+ case CPP_NAME:
+ {
+ // This can be the null, true, or false keyword or a enumerator
+ // name.
+ //
+ string n (IDENTIFIER_POINTER (t));
+
+ if (n == "null" || n == "true" || n == "false")
+ {
+ val = n[0];
+ tt = pragma_lex (&t);
+ break;
+ }
+ // Fall throught.
+ }
+ case CPP_SCOPE:
+ {
+ // We have a potentially scopped enumerator name.
+ //
+ string n;
+ tree decl = parse_scoped_name (t, tt, n, false, p);
+
+ if (decl == 0)
+ return;
+
+ node = decl;
+ val = "e" + n;
+ break;
+ }
+ case CPP_MINUS:
+ case CPP_PLUS:
+ {
+ val = (tt == CPP_MINUS ? "-" : "+");
+ tt = pragma_lex (&t);
+
+ if (tt != CPP_NUMBER)
+ {
+ error () << "expected numeric constant after '" << val
+ << "' in db pragma '" << p << "'" << endl;
+ return;
+ }
+
+ // Fall through.
+ }
+ case CPP_NUMBER:
+ {
+ int tc (TREE_CODE (t));
+
+ if (tc != INTEGER_CST && tc != REAL_CST)
+ {
+ error () << "unexpected numeric constant in db pragma '" << p
+ << "'" << endl;
+ return;
+ }
+
+ if (val.empty ())
+ val = "+";
+
+ node = t;
+ tt = pragma_lex (&t);
+ break;
+ }
+ default:
+ {
+ error () << "unexpected expression in db pragma '" << p << "'" << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error () << "')' expected at the end of db pragma '" << p << "'" << endl;
+ return;
+ }
+
+ tt = pragma_lex (&t);
+ }
else if (p == "inverse")
{
// inverse (name)
@@ -843,6 +973,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
p == "not_null" ||
p == "value_null" ||
p == "value_not_null" ||
+ p == "default" ||
p == "inverse" ||
p == "unordered" ||
p == "transient")
@@ -1033,6 +1164,12 @@ handle_pragma_db_value_not_null (cpp_reader* r)
}
extern "C" void
+handle_pragma_db_default (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "default");
+}
+
+extern "C" void
handle_pragma_db_inverse (cpp_reader* r)
{
handle_pragma_qualifier (r, "inverse");
@@ -1107,6 +1244,7 @@ register_odb_pragmas (void*, void*)
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", "default", handle_pragma_db_default);
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/schema.cxx b/odb/relational/mysql/schema.cxx
index 3848c75..4a7171f 100644
--- a/odb/relational/mysql/schema.cxx
+++ b/odb/relational/mysql/schema.cxx
@@ -58,6 +58,85 @@ namespace relational
}
virtual void
+ default_bool (semantics::data_member&, bool v)
+ {
+ // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still
+ // use them for self-documentation.
+ //
+ os << " DEFAULT " << (v ? "TRUE" : "FALSE");
+ }
+
+ virtual void
+ default_enum (semantics::data_member& m, tree en, string const& name)
+ {
+ // Make sure the column is mapped to an ENUM or integer type.
+ //
+ sql_type const& t (column_sql_type (m));
+
+ switch (t.type)
+ {
+ case sql_type::ENUM:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to MySQL ENUM or integer type"
+ << endl;
+
+ throw generation_failed ();
+ }
+ }
+
+ using semantics::enum_;
+ using semantics::enumerator;
+
+ enumerator& er (dynamic_cast<enumerator&> (*unit.find (en)));
+ enum_& e (er.enum_ ());
+
+ if (t.type == sql_type::ENUM)
+ {
+ // Assuming the enumerators in the C++ enum and MySQL ENUM are
+ // in the same order, calculate the poistion of the C++
+ // enumerator and use that as an index in the MySQL ENUM.
+ //
+ size_t pos (0);
+
+ for (enum_::enumerates_iterator i (e.enumerates_begin ()),
+ end (e.enumerates_end ()); i != end; ++i)
+ {
+ if (&i->enumerator () == &er)
+ break;
+
+ pos++;
+ }
+
+ if (pos < t.enumerators.size ())
+ os << " DEFAULT " << t.enumerators[pos];
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unable to map C++ enumerator '" << name
+ << "' to MySQL ENUM value" << endl;
+
+ throw generation_failed ();
+ }
+ }
+ else
+ {
+ if (e.unsigned_ ())
+ os << " DEFAULT " << er.value ();
+ else
+ os << " DEFAULT " << static_cast<long long> (er.value ());
+ }
+ }
+
+ virtual void
constraints (semantics::data_member& m)
{
base::constraints (m);
diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx
index b7901e5..6530e82 100644
--- a/odb/relational/pgsql/schema.cxx
+++ b/odb/relational/pgsql/schema.cxx
@@ -77,6 +77,43 @@ namespace relational
}
virtual void
+ default_bool (semantics::data_member&, bool v)
+ {
+ os << " DEFAULT " << (v ? "TRUE" : "FALSE");
+ }
+
+ virtual void
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to an integer type.
+ //
+ switch (column_sql_type (m).type)
+ {
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to PostgreSQL integer type" << endl;
+
+ throw generation_failed ();
+ }
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ if (e.enum_ ().unsigned_ ())
+ os << " DEFAULT " << e.value ();
+ else
+ os << " DEFAULT " << static_cast<long long> (e.value ());
+ }
+
+ virtual void
reference (semantics::data_member&)
{
}
diff --git a/odb/relational/schema.cxx b/odb/relational/schema.cxx
index 6acb6dc..a347d49 100644
--- a/odb/relational/schema.cxx
+++ b/odb/relational/schema.cxx
@@ -3,6 +3,12 @@
// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
+#include <odb/gcc.hxx>
+
+#include <cassert>
+#include <limits>
+#include <sstream>
+
#include <odb/emitter.hxx>
#include <odb/relational/schema.hxx>
@@ -14,6 +20,122 @@ namespace relational
{
namespace schema
{
+ // object_columns
+ //
+ void object_columns::
+ default_ (semantics::data_member& m)
+ {
+ string s;
+ tree n (0);
+
+ semantics::type& t (m.type ());
+
+ if (m.count ("default"))
+ {
+ s = m.get<string> ("default");
+
+ // Empty string is a default value override which means
+ // there is no default value.
+ //
+ if (s.empty ())
+ return;
+
+ if (m.count ("default-node"))
+ n = m.get<tree> ("default-node");
+ }
+ else if (t.count ("default"))
+ {
+ s = t.get<string> ("default");
+
+ if (s.empty ())
+ return;
+
+ if (t.count ("default-node"))
+ n = t.get<tree> ("default-node");
+ }
+ else
+ return; // No default value for this column.
+
+ // The first letter in the default value string identifies
+ // the type of the value. See pragma.cxx for details.
+ //
+ switch (s[0])
+ {
+ case 'n':
+ {
+ default_null (m);
+ break;
+ }
+ case 't':
+ case 'f':
+ {
+ default_bool (m, s[0] == 't');
+ break;
+ }
+ case '+':
+ case '-':
+ {
+ switch (TREE_CODE (n))
+ {
+ case INTEGER_CST:
+ {
+ HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n));
+ HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n));
+
+ unsigned long long l (hwl);
+ unsigned long long h (hwh);
+ unsigned short width (HOST_BITS_PER_WIDE_INT);
+
+ unsigned long long v ((h << width) + l);
+
+ default_integer (m, v, s[0] == '-');
+ break;
+ }
+ case REAL_CST:
+ {
+ double v;
+
+ REAL_VALUE_TYPE d (TREE_REAL_CST (n));
+
+ if (REAL_VALUE_ISINF (d))
+ v = numeric_limits<double>::infinity ();
+ else if (REAL_VALUE_ISNAN (d))
+ v = numeric_limits<double>::quiet_NaN ();
+ else
+ {
+ char tmp[256];
+ real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ is >> v;
+ }
+
+ if (s[0] == '-')
+ v = -v;
+
+ default_float (m, v);
+ break;
+ }
+ default:
+ assert (false);
+ }
+ break;
+ }
+
+ case 's':
+ {
+ default_string (m, string (s, 1, string::npos));
+ break;
+ }
+ case 'e':
+ {
+ default_enum (m, n, string (s, 1, string::npos));
+ break;
+ }
+ default:
+ assert (false);
+ }
+ }
+
struct schema_emitter: emitter, context
{
virtual void
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index b218c2b..6ada960 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -190,8 +190,11 @@ namespace relational
type (m);
null (m);
- constraints (m);
- reference (m);
+
+ // An id member cannot have a default value.
+ //
+ if (!m.count ("id"))
+ default_ (m);
// If we have options, add them.
//
@@ -200,6 +203,9 @@ namespace relational
if (!o.empty ())
os << " " << o;
+ constraints (m);
+ reference (m);
+
return true;
}
@@ -217,6 +223,49 @@ namespace relational
}
virtual void
+ default_null (semantics::data_member&)
+ {
+ os << " DEFAULT NULL";
+ }
+
+ virtual void
+ default_bool (semantics::data_member&, bool v)
+ {
+ // Most databases do not support boolean literals. Those that
+ // do should override this.
+ //
+ os << " DEFAULT " << (v ? "1" : "0");
+ }
+
+ virtual void
+ default_integer (semantics::data_member&, unsigned long long v, bool neg)
+ {
+ os << " DEFAULT " << (neg ? "-" : "") << v;
+ }
+
+ virtual void
+ default_float (semantics::data_member&, double v)
+ {
+ os << " DEFAULT " << v;
+ }
+
+ virtual void
+ default_string (semantics::data_member&, string const& v)
+ {
+ os << " DEFAULT " << quote_string (v);
+ }
+
+ virtual void
+ default_enum (semantics::data_member&,
+ tree /*enumerator*/,
+ string const& /*name*/)
+ {
+ // Has to be implemented by the database-specific override.
+ //
+ assert (false);
+ }
+
+ virtual void
constraints (semantics::data_member& m)
{
if (m.count ("id"))
@@ -234,6 +283,10 @@ namespace relational
}
protected:
+ void
+ default_ (semantics::data_member&);
+
+ protected:
string prefix_;
};
diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx
index 195a71e..253772d 100644
--- a/odb/relational/sqlite/schema.cxx
+++ b/odb/relational/sqlite/schema.cxx
@@ -19,11 +19,35 @@ namespace relational
//
// Create.
//
- struct object_columns: relational::object_columns
+ struct object_columns: relational::object_columns, context
{
object_columns (base const& x): base (x) {}
virtual void
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to INTEGER.
+ //
+ if (column_sql_type (m).type != sql_type::INTEGER)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to SQLite INTEGER" << endl;
+
+ throw generation_failed ();
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ if (e.enum_ ().unsigned_ ())
+ os << " DEFAULT " << e.value ();
+ else
+ os << " DEFAULT " << static_cast<long long> (e.value ());
+ }
+
+ virtual void
constraints (semantics::data_member& m)
{
base::constraints (m);
diff --git a/odb/validator.cxx b/odb/validator.cxx
index b07f5cd..790a991 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -287,6 +287,18 @@ namespace
{
c.set ("id-member", id);
+ // Complain if an id member has a default value (default value
+ // for the id's type is ok -- we will ignore it).
+ //
+ if (id->count ("default"))
+ {
+ cerr << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: object id member cannot have default value"
+ << endl;
+
+ valid_ = false;
+ }
+
// Automatically mark the id member as not null. If we already have
// an explicit null pragma for this member, issue an error.
//