From 4701df22146e4e4fc0c7fe58903fbd0482defcb5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 23 Jan 2015 10:53:46 +0200 Subject: Handle name truncation in PostgreSQL --- NEWS | 6 ++++ doc/odb-epilogue.1 | 3 +- doc/odb-epilogue.xhtml | 7 ++-- odb/context.cxx | 6 ++++ odb/context.hxx | 1 + odb/options.cli | 8 +++++ odb/relational/pgsql/context.cxx | 75 ++++++++++++++++++++++++++++++++++++++++ odb/relational/pgsql/context.hxx | 11 ++++++ odb/relational/pgsql/source.cxx | 47 +++++++++++++++---------- 9 files changed, 142 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 5ff9359..49e4aa4 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,12 @@ Version 2.4.0 * Support for calling MySQL stored procedures. For details and limitations refer to Section 17.7, "MySQL Stored Procedures" in the ODB manual. + * New option, --statement-regex, can be used to process prepared statement + names that are used by PostgreSQL. This can be useful, for example, to + shorten names that exceed the PostgreSQL name limit. To this effect, ODB + now also warns when an SQL name exceeds the default PostgreSQL limit of + 63 characters. + * New option, --oracle-warn-truncation, makes ODB warn about SQL names that are longer than 30 characters and are therefore truncated. ODB now also detects when such truncations lead to name conflicts and diff --git a/doc/odb-epilogue.1 b/doc/odb-epilogue.1 index 6968767..0818bf8 100644 --- a/doc/odb-epilogue.1 +++ b/doc/odb-epilogue.1 @@ -41,8 +41,9 @@ can be .BR column , .BR index , .BR fkey , +.BR sequence , or -.BR sequence . +.BR statement . On the other hand, if we want our regular expressions to apply to all SQL names, then we use the .B --sql-name-regex diff --git a/doc/odb-epilogue.xhtml b/doc/odb-epilogue.xhtml index da3fdef..c108df8 100644 --- a/doc/odb-epilogue.xhtml +++ b/doc/odb-epilogue.xhtml @@ -21,9 +21,10 @@ --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.

+ fkey, sequence, or + statement. 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 diff --git a/odb/context.cxx b/odb/context.cxx index 4af13b7..e56b412 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -691,6 +691,12 @@ context (ostream& os_, data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ()); } + if (ops.statement_regex ().count (db) != 0) + { + strings const& s (ops.statement_regex ()[db]); + data_->sql_name_regex_[sql_name_statement].assign (s.begin (), s.end ()); + } + if (ops.sql_name_regex ().count (db) != 0) { strings const& s (ops.sql_name_regex ()[db]); diff --git a/odb/context.hxx b/odb/context.hxx index 805691f..9538389 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -1191,6 +1191,7 @@ public: sql_name_index, sql_name_fkey, sql_name_sequence, + sql_name_statement, sql_name_count }; diff --git a/odb/options.cli b/odb/options.cli index 350bbfc..3e3d070 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -770,6 +770,14 @@ class options TRANSFORMATIONS section below for details." }; + database_map > --statement-regex + { + "", + "Add to the list of regular expressions that is used to + transform automatically-derived prepared statement names. See + the SQL NAME TRANSFORMATIONS section below for details." + }; + database_map > --sql-name-regex { "", diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 9e91e31..f68ff06 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -5,6 +5,8 @@ #include #include +#include + #include #include @@ -243,6 +245,79 @@ namespace relational } string context:: + quote_id_impl (qname const& id) const + { + string r; + + bool f (true); + for (qname::iterator i (id.begin ()); i < id.end (); ++i) + { + if (i->empty ()) + continue; + + // Warn if the name is greater than the (NAMEDATALEN - 1) limit, + // which is 63 in the default PG build. + // + if (i->size () > 63) + { + cerr << "warning: SQL name '" << *i << "' is longer than " + << "the default PostgreSQL name limit of 63 characters " + << "and may be truncated" << endl; + + cerr << "info: consider shortening it using #pragma db " + << "table/column/index or --*-regex options" << endl; + } + + if (f) + f = false; + else + r += '.'; + + r += '"'; + r += *i; + r += '"'; + } + + return r; + } + + string context:: + statement_name (string const& type, string const& name, semantics::node& n) + { + // Put the type first so that in the case of truncation it + // remains thus lowering the chance of a clash. + // + string r (type); + r += '_'; + r += name; + + r = transform_name (r, sql_name_statement); + + // Warn if the name is greater than the (NAMEDATALEN - 1) limit, + // which is 63 in the default PG build. + // + // Note that we have to do it in addition to the above since this + // name doesn't go through quote_id(). + // + if (r.size () > 63) + { + location const& l (n.location ()); + + warn (l) << "prepared statement name '" << r << "' is longer than " + << "the default PostgreSQL name limit of 63 characters " + << "and may be truncated" << endl; + + info (l) << "consider shortening the corresponding namespace " + << "name, class name, or data member name" << endl; + + info (l) << "or shortening the statement name itself using the " + << "--statement-regex option" << endl; + } + + return r; + } + + string context:: database_type_impl (semantics::type& t, semantics::names* hint, bool id, diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index b7ec873..f3e195c 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -98,10 +98,21 @@ namespace relational static sql_type parse_sql_type (string, custom_db_types const* = 0); + public: + // Construct statement name from a given type and name. + // + string + statement_name (string const& type, + string const& name, + semantics::node&); + protected: virtual string const& convert_expr (string const&, semantics::data_member&, bool); + virtual string + quote_id_impl (qname const&) const; + virtual bool grow_impl (semantics::class_&, user_section*); diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index b625524..b3d934d 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -765,7 +765,8 @@ namespace relational string traits ("access::object_traits_impl< " + n + ", id_pgsql >"); os << "const char " << traits << "::" << endl - << "persist_statement_name[] = " << strlit (fn + "_persist") << ";" + << "persist_statement_name[] = " << + strlit (statement_name ("persist", fn, c)) << ";" << endl; if (id != 0) @@ -784,37 +785,39 @@ namespace relational os << "," << endl; ostringstream ostr; - ostr << fn << "_find_" << i; - os << strlit (ostr.str ()); + ostr << "find_" << i; + os << strlit (statement_name (ostr.str (), fn, c)); } os << "};"; } else os << "const char " << traits << "::" << endl - << "find_statement_name[] = " << strlit (fn + "_find") << ";" + << "find_statement_name[] = " << + strlit (statement_name ("find", fn, c)) << ";" << endl; if (poly && !poly_derived) os << "const char " << traits << "::" << endl << "find_discriminator_statement_name[] = " << - strlit (fn + "_find_discriminator") << ";" + strlit (statement_name ("find_discriminator", fn, c)) << ";" << endl; if (update_columns != 0) os << "const char " << traits << "::" << endl - << "update_statement_name[] = " << strlit (fn + "_update") << - ";" + << "update_statement_name[] = " << + strlit (statement_name ("update", fn, c)) << ";" << endl; os << "const char " << traits << "::" << endl - << "erase_statement_name[] = " << strlit (fn + "_erase") << ";" + << "erase_statement_name[] = " << + strlit (statement_name ("erase", fn, c)) << ";" << endl; if (optimistic != 0) os << "const char " << traits << "::" << endl << "optimistic_erase_statement_name[] = " << - strlit (fn + "_optimistic_erase") << ";" + strlit (statement_name ("erase_optimistic", fn, c)) << ";" << endl; } @@ -823,11 +826,12 @@ namespace relational if (options.generate_query ()) { os << "const char " << traits << "::" << endl - << "query_statement_name[] = " << strlit (fn + "_query") << ";" + << "query_statement_name[] = " << + strlit (statement_name ("query", fn, c)) << ";" << endl << "const char " << traits << "::" << endl << "erase_query_statement_name[] = " << - strlit (fn + "_erase_query") << ";" + strlit (statement_name ("erase_query", fn, c)) << ";" << endl; } @@ -927,7 +931,8 @@ namespace relational string traits ("access::view_traits_impl< " + n + ", id_pgsql >"); os << "const char " << traits << "::" << endl - << "query_statement_name[] = " << strlit (fn + "_query") << ";" + << "query_statement_name[] = " << + strlit (statement_name ("query", fn, c)) << ";" << endl; } @@ -1026,19 +1031,23 @@ namespace relational class_fq_name (*top_object) + "_" + flat_prefix_ + pn)); os << "const char " << scope << "::" << endl - << "select_name[] = " << strlit (fn + "_select") << ";" + << "select_name[] = " << + strlit (statement_name ("select", fn, m)) << ";" << endl << "const char " << scope << "::" << endl - << "insert_name[] = " << strlit (fn + "_insert") << ";" + << "insert_name[] = " << + strlit (statement_name ("insert", fn, m)) << ";" << endl; if (smart) os << "const char " << scope << "::" << endl - << "update_name[] = " << strlit (fn + "_update") << ";" + << "update_name[] = " << + strlit (statement_name ("update", fn, m)) << ";" << endl; os << "const char " << scope << "::" << endl - << "delete_name[] = " << strlit (fn + "_delete") << ";" + << "delete_name[] = " << + strlit (statement_name ("delete", fn, m)) << ";" << endl; // Statement types. @@ -1209,12 +1218,14 @@ namespace relational if (load || load_opt) os << "const char " << scope << "::" << endl - << "select_name[] = " << strlit (fn + "_select") << ";" + << "select_name[] = " << + strlit (statement_name ("select", fn, *s.member)) << ";" << endl; if (update || update_opt) os << "const char " << scope << "::" << endl - << "update_name[] = " << strlit (fn + "_update") << ";" + << "update_name[] = " << + strlit (statement_name ("update", fn, *s.member)) << ";" << endl; // Statement types. -- cgit v1.1