From a5f24411433aeb61ad015129354a664820affab0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 12 Dec 2012 11:26:44 +0200 Subject: Add support for SQL name transformations --- NEWS | 9 + doc/odb-epilogue.1 | 105 +++++++++++ doc/odb-epilogue.xhtml | 71 +++++++ odb/common-query.cxx | 8 +- odb/common.cxx | 129 +------------ odb/common.hxx | 42 ++--- odb/context.cxx | 381 ++++++++++++++++++++++++++++++++------ odb/context.hxx | 100 ++++++++-- odb/option-types.cxx | 23 +++ odb/option-types.hxx | 20 ++ odb/options.cli | 112 ++++++++++- odb/relational/common-query.cxx | 11 +- odb/relational/context.cxx | 42 +++++ odb/relational/context.hxx | 19 +- odb/relational/model.hxx | 54 +++--- odb/relational/mssql/context.cxx | 2 + odb/relational/mssql/schema.cxx | 23 +-- odb/relational/mssql/source.cxx | 10 +- odb/relational/mysql/context.cxx | 2 + odb/relational/mysql/schema.cxx | 12 -- odb/relational/oracle/context.cxx | 19 ++ odb/relational/oracle/context.hxx | 6 + odb/relational/oracle/schema.cxx | 28 +-- odb/relational/oracle/source.cxx | 11 +- odb/relational/pgsql/context.cxx | 2 + odb/relational/pgsql/schema.cxx | 12 -- odb/relational/pgsql/source.cxx | 5 +- odb/relational/processor.cxx | 15 +- odb/relational/source.cxx | 21 ++- odb/relational/source.hxx | 53 +++--- odb/relational/sqlite/context.cxx | 2 + odb/relational/sqlite/schema.cxx | 11 +- 32 files changed, 975 insertions(+), 385 deletions(-) diff --git a/NEWS b/NEWS index e4fd9f3..b07df8b 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,15 @@ Version 2.2.0 refer to Section 4.5, "Prepared Queries" in the ODB manual as well as the 'prepared' example in the odb-examples package. + * Support for automatically-derived SQL name (table, column, index, etc.) + transformations. At the higher level, it is possible to assign prefixes + and suffixes (--table-prefix, --{index,fkey,sequence}--suffix options) + as well as to convert to upper or lower case (--sql-name-case option). + At the lower level, it is possible to specify transformations as regular + expressions (--{table,column,index,fkey,sequence,sql-name}-regex options). + For more information, refer to the SQL NAME TRANSFORMATIONS section in + the ODB compiler command line interface documentation (man pages). + * New options, --export-symbol and --extern-symbol, allow DLL exporting of the generated database support code. diff --git a/doc/odb-epilogue.1 b/doc/odb-epilogue.1 index 6bdc5c4..8755374 100644 --- a/doc/odb-epilogue.1 +++ b/doc/odb-epilogue.1 @@ -1,4 +1,109 @@ .\" +.\" SQL NAME TRANSFORMATIONS +.\" +.SH SQL NAME TRANSFORMATIONS +The ODB compiler provides a number of mechanisms for transforming +automatically-derived SQL names, such as tables, columns, etc., +to match a specific naming convention. At the higher level, we can +add a prefix to global names (tables and, for some databases, +indexes and/or foreign keys) with the +.B --table-prefix +option. Similarly, we can specify custom suffixes for automatically-derived +index +.RB ( --index-suffix ; +default is +.BR _i ), +foreign key +.RB ( --fkey-suffix ; +default is +.BR _fk ), +and sequence +.RB ( --sequence-suffix ; +default is +.BR _seq ) +names. Finally, we can also convert all the names to upper or lower +case with the +.B --sql-name-case +option (valid values are +.B upper +and +.BR lower ). + +At the lower level we can specify a set of regular expressions to +implement arbitrary transformations of the automatically-derived SQL +names. If we want a particular regular expression only to apply to +a specific name, for example, table or column, then we use one of the +.B --\fIkind\fB-regex +options, where +.I kind +can be +.BR table , +.BR column , +.BR index , +.BR fkey , +or +.BR sequence . +On the other hand, if we want our regular expressions to apply to all SQL +names, then we use the +.B --sql-name-regex +option. + +The interaction between the higher and lower level transformations +is as follows. Prefixes and suffixes are added first. Then the +regular expression transformations are applied. Finally, if requested, +the name is converted to upper or lower case. Note also that all of +these transformations except for +.B --table-prefix +only apply to automatically-derived names. In other words, if a table, +column, etc., name was explicitly specified with a pragma, then it +is used as is, without applying any (except for the table prefix) +transformations. + +The value for the +.B --*-regex +options is a Perl-like regular expression in the form +.BI / pattern / replacement /\fR. +Any character can be used as a delimiter instead of +.B / +and the delimiter can be escaped inside +.I pattern +and +.I replacement +with a backslash +.RB ( \e ). +You can also specify multiple regular expressions by repeating these +options. + +All the regular expressions are tried in the order specified with the +name-specific expressions (for example, +.BR --table-regex) +tried first followed by the generic expressions +.RB ( --sql-name-regex ). +The first expression that matches is used. + +As an example, consider a regular expression that transforms a class +name in the form +.B CFoo +to a table name in the form +.BR FOO: + +.B --table-regex '/C(.+)/\eU$1/' + +As a more interesting example, consider the transformation of class +names that follow the upper camel case convention (for example, +.BR FooBar ) +to table names that follow the underscore-separated, all upper case +convention (for example, +.BR FOO_BAR ). +For this case we have to use separate expressions to handle one-word, +two-word, etc., names: + +.B --table-regex '/([A-z][a-z]+)/\eU$1/' + +.B --table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\eU$1_$2/' + +See also the REGEX AND SHELL QUOTING section below. +.\" .\" REGEX AND SHELL QUOTING .\" .SH REGEX AND SHELL QUOTING diff --git a/doc/odb-epilogue.xhtml b/doc/odb-epilogue.xhtml index 8dd45e7..0309fc6 100644 --- a/doc/odb-epilogue.xhtml +++ b/doc/odb-epilogue.xhtml @@ -1,3 +1,74 @@ +

SQL NAME TRANSFORMATIONS

+ +

The ODB compiler provides a number of mechanisms for transforming + automatically-derived SQL names, such as tables, columns, etc., + to match a specific naming convention. At the higher level, we can + add a prefix to global names (tables and, for some databases, + indexes and/or foreign keys) with the --table-prefix + option. Similarly, we can specify custom suffixes for + automatically-derived + index (--index-suffix; default is _i), + foreign key (--fkey-suffix; default is _fk), and + sequence (--sequence-suffix; default is _seq) + names. Finally, we can also convert all the names to upper or lower + case with the --sql-name-case option (valid values + are upper and lower).

+ +

At the lower level we can specify a set of regular expressions to + implement arbitrary transformations of the automatically-derived SQL + names. If we want a particular regular expression only to apply to + a specific name, for example, table or column, then we use one of the + --kind-regex options, where + kind can be table, + column, index, + fkey, or sequence. On the + other hand, if we want our regular expressions to apply to all SQL + names, then we use the --sql-name-regex option.

+ +

The interaction between the higher and lower level transformations + is as follows. Prefixes and suffixes are added first. Then the + regular expression transformations are applied. Finally, if requested, + the name is converted to upper or lower case. Note also that all of + these transformations except for --table-prefix + only apply to automatically-derived names. In other words, if a table, + column, etc., name was explicitly specified with a pragma, then it + is used as is, without applying any (except for the table prefix) + transformations.

+ +

The value for the --*-regex options is a Perl-like + regular expression in the form + /pattern/replacement/. + Any character can be used as a delimiter instead of / + and the delimiter can be escaped inside pattern and + replacement with a backslash (\). + You can also specify multiple regular expressions by repeating these + options.

+ +

All the regular expressions are tried in the order specified with the + name-specific expressions (for example, --table-regex) + tried first followed by the generic expressions + (--sql-name-regex). The first expression that + matches is used.

+ +

As an example, consider a regular expression that transforms a class + name in the form CFoo to a table name in the + form FOO:

+ +

--table-regex '/C(.+)/\U$1/'

+ +

As a more interesting example, consider the transformation of class + names that follow the upper camel case convention (for example, + FooBar) to table names that follow the + underscore-separated, all upper case convention (for example, + FOO_BAR). For this case we have to use + separate expressions to handle one-word, two-word, etc., + names:

+ +

--table-regex '/([A-z][a-z]+)/\U$1/'

+

--table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\U$1_$2/'

+ +

See also the REGEX AND SHELL QUOTING section below.

+

REGEX AND SHELL QUOTING

When entering a regular expression argument in the shell diff --git a/odb/common-query.cxx b/odb/common-query.cxx index 67a2674..f7c4714 100644 --- a/odb/common-query.cxx +++ b/odb/common-query.cxx @@ -577,14 +577,12 @@ traverse_pointer (semantics::data_member& m, semantics::class_& c) // Simple id. // string type (t.fq_name (hint)); - string column ( - compose_name ( - column_prefix_, column_name (m, key_prefix_, default_name_))); + string col (column_name (m, key_prefix_, default_name_, column_prefix_)); // For pointer_query_columns generate normal column mapping. // if (ptr_) - column_common (m, type, column); + column_common (m, type, col); else { // If this is a non-inverse relationship, then make the column have @@ -593,7 +591,7 @@ traverse_pointer (semantics::data_member& m, semantics::class_& c) // test in a natural way. For inverse relationships there is no // column and so the column interface is not available. // - column_common (m, type, column, "_column_type_"); + column_common (m, type, col, "_column_type_"); if (decl_) { diff --git a/odb/common.cxx b/odb/common.cxx index 34d8372..9689d83 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -114,11 +114,7 @@ traverse (semantics::class_& c) if (table_prefix_.level == 0) { - table_prefix_.ns_schema = schema (c.scope ()); - table_prefix_.ns_prefix = table_name_prefix (c.scope ()); - table_prefix_.prefix = table_name (c); - table_prefix_.prefix += "_"; - table_prefix_.level = 1; + table_prefix_ = table_prefix (c); tb = true; } @@ -128,12 +124,7 @@ traverse (semantics::class_& c) traverse_view (c); if (tb) - { - table_prefix_.level = 0; - table_prefix_.prefix.clear (); - table_prefix_.ns_prefix.clear (); - table_prefix_.ns_schema.clear (); - } + table_prefix_ = table_prefix (); } else { @@ -162,7 +153,7 @@ traverse_member (semantics::data_member& m, semantics::type& t) member_scope_.push_back (class_inheritance_chain ()); member_scope_.back ().push_back (comp); - qname old_table_prefix; + table_prefix old_table_prefix; string old_flat_prefix, old_member_prefix; if (build_flat_prefix_) @@ -181,17 +172,14 @@ traverse_member (semantics::data_member& m, semantics::type& t) if (build_table_prefix_) { - old_table_prefix = table_prefix_.prefix; - append (m, table_prefix_); + old_table_prefix = table_prefix_; + table_prefix_.append (m); } traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0)); if (build_table_prefix_) - { - table_prefix_.level--; - table_prefix_.prefix = old_table_prefix; - } + table_prefix_ = old_table_prefix; if (build_flat_prefix_) flat_prefix_ = old_flat_prefix; @@ -225,57 +213,6 @@ traverse (semantics::data_member& m) om_.member_path_.pop_back (); } -void object_members_base:: -append (semantics::data_member& m, table_prefix& tp) -{ - context& ctx (context::current ()); - - assert (tp.level > 0); - - // If a custom table prefix was specified, then ignore the top-level - // table prefix (this corresponds to a container directly inside an - // object) but keep the schema unless the alternative schema is fully - // qualified. - // - if (m.count ("table")) - { - qname p, n (m.get ("table")); - - if (n.fully_qualified ()) - p = n.qualifier (); - else - { - if (n.qualified ()) - { - p = tp.ns_schema; - p.append (n.qualifier ()); - } - else - p = tp.prefix.qualifier (); - } - - p.append (tp.level == 1 ? tp.ns_prefix : tp.prefix.uname ()); - p += n.uname (); - - tp.prefix.swap (p); - } - // Otherwise use the member name and add an underscore unless it is - // already there. - // - else - { - string name (ctx.public_name_db (m)); - size_t n (name.size ()); - - tp.prefix += name; - - if (n != 0 && name[n - 1] != '_') - tp.prefix += "_"; - } - - tp.level++; -} - // // object_columns_base // @@ -419,52 +356,6 @@ traverse (semantics::class_& c) flush (); } -string object_columns_base:: -column_prefix (semantics::data_member& m, string const& kp, string const& dn) -{ - bool custom; - string r; - - if (kp.empty ()) - { - custom = m.count ("column"); - r = context::current ().column_name (m); - } - else - { - custom = m.count (kp + "-column"); - r = context::current ().column_name (m, kp, dn); - } - - // If the user provided the column prefix, then use it verbatime. - // Otherwise, append the underscore, unless it is already there. - // - if (!custom) - { - size_t n (r.size ()); - - if (n != 0 && r[n - 1] != '_') - r += '_'; - } - - return r; -} - -string object_columns_base:: -column_prefix (data_member_path const& mp) -{ - if (mp.size () < 2) - return ""; - - string r; - - for (data_member_path::const_iterator i (mp.begin ()), e (mp.end () - 1); - i != e; ++i) - r += column_prefix (**i); - - return r; -} - void object_columns_base:: traverse_member (semantics::data_member& m, semantics::type& t) { @@ -473,8 +364,8 @@ traverse_member (semantics::data_member& m, semantics::type& t) member_scope_.push_back (class_inheritance_chain ()); member_scope_.back ().push_back (comp); - string old_prefix (column_prefix_); - column_prefix_ += column_prefix (m, key_prefix_, default_name_); + column_prefix old_prefix (column_prefix_); + column_prefix_.append (m, key_prefix_, default_name_); // Save and clear the key prefix and default name. // @@ -492,9 +383,7 @@ traverse_member (semantics::data_member& m, semantics::type& t) } else { - string name ( - compose_name ( - column_prefix_, column_name (m, key_prefix_, default_name_))); + string name (column_name (m, key_prefix_, default_name_, column_prefix_)); if (traverse_column (m, name, first_)) { diff --git a/odb/common.hxx b/odb/common.hxx index 7ce59a5..80f91c4 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -100,12 +100,6 @@ public: virtual void traverse (semantics::class_&); -public: - // Append composite member prefix. - // - static void - append (semantics::data_member&, table_prefix&); - protected: string flat_prefix_; table_prefix table_prefix_; @@ -219,10 +213,19 @@ struct object_columns_base: traversal::class_, virtual context public: object_columns_base (bool first = true, - string const& column_prefix = string (), - bool traverse_poly_base = false) - : column_prefix_ (column_prefix), + column_prefix const& cp = column_prefix ()) + : column_prefix_ (cp), root_ (0), + traverse_poly_base_ (false), + first_ (first), + top_level_ (true), + member_ (*this) + { + init (); + } + + object_columns_base (bool first, bool traverse_poly_base) + : root_ (0), traverse_poly_base_ (traverse_poly_base), first_ (first), top_level_ (true), @@ -262,25 +265,11 @@ public: string const& default_name, semantics::class_* top_object = 0); // If not 0, switch top object. -public: - // Return column prefix for composite data member. - // - static string - column_prefix (semantics::data_member&, - string const& key_prefix = string (), - string const& default_name = string ()); - - // Return column prefix up to (but not including) the last member - // in the path. - // - static string - column_prefix (data_member_path const&); - protected: string key_prefix_; string default_name_; - string column_prefix_; + column_prefix column_prefix_; data_member_path member_path_; data_member_scope member_scope_; @@ -365,9 +354,8 @@ struct object_columns_list: object_columns_base { } - object_columns_list (string const& column_prefix, bool ignore_inverse = true) - : object_columns_base (true, column_prefix), - ignore_inverse_ (ignore_inverse) + object_columns_list (column_prefix const& cp, bool ignore_inverse = true) + : object_columns_base (true, cp), ignore_inverse_ (ignore_inverse) { } diff --git a/odb/context.cxx b/odb/context.cxx index f5ff975..46116f6 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -472,6 +472,46 @@ context (ostream& os_, for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i) data_->keyword_set_.insert (keywords[i]); + // SQL name regex. + // + if (ops.table_regex ().count (db) != 0) + { + strings const& s (ops.table_regex ()[db]); + data_->sql_name_regex_[sql_name_table].assign (s.begin (), s.end ()); + } + + if (ops.column_regex ().count (db) != 0) + { + strings const& s (ops.column_regex ()[db]); + data_->sql_name_regex_[sql_name_column].assign (s.begin (), s.end ()); + } + + if (ops.index_regex ().count (db) != 0) + { + strings const& s (ops.index_regex ()[db]); + data_->sql_name_regex_[sql_name_index].assign (s.begin (), s.end ()); + } + + if (ops.fkey_regex ().count (db) != 0) + { + strings const& s (ops.fkey_regex ()[db]); + data_->sql_name_regex_[sql_name_fkey].assign (s.begin (), s.end ()); + } + + if (ops.sequence_regex ().count (db) != 0) + { + strings const& s (ops.sequence_regex ()[db]); + data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ()); + } + + if (ops.sql_name_regex ().count (db) != 0) + { + strings const& s (ops.sql_name_regex ()[db]); + data_->sql_name_regex_[sql_name_all].assign (s.begin (), s.end ()); + } + + // Include regex. + // for (strings::const_iterator i (ops.include_regex ().begin ()); i != ops.include_regex ().end (); ++i) data_->include_regex_.push_back (regexsub (*i)); @@ -1088,6 +1128,79 @@ composite_ (semantics::class_& c) return r; } +// context::table_prefix +// +context::table_prefix:: +table_prefix (semantics::class_& c) + : level (1) +{ + context& ctx (context::current ()); + + ns_schema = ctx.schema (c.scope ()); + ns_prefix = ctx.table_name_prefix (c.scope ()); + prefix = ctx.table_name (c, &derived); + prefix += "_"; +} + +void context::table_prefix:: +append (semantics::data_member& m) +{ + assert (level > 0); + + context& ctx (context::current ()); + + // If a custom table prefix was specified, then ignore the top-level + // table prefix (this corresponds to a container directly inside an + // object) but keep the schema unless the alternative schema is fully + // qualified. + // + if (m.count ("table")) + { + qname p, n (m.get ("table")); + + if (n.fully_qualified ()) + p = n.qualifier (); + else + { + if (n.qualified ()) + { + p = ns_schema; + p.append (n.qualifier ()); + } + else + p = prefix.qualifier (); + } + + if (level == 1) + { + p.append (ns_prefix); + derived = false; + } + else + p.append (prefix.uname ()); + + p += n.uname (); + prefix.swap (p); + } + // Otherwise use the member name and add an underscore unless it is + // already there. + // + else + { + string name (ctx.public_name_db (m)); + size_t n (name.size ()); + + prefix += name; + + if (n != 0 && name[n - 1] != '_') + prefix += "_"; + + derived = true; + } + + level++; +} + qname context:: schema (semantics::scope& s) const { @@ -1198,14 +1311,14 @@ table_name_prefix (semantics::scope& s) const } qname context:: -table_name (semantics::class_& c) const +table_name (semantics::class_& c, bool* pd) const { if (c.count ("qualified-table")) return c.get ("qualified-table"); qname r; - bool sf (c.count ("schema")); + bool derived; if (c.count ("table")) { @@ -1221,9 +1334,14 @@ table_name (semantics::class_& c) const c.get ("table-location") < c.get ("schema-location"); } + + derived = false; } else + { r = class_name (c); + derived = true; + } if (sf) { @@ -1247,16 +1365,21 @@ table_name (semantics::class_& c) const // r.uname () = table_name_prefix (c.scope ()) + r.uname (); + if (derived) + r.uname () = transform_name (r.uname (), sql_name_table); + c.set ("qualified-table", r); + + if (pd != 0) + *pd = derived; + return r; } qname context:: table_name (semantics::class_& obj, data_member_path const& mp) const { - table_prefix tp (schema (obj.scope ()), - table_name_prefix (obj.scope ()), - table_name (obj) + "_"); + table_prefix tp (obj); if (mp.size () == 1) { @@ -1271,7 +1394,7 @@ table_name (semantics::class_& obj, data_member_path const& mp) const // The last member is the container. // for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i) - object_members_base::append (**i, tp); + tp.append (**i); return table_name (**i, tp); } @@ -1285,12 +1408,14 @@ table_name (semantics::data_member& m, table_prefix const& p) const { assert (p.level > 0); qname r; + string rn; + bool derived; // Any of the components in the table name is derived. // If a custom table name was specified, then ignore the top-level // table prefix (this corresponds to a container directly inside an // object). If the container table is unqualifed, then we use the // object schema. If it is fully qualified, then we use that name. - // Finally, if it is qualified by not fully qualifed, then we + // Finally, if it is qualified but not fully qualifed, then we // append the object's namespace schema. // if (m.count ("table")) @@ -1310,59 +1435,111 @@ table_name (semantics::data_member& m, table_prefix const& p) const r = p.prefix.qualifier (); } - r.append (p.level == 1 ? p.ns_prefix : p.prefix.uname ()); - r += n.uname (); + if (p.level == 1) + { + rn = p.ns_prefix; + derived = false; + } + else + { + rn = p.prefix.uname (); + derived = p.derived; + } + + rn += n.uname (); } else { - r = p.prefix; - r += public_name_db (m); + r = p.prefix.qualifier (); + rn = p.prefix.uname () + public_name_db (m); + derived = true; } + if (derived) + r.append (transform_name (rn, sql_name_table)); + else + r.append (rn); + return r; } -string context:: -column_name (semantics::data_member& m) const +// context::column_prefix +// +context::column_prefix:: +column_prefix (data_member_path const& mp, bool l) + : derived (false) { - if (m.count ("column")) - return m.get ("column").column; - else - return public_name_db (m); + if (mp.size () < (l ? 1 : 2)) + return; + + for (data_member_path::const_iterator i (mp.begin ()), + e (mp.end () - (l ? 0 : 1)); i != e; ++i) + append (**i); } -string context:: -column_name (data_member_path const& mp) const +void context::column_prefix:: +append (semantics::data_member& m, string const& kp, string const& dn) { - // The path can lead to a composite value member and column names for - // such members are derived dynamically using the same derivation - // process as when generating object columns (see object_columns_base). - // - string r; + bool d; + context& ctx (context::current ()); - for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i) + if (kp.empty ()) + prefix += ctx.column_name (m, d); + else + prefix += ctx.column_name (m, kp, dn, d); + + // If the user provided the column prefix, then use it verbatime. + // Otherwise, append the underscore, unless it is already there. + // + if (d) { - semantics::data_member& m (**i); + size_t n (prefix.size ()); - if (composite_wrapper (utype (m))) - r += object_columns_base::column_prefix (m); - else - r = compose_name (r, column_name (m)); + if (n != 0 && prefix[n - 1] != '_') + prefix += '_'; } - return r; + derived = derived || d; +} + +string context:: +column_name (semantics::data_member& m, bool& derived) const +{ + derived = !m.count ("column"); + return derived + ? public_name_db (m) + : m.get ("column").column; +} + +string context:: +column_name (semantics::data_member& m, column_prefix const& cp) const +{ + bool d; + string n (column_name (m, d)); + n = compose_name (cp.prefix, n); + + // If any component is derived, the run it through the SQL name regex. + // + if (d || cp.derived) + n = transform_name (n, sql_name_column); + + return n; } string context:: -column_name (semantics::data_member& m, string const& p, string const& d) const +column_name (semantics::data_member& m, + string const& p, + string const& d, + bool& derived) const { if (p.empty () && d.empty ()) - return column_name (m); + return column_name (m, derived); // A container column name can be specified for the member or for the // container type. // string key (p + "-column"); + derived = false; if (m.count (key)) return m.get (key); @@ -1374,35 +1551,32 @@ column_name (semantics::data_member& m, string const& p, string const& d) const return t.get (key); } + derived = true; return d; } string context:: -compose_name (string const& prefix, string const& name) +column_name (semantics::data_member& m, + string const& kp, + string const& dn, + column_prefix const& cp) const { - string r (prefix); - size_t n (r.size ()); + bool d; + string n (column_name (m, kp, dn, d)); + n = compose_name (cp.prefix, n); - // Add an underscore unless one is already in the prefix or - // the name is empty. Similarly, remove it if it is there but - // the name is empty. + // If any component is derived, the run it through the SQL name regex. // - if (n != 0) - { - if (r[n - 1] != '_') - { - if (!name.empty ()) - r += '_'; - } - else - { - if (name.empty ()) - r.resize (n - 1); - } - } + if (d || cp.derived) + n = transform_name (n, sql_name_column); - r += name; - return r; + return n; +} + +string context:: +column_name (data_member_path const& mp) const +{ + return column_name (*mp.back (), column_prefix (mp)); } string context:: @@ -1614,6 +1788,105 @@ public_name_db (semantics::data_member& m) const } string context:: +compose_name (string const& prefix, string const& name) +{ + string r (prefix); + size_t n (r.size ()); + + // Add an underscore unless one is already in the prefix or + // the name is empty. Similarly, remove it if it is there but + // the name is empty. + // + if (n != 0) + { + if (r[n - 1] != '_') + { + if (!name.empty ()) + r += '_'; + } + else + { + if (name.empty ()) + r.resize (n - 1); + } + } + + r += name; + return r; +} + +string context:: +transform_name (string const& name, sql_name_type type) const +{ + string r; + + if (!data_->sql_name_regex_[type].empty () || + !data_->sql_name_regex_[sql_name_all].empty ()) + { + bool t (options.sql_name_regex_trace ()); + + if (t) + cerr << "name: '" << name << "'" << endl; + + bool found (false); + + // First try the type-specific transformations, if that didn't work, + // try common transformations. + // + for (unsigned short j (0); !found && j < 2; ++j) + { + regex_mapping const& rm = data_->sql_name_regex_[ + j == 0 ? type : sql_name_all]; + + for (regex_mapping::const_iterator i (rm.begin ()); i != rm.end (); ++i) + { + if (t) + cerr << "try: '" << i->regex () << "' : "; + + if (i->match (name)) + { + r = i->replace (name); + found = true; + + if (t) + cerr << "'" << r << "' : "; + } + + if (t) + cerr << (found ? '+' : '-') << endl; + + if (found) + break; + } + } + + if (!found) + r = name; + } + else + r = name; + + if (options.sql_name_case ().count (db) != 0) + { + switch (options.sql_name_case ()[db]) + { + case name_case::upper: + { + r = data_->sql_name_upper_.replace (r); + break; + } + case name_case::lower: + { + r = data_->sql_name_lower_.replace (r); + break; + } + } + } + + return r; +} + +string context:: public_name (semantics::data_member& m, bool e) const { return e ? escape (public_name_impl (m)) : public_name_impl (m); diff --git a/odb/context.hxx b/odb/context.hxx index 22750de..6b532fa 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -620,24 +620,27 @@ public: // // - qname - table_name (semantics::class_&) const; - - qname - table_name (semantics::class_&, data_member_path const&) const; - struct table_prefix { - table_prefix (): level (0) {} - table_prefix (qname const& ns_s, string const& ns_p, qname const& p) - : ns_schema (ns_s), ns_prefix (ns_p), prefix (p), level (1) {} + table_prefix (): level (0), derived (false) {} + table_prefix (semantics::class_&); + + void + append (semantics::data_member&); qname ns_schema; // Object's namespace schema. string ns_prefix; // Object's namespace table prefix. qname prefix; size_t level; + bool derived; // One of the components in the prefix was derived. }; + qname + table_name (semantics::class_&, bool* derived = 0) const; + + qname + table_name (semantics::class_&, data_member_path const&) const; + // Table name for the container member. The table prefix passed as the // second argument must include the table prefix specified with the // --table-prefix option. @@ -645,23 +648,58 @@ public: qname table_name (semantics::data_member&, table_prefix const&) const; + // + // + struct column_prefix + { + column_prefix (): derived (false) {} + + column_prefix (semantics::data_member& m, + string const& key_prefix = string (), + string const& default_name = string ()) + : derived (false) + { + append (m, key_prefix, default_name); + } + + // If the last argument is true, the prefix will include the last member + // in the path. + // + column_prefix (data_member_path const&, bool last = false); + + void + append (semantics::data_member&, + string const& key_prefix = string (), + string const& default_name = string ()); + + string prefix; + bool derived; // One of the components in the prefix was derived. + }; + string - column_name (semantics::data_member&) const; + column_name (semantics::data_member&, bool& derived) const; string - column_name (data_member_path const&) const; + column_name (semantics::data_member&, column_prefix const&) const; string column_name (semantics::data_member&, string const& key_prefix, - string const& default_name) const; + string const& default_name, + bool& derived) const; - // Compose the name by inserting/removing an underscore, as necessary. - // - static string - compose_name (string const& prefix, string const& name); + string + column_name (semantics::data_member&, + string const& key_prefix, + string const& default_name, + column_prefix const&) const; string + column_name (data_member_path const&) const; + + // + // + string column_type (const data_member_path&, string const& key_prefix = string (), bool id = false); // Pass true if this type is object id other @@ -680,6 +718,27 @@ public: string public_name_db (semantics::data_member&) const; + // Compose the name by inserting/removing an underscore, as necessary. + // + static string + compose_name (string const& prefix, string const& name); + + // SQL name transformations. + // + enum sql_name_type + { + sql_name_all, + sql_name_table, + sql_name_column, + sql_name_index, + sql_name_fkey, + sql_name_sequence, + sql_name_count + }; + + string + transform_name (string const& name, sql_name_type) const; + // C++ names. // public: @@ -959,8 +1018,11 @@ protected: { virtual ~data () {} + data (std::ostream& os) - : os_ (os.rdbuf ()), top_object_ (0), cur_object_ (0) + : os_ (os.rdbuf ()), top_object_ (0), cur_object_ (0), + sql_name_upper_ ("(.+)", "\\U$1"), + sql_name_lower_ ("(.+)", "\\L$1") { } @@ -977,6 +1039,10 @@ protected: keyword_set_type keyword_set_; type_map_type type_map_; + regex_mapping sql_name_regex_[sql_name_count]; + regexsub sql_name_upper_; + regexsub sql_name_lower_; + regex_mapping include_regex_; regex_mapping accessor_regex_; regex_mapping modifier_regex_; diff --git a/odb/option-types.cxx b/odb/option-types.cxx index 8efb061..60fea73 100644 --- a/odb/option-types.cxx +++ b/odb/option-types.cxx @@ -195,6 +195,29 @@ operator<< (ostream& os, schema_format sf) } // +// name_case +// + +istream& +operator>> (istream& is, name_case& v) +{ + string s; + is >> s; + + if (!is.fail ()) + { + if (s == "upper") + v = name_case::upper; + else if (s == "lower") + v = name_case::lower; + else + is.setstate (istream::failbit); + } + + return is; +} + +// // oracle_version // diff --git a/odb/option-types.hxx b/odb/option-types.hxx index 09be4ee..0b6d47a 100644 --- a/odb/option-types.hxx +++ b/odb/option-types.hxx @@ -151,6 +151,26 @@ operator<< (std::ostream&, schema_format); // // +struct name_case +{ + enum value + { + upper, + lower + }; + + name_case (value v = value (0)) : v_ (v) {} + operator value () const {return v_;} + +private: + value v_; +}; + +std::istream& +operator>> (std::istream&, name_case&); + +// +// struct oracle_version { oracle_version (unsigned short major, unsigned short minor) diff --git a/odb/options.cli b/odb/options.cli index d756494..5b67708 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -229,16 +229,6 @@ class options \cb{--schema-name} option." }; - database_map --table-prefix - { - "", - "Add to table and index names. The prefix is added to both - names that were specified with the \cb{db table} pragma and those - that were automatically derived from class names. If you require a - separator, such as an underscore, between the prefix and the name, - then you should include it into the prefix value." - }; - // Export control. // database_map --export-symbol @@ -529,6 +519,108 @@ class options option)." }; + // SQL names. + // + database_map --table-prefix + { + "", + "Add to table names and, for databases that have global index + and/or foreign key names, to those names as well. The prefix is added to + both names that were specified with the \cb{db table} and \cb{db index} + pragmas and those that were automatically derived from class and data + member names. If you require a separator, such as an underscore, + between the prefix and the name, then you should include it into the + prefix value." + }; + + database_map --index-suffix + { + "", + "Use instead of the default \cb{_i} to construct index names. + The suffix is only added to names that were automatically derived from + data member names. If you require a separator, such as an underscore, + between the name and the suffix, then you should include it into the + suffix value." + }; + + database_map --fkey-suffix + { + "", + "Use instead of the default \cb{_fk} to construct foreign key + names. If you require a separator, such as an underscore, between the + name and the suffix, then you should include it into the suffix value." + }; + + database_map --sequence-suffix + { + "", + "Use instead of the default \cb{_seq} to construct sequence + names. If you require a separator, such as an underscore, between the + name and the suffix, then you should include it into the suffix value." + }; + + database_map --sql-name-case + { + "", + "Convert all automatically-derived SQL names to upper or lower case. + Valid values for this option are \cb{upper} and \cb{lower}." + }; + + database_map > --table-regex + { + "", + "Add to the list of regular expressions that is used to + transform automatically-derived table names. See the SQL NAME + TRANSFORMATIONS section below for details." + }; + + database_map > --column-regex + { + "", + "Add to the list of regular expressions that is used to + transform automatically-derived column names. See the SQL NAME + TRANSFORMATIONS section below for details." + }; + + database_map > --index-regex + { + "", + "Add to the list of regular expressions that is used to + transform automatically-derived index names. See the SQL NAME + TRANSFORMATIONS section below for details." + }; + + database_map > --fkey-regex + { + "", + "Add to the list of regular expressions that is used to + transform automatically-derived foreign key names. See the SQL NAME + TRANSFORMATIONS section below for details." + }; + + database_map > --sequence-regex + { + "", + "Add to the list of regular expressions that is used to + transform automatically-derived sequence names. See the SQL NAME + TRANSFORMATIONS section below for details." + }; + + database_map > --sql-name-regex + { + "", + "Add to the list of regular expressions that is used to + transform all automatically-derived SQL names. See the SQL NAME + TRANSFORMATIONS section below for details." + }; + + bool --sql-name-regex-trace + { + "Trace the process of applying regular expressions specified with the + SQL name \cb{--*-regex} options. Use this option to find out why your + regular expressions don't do what you expected them to do." + }; + // Accessor/modifier options. // std::vector --accessor-regex diff --git a/odb/relational/common-query.cxx b/odb/relational/common-query.cxx index e49132f..40ed71b 100644 --- a/odb/relational/common-query.cxx +++ b/odb/relational/common-query.cxx @@ -34,17 +34,20 @@ namespace relational if (composite_wrapper (utype (*id_member (c)))) { - n = column_prefix (m, key_prefix_, default_name_); + n = column_prefix (m, key_prefix_, default_name_).prefix; if (n.empty ()) n = public_name_db (m); - else + else if (n[n.size () - 1] == '_') n.resize (n.size () - 1); // Remove trailing underscore. } else - n = column_name (m, key_prefix_, default_name_); + { + bool dummy; + n = column_name (m, key_prefix_, default_name_, dummy); + } - alias = compose_name (column_prefix_, n); + alias = compose_name (column_prefix_.prefix, n); } generate_def (public_name (m), c, alias); diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx index 08e68e5..b484ad9 100644 --- a/odb/relational/context.cxx +++ b/odb/relational/context.cxx @@ -28,6 +28,8 @@ namespace relational insert_send_auto_id (current ().insert_send_auto_id), delay_freeing_statement_result (current ().delay_freeing_statement_result), need_image_clone (current ().need_image_clone), + global_index (current ().global_index), + global_fkey (current ().global_fkey), bind_vector (data_->bind_vector_), truncated_vector (data_->truncated_vector_) { @@ -45,6 +47,46 @@ namespace relational } string context:: + index_name (qname const& table, string const& base) + { + string n; + + if (options.index_suffix ().count (db) != 0) + n = base + options.index_suffix ()[db]; + else + n = compose_name (base, "i"); + + // If this database has global index names, then add the table + // name as a prefix (the schema, if needed, will be added by + // database-specific create_index overrides). + // + if (global_index) + n = compose_name (table.uname (), n); + + return transform_name (n, sql_name_index); + } + + string context:: + fkey_name (qname const& table, string const& base) + { + string n; + + if (options.fkey_suffix ().count (db) != 0) + n = base + options.fkey_suffix ()[db]; + else + n = compose_name (base, "fk"); + + // If this database has global index names, then add the table + // name as a prefix (the schema, if needed, will be added by + // database-specific create_foreign_key overrides). + // + if (global_fkey) + n = compose_name (table.uname (), n); + + return transform_name (n, sql_name_fkey); + } + + string context:: convert (string const& e, string const& c) { size_t p (c.find ("(?)")); diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx index 990e0aa..55c9a23 100644 --- a/odb/relational/context.hxx +++ b/odb/relational/context.hxx @@ -105,9 +105,9 @@ namespace relational // Quoted column and table names. // string - column_qname (semantics::data_member& m) const + column_qname (semantics::data_member& m, column_prefix const& cp) const { - return quote_id (column_name (m)); + return quote_id (column_name (m, cp)); } string @@ -119,9 +119,10 @@ namespace relational string column_qname (semantics::data_member& m, string const& key_prefix, - string const& default_name) const + string const& default_name, + column_prefix const& cp) const { - return quote_id (column_name (m, key_prefix, default_name)); + return quote_id (column_name (m, key_prefix, default_name, cp)); } string @@ -142,6 +143,13 @@ namespace relational return quote_id (table_name (m, p)); } + public: + string + index_name (qname const& table, string const& base); + + string + fkey_name (qname const& table, string const& base); + // Custom database type conversion. // public: @@ -261,6 +269,9 @@ namespace relational bool delay_freeing_statement_result; bool need_image_clone; + bool global_index; + bool global_fkey; + string const& bind_vector; string const& truncated_vector; }; diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index 4245c91..2835a3a 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -298,16 +298,18 @@ namespace relational name = fk.contains_begin ()->column ().name (); else { - string p (column_prefix (m, key_prefix_, default_name_)); + string p (column_prefix (m, key_prefix_, default_name_).prefix); if (p.empty ()) p = public_name_db (m); + else if (p[p.size () - 1] == '_') + p.resize (p.size () - 1); // Remove trailing underscore. - name = compose_name (column_prefix_, p); + name = compose_name (column_prefix_.prefix, p); } model_.new_edge ( - table_, fk, compose_name (name, "fk")); + table_, fk, fkey_name (table_.name (), name)); } protected: @@ -371,10 +373,11 @@ namespace relational if (type* comp = composite_wrapper (utype (*im.path.back ()))) { - // Composite value. Get the list of the columns. Here - // column_name() returns the column prefix. + // Composite value. Get the list of the columns. Note that + // the column prefix needs to contain all the components. // - instance ocl (column_name (im.path)); + instance ocl ( + column_prefix (im.path, true)); ocl->traverse (*comp); for (object_columns_list::iterator i (ocl->begin ()); @@ -504,7 +507,11 @@ namespace relational // composite), in which case it can be empty. In this case // we just fallback on the default name. // - string id_name (column_name (m, "id", "object_id")); + // Finally, this is a top-level column, so there is no column + // prefix. + // + string id_name ( + column_name (m, "id", "object_id", column_prefix ())); if (id_name.empty ()) id_name = "object_id"; @@ -519,7 +526,7 @@ namespace relational sema_rel::foreign_key::cascade)); fk.set ("cxx-location", m.location ()); model_.new_edge ( - t, fk, compose_name (id_name, "fk")); + t, fk, fkey_name (t.name (), id_name)); // Get referenced columns. // @@ -555,19 +562,20 @@ namespace relational in = &model_.new_node ( id + ".id", sin->type, sin->method, sin->options); in->set ("cxx-location", sin->loc); - model_.new_edge ( - t, - *in, - (sin->name.empty () ? compose_name (id_name, "i") : sin->name)); } else { in = &model_.new_node (id + ".id"); in->set ("cxx-location", m.location ()); - model_.new_edge ( - t, *in, compose_name (id_name, "i")); } + model_.new_edge ( + t, + *in, + sin != 0 && !sin->name.empty () + ? sin->name + : index_name (name, id_name)); + // All the columns we have in this table so far are for the // object id. Add them to the index. // @@ -591,9 +599,10 @@ namespace relational instance oc (model_, t); oc->traverse (m, container_it (ct), "index", "index"); - // This is a simple value so the name cannot be empty. + // This is a simple value so the name cannot be empty. It is + // also a top-level column, so there is no column prefix. // - string col (column_name (m, "index", "index")); + string col (column_name (m, "index", "index", column_prefix ())); // Index. See if we have a custom index. // @@ -607,19 +616,20 @@ namespace relational in = &model_.new_node ( id + ".index", sin->type, sin->method, sin->options); in->set ("cxx-location", sin->loc); - model_.new_edge ( - t, - *in, - (sin->name.empty () ? compose_name (col, "i") : sin->name)); } else { in = &model_.new_node (id + ".index"); in->set ("cxx-location", m.location ()); - model_.new_edge ( - t, *in, compose_name (col, "i")); } + model_.new_edge ( + t, + *in, + sin != 0 && !sin->name.empty () + ? sin->name + : index_name (name, col)); + model_.new_edge ( *in, dynamic_cast (t.find (col)->nameable ()), diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx index 7e11564..c64ae57 100644 --- a/odb/relational/mssql/context.cxx +++ b/odb/relational/mssql/context.cxx @@ -88,6 +88,8 @@ namespace relational insert_send_auto_id = false; delay_freeing_statement_result = true; need_image_clone = true; + global_index = false; + global_fkey = true; data_->bind_vector_ = "mssql::bind*"; // Populate the C++ type to DB type map. diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx index 47cb250..20f9faf 100644 --- a/odb/relational/mssql/schema.cxx +++ b/odb/relational/mssql/schema.cxx @@ -80,18 +80,11 @@ namespace relational if (dt_.tables_.find (rt) != dt_.tables_.end () || m.find (rt) == m.names_end ()) { - - // In SQL Server, foreign key names are schema-global. Make them - // unique by prefixing the key name with table name. Note, however, - // that they cannot have a schema. - // - string n (t.name ().uname () + "_" + fk.name ()); - pre_statement (); - os << "IF OBJECT_ID(" << quote_string (n) << ", " << + os << "IF OBJECT_ID(" << quote_string (fk.name ()) << ", " << quote_string ("F") << ") IS NOT NULL" << endl << " ALTER TABLE " << quote_id (t.name ()) << " DROP" << endl - << " CONSTRAINT " << quote_id (n) << endl; + << " CONSTRAINT " << quote_id (fk.name ()) << endl; post_statement (); } } @@ -208,18 +201,6 @@ namespace relational } } - virtual string - name (sema_rel::foreign_key& fk) - { - // In SQL Server, foreign key names are schema-global. Make them - // unique by prefixing the key name with table name. Note, however, - // that they cannot have a schema. - // - return quote_id ( - static_cast (fk.scope ()).name ().uname () - + "_" + fk.name ()); - } - virtual void deferred () { diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 7fce155..5d0c594 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -909,8 +909,14 @@ namespace relational } if (p == persist_after_columns) - os << strlit (" OUTPUT " + convert_from ( - "INSERTED." + column_qname (*id), *id)) << endl; + { + // Top-level auto id. + // + os << strlit ( + " OUTPUT " + convert_from ( + "INSERTED." + column_qname ( + *id, column_prefix ()), *id)) << endl; + } } virtual void diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index 8bf700e..ba22733 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -84,6 +84,8 @@ namespace relational insert_send_auto_id = true; delay_freeing_statement_result = false; need_image_clone = false; + global_index = false; + global_fkey = true; data_->bind_vector_ = "MYSQL_BIND*"; data_->truncated_vector_ = "my_bool*"; diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index 8068fc4..5b34cfa 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -86,18 +86,6 @@ namespace relational base::traverse (fk); } - virtual string - name (sema_rel::foreign_key& fk) - { - // In MySQL, foreign key names are database-global. Make them - // unique by prefixing the key name with table name. Note, - // however, that they cannot be prefixed with the database name. - // - return quote_id ( - static_cast (fk.scope ()).name ().uname () - + "_" + fk.name ()); - } - virtual void deferred () { diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index 4ba8659..df62ce8 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -85,6 +85,8 @@ namespace relational insert_send_auto_id = false; delay_freeing_statement_result = false; need_image_clone = true; + global_index = true; + global_fkey = true; data_->bind_vector_ = "oracle::bind*"; // Populate the C++ type to DB type map. @@ -168,6 +170,23 @@ namespace relational s == "long long unsigned int"; } + qname context:: + sequence_name (qname const& table) + { + string n; + + if (options.sequence_suffix ().count (db) != 0) + n = table.uname () + options.sequence_suffix ()[db]; + else + n = compose_name (table.uname (), "seq"); + + n = transform_name (n, sql_name_sequence); + + qname r (table.qualifier ()); + r.append (n); + return r; + } + // // SQL type parsing. // diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index 6b07a5f..0008724 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -106,6 +106,12 @@ namespace relational static bool unsigned_integer (semantics::type&); + public: + // Construct sequence name from a given table name. + // + qname + sequence_name (qname const& table); + protected: virtual string const& convert_expr (string const&, semantics::data_member&, bool); diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx index f07e0d6..ffe4b31 100644 --- a/odb/relational/oracle/schema.cxx +++ b/odb/relational/oracle/schema.cxx @@ -127,7 +127,7 @@ namespace relational { os << " BEGIN" << endl << " EXECUTE IMMEDIATE 'DROP SEQUENCE " << - quote_id (table + "_seq") << "';" << endl + quote_id (sequence_name (table)) << "';" << endl << " EXCEPTION" << endl << " WHEN OTHERS THEN" << endl << " IF SQLCODE != -2289 THEN RAISE; END IF;" << endl @@ -184,18 +184,6 @@ namespace relational fk.set ("oracle-fk-defined", true); // Mark it as defined. } } - - virtual string - name (sema_rel::foreign_key& fk) - { - // In Oracle, foreign key names are schema-global. Make them - // unique by prefixing the key name with table name. Note, - // however, that they cannot have a schema. - // - return quote_id ( - static_cast (fk.scope ()).name ().uname () - + "_" + fk.name ()); - } }; entry create_foreign_key_; @@ -244,7 +232,8 @@ namespace relational if (pk != 0 && pk->auto_ ()) { pre_statement (); - os_ << "CREATE SEQUENCE " << quote_id (t.name () + "_seq") << endl + os_ << "CREATE SEQUENCE " << + quote_id (sequence_name (t.name ())) << endl << " START WITH 1 INCREMENT BY 1" << endl; post_statement (); } @@ -266,14 +255,11 @@ namespace relational virtual string name (sema_rel::index& in) { - // In Oracle, index names are database-global. Make them unique - // by prefixing the index name with table name (preserving the - // schema). + // In Oracle, index names can be qualified with the schema. // - sema_rel::qname n ( - static_cast (in.scope ()).name ()); - - n.uname () += "_" + in.name (); + sema_rel::table& t (static_cast (in.scope ())); + sema_rel::qname n (t.name ().qualifier ()); + n.append (in.name ()); return quote_id (n); } }; diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index 85be6cc..f6cdfcd 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -17,7 +17,7 @@ namespace relational { namespace relational = relational::source; - struct query_parameters: relational::query_parameters + struct query_parameters: relational::query_parameters, context { query_parameters (base const& x): base (x), i_ (0) {} @@ -33,9 +33,7 @@ namespace relational virtual string auto_id () { - // The same sequence name as used in schema.cxx. - // - return quote_id (table_ + "_seq") + ".nextval"; + return quote_id (sequence_name (table_)) + ".nextval"; } private: @@ -600,9 +598,12 @@ namespace relational if (id != 0 && !poly_derived && id->count ("auto")) { + // Top-level auto id. + // os << endl << strlit (" RETURNING " + - convert_from (column_qname (*id), *id) + + convert_from (column_qname (*id, column_prefix ()), + *id) + " INTO " + qp.next ()); } diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 391c65e..0f68281 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -84,6 +84,8 @@ namespace relational insert_send_auto_id = false; delay_freeing_statement_result = false; need_image_clone = false; + global_index = true; + global_fkey = false; data_->bind_vector_ = "pgsql::bind*"; data_->truncated_vector_ = "bool*"; diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx index 5574bdb..b0d4f50 100644 --- a/odb/relational/pgsql/schema.cxx +++ b/odb/relational/pgsql/schema.cxx @@ -170,18 +170,6 @@ namespace relational { create_index (base const& x): base (x) {} - virtual string - name (sema_rel::index& in) - { - // In PostgreSQL, index names are database-global. Make them unique - // by prefixing the index name with table name. Note, however, that - // they cannot be qualified with the schema name. - // - return quote_id ( - static_cast (in.scope ()).name ().uname () - + "_" + in.name ()); - } - virtual void create (sema_rel::index& in) { diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index 332e25b..e106758 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -618,9 +618,12 @@ namespace relational if (id != 0 && !poly_derived && id->count ("auto")) { + // Top-level auto id. + // os << endl << strlit (" RETURNING " + - convert_from (column_qname (*id), *id)); + convert_from (column_qname (*id, column_prefix ()), + *id)); } } diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index 88e86fc..3e969be 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -1106,6 +1106,11 @@ namespace relational } } + // Add the table prefix if this database has global index names. + // + if (!in.name.empty () && global_index) + in.name = table_name_prefix (c.scope ()) + in.name; + // Handle container indexes. // if (j != in.members.end ()) @@ -1164,12 +1169,14 @@ namespace relational // be empty, in which case we will just fall back on the // member's public name. // - in.name = column_name (in.members.front ().path); + string n (column_prefix (in.members.front ().path, true).prefix); - if (in.name.empty ()) - in.name = public_name_db (*in.members.front ().path.back ()); + if (n.empty ()) + n = public_name_db (*in.members.front ().path.back ()); + else if (n[n.size () - 1] == '_') + n.resize (n.size () - 1); // Remove trailing underscore. - in.name = compose_name (in.name, "i"); + in.name = index_name (table_name (c), n); } ++i; diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 74690d6..f133750 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -699,9 +699,14 @@ traverse_object (type& c) } if (opt != 0 && !poly_derived) + { + // Top-level version column. + // os << endl - << strlit (" AND " + column_qname (*opt) + "=" + + << strlit (" AND " + column_qname (*opt, column_prefix ()) + "=" + convert_to (qp->next (), *opt)); + } + os << ";" << endl; } @@ -744,8 +749,10 @@ traverse_object (type& c) convert_to (qp->next (), i->type, *i->member)); } + // Top-level version column. + // os << endl - << strlit (" AND " + column_qname (*opt) + "=" + + << strlit (" AND " + column_qname (*opt, column_prefix ()) + "=" + convert_to (qp->next (), *opt)) << ";" << endl; } @@ -3339,11 +3346,7 @@ traverse_view (type& c) // function would have to return a member path instead // of just a single member. // - table_prefix tp ( - context::schema (vo->obj->scope ()), - context::table_name_prefix (vo->obj->scope ()), - table_name (*vo->obj) + "_"); - ct = table_qname (*im, tp); + ct = table_qname (*im, table_prefix (*vo->obj)); } else ct = table_qname (*e.vo->obj, e.member_path); @@ -3516,10 +3519,10 @@ traverse_view (type& c) } else { - string col_prefix; + column_prefix col_prefix; if (im == 0) - col_prefix = object_columns_base::column_prefix (e.member_path); + col_prefix = column_prefix (e.member_path); instance l_cols (col_prefix); instance r_cols; diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 610c545..1e223af 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -80,7 +80,7 @@ namespace relational object_columns (statement_kind sk, statement_columns& sc, query_parameters* param = 0) - : object_columns_base (true, "", true), + : object_columns_base (true, true), sk_ (sk), sc_ (sc), param_ (param), depth_ (1) { } @@ -89,7 +89,7 @@ namespace relational statement_kind sk, statement_columns& sc, size_t depth = 1) - : object_columns_base (true, "", true), + : object_columns_base (true, true), sk_ (sk), sc_ (sc), param_ (0), @@ -158,12 +158,7 @@ namespace relational string table; if (!table_name_.empty ()) - { - table_prefix tp (schema (c.scope ()), - table_name_prefix (c.scope ()), - table_name (c) + "_"); - table = table_qname (*im, tp); - } + table = table_qname (*im, table_prefix (c)); instance oc (table, sk_, sc_); oc->traverse (*im, idt, "id", "object_id", &c); @@ -190,17 +185,20 @@ namespace relational if (composite_wrapper (idt)) { - n = column_prefix (m, key_prefix_, default_name_); + n = column_prefix (m, key_prefix_, default_name_).prefix; if (n.empty ()) n = public_name_db (m); - else + else if (n[n.size () - 1] == '_') n.resize (n.size () - 1); // Remove trailing underscore. } else - n = column_name (m, key_prefix_, default_name_); + { + bool dummy; + n = column_name (m, key_prefix_, default_name_, dummy); + } - table = quote_id (compose_name (column_prefix_, n)); + table = quote_id (compose_name (column_prefix_.prefix, n)); } instance oc (table, sk_, sc_); @@ -461,7 +459,7 @@ namespace relational string const& alias = "", string const prefix = "", string const& suffix = "\n") - : object_columns_base (true, "", true), + : object_columns_base (true, true), obj_ (obj), depth_ (depth), alias_ (alias), @@ -537,7 +535,7 @@ namespace relational //@@ context::{cur,top}_object; might have to be created every time. // object_joins (semantics::class_& scope, bool query, size_t depth = 1) - : object_columns_base (true, "", true), + : object_columns_base (true, true), query_ (query), depth_ (depth), table_ (table_qname (scope)), @@ -595,17 +593,20 @@ namespace relational if (composite_wrapper (utype (*id_member (c)))) { - n = column_prefix (m, key_prefix_, default_name_); + n = column_prefix (m, key_prefix_, default_name_).prefix; if (n.empty ()) n = public_name_db (m); - else + else if (n[n.size () - 1] == '_') n.resize (n.size () - 1); // Remove trailing underscore. } else - n = column_name (m, key_prefix_, default_name_); + { + bool dummy; + n = column_name (m, key_prefix_, default_name_, dummy); + } - alias = compose_name (column_prefix_, n); + alias = compose_name (column_prefix_.prefix, n); } semantics::class_* poly_root (polymorphic (c)); @@ -621,11 +622,7 @@ namespace relational // This container is a direct member of the class so the table // prefix is just the class table name. // - qname const& ct (table_name (c)); - table_prefix tp (schema (c.scope ()), - table_name_prefix (c.scope ()), - ct + "_"); - t = table_qname (*im, tp); + t = table_qname (*im, table_prefix (c)); // Container's value is our id. // @@ -1952,10 +1949,7 @@ namespace relational // This other container is a direct member of the class so the // table prefix is just the class table name. // - table_prefix tp (schema (c->scope ()), - table_name_prefix (c->scope ()), - table_name (*c) + "_"); - inv_table = table_name (*im, tp); + inv_table = table_name (*im, table_prefix (*c)); inv_qtable = quote_id (inv_table); inv_id_cols->traverse (*im, utype (inv_id), "id", "object_id", c); @@ -2077,7 +2071,10 @@ namespace relational if (ordered) { - string const& col (column_qname (m, "index", "index")); + // Top-level column. + // + string const& col ( + column_qname (m, "index", "index", column_prefix ())); os << endl << strlit (" ORDER BY " + qtable + "." + col); diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index 5dc3fa8..5e46c77 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -85,6 +85,8 @@ namespace relational insert_send_auto_id = true; delay_freeing_statement_result = false; need_image_clone = false; + global_index = true; + global_fkey = false; data_->bind_vector_ = "sqlite::bind*"; data_->truncated_vector_ = "bool*"; diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx index 8efef66..1d4b1d2 100644 --- a/odb/relational/sqlite/schema.cxx +++ b/odb/relational/sqlite/schema.cxx @@ -56,14 +56,11 @@ namespace relational virtual string name (sema_rel::index& in) { - // In SQLite, index names are database-global. Make them unique - // by prefixing the index name with table name (preserving the - // database). + // In SQLite, index names can be qualified with the database. // - sema_rel::qname n ( - static_cast (in.scope ()).name ()); - - n.uname () += "_" + in.name (); + sema_rel::table& t (static_cast (in.scope ())); + sema_rel::qname n (t.name ().qualifier ()); + n.append (in.name ()); return quote_id (n); } -- cgit v1.1