diff options
42 files changed, 4718 insertions, 2720 deletions
@@ -1,3 +1,20 @@ +Version 2.2.0 + + * Static multi-database support. It allows an application to simultaneously + work with multiple database systems using their static interfaces (i.e., + odb::<database>::database instead of odb::database). + + * Support for prepared queries. Prepared queries are a thin wrapper around + the underlying database system's prepared statements functionality. They + provide a way to perform potentially expensive query preparations tasks + only once and then executing the query multiple time. For more information, + refer to Section 4.5, "Prepared Queries" in the ODB manual as well as the + 'prepared' example in the odb-examples package. + + * Support for early connection release. Now the database connection is + released when commit()/rollback() is called rather than when the + transaction instance goes out of scope. + Version 2.1.0 * The ODB compiler is now capable of automatically discovering accessor and diff --git a/doc/manual.xhtml b/doc/manual.xhtml index 5286d9d..05fdc9b 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -8,8 +8,8 @@ <meta name="copyright" content="© 2009-2012 Code Synthesis Tools CC"/> <meta name="keywords" content="odb,c++,object,persistence,ORM,relational,database,RDBMS,ODBMS,OODBMS"/> <meta name="description" content="C++ Object Persistence with ODB"/> - <meta name="revision" content="2.1"/> - <meta name="version" content="2.1.0"/> + <meta name="revision" content="2.2"/> + <meta name="version" content="2.2.0"/> <!-- @@ -265,8 +265,8 @@ for consistency. no Front-Cover Texts and no Back-Cover Texts.</p> <!-- REMEMBER TO CHANGE VERSIONS IN THE META TAGS ABOVE! --> - <p id="revision">Revision 2.1, September 2012</p> - <p>This revision of the manual describes ODB 2.1.0 and is available + <p id="revision">Revision 2.2, October 2012</p> + <p>This revision of the manual describes ODB 2.2.0 and is available in the following formats: <a href="http://www.codesynthesis.com/products/odb/doc/manual.xhtml">XHTML</a>, <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.pdf">PDF</a>, and @@ -344,6 +344,7 @@ for consistency. <tr><th>4.2</th><td><a href="#4.2">Parameter Binding</a></td></tr> <tr><th>4.3</th><td><a href="#4.3">Executing a Query</a></td></tr> <tr><th>4.4</th><td><a href="#4.4">Query Result</a></td></tr> + <tr><th>4.5</th><td><a href="#4.5">Prepared Queries</a></td></tr> </table> </td> </tr> @@ -683,7 +684,7 @@ for consistency. <tr><th>17.5.1</th><td><a href="#17.5.1">Query Result Caching</a></td></tr> <tr><th>17.5.2</th><td><a href="#17.5.2">Foreign Key Constraints</a></td></tr> <tr><th>17.5.3</th><td><a href="#17.5.3">Unique Constraint Violations</a></td></tr> - <tr><th>17.5.4</th><td><a href="#17.5.4">Multithreaded Windows Applications</a></td></tr> + <tr><th>17.5.4</th><td><a href="#17.5.4">Multi-threaded Windows Applications</a></td></tr> <tr><th>17.5.5</th><td><a href="#17.5.5">Affected Row Count and DDL Statements</a></td></tr> <tr><th>17.5.6</th><td><a href="#17.5.6">Long Data and Automatically Assigned Object Ids</a></td></tr> <tr><th>17.5.7</th><td><a href="#17.5.7">Long Data and By-Value Accessors/Modifiers</a></td></tr> @@ -3897,6 +3898,26 @@ namespace odb what () const throw (); }; + // Prepared query support exceptions. + // + struct prepared_already_cached: exception + { + const char* + name () const; + + virtual const char* + what () const throw (); + }; + + struct prepared_type_mismatch: exception + { + const char* + name () const; + + virtual const char* + what () const throw (); + }; + // Schema catalog exceptions. // struct unknown_schema: exception @@ -3988,6 +4009,14 @@ namespace odb class. For more information on polymorphism support, refer to <a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>.</p> + <p>The <code>prepared_already_cached</code> exception is thrown by the + <code>cache_query()</code> function if a prepared query with the + specified name is already cached. The <code>prepared_type_mismatch</code> + exception is thrown by the <code>lookup_query()</code> function if + the specified prepared query object type or parameters type + does not match the one in the cache. Refer to <a href="#4.5">Section + 4.5, "Prepared Queries"</a> for details.</p> + <p>The <code>unknown_schema</code> exception is thrown by the <code>odb::schema_catalog</code> class if a schema with the specified name is not found. Refer to <a href="#3.4">Section 3.4, "Database"</a> @@ -4077,7 +4106,7 @@ namespace odb example:</p> <pre class="cxx"> - query q ("first = 'John'" + (query::age < query::_ref (age))); + query q ("first = 'John' AND" + (query::age < query::_ref (age))); </pre> <h2><a name="4.1">4.1 ODB Query Language</a></h2> @@ -4312,7 +4341,9 @@ namespace odb <p>Normally, we would create a named query instance if we are planning to run the same query multiple times and would use the - in-line version for those that are executed only once. A named + in-line version for those that are executed only once (see also + <a href="#4.5">Section 4.5, "Prepared Queries"</a> for a more + optimal way to re-execute the same query multiple times). A named query instance that does not have any by-reference parameters is immutable and can be shared between multiple threads without synchronization. On the other hand, a query instance with @@ -4594,6 +4625,442 @@ namespace odb } </pre> + + <h2><a name="4.5">4.5 Prepared Queries</a></h2> + + <p>Most modern relational database systems have the notion of a prepared + statement. Prepared statements allow us to perform the potentially + expensive tasks of parsing SQL, preparing the query execution + plan, etc., once and then executing the same query multiple + times, potentially using different values for parameters in + each execution.</p> + + <p>In ODB all the non-query database operations such as + <code>persist()</code>, <code>load()</code>, <code>update()</code>, + etc., are implemented in terms of prepared statements that are + cached and reused. While the <code>query()</code> database + operation also uses the prepared statement, this statement + is not cached or reused by default since ODB has no knowledge + of whether a query will be executed multiple times or only + once. Instead, ODB provides a mechanism, called prepared queries, + that allows us to prepare a query once and execute it multiple + times. In other words, ODB prepared queries are a thin wrapper + around the underlying database's prepared statement functionality.</p> + + <p>In most cases ODB shields the application developer from database + connection management and multi-threading issues. However, when it + comes to prepared queries, a basic understanding of how ODB manages + these aspects is required. Conceptually, the <code>odb::database</code> + class represents a specific database, that is, a data store. However, + underneath, it maintains one or more connections to this database. + A connection can be used only by a single thread at a time. When + we start a transaction (by calling <code>database::begin()</code>), + the transaction instance obtains a connection and holds on to it + until the transaction is committed or rolled back. During this time + no other thread can use this connection. When the transaction + releases the connection, it may be closed or reused by another + transaction in this or another thread. What exactly happens to + a connection after it has been released depends on the connection + factory that is used by the <code>odb::database</code> instance. + For more information on connection factories, refer to + <a href="#II">Part II, "Database Systems"</a>.</p> + + <p>A query prepared on one connection cannot be executed on another. + In other words, a prepared query is associated with the connection. + One important implication of this restriction is that we cannot + prepare a query in one transaction and then try to execute it + in another without making sure that both transactions use the + same connection.</p> + + <p>To enable the prepared query functionality we need to specify + the <code>--generate-prepared</code> ODB compiler option. If + we are planning to always prepare our queries, then we can + disable the once-off query execution support by also specifying + the <code>--omit-unprepared</code> option.</p> + + <p>To prepare a query we use the <code>prepare_query()</code> function + template. This function can be called on both the <code>odb::database</code> + and <code>odb::connection</code> instances. The <code>odb::database</code> + version simply obtains the connection used by the currently active + transaction and calls the corresponding <code>odb::connection</code> + version. If no transaction is currently active, then this function + throws the <code>odb::not_in_transaction</code> exception + (<a href="#3.5">Section 3.5, "Transactions"</a>). The + <code>prepare_query()</code> function has the following signature:</p> + + <pre class="cxx"> + template <typename T> + prepared_query<T> + prepare_query (const char* name, const odb::query<T>&); + </pre> + + <p>The first argument to the <code>prepare_query()</code> function is + the prepared query name. This name is used as a key for prepared + query caching (discussed later) and must be unique. For some databases, + notably PostgreSQL, it is also used as a name of the underlying prepared + statement. The name <code>"<i>object</i>_query"</code> (e.g., + <code>"person_query"</code>) is reserved for the once-off queries + executed by the <code>database::query()</code> function. Note that + the <code>prepare_query()</code> function makes only a shallow copy + of this argument, which means that the name must be valid for the + lifetime of the returned <code>prepared_query</code> instance.</p> + + <p>The second argument to the <code>prepare_query()</code> function + is the query criteria. It has the same semantics as in the + <code>query()</code> function discussed in <a href="#4.3">Section + 4.3, "Executing a Query"</a>. Similar to <code>query()</code>, we + also have to explicitly specify the object type that we will be + querying. For example:</p> + + <pre class="cxx"> +typedef odb::query<person> query; +typedef odb::prepared_query<person> prep_query; + +prep_query pq ( + db.prepare_query<person> ("person-age-query", query::age > 50)); + </pre> + + <p>The result of executing the <code>prepare_query()</code> function is + the <code>prepared_query</code> instance that represent the prepared + query. It is best to view <code>prepared_query</code> as a handle to + the underlying prepared statement. While we can make a copy of it or + assign one <code>prepared_query</code> to another, the two instances + will refer to the same prepared statement. Once the last instance of + <code>prepared_query</code> referencing a specific prepared statement + is destroyed, this statement is released. The <code>prepared_query</code> + class template has the following interface:</p> + + <pre class="cxx"> +namespace odb +{ + template <typename T> + struct prepared_query + { + prepared_query (); + + prepared_query (const prepared_query&) + prepared_query& operator= (const prepared_query&) + + result<T> + execute (bool cache = true); + + const char* + name () const; + + statement& + statement () const; + + operator unspecified_bool_type () const; + }; +} + </pre> + + <p>The default constructor creates an empty <code>prepared_query</code> + instance, that is, an instance that does not reference a prepared + statement and therefore cannot be executed. The only way to create + a non-empty prepared query is by calling the <code>prepare_query()</code> + function discussed above. To test whether the prepared query is empty, + we can use the implicit conversion operator to a boolean type. For + example:</p> + + <pre class="cxx"> + prepared_query<person> pq; + + if (pq) + { + // Not empty. + ... + } + </pre> + + <p>The <code>execute()</code> function executes the query and returns + the result instance. The <code>cache</code> argument indicates + whether the result should be cached and has the same semantics + as in the <code>query()</code> function. In fact, conceptually, + <code>prepare_query()</code> and <code>execute()</code> are just + the <code>query()</code> function split into two: + <code>prepare_query()</code> takes the first + <code>query()</code> argument (the query condition) while + <code>execute()</code> takes the second (the cache flag). Note + also that re-executing a prepared query invalidates the + previous execution result, whether cached or uncached. </p> + + <p>The <code>name()</code> function returns the prepared query name. + This is the same name as was passed as the first argument in the + <code>prepare_query()</code> call. The <code>statement()</code> + function returns a reference to the underlying prepared statement. + Note also that calling any of these functions on an empty + <code>prepared_query</code> instance results in undefined behavior.</p> + + <p>The simplest use-case for a prepared query is the need to + execute the same query multiple times within a single transaction. + Consider the following example that queries for people that are older + than a number of different ages. This and subsequent code fragments + are taken from the <code>prepared</code> example in the + <code>odb-examples</code> package.</p> + + <pre class="cxx"> +typedef odb::query<person> query; +typedef odb::prepared_query<person> prep_query; +typedef odb::result<person> result; + +transaction t (db.begin ()); + +unsigned short age; +query q (query::age > query::_ref (age)); +prep_query pq (db.prepare_query<person> ("person-age-query", q)); + +for (age = 90; age > 40; age -= 10) +{ + result r (pq.execute ()); + ... +} + +t.commit (); + </pre> + + <p>Another scenario is the need to reuse the same query in multiple + transactions that are executed at once. As was mentioned above, + in this case we need to make sure that the prepared query and + all the transactions use the same connection. Consider an + alternative version of the above example that executes each + query in a separate transaction:</p> + + <pre class="cxx"> +connection_ptr conn (db.connection ()); + +unsigned short age; +query q (query::age > query::_ref (age)); +prep_query pq (conn->prepare_query<person> ("person-age-query", q)); + +for (age = 90; age > 40; age -= 10) +{ + transaction t (conn->begin ()); + + result r (pq.execute ()); + ... + + t.commit (); +} + </pre> + + + <p>Note that with this approach we hold on to the database connection + until all the transactions involving the prepared query are + executed. In particular, this means that while we are busy, the + connection cannot be reused by another thread. Therefore, this + approach is only recommended if all the transactions are executed + close to each other. Also note that an uncached (see below) + prepared query is invalidated once we release the connection + on which it was prepared.</p> + + <p>If we need to reuse a prepared query in transactions that are + executed at various times, potentially in different threads, then + the recommended approach is to cache the prepared query on the + connection. To support this functionality the <code>odb::database</code> + and <code>odb::connection</code> classes provide the following + function templates. Similar to <code>prepare_query()</code>, + the <code>odb::database</code> versions of the below + functions call the corresponding <code>odb::connection</code> + versions using the currently active transaction to resolve + the connection.</p> + + <pre class="cxx"> + template <typename T> + void + cache_query (const prepared_query<T>&); + + template <typename T, typename P> + void + cache_query (const prepared_query<T>&, + std::[auto|unique]_ptr<P> params); + + template <typename T> + prepared_query<T> + lookup_query (const char* name) const; + + template <typename T, typename P> + prepared_query<T> + lookup_query (const char* name, P*& params) const; + </pre> + + <p>The <code>cache_query()</code> function caches the passed prepared + query on the connection. The second overloaded version of + <code>cache_query()</code> also takes a pointer to the + by-reference query parameters. In C++98 it should be + <code>std::auto_ptr</code> while in C++11 <code>std::auto_ptr</code> + or <code>std::unique_ptr</code> can be used. The + <code>cache_query()</code> function assumes ownership of the + passed <code>params</code> argument. If a prepared query + with the same name is already cached on this connection, + then the <code>odb::prepared_already_cached</code> exception + is thrown.</p> + + <p>The <code>lookup_query()</code> function looks up a previously + cached prepared query given its name. The second overloaded + version of <code>lookup_query()</code> also returns a pointer + to the by-reference query parameters. If a prepared query + with this name has not been cached, then an empty + <code>prepared_query</code> instance is returned. If a + prepared query with this name has been cached but either + the object type or the parameters type does not match + that which was cached, then the <code>odb::prepared_type_mismatch</code> + exception is thrown.</p> + + <p>As a first example of the prepared query cache functionality, + consider the case that does not use any by-reference parameters:</p> + + <pre class="cxx"> +for (unsigned short i (0); i < 5; ++i) +{ + transaction t (db.begin ()); + + prep_query pq (db.lookup_query<person> ("person-age-query")); + + if (!pq) + { + pq = db.prepare_query<person> ( + "person-val-age-query", query::age > 50); + db.cache_query (pq); + } + + result r (pq.execute ()); + ... + + t.commit (); + + // Do some other work. + // + ... +} + </pre> + + <p>The following example shows how to do the same but for a query that + includes by-reference parameters. In this case the parameters are + cached together with the prepared query.</p> + + <pre class="cxx"> +for (unsigned short age (90); age > 40; age -= 10) +{ + transaction t (db.begin ()); + + unsigned short* age_param; + prep_query pq ( + db.lookup_query<person> ("person-age-query", age_param)); + + if (!pq) + { + auto_ptr<unsigned short> p (new unsigned short); + age_param = p.get (); + query q (query::age > query::_ref (*age_param)); + pq = db.prepare_query<person> ("person-age-query", q); + db.cache_query (pq, p); // Assumes ownership of p. + } + + *age_param = age; // Initialize the parameter. + result r (pq.execute ()); + ... + + t.commit (); + + // Do some other work. + // + ... +} + </pre> + + <p>As is evident from the above examples, when we use a prepared + query cache, each transaction that executes a query must also + include code that prepares and caches this query if it hasn't already + been done. If a prepared query is used in a single place in the + application, then this is normally not an issue since all the + relevant code is kept in one place. However, if the same query + is used in several different places in the application, then + we may end up duplicating the same preparation and caching + code, which makes it hard to maintain.</p> + + <p>To resolve this issue ODB allows us to register a prepared + query factory that will be called to prepare and cache a + query during the call to <code>lookup_query()</code>. To + register a factory we use the <code>database::query_factory()</code> + function. In C++98 it has the following signature:</p> + + <pre class="cxx"> + void + query_factory (const char* name, + void (*factory) (const char* name, connection&)); + </pre> + + <p>While in C++11 it uses the <code>std::function</code> class + template:</p> + + <pre class="cxx"> + void + query_factory (const char* name, + std::function<void (const char* name, connection&)>); + </pre> + + <p>The first argument to the <code>query_factory()</code> function is + the prepared query name that this factory will be called to prepare + and cache. An empty name is treated as a fallback wildcard factory + that is capable of preparing any query. The second argument is the + factory function or, in C++11, function object or lambda.</p> + + <p>The example fragment shows how we can use the prepared query + factory:</p> + + <pre class="cxx"> +struct params +{ + unsigned short age; + string first; +}; + +static void +query_factory (const char* name, connection& c) +{ + auto_ptr<params> p (new params); + query q (query::age > query::_ref (p->age) && + query::first == query::_ref (p->first)); + prep_query pq (c.prepare_query<person> (name, q)); + c.cache_query (pq, p); +} + +db.query_factory ("person-age-name-query", &query_factory); + +for (unsigned short age (90); age > 40; age -= 10) +{ + transaction t (db.begin ()); + + params* p; + prep_query pq (db.lookup_query<person> ("person-age-name-query", p)); + assert (pq); + + p->age = age; + p->first = "John"; + result r (pq.execute ()); + ... + + t.commit (); +} + </pre> + + <p>In C++11 we could have instead used a lambda function as well as + <code>unique_ptr</code> rather than <code>auto_ptr</code>:</p> + + <pre> +db.query_factory ( + "person-age-name-query", + [] (const char* name, connection& c) + { + unique_ptr<params> p (new params); + query q (query::age > query::_ref (p->age) && + query::first == query::_ref (p->first)); + prep_query pq (c.prepare_query<person> (name, q)); + c.cache_query (pq, std::move (p)); + }); + </pre> + <!-- CHAPTER --> <hr class="page-break"/> @@ -17153,9 +17620,9 @@ namespace odb errors to the <code>object_already_persistent</code> exception (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p> - <h3><a name="17.5.4">17.5.4 Multithreaded Windows Applications</a></h3> + <h3><a name="17.5.4">17.5.4 Multi-threaded Windows Applications</a></h3> - <p>Multithreaded Windows applications must use the + <p>Multi-threaded Windows applications must use the <code>_beginthread()</code>/<code>_beginthreadex()</code> and <code>_endthread()</code>/<code>_endthreadex()</code> CRT functions instead of the <code>CreateThread()</code> and <code>EndThread()</code> diff --git a/doc/odb-prologue.1 b/doc/odb-prologue.1 index 4bc7efe..2e667d0 100644 --- a/doc/odb-prologue.1 +++ b/doc/odb-prologue.1 @@ -1,7 +1,7 @@ .\" Process this file with .\" groff -man -Tascii odb.1 .\" -.TH ODB 1 "September 2012" "ODB 2.1.0" +.TH ODB 1 "October 2012" "ODB 2.2.0" .SH NAME odb \- object-relational mapping (ORM) compiler for C++ .\" diff --git a/doc/odb-prologue.xhtml b/doc/odb-prologue.xhtml index 623fc6b..d610b2b 100644 --- a/doc/odb-prologue.xhtml +++ b/doc/odb-prologue.xhtml @@ -2,7 +2,7 @@ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> - <title>ODB 2.1.0 Compiler Command Line Manual</title> + <title>ODB 2.2.0 Compiler Command Line Manual</title> <meta name="copyright" content="© 2009-2012 Code Synthesis Tools CC"/> <meta name="keywords" content="odb,object,relational,mapping,compiler,c++"/> diff --git a/odb/context.cxx b/odb/context.cxx index 099b10b..fbab993 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -386,8 +386,13 @@ create_context (ostream& os, { auto_ptr<context> r; - switch (ops.database ()) + switch (ops.database ()[0]) { + case database::common: + { + r.reset (new context (os, unit, ops, f)); + break; + } case database::mssql: { r.reset (new relational::mssql::context (os, unit, ops, f, m)); @@ -436,15 +441,17 @@ context (ostream& os_, unit (u), options (ops), features (f), - db (options.database ()), + db (options.database ()[0]), keyword_set (data_->keyword_set_), include_regex (data_->include_regex_), accessor_regex (data_->accessor_regex_), modifier_regex (data_->modifier_regex_), - embedded_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::embedded)), - separate_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::separate)), + embedded_schema ( + ops.generate_schema () && + ops.schema_format ()[db].count (schema_format::embedded)), + separate_schema ( + ops.generate_schema () && + ops.schema_format ()[db].count (schema_format::separate)), top_object (data_->top_object_), cur_object (data_->cur_object_) { @@ -1141,9 +1148,9 @@ schema (semantics::scope& s) const // If we are still not fully qualified, add the schema that was // specified on the command line. // - if (!r.fully_qualified () && options.schema_specified ()) + if (!r.fully_qualified () && options.schema ().count (db) != 0) { - qname n (options.schema ()); + qname n (options.schema ()[db]); n.append (r); n.swap (r); } @@ -1184,8 +1191,8 @@ table_name_prefix (semantics::scope& s) const // Add the prefix that was specified on the command line. // - if (options.table_prefix_specified ()) - r = options.table_prefix () + r; + if (options.table_prefix ().count (db) != 0) + r = options.table_prefix ()[db] + r; s.set ("table-prefix", r); return r; diff --git a/odb/generator.cxx b/odb/generator.cxx index 5500860..ec98a15 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -25,6 +25,7 @@ using namespace std; using namespace cutl; using semantics::path; +typedef vector<string> strings; typedef vector<path> paths; namespace @@ -52,20 +53,21 @@ namespace } void - append (ostream& os, vector<string> const& text, string const& file) + append (ostream& os, strings const& text) { - for (vector<string>::const_iterator i (text.begin ()); + for (strings::const_iterator i (text.begin ()); i != text.end (); ++i) { os << *i << endl; } + } - if (!file.empty ()) - { - ifstream ifs; - open (ifs, file); - os << ifs.rdbuf (); - } + void + append (ostream& os, string const& file) + { + ifstream ifs; + open (ifs, file); + os << ifs.rdbuf (); } } @@ -78,6 +80,8 @@ generate (options const& ops, { try { + database db (ops.database ()[0]); + // First create the database model. // cutl::shared_ptr<semantics::relational::model> model; @@ -86,8 +90,12 @@ generate (options const& ops, { auto_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0)); - switch (ops.database ()) + switch (db) { + case database::common: + { + break; // No schema for common. + } case database::mssql: case database::mysql: case database::oracle: @@ -109,11 +117,11 @@ generate (options const& ops, fs::auto_removes auto_rm; - string hxx_name (base + ops.odb_file_suffix () + ops.hxx_suffix ()); - string ixx_name (base + ops.odb_file_suffix () + ops.ixx_suffix ()); - string cxx_name (base + ops.odb_file_suffix () + ops.cxx_suffix ()); - string sch_name (base + ops.schema_file_suffix () + ops.cxx_suffix ()); - string sql_name (base + ops.sql_suffix ()); + string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ()); + string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ()); + string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ()); + string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ()); + string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ()); path hxx_path (hxx_name); path ixx_path (ixx_name); @@ -173,7 +181,7 @@ generate (options const& ops, // ofstream cxx; - if (gen_cxx) + if (gen_cxx && db != database::common) { cxx.open (cxx_path.string ().c_str (), ios_base::out); @@ -190,7 +198,8 @@ generate (options const& ops, // // bool gen_sql_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::sql)); + ops.schema_format ()[db].count (schema_format::sql) && + db != database::common); ofstream sql; if (gen_sql_schema) @@ -209,9 +218,11 @@ generate (options const& ops, // // - bool gen_sep_schema (gen_cxx && - ops.generate_schema () && - ops.schema_format ().count (schema_format::separate)); + bool gen_sep_schema ( + gen_cxx && + ops.generate_schema () && + ops.schema_format ()[db].count (schema_format::separate) && + db != database::common); ofstream sch; @@ -235,7 +246,9 @@ generate (options const& ops, { hxx << cxx_file_header; ixx << cxx_file_header; - cxx << cxx_file_header; + + if (db != database::common) + cxx << cxx_file_header; } if (gen_sep_schema) @@ -284,14 +297,22 @@ generate (options const& ops, // Copy prologue. // - if (!ops.hxx_prologue ().empty () || !ops.hxx_prologue_file ().empty ()) { - hxx << "// Begin prologue." << endl - << "//" << endl; - append (hxx, ops.hxx_prologue (), ops.hxx_prologue_file ()); - hxx << "//" << endl - << "// End prologue." << endl - << endl; + bool p (ops.hxx_prologue ().count (db) != 0); + bool pf (ops.hxx_prologue_file ().count (db) != 0); + + if (p || pf) + { + hxx << "// Begin prologue." << endl + << "//" << endl; + if (p) + append (hxx, ops.hxx_prologue ()[db]); + if (pf) + append (hxx, ops.hxx_prologue_file ()[db]); + hxx << "//" << endl + << "// End prologue." << endl + << endl; + } } // Include main file(s). @@ -299,10 +320,8 @@ generate (options const& ops, for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i) hxx << "#include " << ctx->process_include_path (i->leaf ().string ()) << endl; - hxx << endl; - { // We don't want to indent prologues/epilogues. // @@ -314,16 +333,31 @@ generate (options const& ops, if (!ops.at_once ()) include::generate (true); - header::generate (); - - switch (ops.database ()) + switch (db) { + case database::common: + { + header::generate (); + break; + } case database::mssql: case database::mysql: case database::oracle: case database::pgsql: case database::sqlite: { + if (ops.multi_database () == multi_database::disabled) + header::generate (); + else + { + string n (base + + ops.odb_file_suffix ()[database::common] + + ops.hxx_suffix ()); + + hxx << "#include " << ctx->process_include_path (n) << endl + << endl; + } + relational::header::generate (); break; } @@ -335,14 +369,22 @@ generate (options const& ops, // Copy epilogue. // - if (!ops.hxx_epilogue ().empty () || !ops.hxx_epilogue_file ().empty ()) { - hxx << "// Begin epilogue." << endl - << "//" << endl; - append (hxx, ops.hxx_epilogue (), ops.hxx_epilogue_file ()); - hxx << "//" << endl - << "// End epilogue." << endl - << endl; + bool e (ops.hxx_epilogue ().count (db) != 0); + bool ef (ops.hxx_epilogue_file ().count (db) != 0); + + if (e || ef) + { + hxx << "// Begin epilogue." << endl + << "//" << endl; + if (e) + append (hxx, ops.hxx_epilogue ()[db]); + if (ef) + append (hxx, ops.hxx_epilogue_file ()[db]); + hxx << "//" << endl + << "// End epilogue." << endl + << endl; + } } hxx << "#include <odb/post.hxx>" << endl @@ -367,14 +409,22 @@ generate (options const& ops, // Copy prologue. // - if (!ops.ixx_prologue ().empty () || !ops.ixx_prologue_file ().empty ()) { - ixx << "// Begin prologue." << endl - << "//" << endl; - append (ixx, ops.ixx_prologue (), ops.ixx_prologue_file ()); - ixx << "//" << endl - << "// End prologue." << endl - << endl; + bool p (ops.ixx_prologue ().count (db) != 0); + bool pf (ops.ixx_prologue_file ().count (db) != 0); + + if (p || pf) + { + ixx << "// Begin prologue." << endl + << "//" << endl; + if (p) + append (ixx, ops.ixx_prologue ()[db]); + if (pf) + append (ixx, ops.ixx_prologue_file ()[db]); + ixx << "//" << endl + << "// End prologue." << endl + << endl; + } } { @@ -382,16 +432,22 @@ generate (options const& ops, // ind_filter ind (ctx->os); - inline_::generate (); - - switch (ops.database ()) + switch (db) { + case database::common: + { + inline_::generate (); + break; + } case database::mssql: case database::mysql: case database::oracle: case database::pgsql: case database::sqlite: { + if (ops.multi_database () == multi_database::disabled) + inline_::generate (); + relational::inline_::generate (); break; } @@ -400,13 +456,22 @@ generate (options const& ops, // Copy epilogue. // - if (!ops.ixx_epilogue ().empty () || !ops.ixx_epilogue_file ().empty ()) { - ixx << "// Begin epilogue." << endl - << "//" << endl; - append (ixx, ops.ixx_epilogue (), ops.ixx_epilogue_file ()); - ixx << "//" << endl - << "// End epilogue." << endl; + bool e (ops.ixx_epilogue ().count (db) != 0); + bool ef (ops.ixx_epilogue_file ().count (db) != 0); + + if (e || ef) + { + ixx << "// Begin epilogue." << endl + << "//" << endl; + if (e) + append (ixx, ops.ixx_epilogue ()[db]); + if (ef) + append (ixx, ops.ixx_epilogue_file ()[db]); + ixx << "//" << endl + << "// End epilogue." << endl + << endl; + } } if (ops.show_sloc ()) @@ -417,7 +482,7 @@ generate (options const& ops, // CXX // - if (gen_cxx) + if (gen_cxx && db != database::common) { auto_ptr<context> ctx ( create_context (cxx, unit, ops, fts, model.get ())); @@ -429,14 +494,22 @@ generate (options const& ops, // Copy prologue. // - if (!ops.cxx_prologue ().empty () || !ops.cxx_prologue_file ().empty ()) { - cxx << "// Begin prologue." << endl - << "//" << endl; - append (cxx, ops.cxx_prologue (), ops.cxx_prologue_file ()); - cxx << "//" << endl - << "// End prologue." << endl - << endl; + bool p (ops.cxx_prologue ().count (db) != 0); + bool pf (ops.cxx_prologue_file ().count (db) != 0); + + if (p || pf) + { + cxx << "// Begin prologue." << endl + << "//" << endl; + if (p) + append (cxx, ops.cxx_prologue ()[db]); + if (pf) + append (cxx, ops.cxx_prologue_file ()[db]); + cxx << "//" << endl + << "// End prologue." << endl + << endl; + } } cxx << "#include " << ctx->process_include_path (hxx_name) << endl @@ -453,8 +526,12 @@ generate (options const& ops, if (!ops.at_once ()) include::generate (false); - switch (ops.database ()) + switch (db) { + case database::common: + { + assert (false); + } case database::mssql: case database::mysql: case database::oracle: @@ -469,14 +546,22 @@ generate (options const& ops, // Copy epilogue. // - if (!ops.cxx_epilogue ().empty () || !ops.cxx_epilogue_file ().empty ()) { - cxx << "// Begin epilogue." << endl - << "//" << endl; - append (cxx, ops.cxx_epilogue (), ops.cxx_epilogue_file ()); - cxx << "//" << endl - << "// End epilogue." << endl - << endl; + bool e (ops.cxx_epilogue ().count (db) != 0); + bool ef (ops.cxx_epilogue_file ().count (db) != 0); + + if (e || ef) + { + cxx << "// Begin epilogue." << endl + << "//" << endl; + if (e) + append (cxx, ops.cxx_epilogue ()[db]); + if (ef) + append (cxx, ops.cxx_epilogue_file ()[db]); + cxx << "//" << endl + << "// End epilogue." << endl + << endl; + } } cxx << "#include <odb/post.hxx>" << endl; @@ -501,15 +586,22 @@ generate (options const& ops, // Copy prologue. // - if (!ops.schema_prologue ().empty () || - !ops.schema_prologue_file ().empty ()) { - sch << "// Begin prologue." << endl - << "//" << endl; - append (sch, ops.schema_prologue (), ops.schema_prologue_file ()); - sch << "//" << endl - << "// End prologue." << endl - << endl; + bool p (ops.schema_prologue ().count (db) != 0); + bool pf (ops.schema_prologue_file ().count (db) != 0); + + if (p || pf) + { + sch << "// Begin prologue." << endl + << "//" << endl; + if (p) + append (sch, ops.schema_prologue ()[db]); + if (pf) + append (sch, ops.schema_prologue_file ()[db]); + sch << "//" << endl + << "// End prologue." << endl + << endl; + } } sch << "#include " << ctx->process_include_path (hxx_name) << endl @@ -520,8 +612,12 @@ generate (options const& ops, // ind_filter ind (ctx->os); - switch (ops.database ()) + switch (db) { + case database::common: + { + assert (false); + } case database::mssql: case database::mysql: case database::oracle: @@ -536,15 +632,22 @@ generate (options const& ops, // Copy epilogue. // - if (!ops.schema_epilogue ().empty () || - !ops.schema_epilogue_file ().empty ()) { - sch << "// Begin epilogue." << endl - << "//" << endl; - append (sch, ops.schema_epilogue (), ops.schema_epilogue_file ()); - sch << "//" << endl - << "// End epilogue." << endl - << endl; + bool e (ops.schema_epilogue ().count (db) != 0); + bool ef (ops.schema_epilogue_file ().count (db) != 0); + + if (e || ef) + { + sch << "// Begin epilogue." << endl + << "//" << endl; + if (e) + append (sch, ops.schema_epilogue ()[db]); + if (ef) + append (sch, ops.schema_epilogue_file ()[db]); + sch << "//" << endl + << "// End epilogue." << endl + << endl; + } } sch << "#include <odb/post.hxx>" << endl; @@ -562,8 +665,12 @@ generate (options const& ops, auto_ptr<context> ctx ( create_context (sql, unit, ops, fts, model.get ())); - switch (ops.database ()) + switch (db) { + case database::common: + { + assert (false); + } case database::mssql: case database::mysql: case database::oracle: @@ -574,15 +681,22 @@ generate (options const& ops, // Copy prologue. // - if (!ops.sql_prologue ().empty () || - !ops.sql_prologue_file ().empty ()) { - sql << "/* Begin prologue." << endl - << " */" << endl; - append (sql, ops.sql_prologue (), ops.sql_prologue_file ()); - sql << "/*" << endl - << " * End prologue. */" << endl - << endl; + bool p (ops.sql_prologue ().count (db) != 0); + bool pf (ops.sql_prologue_file ().count (db) != 0); + + if (p || pf) + { + sql << "/* Begin prologue." << endl + << " */" << endl; + if (p) + append (sql, ops.sql_prologue ()[db]); + if (pf) + append (sql, ops.sql_prologue_file ()[db]); + sql << "/*" << endl + << " * End prologue. */" << endl + << endl; + } } if (!ops.omit_drop ()) @@ -590,15 +704,22 @@ generate (options const& ops, // Copy interlude. // - if (!ops.sql_interlude ().empty () || - !ops.sql_interlude_file ().empty ()) { - sql << "/* Begin interlude." << endl - << " */" << endl; - append (sql, ops.sql_interlude (), ops.sql_interlude_file ()); - sql << "/*" << endl - << " * End interlude. */" << endl - << endl; + bool i (ops.sql_interlude ().count (db) != 0); + bool ifl (ops.sql_interlude_file ().count (db) != 0); + + if (i || ifl) + { + sql << "/* Begin interlude." << endl + << " */" << endl; + if (i) + append (sql, ops.sql_interlude ()[db]); + if (ifl) + append (sql, ops.sql_interlude_file ()[db]); + sql << "/*" << endl + << " * End interlude. */" << endl + << endl; + } } if (!ops.omit_create ()) @@ -606,15 +727,22 @@ generate (options const& ops, // Copy epilogue. // - if (!ops.sql_epilogue ().empty () || - !ops.sql_epilogue_file ().empty ()) { - sql << "/* Begin epilogue." << endl - << " */" << endl; - append (sql, ops.sql_epilogue (), ops.sql_epilogue_file ()); - sql << "/*" << endl - << " * End epilogue. */" << endl - << endl; + bool e (ops.sql_epilogue ().count (db) != 0); + bool ef (ops.sql_epilogue_file ().count (db) != 0); + + if (e || ef) + { + sql << "/* Begin epilogue." << endl + << " */" << endl; + if (e) + append (sql, ops.sql_epilogue ()[db]); + if (ef) + append (sql, ops.sql_epilogue_file ()[db]); + sql << "/*" << endl + << " * End epilogue. */" << endl + << endl; + } } relational::schema::generate_epilogue (); diff --git a/odb/header.cxx b/odb/header.cxx index 8bf5453..67e4cc0 100644 --- a/odb/header.cxx +++ b/odb/header.cxx @@ -10,6 +10,297 @@ using namespace std; namespace header { + struct class_: traversal::class_, virtual context + { + virtual void + traverse (type& c) + { + if (!options.at_once () && class_file (c) != unit.file ()) + return; + + if (object (c)) + traverse_object (c); + else if (view (c)) + traverse_view (c); + } + + void + traverse_object (type&); + + void + traverse_view (type&); + + void + traverse_composite (type&); + }; +} + +void header::class_:: +traverse_object (type& c) +{ + using semantics::data_member; + + data_member* id (id_member (c)); + bool auto_id (id && auto_ (*id)); + bool base_id (id && &id->scope () != &c); // Comes from base. + + data_member* optimistic (context::optimistic (c)); + + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + type* poly_base (poly_derived ? &polymorphic_base (c) : 0); + data_member* discriminator (poly ? context::discriminator (*poly_root) : 0); + + bool abst (abstract (c)); + bool reuse_abst (abst && !poly); + + string const& type (class_fq_name (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + // class_traits + // + os << "template <>" << endl + << "struct class_traits< " << type << " >" + << "{" + << "static const class_kind kind = class_object;" + << "};"; + + // object_traits + // + os << "template <>" << endl + << "class access::object_traits< " << type << " >" + << "{" + << "public:" << endl; + + // object_type & pointer_type + // + os << "typedef " << type << " object_type;" + << "typedef " << c.get<string> ("object-pointer") << " pointer_type;" + << "typedef odb::pointer_traits<pointer_type> pointer_traits;" + << endl; + + // polymorphic, root_type, base_type, etc. + // + os << "static const bool polymorphic = " << (poly ? "true" : "false") << ";" + << endl; + + if (poly) + { + os << "typedef " << class_fq_name (*poly_root) << " root_type;"; + + if (poly_derived) + { + os << "typedef " << class_fq_name (*poly_base) << " base_type;" + << "typedef object_traits<root_type>::discriminator_type " << + "discriminator_type;" + << "typedef polymorphic_concrete_info<root_type> info_type;"; + + if (abst) + os << "typedef polymorphic_abstract_info<root_type> " << + "abstract_info_type;"; + + // Calculate our hierarchy depth (number of classes). + // + size_t depth (polymorphic_depth (c)); + + os << endl + << "static const std::size_t depth = " << depth << "UL;"; + } + else + { + semantics::names* hint; + semantics::type& t (utype (*discriminator, hint)); + + os << "typedef " << t.fq_name (hint) << " discriminator_type;" + << "typedef polymorphic_map<object_type> map_type;" + << "typedef polymorphic_concrete_info<object_type> info_type;"; + + if (abst) + os << "typedef polymorphic_abstract_info<object_type> " << + "abstract_info_type;"; + + os << endl + << "static const std::size_t depth = 1UL;"; + } + + os << endl; + } + + // id_type, version_type, etc. + // + if (id != 0) + { + if (base_id) + { + semantics::class_& b ( + dynamic_cast<semantics::class_&> (id->scope ())); + string const& type (class_fq_name (b)); + + os << "typedef object_traits< " << type << " >::id_type id_type;"; + + if (optimistic != 0) + os << "typedef object_traits< " << type << " >::version_type " << + "version_type;"; + + os << endl; + + if (poly_derived) + os << "static const bool auto_id = false;"; + else + os << "static const bool auto_id = object_traits< " << type << + " >::auto_id;"; + } + else + { + { + semantics::names* hint; + semantics::type& t (utype (*id, hint)); + os << "typedef " << t.fq_name (hint) << " id_type;"; + } + + if (optimistic != 0) + { + semantics::names* hint; + semantics::type& t (utype (*optimistic, hint)); + os << "typedef " << t.fq_name (hint) << " version_type;"; + } + + os << endl + << "static const bool auto_id = " << (auto_id ? "true;" : "false;"); + } + + os << endl; + } + else if (!reuse_abst) + { + // Object without id. + // + os << "typedef void id_type;" + << endl + << "static const bool auto_id = false;" + << endl; + } + + // abstract + // + os << "static const bool abstract = " << (abst ? "true" : "false") << ";" + << endl; + + // id () + // + if (id != 0 || !reuse_abst) + { + // We want to generate a dummy void id() accessor even if this + // object has no id to help us in the runtime. This way we can + // write generic code that will work for both void and non-void + // ids. + // + os << "static id_type" << endl + << "id (const object_type&);" + << endl; + } + + if (!reuse_abst) + { + // Cache traits typedefs. + // + if (id == 0) + { + os << "typedef" << endl + << "no_id_pointer_cache_traits<pointer_type>" << endl + << "pointer_cache_traits;" + << endl + << "typedef" << endl + << "no_id_reference_cache_traits<object_type>" << endl + << "reference_cache_traits;" + << endl; + } + else + { + char const* p (session (c) ? "odb::" : "no_op_"); + + if (poly_derived) + { + os << "typedef" << endl + << p << "pointer_cache_traits<" << + "object_traits<root_type>::pointer_type>" << endl + << "pointer_cache_traits;" + << endl + << "typedef" << endl + << p << "reference_cache_traits<root_type>" << endl + << "reference_cache_traits;" + << endl; + } + else + { + os << "typedef" << endl + << p << "pointer_cache_traits<pointer_type>" << endl + << "pointer_cache_traits;" + << endl + << "typedef" << endl + << p << "reference_cache_traits<object_type>" << endl + << "reference_cache_traits;" + << endl; + } + } + + // callback () + // + os << "static void" << endl + << "callback (database&, object_type&, callback_event);" + << endl; + + os << "static void" << endl + << "callback (database&, const object_type&, callback_event);" + << endl; + } + + os << "};"; +} + +void header::class_:: +traverse_view (type& c) +{ + string const& type (class_fq_name (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + // class_traits + // + os << "template <>" << endl + << "struct class_traits< " << type << " >" + << "{" + << "static const class_kind kind = class_view;" + << "};"; + + // view_traits + // + os << "template <>" << endl + << "class access::view_traits< " << type << " >" + << "{" + << "public:" << endl; + + // view_type & pointer_type + // + os << "typedef " << type << " view_type;" + << "typedef " << c.get<string> ("object-pointer") << " pointer_type;" + << endl; + + // callback () + // + os << "static void" << endl + << "callback (database&, view_type&, callback_event);" + << endl; + + os << "};"; +} + +namespace header +{ void generate () { @@ -60,6 +351,9 @@ namespace header if (ctx.options.generate_query ()) { + if (ctx.options.generate_prepared ()) + os << "#include <odb/prepared-query.hxx>" << endl; + os << "#include <odb/result.hxx>" << endl; if (ctx.features.simple_object) @@ -75,6 +369,34 @@ namespace header os << "#include <odb/view-result.hxx>" << endl; } - os << endl; + os << endl + << "#include <odb/details/unused.hxx>" << endl + << endl; + + // Generate common code. + // + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (false); + traversal::namespace_ ns; + class_ c; + + unit >> unit_defines >> ns; + unit_defines >> c; + unit >> unit_typedefs >> c; + + traversal::defines ns_defines; + typedefs ns_typedefs (false); + + ns >> ns_defines >> ns; + ns_defines >> c; + ns >> ns_typedefs >> c; + + os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + os << "}"; } } diff --git a/odb/include.cxx b/odb/include.cxx index 3d17b9a..a921451 100644 --- a/odb/include.cxx +++ b/odb/include.cxx @@ -169,6 +169,7 @@ namespace { string f (file); size_t n (f.size ()); + database db (options_.database ()[0]); // Check if we have a synthesized prologue/epilogue fragment. // @@ -201,18 +202,18 @@ namespace size_t size (options_.odb_prologue ().size ()); if (n < size) - ss << options_.odb_prologue ()[n]; + ss << options_.odb_prologue ()[db][n]; else - f = options_.odb_prologue_file ()[n - size]; + f = options_.odb_prologue_file ()[db][n - size]; } else { size_t size (options_.odb_epilogue ().size ()); if (n < size) - ss << options_.odb_epilogue ()[n]; + ss << options_.odb_epilogue ()[db][n]; else - f = options_.odb_epilogue_file ()[n - size]; + f = options_.odb_epilogue_file ()[db][n - size]; } if (f.empty ()) @@ -688,7 +689,7 @@ namespace include } path f (inc->path_.base ()); - f += ctx.options.odb_file_suffix (); + f += ctx.options.odb_file_suffix ()[ctx.options.database ()[0]]; f += ctx.options.hxx_suffix (); char o (inc->type_ == include_directive::quote ? '"' : '<'); diff --git a/odb/inline.cxx b/odb/inline.cxx index e39f85d..39bde4d 100644 --- a/odb/inline.cxx +++ b/odb/inline.cxx @@ -5,13 +5,233 @@ #include <odb/common.hxx> #include <odb/context.hxx> #include <odb/generate.hxx> +#include <odb/diagnostics.hxx> using namespace std; namespace inline_ { + // + // + struct callback_calls: traversal::class_, virtual context + { + callback_calls () + { + *this >> inherits_ >> *this; + } + + void + traverse (type& c, bool constant) + { + const_ = constant; + traverse (c); + } + + virtual void + traverse (type& c) + { + bool obj (object (c)); + + // Ignore transient bases. + // + if (!(obj || view (c))) + return; + + if (c.count ("callback")) + { + string name (c.get<string> ("callback")); + + // In case of the const instance, we only generate the call if + // there is a const callback. Note also that we cannot use + // object_type/view_type alias because it can be a base type. + // + string const& type (class_fq_name (c)); + + if (const_) + { + if (c.count ("callback-const")) + os << "static_cast<const " << type << "&> (x)." << name << + " (e, db);"; + } + else + os << "static_cast< " << type << "&> (x)." << name << " (e, db);"; + } + else if (obj) + inherits (c); + } + + protected: + bool const_; + traversal::inherits inherits_; + }; + + struct class_: traversal::class_, virtual context + { + virtual void + traverse (type& c) + { + if (!options.at_once () && class_file (c) != unit.file ()) + return; + + if (object (c)) + traverse_object (c); + else if (view (c)) + traverse_view (c); + } + + void + traverse_object (type&); + + void + traverse_view (type&); + + private: + callback_calls callback_calls_; + }; +} + +void inline_::class_:: +traverse_object (type& c) +{ + using semantics::data_member; + + data_member* id (id_member (c)); + bool base_id (id && &id->scope () != &c); // Comes from base. + + // Base class that contains the object id. + // + type* base (id != 0 && base_id ? dynamic_cast<type*> (&id->scope ()) : 0); + + bool poly (polymorphic (c)); + bool abst (abstract (c)); + bool reuse_abst (abst && !poly); + + string const& type (class_fq_name (c)); + string traits ("access::object_traits< " + type + " >"); + + os << "// " << class_name (c) << endl + << "//" << endl + << endl; + + // id (object_type) + // + if (id != 0 || !reuse_abst) + { + os << "inline" << endl + << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const object_type&" << (id != 0 ? " o" : "") << ")" + << "{"; + + if (id != 0) + { + if (base_id) + os << "return object_traits< " << class_fq_name (*base) << + " >::id (o);"; + else + { + // Get the id using the accessor expression. If this is not + // a synthesized expression, then output its location for + // easier error tracking. + // + member_access& ma (id->get<member_access> ("get")); + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + os << "return " << ma.translate ("o") << ";"; + } + } + + os << "}"; + } + + // The rest does not apply to reuse-abstract objects. + // + if (reuse_abst) + return; + + // callback () + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "callback (database& db, object_type& x, callback_event e)" + << endl + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << "ODB_POTENTIALLY_UNUSED (x);" + << "ODB_POTENTIALLY_UNUSED (e);" + << endl; + callback_calls_.traverse (c, false); + os << "}"; + + os << "inline" << endl + << "void " << traits << "::" << endl + << "callback (database& db, const object_type& x, callback_event e)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << "ODB_POTENTIALLY_UNUSED (x);" + << "ODB_POTENTIALLY_UNUSED (e);" + << endl; + callback_calls_.traverse (c, true); + os << "}"; +} + +void inline_::class_:: +traverse_view (type& c) +{ + string const& type (class_fq_name (c)); + string traits ("access::view_traits< " + type + " >"); + + os << "// " << class_name (c) << endl + << "//" << endl + << endl; + + // callback () + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "callback (database& db, view_type& x, callback_event e)" + << endl + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << "ODB_POTENTIALLY_UNUSED (x);" + << "ODB_POTENTIALLY_UNUSED (e);" + << endl; + callback_calls_.traverse (c, false); + os << "}"; +} + +namespace inline_ +{ void generate () { + context ctx; + ostream& os (ctx.os); + + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (false); + traversal::namespace_ ns; + class_ c; + + unit >> unit_defines >> ns; + unit_defines >> c; + unit >> unit_typedefs >> c; + + traversal::defines ns_defines; + typedefs ns_typedefs (false); + + ns >> ns_defines >> ns; + ns_defines >> c; + ns >> ns_typedefs >> c; + + os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + os << "}"; } } diff --git a/odb/makefile b/odb/makefile index c57ed9f..bb0deba 100644 --- a/odb/makefile +++ b/odb/makefile @@ -200,7 +200,8 @@ $(gen): cli_options += \ --generate-file-scanner \ --include-with-brackets \ --include-prefix odb \ ---guard-prefix ODB +--guard-prefix ODB \ +--cxx-prologue '\#include <odb/option-parsers.hxx>' $(call include-dep,$(cxx_pod) $(cxx_dod) $(cxx_cod),\ $(cxx_pobj) $(cxx_dobj) $(cxx_cobj),$(gen)) diff --git a/odb/odb.cxx b/odb/odb.cxx index e18feeb..260f9b8 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -123,6 +123,7 @@ profile_paths (strings const& args, char const* name); static char const* const db_macro[] = { + "-DODB_DATABASE_COMMON", "-DODB_DATABASE_MSSQL", "-DODB_DATABASE_MYSQL", "-DODB_DATABASE_ORACLE", @@ -491,7 +492,9 @@ main (int argc, char* argv[]) oi[1].option = "-p"; oi[2].option = "--profile"; - database db; + vector<database> dbs; + bool show_sloc; + size_t sloc_limit; { oi[1].search_func = &profile_search_ignore; oi[2].search_func = &profile_search_ignore; @@ -527,14 +530,24 @@ main (int argc, char* argv[]) // Check that required options were specifed. // - if (!ops.database_specified ()) + dbs = ops.database (); + + if (dbs.empty ()) { e << argv[0] << ": error: no database specified with the --database " << "option" << endl; return 1; } - db = ops.database (); + if (dbs.size () > 1 && !ops.multi_database_specified ()) + { + e << argv[0] << ": error: --multi-database option required when " << + "multiple databases are specified"<< endl; + return 1; + } + + show_sloc = ops.show_sloc (); + sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0; // Translate some ODB options to GCC options. // @@ -566,26 +579,15 @@ main (int argc, char* argv[]) e << " " << *i << endl; } - // Second parse. + // Pass profile search paths (svc-path option). // - profile_data pd (prof_paths, db, argv[0]); - oi[1].search_func = &profile_search; - oi[2].search_func = &profile_search; - oi[1].arg = &pd; - oi[2].arg = &pd; - - cli::argv_file_scanner scan (ac, &av[0], oi, 3); - options ops (scan); - - size_t end (scan.end () - 1); // We have one less in plugin_args. - - if (end == plugin_args.size ()) + for (paths::const_iterator i (prof_paths.begin ()); + i != prof_paths.end (); ++i) { - e << argv[0] << ": error: input file expected" << endl; - return 1; + args.push_back (encode_plugin_option ("svc-path", i->string ())); } - // Add ODB macros. + // Add common ODB macros. // args.push_back ("-DODB_COMPILER"); @@ -595,396 +597,442 @@ main (int argc, char* argv[]) args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ()); } - args.push_back (db_macro[ops.database ()]); - - // Encode plugin options. + // Compile for each database. // - cli::options const& desc (options::description ()); - for (size_t i (0); i < end; ++i) + size_t sloc_total (0); + + for (vector<database>::iterator i (dbs.begin ()); i != dbs.end (); ++i) { - string k, v; - string a (plugin_args[i]); + database db (*i); + strings db_args (args); - // Ignore certain options. + // Add database-specific ODB macro. // - if (a == "--") - { - // Ignore the option seperator since GCC doesn't understand it. - // - continue; - } - else if (a == "--std") - { - // Translated to GCC -std=. - // - ++i; - continue; - } + db_args.push_back (db_macro[db]); - cli::options::const_iterator it (desc.find (a)); + // Second parse. + // + profile_data pd (prof_paths, db, argv[0]); + oi[1].search_func = &profile_search; + oi[2].search_func = &profile_search; + oi[1].arg = &pd; + oi[2].arg = &pd; - if (it == desc.end ()) + cli::argv_file_scanner scan (ac, &av[0], oi, 3); + options ops (scan); + + size_t end (scan.end () - 1); // We have one less in plugin_args. + + if (end == plugin_args.size ()) { - e << argv[0] << ": ice: unexpected option '" << a << "'" << endl; + e << argv[0] << ": error: input file expected" << endl; return 1; } - if (a.size () > 2 && a[0] == '-' && a[1] == '-') - k = string (a, 2); // long format - else - k = string (a, 1); // short format - - // If there are more arguments then we may have a value. + // Encode plugin options. // - if (!it->flag ()) + cli::options const& desc (options::description ()); + for (size_t i (0); i < end; ++i) { - if (i + 1 == end) + string k, v; + string a (plugin_args[i]); + + // Ignore certain options. + // + if (a == "--") { - e << argv[0] << ": ice: expected argument for '" << a << "'" << endl; - return 1; + // Ignore the option seperator since GCC doesn't understand it. + // + continue; + } + else if (a == "--std") + { + // Translated to GCC -std=. + // + ++i; + continue; + } + else if (a == "-d" || a == "--databse") + { + // Ignore all other databases. + // + if (plugin_args[i + 1] != db.string ()) + { + ++i; + continue; + } } - v = plugin_args[++i]; - } - - args.push_back (encode_plugin_option (k, v)); - } - - // Pass profile search paths (svc-path option). - // - for (paths::const_iterator i (prof_paths.begin ()); - i != prof_paths.end (); ++i) - { - args.push_back (encode_plugin_option ("svc-path", i->string ())); - } - - // Reserve space for and remember the position of the svc-file - // option. - // - size_t svc_file_pos (args.size ()); - args.push_back (""); - - // If compiling multiple input files at once, pass them also with - // the --svc-file option. - // - bool at_once (ops.at_once () && plugin_args.size () - end > 1); - if (at_once) - { - if (ops.output_name ().empty ()) - { - e << "error: --output-name required when compiling multiple " << - "input files at once (--at-once)" << endl; - return 1; - } - - for (size_t i (end); i < plugin_args.size (); ++i) - args.push_back (encode_plugin_option ("svc-file", plugin_args[i])); - } + cli::options::const_iterator it (desc.find (a)); - // Create an execvp-compatible argument array. - // - typedef vector<char const*> cstrings; - cstrings exec_args; + if (it == desc.end ()) + { + e << argv[0] << ": ice: unexpected option '" << a << "'" << endl; + return 1; + } - for (strings::const_iterator i (args.begin ()), end (args.end ()); - i != end; ++i) - { - exec_args.push_back (i->c_str ()); - } + if (a.size () > 2 && a[0] == '-' && a[1] == '-') + k = string (a, 2); // long format + else + k = string (a, 1); // short format - exec_args.push_back ("-"); // Compile stdin. - exec_args.push_back (0); + // If there are more arguments then we may have a value. + // + if (!it->flag ()) + { + if (i + 1 == end) + { + e << argv[0] << ": ice: expected argument for '" << a << "'" + << endl; + return 1; + } - // Iterate over the input files and compile each of them. - // - size_t sloc_total (0); + v = plugin_args[++i]; + } - for (; end < plugin_args.size (); ++end) - { - string name (at_once ? ops.output_name () : plugin_args[end]); + db_args.push_back (encode_plugin_option (k, v)); + } - // Set the --svc-file option. + // Reserve space for and remember the position of the svc-file + // option. // - args[svc_file_pos] = encode_plugin_option ("svc-file", name); - exec_args[svc_file_pos] = args[svc_file_pos].c_str (); + size_t svc_file_pos (db_args.size ()); + db_args.push_back (""); + // If compiling multiple input files at once, pass them also with + // the --svc-file option. // - // - ifstream ifs; - - if (!at_once) + bool at_once (ops.at_once () && plugin_args.size () - end > 1); + if (at_once) { - ifs.open (name.c_str (), ios_base::in | ios_base::binary); - - if (!ifs.is_open ()) + if (ops.output_name ().empty ()) { - e << name << ": error: unable to open in read mode" << endl; + e << "error: --output-name required when compiling multiple " << + "input files at once (--at-once)" << endl; return 1; } - } - if (v) - { - e << "Compiling " << name << endl; - for (cstrings::const_iterator i (exec_args.begin ()); - i != exec_args.end (); ++i) - { - if (*i != 0) - e << *i << (*(i + 1) != 0 ? ' ' : '\n'); - } + for (size_t i (end); i < plugin_args.size (); ++i) + db_args.push_back ( + encode_plugin_option ("svc-file", plugin_args[i])); } - process_info pi (start_process (&exec_args[0], argv[0], false, true)); + // Create an execvp-compatible argument array. + // + typedef vector<char const*> cstrings; + cstrings exec_args; + for (strings::const_iterator i (db_args.begin ()), end (db_args.end ()); + i != end; ++i) { - __gnu_cxx::stdio_filebuf<char> fb ( - pi.out_fd, ios_base::out | ios_base::binary); - ostream os (&fb); + exec_args.push_back (i->c_str ()); + } - if (!ops.trace ()) - { - // Add the standard prologue. - // - os << "#line 1 \"<standard-odb-prologue>\"" << endl; + exec_args.push_back ("-"); // Compile stdin. + exec_args.push_back (0); - // Make sure ODB compiler and libodb versions are compatible. - // - os << "#include <odb/version.hxx>" << endl - << endl - << "#if ODB_VERSION != " << ODB_VERSION << endl - << "# error incompatible ODB compiler and runtime " << - "versions" << endl - << "#endif" << endl - << endl; - - // Add ODB compiler metaprogramming tests. - // - os << "namespace odb" << endl - << "{" << endl - << "namespace compiler" << endl - << "{" << endl; + // Iterate over the input files and compile each of them. + // + for (; end < plugin_args.size (); ++end) + { + string name (at_once ? ops.output_name () : plugin_args[end]); - // operator< test, used in validator. - // - os << "template <typename T>" << endl - << "bool" << endl - << "has_lt_operator (const T& x, const T& y)" << endl - << "{" << endl - << "bool r (x < y);" << endl - << "return r;" << endl - << "}" << endl; - - os << "}" << endl - << "}" << endl; - } + // Set the --svc-file option. + // + db_args[svc_file_pos] = encode_plugin_option ("svc-file", name); + exec_args[svc_file_pos] = db_args[svc_file_pos].c_str (); - // Add custom prologue if any. // - // NOTE: if you change the format, you also need to update code - // in include.cxx // - strings const& pro (ops.odb_prologue ()); - for (size_t i (0); i < pro.size (); ++i) - { - os << "#line 1 \"<odb-prologue-" << i + 1 << ">\"" << endl - << pro[i] - << endl; - } + ifstream ifs; - strings const& prof (ops.odb_prologue_file ()); - for (size_t i (0); i < prof.size (); ++i) + if (!at_once) { - os << "#line 1 \"<odb-prologue-" << pro.size () + i + 1 << ">\"" - << endl; - - ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary); + ifs.open (name.c_str (), ios_base::in | ios_base::binary); if (!ifs.is_open ()) { - e << prof[i] << ": error: unable to open in read mode" << endl; - fb.close (); - wait_process (pi, argv[0]); + e << name << ": error: unable to open in read mode" << endl; return 1; } + } - if (!(os << ifs.rdbuf ())) + if (v) + { + e << "Compiling " << name << endl; + for (cstrings::const_iterator i (exec_args.begin ()); + i != exec_args.end (); ++i) { - e << prof[i] << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + if (*i != 0) + e << *i << (*(i + 1) != 0 ? ' ' : '\n'); } - - os << endl; } - if (at_once) - { - // Include all the input files (no need to escape). - // - os << "#line 1 \"<command-line>\"" << endl; - - bool b (ops.include_with_brackets ()); - char op (b ? '<' : '"'), cl (b ? '>' : '"'); + process_info pi (start_process (&exec_args[0], argv[0], false, true)); - for (; end < plugin_args.size (); ++end) - os << "#include " << op << plugin_args[end] << cl << endl; - } - else { - // Write the synthesized translation unit to stdout. - // - os << "#line 1 \"" << escape_path (name) << "\"" << endl; + __gnu_cxx::stdio_filebuf<char> fb ( + pi.out_fd, ios_base::out | ios_base::binary); + ostream os (&fb); - if (!(os << ifs.rdbuf ())) + if (!ops.trace ()) { - e << name << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + // Add the standard prologue. + // + os << "#line 1 \"<standard-odb-prologue>\"" << endl; + + // Make sure ODB compiler and libodb versions are compatible. + // + os << "#include <odb/version.hxx>" << endl + << endl + << "#if ODB_VERSION != " << ODB_VERSION << endl + << "# error incompatible ODB compiler and runtime " << + "versions" << endl + << "#endif" << endl + << endl; + + // Add ODB compiler metaprogramming tests. + // + os << "namespace odb" << endl + << "{" << endl + << "namespace compiler" << endl + << "{" << endl; + + // operator< test, used in validator. + // + os << "template <typename T>" << endl + << "bool" << endl + << "has_lt_operator (const T& x, const T& y)" << endl + << "{" << endl + << "bool r (x < y);" << endl + << "return r;" << endl + << "}" << endl; + + os << "}" << endl + << "}" << endl; } - // Add a new line in case the input file doesn't end with one. + // Add custom prologue if any. // - os << endl; - } + // NOTE: if you change the format, you also need to update code + // in include.cxx + // + size_t pro_count (1); + if (ops.odb_prologue ().count (db) != 0) + { + strings const& pro (ops.odb_prologue ()[db]); + for (size_t i (0); i < pro.size (); ++i, ++pro_count) + { + os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" << endl + << pro[i] << endl; + } + } - // Add custom epilogue if any. - // - // NOTE: if you change the format, you also need to update code - // in include.cxx - // - strings const& epi (ops.odb_epilogue ()); - for (size_t i (0); i < epi.size (); ++i) - { - os << "#line 1 \"<odb-epilogue-" << i + 1 << ">\"" << endl - << epi[i] - << endl; - } + if (ops.odb_prologue_file ().count (db) != 0) + { + strings const& prof (ops.odb_prologue_file ()[db]); + for (size_t i (0); i < prof.size (); ++i, ++pro_count) + { + os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" + << endl; - strings const& epif (ops.odb_epilogue_file ()); - for (size_t i (0); i < epif.size (); ++i) - { - os << "#line 1 \"<odb-epilogue-" << epi.size () + i + 1 << ">\"" - << endl; + ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary); - ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary); + if (!ifs.is_open ()) + { + e << prof[i] << ": error: unable to open in read mode" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } - if (!ifs.is_open ()) - { - e << epif[i] << ": error: unable to open in read mode" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + if (!(os << ifs.rdbuf ())) + { + e << prof[i] << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } + + os << endl; + } } - if (!(os << ifs.rdbuf ())) + if (at_once) { - e << epif[i] << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } + // Include all the input files (no need to escape). + // + os << "#line 1 \"<command-line>\"" << endl; - os << endl; - } + bool b (ops.include_with_brackets ()); + char op (b ? '<' : '"'), cl (b ? '>' : '"'); - if (!ops.trace ()) - { - // Add the standard epilogue at the end so that we see all - // the declarations. - // - os << "#line 1 \"<standard-odb-epilogue>\"" << endl; + for (; end < plugin_args.size (); ++end) + os << "#include " << op << plugin_args[end] << cl << endl; + } + else + { + // Write the synthesized translation unit to stdout. + // + os << "#line 1 \"" << escape_path (name) << "\"" << endl; - // Includes for standard smart pointers. The Boost TR1 header - // may or may not delegate to the GCC implementation. In either - // case, the necessary declarations will be provided so we don't - // need to do anything. - // - os << "#include <memory>" << endl - << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl - << "# include <tr1/memory>" << endl - << "#endif" << endl; + if (!(os << ifs.rdbuf ())) + { + e << name << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } - // Standard wrapper traits. - // - os << "#include <odb/wrapper-traits.hxx>" << endl - << "#include <odb/tr1/wrapper-traits.hxx>" << endl; + // Add a new line in case the input file doesn't end with one. + // + os << endl; + } - // Standard pointer traits. + // Add custom epilogue if any. // - os << "#include <odb/pointer-traits.hxx>" << endl - << "#include <odb/tr1/pointer-traits.hxx>" << endl; - - // Standard container traits. + // NOTE: if you change the format, you also need to update code + // in include.cxx // - os << "#include <odb/container-traits.hxx>" << endl; - } - } - - // Filter the output stream looking for communication from the - // plugin. - // - { - __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in); - istream is (&fb); - - for (bool first (true); !is.eof (); ) - { - string line; - getline (is, line); - - if (is.fail () && !is.eof ()) + size_t epi_count (1); + if (ops.odb_epilogue ().count (db) != 0) { - e << argv[0] << ": error: io failure while parsing output" << endl; - wait_process (pi, argv[0]); - return 1; + strings const& epi (ops.odb_epilogue ()[db]); + for (size_t i (0); i < epi.size (); ++i, ++epi_count) + { + os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" << endl + << epi[i] << endl; + } } - if (line.compare (0, 9, "odb:sloc:") == 0) + if (ops.odb_epilogue_file ().count (db) != 0) { - if (ops.show_sloc () || ops.sloc_limit_specified ()) + strings const& epif (ops.odb_epilogue_file ()[db]); + for (size_t i (0); i < epif.size (); ++i, ++epi_count) { - size_t n; - istringstream is (string (line, 9, string::npos)); + os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" + << endl; - if (!(is >> n && is.eof ())) + ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary); + + if (!ifs.is_open ()) { - e << argv[0] << ": error: invalid odb:sloc value" << endl; + e << epif[i] << ": error: unable to open in read mode" << endl; + fb.close (); wait_process (pi, argv[0]); return 1; } - sloc_total += n; + if (!(os << ifs.rdbuf ())) + { + e << epif[i] << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } + + os << endl; } + } - continue; + if (!ops.trace ()) + { + // Add the standard epilogue at the end so that we see all + // the declarations. + // + os << "#line 1 \"<standard-odb-epilogue>\"" << endl; + + // Includes for standard smart pointers. The Boost TR1 header + // may or may not delegate to the GCC implementation. In either + // case, the necessary declarations will be provided so we don't + // need to do anything. + // + os << "#include <memory>" << endl + << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl + << "# include <tr1/memory>" << endl + << "#endif" << endl; + + // Standard wrapper traits. + // + os << "#include <odb/wrapper-traits.hxx>" << endl + << "#include <odb/tr1/wrapper-traits.hxx>" << endl; + + // Standard pointer traits. + // + os << "#include <odb/pointer-traits.hxx>" << endl + << "#include <odb/tr1/pointer-traits.hxx>" << endl; + + // Standard container traits. + // + os << "#include <odb/container-traits.hxx>" << endl; } + } - if (first) - first = false; - else - cout << endl; + // Filter the output stream looking for communication from the + // plugin. + // + { + __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in); + istream is (&fb); + + for (bool first (true); !is.eof (); ) + { + string line; + getline (is, line); - cout << line; + if (is.fail () && !is.eof ()) + { + e << argv[0] << ": error: io failure while parsing output" + << endl; + wait_process (pi, argv[0]); + return 1; + } + + if (line.compare (0, 9, "odb:sloc:") == 0) + { + if (show_sloc || sloc_limit != 0) + { + size_t n; + istringstream is (string (line, 9, string::npos)); + + if (!(is >> n && is.eof ())) + { + e << argv[0] << ": error: invalid odb:sloc value" << endl; + wait_process (pi, argv[0]); + return 1; + } + + sloc_total += n; + } + + continue; + } + + if (first) + first = false; + else + cout << endl; + + cout << line; + } } - } - if (!wait_process (pi, argv[0])) - return 1; - } + if (!wait_process (pi, argv[0])) + return 1; + } // End input file loop. + } // End database loop. // Handle SLOC. // - if (ops.show_sloc ()) + if (show_sloc) e << "total: " << sloc_total << endl; - if (ops.sloc_limit_specified () && ops.sloc_limit () < sloc_total) + if (sloc_limit != 0 && sloc_limit < sloc_total) { - e << argv[0] << ": error: SLOC limit of " << ops.sloc_limit () - << " lines has been exceeded" << endl; + e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " << + "has been exceeded" << endl; - if (!ops.show_sloc ()) + if (!show_sloc) e << argv[0] << ": info: use the --show-sloc option to see the " << "current total" << endl; diff --git a/odb/option-functions.cxx b/odb/option-functions.cxx index 6b7442e..b0b2523 100644 --- a/odb/option-functions.cxx +++ b/odb/option-functions.cxx @@ -3,6 +3,7 @@ // license : GNU GPL v3; see accompanying LICENSE file #include <set> +#include <utility> // std::make_pair() #include <odb/option-functions.hxx> @@ -11,6 +12,8 @@ using namespace std; void process_options (options& o) { + database db (o.database ()[0]); + // If --generate-schema-only was specified, then set --generate-schema // as well. // @@ -19,12 +22,16 @@ process_options (options& o) // Set the default schema format depending on the database. // - if (o.generate_schema () && o.schema_format ().empty ()) + if (o.generate_schema () && o.schema_format ()[db].empty ()) { - set<schema_format> f; + set<schema_format>& f (o.schema_format ()[db]); - switch (o.database ()) + switch (db) { + case database::common: + { + break; // No schema for common. + } case database::mssql: case database::mysql: case database::oracle: @@ -39,7 +46,62 @@ process_options (options& o) break; } } + } - o.schema_format (f); + // Set default --schema-name value. + // + if (o.schema_name ().count (db) == 0) + o.schema_name ()[db] = ""; + + // Set default --*--file-suffix values. + // + { + database cm (database::common); + + o.odb_file_suffix ().insert (make_pair (cm, string ("-odb"))); + o.sql_file_suffix ().insert (make_pair (cm, string (""))); + o.schema_file_suffix ().insert (make_pair (cm, string ("-schema"))); + } + + if (o.multi_database () == multi_database::disabled) + { + o.odb_file_suffix ().insert (make_pair (db, string ("-odb"))); + o.sql_file_suffix ().insert (make_pair (db, string (""))); + o.schema_file_suffix ().insert (make_pair (db, string ("-schema"))); + } + else + { + o.odb_file_suffix ().insert ( + make_pair (db, string ("-odb-") + db.string ())); + o.sql_file_suffix ().insert ( + make_pair (db, string ("-") + db.string ())); + o.schema_file_suffix ().insert ( + make_pair (db, string ("-schema-") + db.string ())); + } + + // Set default --default-database value. + // + if (!o.default_database_specified ()) + { + switch (o.multi_database ()) + { + case multi_database::disabled: + { + o.default_database (db); + o.default_database_specified (true); + break; + } + case multi_database::dynamic: + { + o.default_database (database::common); + o.default_database_specified (true); + break; + } + case multi_database::static_: + { + // No default database unless explicitly specified. + break; + } + } } } diff --git a/odb/option-parsers.hxx b/odb/option-parsers.hxx new file mode 100644 index 0000000..a974a7c --- /dev/null +++ b/odb/option-parsers.hxx @@ -0,0 +1,200 @@ +// file : odb/option-parsers.hxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_OPTION_PARSERS_HXX +#define ODB_OPTION_PARSERS_HXX + +#include <vector> +#include <sstream> + +#include <odb/option-types.hxx> +#include <odb/options.hxx> + +namespace cli +{ + // Return true if there is a database prefix. + // + template <typename V> + bool + parse_option_value (std::string const& o, std::string const& ov, + database& k, V& v) + { + bool r (false); + std::string::size_type p = ov.find (':'); + + std::string vstr; + if (p != std::string::npos) + { + std::string kstr (ov, 0, p); + + // See if this prefix resolves to the database name. If not, + // assume there is no prefix. + // + std::istringstream ks (kstr); + + if (ks >> k && ks.eof ()) + { + r = true; + vstr.assign (ov, p + 1, std::string::npos); + } + } + + if (!r) + vstr = ov; // Use the whole value. + + if (!vstr.empty ()) + { + std::istringstream vs (vstr); + + if (!(vs >> v && vs.eof ())) + throw invalid_value (o, ov); + } + else + v = V (); + + return r; + } + + // Specialization for std::string. + // + bool + parse_option_value (std::string const&, std::string const& ov, + database& k, std::string& v) + { + bool r (false); + std::string::size_type p = ov.find (':'); + + if (p != std::string::npos) + { + std::string kstr (ov, 0, p); + + // See if this prefix resolves to the database name. If not, + // assume there is no prefix. + // + std::istringstream ks (kstr); + + if (ks >> k && ks.eof ()) + { + r = true; + v.assign (ov, p + 1, std::string::npos); + } + } + + if (!r) + v = ov; // Use the whole value. + + return r; + } + + template <typename V> + struct parser<database_map<V> > + { + typedef database_map<V> map; + + static void + parse (map& m, bool& xs, scanner& s) + { + xs = true; + std::string o (s.next ()); + + if (s.more ()) + { + database k; + V v; + + if (parse_option_value (o, s.next (), k, v)) + m[k] = v; // Override any old value. + else + { + // No database prefix is specified which means it applies to + // all the databases. We also don't want to override database- + // specific values, so use insert(). + // + m.insert (typename map::value_type (database::common, v)); + m.insert (typename map::value_type (database::mssql, v)); + m.insert (typename map::value_type (database::mysql, v)); + m.insert (typename map::value_type (database::oracle, v)); + m.insert (typename map::value_type (database::pgsql, v)); + m.insert (typename map::value_type (database::sqlite, v)); + } + } + else + throw missing_value (o); + } + }; + + template <typename V> + struct parser<database_map<std::vector<V> > > + { + typedef database_map<std::vector<V> > map; + + static void + parse (map& m, bool& xs, scanner& s) + { + xs = true; + std::string o (s.next ()); + + if (s.more ()) + { + database k; + V v; + + if (parse_option_value (o, s.next (), k, v)) + m[k].push_back (v); + else + { + // No database prefix is specified which means it applies to + // all the databases. + // + m[database::common].push_back (v); + m[database::mssql].push_back (v); + m[database::mysql].push_back (v); + m[database::oracle].push_back (v); + m[database::pgsql].push_back (v); + m[database::sqlite].push_back (v); + } + } + else + throw missing_value (o); + } + }; + + template <typename V> + struct parser<database_map<std::set<V> > > + { + typedef database_map<std::set<V> > map; + + static void + parse (map& m, bool& xs, scanner& s) + { + xs = true; + std::string o (s.next ()); + + if (s.more ()) + { + database k; + V v; + + if (parse_option_value (o, s.next (), k, v)) + m[k].insert (v); + else + { + // No database prefix is specified which means it applies to + // all the databases. + // + m[database::common].insert (v); + m[database::mssql].insert (v); + m[database::mysql].insert (v); + m[database::oracle].insert (v); + m[database::pgsql].insert (v); + m[database::sqlite].insert (v); + } + } + else + throw missing_value (o); + } + }; +} + +#endif // ODB_OPTION_PARSERS_HXX diff --git a/odb/option-types.cxx b/odb/option-types.cxx index ceb71db..c52935e 100644 --- a/odb/option-types.cxx +++ b/odb/option-types.cxx @@ -52,6 +52,7 @@ operator>> (istream& is, cxx_version& v) static const char* database_[] = { + "common", "mssql", "mysql", "oracle", @@ -92,6 +93,50 @@ operator<< (ostream& os, database db) } // +// multi_database +// + +static const char* multi_database_[] = +{ + "dynamic", + "static", + "disabled" +}; + +const char* multi_database:: +string () const +{ + return multi_database_[v_]; +} + +istream& +operator>> (istream& is, multi_database& db) +{ + string s; + is >> s; + + if (!is.fail ()) + { + const char** e ( + multi_database_ + sizeof (multi_database_) / sizeof (char*) - 1); + const char** i (lower_bound (multi_database_, e, s)); + + if (i != e && *i == s) + db = multi_database::value (i - multi_database_); + else + is.setstate (istream::failbit); + } + + return is; +} + +ostream& +operator<< (ostream& os, multi_database db) +{ + return os << db.string (); +} + +// // schema_format // diff --git a/odb/option-types.hxx b/odb/option-types.hxx index 66e2f28..732cd5a 100644 --- a/odb/option-types.hxx +++ b/odb/option-types.hxx @@ -5,7 +5,9 @@ #ifndef ODB_OPTION_TYPES_HXX #define ODB_OPTION_TYPES_HXX +#include <map> #include <iosfwd> +#include <cassert> #include <odb/semantics/relational/name.hxx> @@ -40,6 +42,7 @@ struct database { // Keep in alphabetic order. // + common, mssql, mysql, oracle, @@ -65,6 +68,53 @@ operator<< (std::ostream&, database); // // +template <typename V> +struct database_map: std::map<database, V> +{ + typedef std::map<database, V> base_type; + + using base_type::operator[]; + + V const& + operator[] (database const& k) const + { + typename base_type::const_iterator i (this->find (k)); + assert (i != this->end ()); + return i->second; + } +}; + +// +// +struct multi_database +{ + enum value + { + // Keep in alphabetic order. + // + dynamic, + static_, + disabled // Special value. + }; + + multi_database (value v = disabled) : v_ (v) {} + operator value () const {return v_;} + + const char* + string () const; + +private: + value v_; +}; + +std::istream& +operator>> (std::istream&, multi_database&); + +std::ostream& +operator<< (std::ostream&, multi_database); + +// +// struct schema_format { enum value diff --git a/odb/options.cli b/odb/options.cli index f61aa0e..3c7efa8 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -45,11 +45,26 @@ class options // // Plugin options. // - ::database --database | -d + std::vector< ::database> --database | -d { "<db>", "Generate code for the <db> database. Valid values are \cb{mssql}, - \cb{mysql}, \cb{oracle}, \cb{pgsql}, and \cb{sqlite}." + \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}." + }; + + ::multi_database --multi-database | -m = ::multi_database::disabled + { + "<type>", + "Enable multi-database support and specify its type. Valid values + for this option are \cb{static} and \cb{dynamic}." + }; + + ::database --default-database + { + "<db>", + "When static multi-database support is used, specify the database that + should be made the default. When dynamic multi-database support is used, + \cb{common} is always made the default database." }; bool --generate-query | -q @@ -58,6 +73,16 @@ class options and can only load objects via their ids." }; + bool --generate-prepared + { + "Generate prepared query execution support code." + }; + + bool --omit-unprepared + { + "Omit un-prepared (once-off) query execution support code." + }; + bool --generate-session | -e { "Generate session support code. With this option session support will @@ -88,7 +113,7 @@ class options for details)." }; - std::set< ::schema_format> --schema-format + database_map<std::set< ::schema_format> > --schema-format { "<format>", "Generate the database schema in the specified format. Pass \cb{sql} as @@ -112,7 +137,7 @@ class options "Omit \cb{CREATE} statements from the generated database schema." }; - std::string --schema-name = "" + database_map<std::string> --schema-name { "<name>", "Use <name> as the database schema name. Schema names are primarily @@ -185,7 +210,7 @@ class options \cb{#include} directive resolution." }; - qname --schema + database_map<qname> --schema { "<schema>", "Specify a database schema (database namespace) that should be @@ -195,7 +220,7 @@ class options \cb{--schema-name} option." }; - std::string --table-prefix + database_map<std::string> --table-prefix { "<prefix>", "Add <prefix> to table and index names. The prefix is added to both @@ -229,14 +254,21 @@ class options generated files." }; - std::string --odb-file-suffix = "-odb" + database_map<std::string> --odb-file-suffix { "<suffix>", "Use <suffix> instead of the default \cb{-odb} to construct the names of the generated C++ files." }; - std::string --schema-file-suffix = "-schema" + database_map<std::string> --sql-file-suffix + { + "<suffix>", + "Use <suffix> to construct the name of the generated schema SQL file. + By default no suffix is used." + }; + + database_map<std::string> --schema-file-suffix { "<suffix>", "Use <suffix> instead of the default \cb{-schema} to construct the name @@ -274,31 +306,31 @@ class options // Prologues. // - std::vector<std::string> --hxx-prologue + database_map<std::vector<std::string> > --hxx-prologue { "<text>", "Insert <text> at the beginning of the generated C++ header file." }; - std::vector<std::string> --ixx-prologue + database_map<std::vector<std::string> > --ixx-prologue { "<text>", "Insert <text> at the beginning of the generated C++ inline file." }; - std::vector<std::string> --cxx-prologue + database_map<std::vector<std::string> > --cxx-prologue { "<text>", "Insert <text> at the beginning of the generated C++ source file." }; - std::vector<std::string> --schema-prologue + database_map<std::vector<std::string> > --schema-prologue { "<text>", "Insert <text> at the beginning of the generated schema C++ source file." }; - std::vector<std::string> --sql-prologue + database_map<std::vector<std::string> > --sql-prologue { "<text>", "Insert <text> at the beginning of the generated database schema file." @@ -306,7 +338,7 @@ class options // Interludes. // - std::vector<std::string> --sql-interlude + database_map<std::vector<std::string> > --sql-interlude { "<text>", "Insert <text> after all the \cb{DROP} and before any \cb{CREATE} @@ -315,31 +347,31 @@ class options // Epilogues. // - std::vector<std::string> --hxx-epilogue + database_map<std::vector<std::string> > --hxx-epilogue { "<text>", "Insert <text> at the end of the generated C++ header file." }; - std::vector<std::string> --ixx-epilogue + database_map<std::vector<std::string> > --ixx-epilogue { "<text>", "Insert <text> at the end of the generated C++ inline file." }; - std::vector<std::string> --cxx-epilogue + database_map<std::vector<std::string> > --cxx-epilogue { "<text>", "Insert <text> at the end of the generated C++ source file." }; - std::vector<std::string> --schema-epilogue + database_map<std::vector<std::string> > --schema-epilogue { "<text>", "Insert <text> at the end of the generated schema C++ source file." }; - std::vector<std::string> --sql-epilogue + database_map<std::vector<std::string> > --sql-epilogue { "<text>", "Insert <text> at the end of the generated database schema file." @@ -347,35 +379,35 @@ class options // Prologue files. // - std::string --hxx-prologue-file + database_map<std::string> --hxx-prologue-file { "<file>", "Insert the content of <file> at the beginning of the generated C++ header file." }; - std::string --ixx-prologue-file + database_map<std::string> --ixx-prologue-file { "<file>", "Insert the content of <file> at the beginning of the generated C++ inline file." }; - std::string --cxx-prologue-file + database_map<std::string> --cxx-prologue-file { "<file>", "Insert the content of <file> at the beginning of the generated C++ source file." }; - std::string --schema-prologue-file + database_map<std::string> --schema-prologue-file { "<file>", "Insert the content of <file> at the beginning of the generated schema C++ source file." }; - std::string --sql-prologue-file + database_map<std::string> --sql-prologue-file { "<file>", "Insert the content of <file> at the beginning of the generated @@ -384,7 +416,7 @@ class options // Interlude files. // - std::string --sql-interlude-file + database_map<std::string> --sql-interlude-file { "<file>", "Insert the content of <file> after all the \cb{DROP} and before any @@ -393,35 +425,35 @@ class options // Epilogue files. // - std::string --hxx-epilogue-file + database_map<std::string> --hxx-epilogue-file { "<file>", "Insert the content of <file> at the end of the generated C++ header file." }; - std::string --ixx-epilogue-file + database_map<std::string> --ixx-epilogue-file { "<file>", "Insert the content of <file> at the end of the generated C++ inline file." }; - std::string --cxx-epilogue-file + database_map<std::string> --cxx-epilogue-file { "<file>", "Insert the content of <file> at the end of the generated C++ source file." }; - std::string --schema-epilogue-file + database_map<std::string> --schema-epilogue-file { "<file>", "Insert the content of <file> at the end of the generated schema C++ source file." }; - std::string --sql-epilogue-file + database_map<std::string> --sql-epilogue-file { "<file>", "Insert the content of <file> at the end of the generated database @@ -430,7 +462,7 @@ class options // ODB compilation prologue/epilogue. // - std::vector<std::string> --odb-prologue + database_map<std::vector<std::string> > --odb-prologue { "<text>", "Compile <text> before the input header file. This option allows you @@ -438,7 +470,7 @@ class options to the ODB compilation process." }; - std::vector<std::string> --odb-prologue-file + database_map<std::vector<std::string> > --odb-prologue-file { "<file>", "Compile <file> contents before the input header file. Prologue files @@ -446,7 +478,7 @@ class options option)." }; - std::vector<std::string> --odb-epilogue + database_map<std::vector<std::string> > --odb-epilogue { "<text>", "Compile <text> after the input header file. This option allows you @@ -454,7 +486,7 @@ class options to the ODB compilation process." }; - std::vector<std::string> --odb-epilogue-file + database_map<std::vector<std::string> > --odb-epilogue-file { "<file>", "Compile <file> contents after the input header file. Epilogue files diff --git a/odb/plugin.cxx b/odb/plugin.cxx index 1d73b7f..e7eee3d 100644 --- a/odb/plugin.cxx +++ b/odb/plugin.cxx @@ -268,7 +268,7 @@ plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*) cli::argv_file_scanner scan (argc, &argv[0], oi, 3); options ops (scan); assert (ops.database_specified ()); - db = ops.database (); + db = ops.database ()[0]; } profile_data pd (profile_paths_, db, "odb plugin"); @@ -286,6 +286,7 @@ plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*) process_options (*ops); options_ = ops; + pragma_db_ = db; } if (options_->trace ()) diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 911afce..ef10a51 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -28,6 +28,7 @@ loc_pragmas loc_pragmas_; decl_pragmas decl_pragmas_; ns_loc_pragmas ns_loc_pragmas_; pragma_name_set simple_value_pragmas_; +database pragma_db_; template <typename X> void @@ -602,7 +603,8 @@ add_pragma (pragma const& prag, declaration const& decl, bool ns) static void handle_pragma (cxx_lexer& l, - string const& p, + string db, + string p, string const& qualifier, any& qualifier_value, declaration const& decl, @@ -613,6 +615,36 @@ handle_pragma (cxx_lexer& l, string tl; tree tn; + // See if there is a database prefix. The db argument may already + // contain it. + // + if (db.empty () && + (p == "mysql" || + p == "sqlite" || + p == "pgsql" || + p == "oracle" || + p == "mssql")) + { + tt = l.next (tl); + + if (tt != CPP_COLON) + { + error (l) << "':' expected after database prefix " << p << endl; + return; + } + + // Specifier prefix. + // + db = p; + tt = l.next (p); + + if (tt != CPP_NAME && tt != CPP_KEYWORD) + { + error (l) << "expected specifier after databas prefix " << db << endl; + return; + } + } + string name (p); // Pragma name. any val; // Pragma value. pragma::add_func adder (0); // Custom context adder. @@ -926,8 +958,10 @@ handle_pragma (cxx_lexer& l, // if (p == "access") { - add_pragma ( - pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns); + if (db.empty () || db == pragma_db_.string ()) + add_pragma ( + pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns); + name = "set"; } @@ -1014,10 +1048,15 @@ handle_pragma (cxx_lexer& l, // Add the "table" pragma. // if (vo.alias.empty () && vo.cond.empty ()) - add_pragma ( - pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0), - decl, - ns); + { + if (db.empty () || db == pragma_db_.string ()) + { + add_pragma ( + pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0), + decl, + ns); + } + } vo.scope = current_scope (); vo.loc = loc; @@ -2025,7 +2064,7 @@ handle_pragma (cxx_lexer& l, // Add the pragma unless was indicated otherwise. // - if (!name.empty ()) + if (!name.empty () && (db.empty () || db == pragma_db_.string ())) { // If the value is not specified and we don't use a custom adder, // then make it bool (flag). @@ -2051,6 +2090,7 @@ handle_pragma (cxx_lexer& l, if (tt == CPP_NAME || tt == CPP_KEYWORD) { handle_pragma (l, + "", tl, qualifier, qualifier_value, @@ -2163,7 +2203,7 @@ add_qual_entry (compiler::context& ctx, } static void -handle_pragma_qualifier (cxx_lexer& l, string const& p) +handle_pragma_qualifier (cxx_lexer& l, string p) { cpp_ttype tt; string tl; @@ -2173,9 +2213,62 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) tree orig_decl (0); // Original declarations as used in the pragma. string decl_name; + // Check for a database prefix. + // + string db; + + if (p == "mysql" || + p == "sqlite" || + p == "pgsql" || + p == "oracle" || + p == "mssql") + { + tt = l.next (tl); + + if (tt == CPP_COLON) + { + // Specifier prefix. + // + db = p; + tt = l.next (p); + } + else + { + // Qualifier prefix. Ignore the rest if the databases don't match. + // + if (p != pragma_db_.string ()) + return; + + p = tl; + } + + if (tt != CPP_NAME && tt != CPP_KEYWORD) + { + error (l) << "expected specifier after db " << db << " pragma" << endl; + return; + } + + // Make sure a qualifier prefix is not used before a specifier. + // + if (!db.empty () && + (p == "map" || + p == "namespace" || + p == "object" || + p == "view" || + p == "value" || + p == "member")) + { + error (l) << "specifier prefix '" << db << ":' used before qualifier " << + p << endl; + return; + } + } + + // + // string name (p); // Pragma name. any val; // Pragma value. - location_t loc (l. location ()); // Pragma location. + location_t loc (l.location ()); // Pragma location. pragma::add_func adder (0); // Custom context adder. bool ns (false); // Namespace location pragma. @@ -2275,6 +2368,16 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) // token type (tt) and token literal (tl) variables should contain // the correct values. // + + // Also check that no specifier prefix was used for this qualifer. + // + if (!db.empty ()) + { + error (loc) << "specifier prefix '" << db << ":' used before " << + "qualifier index" << endl; + return; + } + orig_decl = current_scope (); decl = declaration (orig_decl); } @@ -2285,7 +2388,8 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) // cxx_tokens_lexer l; l.start (saved_tokens, loc); - handle_pragma (l, "index", "member", val, declaration (), "", false); + handle_pragma ( + l, db, "index", "member", val, declaration (), "", false); return; } } @@ -2783,7 +2887,7 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) p == "version" || p == "virtual") { - handle_pragma (l, p, "member", val, declaration (), "", false); + handle_pragma (l, db, p, "member", val, declaration (), "", false); return; } else @@ -2792,11 +2896,12 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) return; } + // Record this pragma. Delay this until after we process the + // specifiers for value (see comment below for the reason). + // if (adder == 0) val = orig_decl; - // Record this pragma. - // pragma prag (p, name, // For now no need to translate '_' to '-'. val, @@ -2804,29 +2909,37 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) &check_qual_decl_type, adder != 0 ? adder : &add_qual_entry); - any* pval; - - if (decl) - pval = &decl_pragmas_[decl].insert (prag).value; - else + tree scope; + if (!decl) { - tree scope (current_scope ()); + scope = current_scope (); - if (!ns) - { - if (!CLASS_TYPE_P (scope)) - scope = global_namespace; + if (!ns && !CLASS_TYPE_P (scope)) + scope = global_namespace; + } - pragma_list& pl (loc_pragmas_[scope]); - pl.push_back (prag); - pval = &pl.back ().value; - } + any* pval; + if (p != "value") + { + if (decl) + pval = &decl_pragmas_[decl].insert (prag).value; else { - ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag)); - pval = &ns_loc_pragmas_.back ().pragma.value; + if (!ns) + { + pragma_list& pl (loc_pragmas_[scope]); + pl.push_back (prag); + pval = &pl.back ().value; + } + else + { + ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag)); + pval = &ns_loc_pragmas_.back ().pragma.value; + } } } + else + pval = &val; // See if there are any saved tokens to replay. // @@ -2840,7 +2953,7 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) if (tt == CPP_NAME || tt == CPP_KEYWORD) { - handle_pragma (l, tl, p, *pval, decl, decl_name, ns); + handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns); if (errorcount != 0) // Avoid parsing the rest if there was an error. return; @@ -2852,16 +2965,86 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) } } + size_t count (0); if (tt == CPP_NAME || tt == CPP_KEYWORD) { - handle_pragma (l, tl, p, *pval, decl, decl_name, ns); + if (decl) + count = decl_pragmas_[decl].size (); + else + count = loc_pragmas_[scope].size (); + + handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns); } else if (tt != CPP_EOF) + { error (l) << "unexpected text after " << p << " in db pragma" << endl; + return; + } + + // Record the value pragma. Here things are complicated by the fact + // that we use the value pragma by itself to signify a composite value + // type declaration. Consider this example: + // + // #pragma db value pgsql:type("POINT") + // class point {...}; + // + // Should this class be considered composite value type in other + // databases (because that's what would happen by default)? Probably + // not. So to overcome this we are going detect and ignore cases where + // (a) some specifiers followed the value qualifier but (b) none of + // them are for the database that we are compiling. + // + if (p == "value") + { + if (decl) + { + pragma_set& ps (decl_pragmas_[decl]); + + if (tt == CPP_EOF || ps.size () > count) + ps.insert (prag); + } + else + { + pragma_list& pl (loc_pragmas_[scope]); + + if (tt == CPP_EOF || pl.size () > count) + pl.push_back (prag); + } + } } /* extern "C" void +handle_pragma_db_mysql (cpp_reader* r) +{ + handle_pragma_qualifier (r, "mysql"); +} + +extern "C" void +handle_pragma_db_sqlite (cpp_reader* r) +{ + handle_pragma_qualifier (r, "sqlite"); +} + +extern "C" void +handle_pragma_db_pgsql (cpp_reader* r) +{ + handle_pragma_qualifier (r, "pgsql"); +} + +extern "C" void +handle_pragma_db_oracle (cpp_reader* r) +{ + handle_pragma_qualifier (r, "oracle"); +} + +extern "C" void +handle_pragma_db_mssql (cpp_reader* r) +{ + handle_pragma_qualifier (r, "mssql"); +} + +extern "C" void handle_pragma_db_map (cpp_reader* r) { handle_pragma_qualifier (r, "map"); @@ -3083,10 +3266,7 @@ handle_pragma_db (cpp_reader*) if (tt != CPP_NAME && tt != CPP_KEYWORD) { - if (tt == CPP_EOF) - error (l) << "expected specifier after db pragma" << endl; - else - error (l) << "unexpected token after db pragma" << endl; + error (l) << "expected specifier after db pragma" << endl; return; } @@ -3135,6 +3315,11 @@ register_odb_pragmas (void*, void*) c_register_pragma_with_expansion (0, "db", handle_pragma_db); /* + c_register_pragma_with_expansion ("db", "mysql", handle_pragma_db_mysql); + c_register_pragma_with_expansion ("db", "sqlite", handle_pragma_db_sqlite); + c_register_pragma_with_expansion ("db", "pgsql", handle_pragma_db_pgsql); + c_register_pragma_with_expansion ("db", "oracle", handle_pragma_db_oracle); + c_register_pragma_with_expansion ("db", "mssql", handle_pragma_db_mssql); c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map); c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace); c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object); diff --git a/odb/pragma.hxx b/odb/pragma.hxx index efb80d8..cafaedd 100644 --- a/odb/pragma.hxx +++ b/odb/pragma.hxx @@ -12,6 +12,8 @@ #include <vector> #include <string> +#include <odb/option-types.hxx> // database + #include <cutl/container/any.hxx> #include <cutl/container/multi-index.hxx> #include <cutl/compiler/context.hxx> @@ -256,6 +258,11 @@ extern decl_pragmas decl_pragmas_; typedef std::set<std::string> pragma_name_set; extern pragma_name_set simple_value_pragmas_; +// Database we are generating code for. Used to ignore database-specific +// pragmas. +// +extern database pragma_db_; + extern "C" void register_odb_pragmas (void*, void*); diff --git a/odb/processor.cxx b/odb/processor.cxx index 0299ad5..616eddc 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -2,15 +2,1147 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include <odb/gcc.hxx> + #include <iostream> +#include <odb/common.hxx> +#include <odb/lookup.hxx> #include <odb/context.hxx> +#include <odb/cxx-lexer.hxx> #include <odb/processor.hxx> +#include <odb/diagnostics.hxx> #include <odb/relational/processor.hxx> using namespace std; +namespace +{ + struct data_member: traversal::data_member, context + { + virtual void + traverse (semantics::data_member& m) + { + if (transient (m)) + return; + + process_access (m, "get"); + process_access (m, "set"); + } + + // + // Process member access expressions. + // + + enum found_type + { + found_none, + found_some, // Found something but keep looking for a better one. + found_best + }; + + // Check if a function is a suitable accessor for this member. + // + found_type + check_accessor (semantics::data_member& m, + tree f, + string const& n, + member_access& ma, + bool strict) + { + // Must be const. + // + if (!DECL_CONST_MEMFUNC_P (f)) + return found_none; + + // Accessor is a function with no arguments (other than 'this'). + // + if (DECL_CHAIN (DECL_ARGUMENTS (f)) != NULL_TREE) + return found_none; + + // Note that to get the return type we have to use + // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as + // suggested in the documentation. + // + tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); + int tc (TREE_CODE (r)); + + // In the strict mode make sure the function returns for non-array + // types a value or a (const) reference to the member type and for + // array types a (const) pointer to element type. In the lax mode + // we just check that the return value is not void. + // + if (strict) + { + semantics::type& t (utype (m)); + semantics::array* ar (dynamic_cast<semantics::array*> (&t)); + + if (ar != 0 && tc != POINTER_TYPE) + return found_none; + + tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r); + tree bt_mv (TYPE_MAIN_VARIANT (bt)); + + if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) + return found_none; + } + else if (r == void_type_node) + return found_none; + + cxx_tokens& e (ma.expr); + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); + e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); + + // See if it returns by value. + // + ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE); + + return found_best; + } + + // Check if a function is a suitable modifier for this member. + // + found_type + check_modifier (semantics::data_member& m, + tree f, + string const& n, + member_access& ma, + bool strict) + { + tree a (DECL_ARGUMENTS (f)); + a = DECL_CHAIN (a); // Skip this. + + // For a modifier, it can either be a function that returns a non- + // const reference (or non-const pointer, in case the member is an + // array) or a by-value modifier that sets a new value. If both are + // available, we prefer the former for efficiency. + // + cxx_tokens& e (ma.expr); + semantics::type& t (utype (m)); + semantics::array* ar (dynamic_cast<semantics::array*> (&t)); + + if (a == NULL_TREE) + { + // Note that to get the return type we have to use + // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as + // suggested in the documentation. + // + tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); + int tc (TREE_CODE (r)); + + // By-reference modifier. Should return a reference or a pointer. + // + if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE)) + return found_none; + + // The base type should not be const and, in strict mode, should + // match the member type. + // + tree bt (TREE_TYPE (r)); + + if (CP_TYPE_CONST_P (bt)) + return found_none; + + tree bt_mv (TYPE_MAIN_VARIANT (bt)); + + if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) + return found_none; + + e.clear (); // Could contain by value modifier. + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); + e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); + + return found_best; + } + // Otherwise look for a by value modifier, which is a function + // with a single argument. + // + else if (DECL_CHAIN (a) == NULL_TREE) + { + // In the lax mode any function with a single argument works + // for us. And we don't care what it returns. + // + if (strict) + { + // In the strict mode make sure the argument matches the + // member. This is exactly the same logic as in accessor + // with regards to arrays, references, etc. + // + tree at (TREE_TYPE (a)); + int tc (TREE_CODE (at)); + + if (ar != 0 && tc != POINTER_TYPE) + return found_none; + + tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at); + tree bt_mv (TYPE_MAIN_VARIANT (bt)); + + if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) + return found_none; + } + + if (e.empty ()) + { + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); + e.push_back (cxx_token (0, CPP_QUERY)); + e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); + + // Continue searching in case there is version that returns a + // non-const reference which we prefer for efficiency. + // + return found_some; + } + else + return found_none; // We didn't find anything better. + } + + return found_none; + } + + void + process_access (semantics::data_member& m, std::string const& k) + { + bool virt (m.count ("virtual")); + + // Ignore certain special virtual members. + // + if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator"))) + return; + + char const* kind (k == "get" ? "accessor" : "modifier"); + semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ())); + + // If we don't have an access expression, try to come up with + // one. + // + if (!m.count (k)) + { + found_type found (found_none); + semantics::access const& a (m.named ().access ()); + member_access& ma (m.set (k, member_access (m.location (), true))); + + // If this member is not virtual and is either public or if we + // are a friend of this class, then go for the member directly. + // + if (!virt && (a == semantics::access::public_ || + c.get<bool> ("friend"))) + { + ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this")); + ma.expr.push_back (cxx_token (0, CPP_DOT)); + ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ())); + found = found_best; + } + + // Otherwise, try to find a suitable accessor/modifier. + // + + // First try the original name. If that doesn't produce anything, + // then try the public name. + // + bool t (k == "get" + ? options.accessor_regex_trace () + : options.modifier_regex_trace ()); + regex_mapping const& re ( + k == "get" ? accessor_regex : modifier_regex); + + for (unsigned short j (0); found != found_best && j != 2; ++j) + { + string b (j == 0 ? m.name () : public_name (m, false)); + + // Skip the second pass if original and public names are the same. + // + if (j == 1 && b == m.name ()) + continue; + + if (t) + cerr << kind << (j == 0 ? " original" : " public") + << " name '" << b << "'" << endl; + + for (regex_mapping::const_iterator i (re.begin ()); + found != found_best && i != re.end (); + ++i) + { + if (t) + cerr << "try: '" << i->regex () << "' : "; + + if (!i->match (b)) + { + if (t) + cerr << '-' << endl; + continue; + } + + string n (i->replace (b)); + + if (t) + cerr << "'" << n << "' : "; + + tree decl ( + lookup_qualified_name ( + c.tree_node (), get_identifier (n.c_str ()), false, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) + { + if (t) + cerr << '-' << endl; + continue; + } + + // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. + // + for (tree o (BASELINK_FUNCTIONS (decl)); + o != 0; + o = OVL_NEXT (o)) + { + tree f (OVL_CURRENT (o)); + + // We are only interested in public non-static member + // functions. Note that TREE_PUBLIC() returns something + // other than what we need. + // + if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) || + TREE_PRIVATE (f) || TREE_PROTECTED (f)) + continue; + + found_type r (k == "get" + ? check_accessor (m, f, n, ma, true) + : check_modifier (m, f, n, ma, true)); + + if (r != found_none) + { + // Update the location of the access expression to point + // to this function. + // + ma.loc = location (DECL_SOURCE_LOCATION (f)); + found = r; + } + } + + if (t) + cerr << (found != found_none ? '+' : '-') << endl; + } + } + + // If that didn't work then the generated code won't be able + // to access this member. + // + if (found == found_none) + { + location const& l (m.location ()); + + if (virt) + { + error (l) << "no suitable " << kind << " function could be " + << "automatically found for virtual data member '" + << m.name () << "'" << endl; + + info (l) << "use '#pragma db " << k << "' to explicitly " + << "specify the " << kind << " function or " + << "expression" << endl; + } + else + { + error (l) << "data member '" << m.name () << "' is " + << a.string () << " and no suitable " << kind + << " function could be automatically found" << endl; + + info (l) << "consider making class 'odb::access' a friend of " + << "class '" << class_name (c) << "'" << endl; + + info (l) << "or use '#pragma db " << k << "' to explicitly " + << "specify the " << kind << " function or " + << "expression" << endl; + } + + throw operation_failed (); + } + } + + member_access& ma (m.get<member_access> (k)); + cxx_tokens& e (ma.expr); + + // If it is just a name, resolve it and convert to an appropriate + // expression. + // + if (e.size () == 1 && e.back ().type == CPP_NAME) + { + string n (e.back ().literal); + e.clear (); + + tree decl ( + lookup_qualified_name ( + c.tree_node (), get_identifier (n.c_str ()), false, false)); + + if (decl == error_mark_node) + { + error (ma.loc) << "unable to resolve data member or function " + << "name '" << n << "'" << endl; + throw operation_failed (); + } + + switch (TREE_CODE (decl)) + { + case FIELD_DECL: + { + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + break; + } + case BASELINK: + { + // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. + // + for (tree o (BASELINK_FUNCTIONS (decl)); + o != 0; + o = OVL_NEXT (o)) + { + tree f (OVL_CURRENT (o)); + + // We are only interested in non-static member functions. + // + if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f)) + continue; + + if ((k == "get" + ? check_accessor (m, f, n, ma, false) + : check_modifier (m, f, n, ma, false)) == found_best) + break; + } + + if (e.empty ()) + { + error (ma.loc) << "unable to find suitable " << kind + << " function '" << n << "'" << endl; + throw operation_failed (); + } + break; + } + default: + { + error (ma.loc) << "name '" << n << "' does not refer to a data " + << "member or function" << endl; + throw operation_failed (); + } + } + } + + // If there is no 'this' keyword, then add it as a prefix. + // + { + bool t (false); + for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i) + { + if (i->type == CPP_KEYWORD && i->literal == "this") + { + t = true; + break; + } + } + + if (!t) + { + e.insert (e.begin (), cxx_token (0, CPP_DOT)); + e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this")); + } + } + + // Check that there is no placeholder in the accessor expression. + // + if (k == "get" && ma.placeholder ()) + { + error (ma.loc) << "(?) placeholder in the accessor expression" + << endl; + throw operation_failed (); + } + + // Check that the member type is default-constructible if we + // have a by value modifier. + // + if (k == "set" && ma.placeholder ()) + { + semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m))); + + // Assume all other types are default-constructible. + // + if (c != 0) + { + // If this type is a class template instantiation, then make + // sure it is instantiated. While types used in real members + // will be instantiated, this is not necessarily the case for + // virtual members. Without the instantiation we won't be able + // to detect whether the type has the default ctor. + // + // It would have been cleaner to do it in post_process_pragmas() + // but there we don't yet know whether we need the default ctor. + // And it is a good idea not to require instantiability unless + // we really need it. + // + tree type (c->tree_node ()); + + if (!COMPLETE_TYPE_P (type) && + CLASSTYPE_TEMPLATE_INSTANTIATION (type)) + { + // Reset input location so that we get nice diagnostics in + // case of an error. Use the location of the virtual pragma. + // + location_t loc (m.get<location_t> ("virtual-location")); + input_location = loc; + + if (instantiate_class_template (type) == error_mark_node || + errorcount != 0 || + !COMPLETE_TYPE_P (type)) + { + error (loc) << "unable to instantiate virtual data member " << + "type" << endl; + throw operation_failed (); + } + } + + if (!c->default_ctor ()) + { + error (ma.loc) << "modifier expression requires member type " << + "to be default-constructible" << endl; + throw operation_failed (); + } + } + } + } + }; + + struct class_: traversal::class_, context + { + class_ () + : std_string_ (0), std_string_hint_ (0), access_ (0) + { + *this >> member_names_ >> member_; + + // Resolve the std::string type node. + // + using semantics::scope; + + for (scope::names_iterator_pair ip (unit.find ("std")); + ip.first != ip.second; ++ip.first) + { + if (scope* ns = dynamic_cast<scope*> (&ip.first->named ())) + { + scope::names_iterator_pair jp (ns->find ("string")); + + if (jp.first != jp.second) + { + std_string_ = dynamic_cast<semantics::type*> ( + &jp.first->named ()); + std_string_hint_ = &*jp.first; + break; + } + } + } + + assert (std_string_ != 0); // No std::string? + + // Resolve odb::access, if any. + // + tree odb = lookup_qualified_name ( + global_namespace, get_identifier ("odb"), false, false); + + if (odb != error_mark_node) + { + access_ = lookup_qualified_name ( + odb, get_identifier ("access"), true, false); + + access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0); + } + } + + virtual void + traverse (type& c) + { + class_kind_type k (class_kind (c)); + + if (k == class_other) + return; + + // Check if odb::access is a friend of this class. + // + c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_)); + + // Assign pointer. + // + if (k == class_object || k == class_view) + assign_pointer (c); + + if (k == class_object) + traverse_object (c); + + names (c); + } + + // + // Object. + // + + virtual void + traverse_object (type& c) + { + semantics::class_* poly_root (polymorphic (c)); + + // Determine whether it is a session object. + // + if (!c.count ("session")) + { + // If this is a derived class in a polymorphic hierarchy, + // then it should have the same session value as the root. + // + if (poly_root != 0 && poly_root != &c) + c.set ("session", session (*poly_root)); + else + { + // See if any of the namespaces containing this class specify + // the session value. + // + bool found (false); + for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) + { + using semantics::namespace_; + + namespace_* ns (dynamic_cast<namespace_*> (s)); + + if (ns == 0) + continue; // Some other scope. + + if (ns->extension ()) + ns = &ns->original (); + + if (ns->count ("session")) + { + c.set ("session", ns->get<bool> ("session")); + found = true; + break; + } + + if (ns->global_scope ()) + break; + } + + // If still not found, then use the default value. + // + if (!found) + c.set ("session", options.generate_session ()); + } + } + + if (session (c)) + features.session_object = true; + + if (poly_root != 0) + { + using namespace semantics; + + semantics::data_member& idm (*id_member (*poly_root)); + + if (poly_root != &c) + { + // If we are a derived class in the polymorphic persistent + // class hierarchy, then add a synthesized virtual pointer + // member that points back to the root. + // + path const& f (idm.file ()); + size_t l (idm.line ()), col (idm.column ()); + + semantics::data_member& m ( + unit.new_node<semantics::data_member> (f, l, col, tree (0))); + m.set ("virtual", true); + + // Make it the first member in the class. + // + node_position<type, scope::names_iterator> np (c, c.names_end ()); + unit.new_edge<semantics::names> ( + np, m, idm.name (), access::public_); + + // Use the raw pointer as this member's type. + // + if (!poly_root->pointed_p ()) + { + // Create the pointer type in the graph. The pointer node + // in GCC seems to always be present, even if not explicitly + // used in the translation unit. + // + tree t (poly_root->tree_node ()); + tree ptr (TYPE_POINTER_TO (t)); + assert (ptr != 0); + ptr = TYPE_MAIN_VARIANT (ptr); + pointer& p (unit.new_node<pointer> (f, l, col, ptr)); + unit.insert (ptr, p); + unit.new_edge<points> (p, *poly_root); + assert (poly_root->pointed_p ()); + } + + unit.new_edge<belongs> (m, poly_root->pointed ().pointer ()); + + // Mark it as a special kind of id. + // + m.set ("id", true); + m.set ("polymorphic-ref", true); + } + else + { + // If we are a root of the polymorphic persistent class hierarchy, + // then add a synthesized virtual member for the discriminator. + // Use the location of the polymorphic pragma as the location of + // this member. + // + location_t loc (c.get<location_t> ("polymorphic-location")); + semantics::data_member& m ( + unit.new_node<semantics::data_member> ( + path (LOCATION_FILE (loc)), + LOCATION_LINE (loc), + LOCATION_COLUMN (loc), + tree (0))); + m.set ("virtual", true); + + // Insert it after the id member (or first if this id comes + // from reuse-base). + // + node_position<type, scope::names_iterator> np ( + c, c.find (idm.named ())); + unit.new_edge<semantics::names> ( + np, m, "typeid_", access::public_); + + belongs& edge (unit.new_edge<belongs> (m, *std_string_)); + edge.hint (*std_string_hint_); + + m.set ("readonly", true); + m.set ("discriminator", true); + + c.set ("discriminator", &m); + } + } + } + + void + assign_pointer (type& c) + { + location_t loc (0); // Pragma location, or 0 if not used. + + try + { + string ptr; + string const& type (class_fq_name (c)); + + tree decl (0); // Resolved template node. + string decl_name; // User-provided template name. + tree resolve_scope (0); // Scope in which we resolve names. + + class_pointer const* cp (0); + bool cp_template (false); + + if (c.count ("pointer")) + { + cp = &c.get<class_pointer> ("pointer"); + } + // If we are a derived type in polymorphic hierarchy, then use + // our root's pointer type by default. + // + else if (semantics::class_* r = polymorphic (c)) + { + if (&c != r && r->count ("pointer-template")) + cp = r->get<class_pointer const*> ("pointer-template"); + } + + if (cp != 0) + { + string const& p (cp->name); + + if (p == "*") + { + ptr = type + "*"; + cp_template = true; + } + else if (p[p.size () - 1] == '*') + ptr = p; + else if (p.find ('<') != string::npos) + { + // Template-id. + // + ptr = p; + decl_name.assign (p, 0, p.find ('<')); + } + else + { + // This is not a template-id. Resolve it and see if it is a + // template or a type. + // + decl = resolve_name (p, cp->scope, true); + int tc (TREE_CODE (decl)); + + if (tc == TYPE_DECL) + { + ptr = p; + + // This can be a typedef'ed alias for a TR1 template-id. + // + if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl))) + { + decl = TI_TEMPLATE (ti); // DECL_TEMPLATE + + // Get to the most general template declaration. + // + while (DECL_TEMPLATE_INFO (decl)) + decl = DECL_TI_TEMPLATE (decl); + } + else + decl = 0; // Not a template. + } + else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) + { + ptr = p + "< " + type + " >"; + decl_name = p; + cp_template = true; + } + else + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "does not name a type or a template" << endl; + + throw operation_failed (); + } + } + + // Resolve scope is the scope of the pragma. + // + resolve_scope = cp->scope; + loc = cp->loc; + } + else + { + // See if any of the namespaces containing this class specify + // a pointer. + // + for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) + { + using semantics::namespace_; + + namespace_* ns (dynamic_cast<namespace_*> (s)); + + if (ns == 0) + continue; // Some other scope. + + if (ns->extension ()) + ns = &ns->original (); + + if (!ns->count ("pointer")) + { + if (ns->global_scope ()) + break; + else + continue; + } + + cp = &ns->get<class_pointer> ("pointer"); + string const& p (cp->name); + + // Namespace-specified pointer can only be '*' or are template. + // + if (p == "*") + ptr = type + "*"; + else if (p[p.size () - 1] == '*') + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "at namespace level cannot be a raw pointer" << endl; + } + else if (p.find ('<') != string::npos) + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "at namespace level cannot be a template-id" << endl; + } + else + { + // Resolve this name and make sure it is a template. + // + decl = resolve_name (p, cp->scope, true); + int tc (TREE_CODE (decl)); + + if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) + { + ptr = p + "< " + type + " >"; + decl_name = p; + } + else + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "does not name a template" << endl; + } + } + + if (ptr.empty ()) + throw operation_failed (); + + cp_template = true; + + // Resolve scope is the scope of the pragma. + // + resolve_scope = cp->scope; + loc = cp->loc; + break; + } + + // Use the default pointer. + // + if (ptr.empty ()) + { + string const& p (options.default_pointer ()); + + if (p == "*") + ptr = type + "*"; + else + { + ptr = p + "< " + type + " >"; + decl_name = p; + } + + // Resolve scope is the scope of the class. + // + resolve_scope = c.scope ().tree_node (); + } + } + + // If this class is a root of a polymorphic hierarchy, then cache + // the pointer template so that we can use it for derived classes. + // + if (cp != 0 && cp_template && polymorphic (c) == &c) + c.set ("pointer-template", cp); + + // Check if we are using TR1. + // + if (decl != 0 || !decl_name.empty ()) + { + bool& tr1 (features.tr1_pointer); + bool& boost (features.boost_pointer); + + // First check the user-supplied name. + // + tr1 = tr1 + || decl_name.compare (0, 8, "std::tr1") == 0 + || decl_name.compare (0, 10, "::std::tr1") == 0; + + // If there was no match, also resolve the name since it can be + // a using-declaration for a TR1 template. + // + if (!tr1) + { + if (decl == 0) + decl = resolve_name (decl_name, resolve_scope, false); + + if (TREE_CODE (decl) != TEMPLATE_DECL || ! + DECL_CLASS_TEMPLATE_P (decl)) + { + // This is only checked for the --default-pointer option. + // + error (c.file (), c.line (), c.column ()) + << "name '" << decl_name << "' specified with the " + << "--default-pointer option does not name a class " + << "template" << endl; + + throw operation_failed (); + } + + string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); + + // In case of a boost TR1 implementation, we cannot distinguish + // between the boost:: and std::tr1:: usage since the latter is + // just a using-declaration for the former. + // + tr1 = tr1 + || n.compare (0, 8, "std::tr1") == 0 + || n.compare (0, 10, "::std::tr1") == 0; + + boost = boost + || n.compare (0, 17, "boost::shared_ptr") == 0 + || n.compare (0, 19, "::boost::shared_ptr") == 0; + } + } + + // Fully-qualify all the unqualified components of the name. + // + try + { + lex_.start (ptr); + ptr.clear (); + + string t; + bool punc (false); + bool scoped (false); + + for (cpp_ttype tt (lex_.next (t)); + tt != CPP_EOF; + tt = lex_.next (t)) + { + if (punc && tt > CPP_LAST_PUNCTUATOR) + ptr += ' '; + + punc = false; + + switch (static_cast<unsigned> (tt)) + { + case CPP_LESS: + { + ptr += "< "; + break; + } + case CPP_GREATER: + { + ptr += " >"; + break; + } + case CPP_COMMA: + { + ptr += ", "; + break; + } + case CPP_NAME: + { + // If the name was not preceeded with '::', look it + // up in the pragmas's scope and add the qualifer. + // + if (!scoped) + { + tree decl (resolve_name (t, resolve_scope, false)); + tree scope (CP_DECL_CONTEXT (decl)); + + if (scope != global_namespace) + { + ptr += "::"; + ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER); + } + + ptr += "::"; + } + + ptr += t; + punc = true; + break; + } + case CPP_KEYWORD: + case CPP_NUMBER: + { + ptr += t; + punc = true; + break; + } + default: + { + ptr += t; + break; + } + } + + scoped = (tt == CPP_SCOPE); + } + } + catch (cxx_lexer::invalid_input const&) + { + throw operation_failed (); + } + + c.set ("object-pointer", ptr); + } + catch (invalid_name const& ex) + { + if (loc != 0) + error (loc) + << "name '" << ex.name () << "' specified with db pragma " + << "pointer is invalid" << endl; + else + error (c.file (), c.line (), c.column ()) + << "name '" << ex.name () << "' specified with the " + << "--default-pointer option is invalid" << endl; + + + throw operation_failed (); + } + catch (unable_to_resolve const& ex) + { + if (loc != 0) + error (loc) + << "unable to resolve name '" << ex.name () << "' specified " + << "with db pragma pointer" << endl; + else + error (c.file (), c.line (), c.column ()) + << "unable to resolve name '" << ex.name () << "' specified " + << "with the --default-pointer option" << endl; + + throw operation_failed (); + } + } + + private: + struct invalid_name + { + invalid_name (string const& n): name_ (n) {} + + string const& + name () const {return name_;} + + private: + string name_; + }; + + typedef lookup::unable_to_resolve unable_to_resolve; + + tree + resolve_name (string const& qn, tree scope, bool is_type) + { + try + { + string tl; + tree tn; + cpp_ttype tt, ptt; + + nlex_.start (qn); + tt = nlex_.next (tl, &tn); + + string name; + return lookup::resolve_scoped_name ( + nlex_, tt, tl, tn, ptt, scope, name, is_type); + } + catch (cxx_lexer::invalid_input const&) + { + throw invalid_name (qn); + } + catch (lookup::invalid_name const&) + { + throw invalid_name (qn); + } + } + + private: + data_member member_; + traversal::names member_names_; + + cxx_string_lexer lex_; + cxx_string_lexer nlex_; // Nested lexer. + + semantics::type* std_string_; + semantics::names* std_string_hint_; + + tree access_; // odb::access node. + }; +} + void processor:: process (options const& ops, features& f, @@ -19,10 +1151,49 @@ process (options const& ops, { try { - // Process types. - // auto_ptr<context> ctx (create_context (cerr, unit, ops, f, 0)); - relational::process (); + + // Common processing. + // + { + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (true); + traversal::namespace_ ns; + class_ c; + + unit >> unit_defines >> ns; + unit_defines >> c; + unit >> unit_typedefs >> c; + + traversal::defines ns_defines; + typedefs ns_typedefs (true); + + ns >> ns_defines >> ns; + ns_defines >> c; + ns >> ns_typedefs >> c; + + unit.dispatch (ctx->unit); + } + + // Database-specific processing. + // + switch (ops.database ()[0]) + { + case database::common: + { + break; + } + case database::mssql: + case database::mysql: + case database::oracle: + case database::pgsql: + case database::sqlite: + { + relational::process (); + break; + } + } } catch (operation_failed const&) { diff --git a/odb/profile.cxx b/odb/profile.cxx index fedd7a9..03bf8a4 100644 --- a/odb/profile.cxx +++ b/odb/profile.cxx @@ -12,8 +12,6 @@ using namespace std; - - static bool exist (profile_data::path const& p) { @@ -64,6 +62,12 @@ profile_search (char const* prof, void* arg) if (i == end) { + // Ignore the case where we didn't find the profile and this is the + // common database. + // + if (pd->db == database::common) + return string (); + cerr << pd->name << ": error: unable to locate options file for profile '" << prof << "'" << endl; throw profile_failure (); diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx index 3916184..e9b3485 100644 --- a/odb/relational/common.cxx +++ b/odb/relational/common.cxx @@ -88,8 +88,8 @@ namespace relational { string const& fq_name (class_fq_name (c)); string guard ( - make_guard ( - "ODB_ALIAS_TRAITS_" + alias + "_FOR_" + flat_name (fq_name))); + make_guard ("ODB_" + string (db.string ()) + "_ALIAS_TRAITS_" + + alias + "_FOR_" + flat_name (fq_name))); if (specs_.find (guard) != specs_.end ()) return; @@ -107,19 +107,20 @@ namespace relational << "#define " << guard << endl; os << "template <bool d>" << endl - << "struct alias_traits< " << fq_name << ", " << tag << ", d >" + << "struct alias_traits< " << fq_name << ", id_" << db << ", " << + tag << ", d >" << "{" << "static const char table_name[];"; if (poly_derived) os << "typedef alias_traits< " << class_fq_name (*poly_base) << ", " << - tag << " > base_traits;"; + "id_" << db << ", " << tag << " > base_traits;"; os << "};"; os << "template <bool d>" << endl - << "const char alias_traits< " << fq_name << ", " << tag << - ", d >::" << endl + << "const char alias_traits< " << fq_name << ", id_" << db << ", " << + tag << ", d >::" << endl << "table_name[] = "; if (poly_root != 0) @@ -146,7 +147,8 @@ namespace relational query_columns_base (semantics::class_& c) //@@ context::{cur,top}_object : decl_ (false) { - scope_ = "query_columns_base< " + class_fq_name (c) + " >"; + scope_ = "query_columns_base< " + class_fq_name (c) + ", id_" + + db.string () + " >"; } void query_columns_base:: @@ -244,7 +246,8 @@ namespace relational string const& fq_name (class_fq_name (c)); os << "typedef" << endl - << "odb::alias_traits< " << fq_name << ", " << tag << " >" << endl + << "odb::alias_traits< " << fq_name << ", id_" << db << ", " << + tag << " >" << endl << name << "_alias_;" << endl; @@ -254,6 +257,7 @@ namespace relational << "odb::query_pointer<" << endl << " odb::pointer_query_columns<" << endl << " " << fq_name << "," << endl + << " id_" << db << "," << endl << " " << name << "_alias_ > >" << endl << name << "_type_ ;" << endl @@ -284,7 +288,7 @@ namespace relational : ptr_ (ptr), decl_ (false), in_ptr_ (false) { scope_ = ptr ? "pointer_query_columns" : "query_columns"; - scope_ += "< " + class_fq_name (c) + ", A >"; + scope_ += "< " + class_fq_name (c) + ", id_" + db.string () + ", A >"; } void query_columns:: @@ -474,6 +478,7 @@ namespace relational << "odb::query_pointer<" << endl << " odb::pointer_query_columns<" << endl << " " << class_fq_name (c) << "," << endl + << " id_" << db << "," << endl << " " << name << "_alias_ > >" << endl << name << "_pointer_type_;" << endl; @@ -519,6 +524,7 @@ namespace relational << "odb::query_pointer<" << endl << " odb::pointer_query_columns<" << endl << " " << class_fq_name (c) << "," << endl + << " id_" << db << "," << endl << " " << name << "_alias_ > >" << endl << name << "_pointer_type_;" << endl; diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index 26e8197..b951d15 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -336,7 +336,7 @@ namespace relational static B* create (B const& prototype) { - database db (context::current ().options.database ()); + database db (context::current ().options.database ()[0]); if (map_ != 0) { diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx index 538962b..1295106 100644 --- a/odb/relational/header.cxx +++ b/odb/relational/header.cxx @@ -13,15 +13,14 @@ traverse_object (type& c) using semantics::data_member; data_member* id (id_member (c)); - bool auto_id (id ? id->count ("auto") : false); - bool base_id (id ? &id->scope () != &c : false); // Comes from base. + bool auto_id (id && auto_ (*id)); + bool base_id (id && &id->scope () != &c); // Comes from base. data_member* optimistic (context::optimistic (c)); type* poly_root (polymorphic (c)); bool poly (poly_root != 0); bool poly_derived (poly && poly_root != &c); - type* poly_base (poly_derived ? &polymorphic_base (c) : 0); data_member* discriminator (poly ? context::discriminator (*poly_root) : 0); bool abst (abstract (c)); @@ -33,14 +32,6 @@ traverse_object (type& c) os << "// " << class_name (c) << endl << "//" << endl; - // class_traits - // - os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_object;" - << "};"; - // pointer_query_columns & query_columns // if (options.generate_query ()) @@ -58,59 +49,38 @@ traverse_object (type& c) pointer_query_columns_type_->traverse (c); } - // object_traits + // object_traits_impl // os << "template <>" << endl - << "class access::object_traits< " << type << " >" + << "class access::object_traits_impl< " << type << ", id_" << db << + " >:" << endl + << " public access::object_traits< " << type << " >" << "{" << "public:" << endl; object_public_extra_pre (c); - // object_type & pointer_type - // - os << "typedef " << type << " object_type;" - << "typedef " << c.get<string> ("object-pointer") << " pointer_type;" - << "typedef odb::pointer_traits<pointer_type> pointer_traits;" - << endl; - - // polymorphic, root_type, base_type, etc. + // Polymorphic root_traits, base_traits, and discriminator_image_type. // - os << "static const bool polymorphic = " << (poly ? "true" : "false") << ";"; - if (poly) { - os << "typedef " << class_fq_name (*poly_root) << " root_type;" - << "typedef object_traits<root_type> root_traits;"; + if (!abst) + os << "typedef polymorphic_entry<object_type, id_" << db << + "> entry_type;"; + + os << "typedef object_traits_impl<root_type, id_" << db << "> " << + "root_traits;"; if (poly_derived) { - os << "typedef " << class_fq_name (*poly_base) << " base_type;" - << "typedef object_traits<base_type> base_traits;" - << "typedef root_traits::discriminator_type discriminator_type;" - << "typedef odb::polymorphic_concrete_info<root_type> info_type;"; - - if (abst) - os << "typedef odb::polymorphic_abstract_info<root_type> " << - "abstract_info_type;"; - else - os << "typedef odb::polymorphic_entry<object_type> entry_type;"; - - // Calculate our hierarchy depth (number of classes). - // - size_t depth (polymorphic_depth (c)); - - os << "static const std::size_t depth = " << depth << "UL;"; + os << "typedef object_traits_impl<base_type, id_" << db << "> " << + "base_traits;" + << endl; } else { - semantics::names* hint; - semantics::type& t (utype (*discriminator, hint)); - - os << "typedef " << t.fq_name (hint) << " discriminator_type;" - << endl; - - os << "struct discriminator_image_type" + os << endl + << "struct discriminator_image_type" << "{"; discriminator_image_member_->traverse (*discriminator); @@ -120,23 +90,10 @@ traverse_object (type& c) os << "std::size_t version;" << "};"; - - os << "typedef odb::polymorphic_map<object_type> map_type;" - << "typedef odb::polymorphic_concrete_info<object_type> info_type;"; - - if (abst) - os << "typedef odb::polymorphic_abstract_info<object_type> " << - "abstract_info_type;"; - else - os << "typedef odb::polymorphic_entry<object_type> entry_type;"; - - os << "static const std::size_t depth = 1UL;"; } } - os << endl; - - // id_type & id_image_type + // id_image_type // if (id != 0) { @@ -144,44 +101,14 @@ traverse_object (type& c) { semantics::class_& b ( dynamic_cast<semantics::class_&> (id->scope ())); - string const& type (class_fq_name (b)); + string const& type (); - os << "typedef object_traits< " << type << " >::id_type id_type;"; - - if (optimistic != 0) - os << "typedef object_traits< " << type << " >::version_type " << - "version_type;"; - - if (poly_derived) - os << "typedef object_traits< " << type << " >:: id_image_type " << - "id_image_type;" - << "static const bool auto_id = false;" - << endl; - else - os << "typedef object_traits< " << type << " >::id_image_type " << - "id_image_type;" - << "static const bool auto_id = object_traits< " << type << - " >::auto_id;" - << endl; + os << "typedef object_traits_impl< " << class_fq_name (b) << ", " << + "id_" << db << " >::id_image_type id_image_type;" + << endl; } else { - { - semantics::names* hint; - semantics::type& t (utype (*id, hint)); - os << "typedef " << t.fq_name (hint) << " id_type;"; - } - - if (optimistic != 0) - { - semantics::names* hint; - semantics::type& t (utype (*optimistic, hint)); - os << "typedef " << t.fq_name (hint) << " version_type;"; - } - - os << "static const bool auto_id = " << (auto_id ? "true;" : "false;") - << endl; - os << "struct id_image_type" << "{"; @@ -194,19 +121,6 @@ traverse_object (type& c) << "};"; } } - else if (!reuse_abst) - { - // Object without id. - // - os << "typedef void id_type;" - << "static const bool auto_id = false;" - << endl; - } - - // abstract - // - os << "static const bool abstract = " << (abst ? "true" : "false") << ";" - << endl; // Polymorphic map. // @@ -236,19 +150,9 @@ traverse_object (type& c) // Functions (abstract and concrete). // - // id () - // if (id != 0 || !reuse_abst) - { - // We want to generate a dummy void id() accessor even if this - // object has no id to help us in the runtime. This way we can - // write generic code that will work for both void and non-void - // ids. - // - os << "static id_type" << endl - << "id (const object_type&);" + os << "using object_traits<object_type>::id;" << endl; - } if (!poly_derived && id != 0) { @@ -370,45 +274,6 @@ traverse_object (type& c) return; } - // Cache traits typedefs. - // - if (id == 0) - { - os << "typedef" << endl - << "odb::no_id_pointer_cache_traits<pointer_type>" << endl - << "pointer_cache_traits;" - << "typedef" << endl - << "odb::no_id_reference_cache_traits<object_type>" << endl - << "reference_cache_traits;" - << endl; - } - else - { - char const* p (session (c) ? "" : "no_op_"); - - if (poly_derived) - { - os << "typedef" << endl - << "odb::" << p << "pointer_cache_traits<" << - "root_traits::pointer_type>" << endl - << "pointer_cache_traits;" - << "typedef" << endl - << "odb::" << p << "reference_cache_traits<root_type>" << endl - << "reference_cache_traits;" - << endl; - } - else - { - os << "typedef" << endl - << "odb::" << p << "pointer_cache_traits<pointer_type>" << endl - << "pointer_cache_traits;" - << "typedef" << endl - << "odb::" << p << "reference_cache_traits<object_type>" << endl - << "reference_cache_traits;" - << endl; - } - } - // Statements typedefs. // if (poly) @@ -418,6 +283,7 @@ traverse_object (type& c) << db << "::polymorphic_derived_object_statements" << "<object_type>" << endl << "statements_type;" + << endl << "typedef" << endl << db << "::polymorphic_root_object_statements<root_type>" << endl << "root_statements_type;" @@ -426,6 +292,7 @@ traverse_object (type& c) os << "typedef" << endl << db << "::polymorphic_root_object_statements<object_type>" << endl << "statements_type;" + << endl << "typedef statements_type root_statements_type;" << "typedef " << db << "::object_statements<object_type> " << "base_statements_type;" @@ -451,7 +318,7 @@ traverse_object (type& c) { // query_base_type // - os << "typedef " << db << "::query query_base_type;" + os << "typedef " << db << "::query_base query_base_type;" << endl; } @@ -526,16 +393,6 @@ traverse_object (type& c) // Functions (concrete). // - // callback () - // - os << "static void" << endl - << "callback (database&, object_type&, callback_event);" - << endl; - - os << "static void" << endl - << "callback (database&, const object_type&, callback_event);" - << endl; - // persist () // os << "static void" << endl @@ -622,13 +479,27 @@ traverse_object (type& c) // if (options.generate_query ()) { - os << "static result<object_type>" << endl - << "query (database&, const query_base_type&);" - << endl; + if (!options.omit_unprepared ()) + { + os << "static result<object_type>" << endl + << "query (database&, const query_base_type&);" + << endl; + } os << "static unsigned long long" << endl << "erase_query (database&, const query_base_type&);" << endl; + + if (options.generate_prepared ()) + { + os << "static odb::details::shared_ptr<prepared_query_impl>" << endl + << "prepare_query (connection&, const char*, const query_base_type&);" + << endl; + + os << "static odb::details::shared_ptr<result_impl>" << endl + << "execute_query (prepared_query_impl&);" + << endl; + } } // create_schema () @@ -736,6 +607,224 @@ traverse_object (type& c) } os << "};"; + + + // object_traits_impl< , id_default> + // + // Note that it is not generated for reuse-abstract classes. + // + if (options.default_database_specified () && + options.default_database () == db) + { + os << "template <>" << endl + << "class access::object_traits_impl< " << type << ", " << + "id_default >:" << endl + << " public access::object_traits_impl< " << type << ", " << + "id_" << db << " >" + << "{" + << "};"; + } +} + +void relational::header::class1:: +traverse_view (type& c) +{ + string const& type (class_fq_name (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + // view_traits_impl + // + os << "template <>" << endl + << "class access::view_traits_impl< " << type << ", id_" << + db << " >:" << endl + << " public access::view_traits< " << type << " >" + << "{" + << "public:" << endl; + + view_public_extra_pre (c); + + // image_type + // + image_type_->traverse (c); + + os << "typedef " << db << "::view_statements<view_type> statements_type;" + << endl; + + // + // Query. + // + + // query_base_type and query_columns (definition generated by class2). + // + os << "typedef " << db << "::query_base query_base_type;" + << "struct query_columns"; + + if (c.get<size_t> ("object-count") == 0) + os << "{" + << "};"; + else + os << ";" + << endl; + + // + // Functions. + // + + // grow () + // + if (generate_grow) + { + os << "static bool" << endl + << "grow (image_type&, " << truncated_vector << ");" + << endl; + } + + // bind (image_type) + // + os << "static void" << endl + << "bind (" << bind_vector << ", image_type&);" + << endl; + + // init (view, image) + // + os << "static void" << endl + << "init (view_type&, const image_type&, database*);" + << endl; + + // column_count + // + os << "static const std::size_t column_count = " << + column_count (c).total << "UL;" + << endl; + + // Statements. + // + view_query& vq (c.get<view_query> ("query")); + + if (vq.kind != view_query::runtime) + { + os << "static query_base_type" << endl + << "query_statement (const query_base_type&);" + << endl; + } + + // + // Functions. + // + + // query () + // + if (!options.omit_unprepared ()) + os << "static result<view_type>" << endl + << "query (database&, const query_base_type&);" + << endl; + + if (options.generate_prepared ()) + { + os << "static odb::details::shared_ptr<prepared_query_impl>" << endl + << "prepare_query (connection&, const char*, const query_base_type&);" + << endl; + + os << "static odb::details::shared_ptr<result_impl>" << endl + << "execute_query (prepared_query_impl&);" + << endl; + } + + view_public_extra_post (c); + + os << "};"; + + // view_traits_impl< , id_default> + // + if (options.default_database_specified () && + options.default_database () == db) + { + os << "template <>" << endl + << "class access::view_traits_impl< " << type << ", " << + "id_default >:" << endl + << " public access::view_traits_impl< " << type << ", " << + "id_" << db << " >" + << "{" + << "};"; + } +} + +void relational::header::class1:: +traverse_composite (type& c) +{ + string const& type (class_fq_name (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + os << "template <>" << endl + << "class access::composite_value_traits< " << type << ", " << + "id_" << db << " >" + << "{" + << "public:" << endl; + + // value_type + // + os << "typedef " << type << " value_type;" + << endl; + + // image_type + // + image_type_->traverse (c); + + // Containers. + // + { + instance<container_traits> t (c); + t->traverse (c); + } + + // grow () + // + if (generate_grow) + { + os << "static bool" << endl + << "grow (image_type&, " << truncated_vector << ");" + << endl; + } + + // bind (image_type) + // + os << "static void" << endl + << "bind (" << bind_vector << ", image_type&, " << + db << "::statement_kind);" + << endl; + + // init (image, value) + // + os << "static " << (generate_grow ? "bool" : "void") << endl + << "init (image_type&, const value_type&, " << db << "::statement_kind);" + << endl; + + // init (value, image) + // + os << "static void" << endl + << "init (value_type&, const image_type&, database*);" + << endl; + + if (!has_a (c, test_container)) + { + // get_null (image) + // + os << "static bool" << endl + << "get_null (const image_type&);" + << endl; + + // set_null (image) + // + os << "static void" << endl + << "set_null (image_type&, " << db << "::statement_kind);" + << endl; + } + + os << "};"; } void relational::header:: diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 25f9480..c0e1e89 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -64,9 +64,11 @@ namespace relational string const& type (class_fq_name (c)); if (obj) - os << "object_traits< " << type << " >::image_type"; + os << "object_traits_impl< " << type << ", id_" << db << + " >::image_type"; else - os << "composite_value_traits< " << type << " >::image_type"; + os << "composite_value_traits< " << type << ", id_" << db << + " >::image_type"; } private: @@ -117,7 +119,7 @@ namespace relational os << "{"; if (poly_derived) - os << "object_traits<base_type>::image_type* base;" + os << "base_traits::image_type* base;" << endl; names (c); @@ -173,7 +175,7 @@ namespace relational } os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " << - class_fq_name (c) << ", "; + class_fq_name (c) << ", id_" << db << ", "; // If our base is polymorphic, then it has its own table/alias. // @@ -213,7 +215,7 @@ namespace relational << "//" << endl << "typedef " << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " << - class_fq_name (c) << ", "; + class_fq_name (c) << ", id_" << db << ", "; if (polymorphic (c)) os << "typename A::base_traits"; @@ -246,7 +248,8 @@ namespace relational if (ptr_) { os << "template <typename A>" << endl - << "struct pointer_query_columns< " << type << ", A >"; + << "struct pointer_query_columns< " << type << ", id_" << db << + ", A >"; // If we don't have pointers (in the whole hierarchy), then // pointer_query_columns and query_columns are the same. @@ -254,7 +257,7 @@ namespace relational if (!has_a (c, test_pointer | include_base)) { os << ":" << endl - << " query_columns< " << type << ", A >" + << " query_columns< " << type << ", id_" << db << ", A >" << "{" << "};"; } @@ -298,7 +301,8 @@ namespace relational // table alias (A) template argument. // os << "template <>" << endl - << "struct query_columns_base< " << type << " >" + << "struct query_columns_base< " << type << ", id_" << + db << " >" << "{"; instance<query_columns_base> t; @@ -308,11 +312,11 @@ namespace relational } os << "template <typename A>" << endl - << "struct query_columns< " << type << ", A >"; + << "struct query_columns< " << type << ", id_" << db << ", A >"; if (has_ptr) os << ":" << endl - << " query_columns_base< " << type << " >"; + << " query_columns_base< " << type << ", id_" << db << " >"; { instance<query_columns_bases> b (ptr_, !has_ptr); @@ -540,10 +544,11 @@ namespace relational string const& type (class_fq_name (b)); if (object (b)) - os << ": access::object_traits< " << type << " >::" << name; + os << ": access::object_traits_impl< " << type << ", id_" << + db << " >::" << name; else - os << ": access::composite_value_traits< " << type << " >::" << - public_name (m) << "_traits"; // No prefix_. + os << ": access::composite_value_traits< " << type << ", id_" << + db << " >::" << public_name (m) << "_traits"; // No prefix_. } os << "{"; @@ -992,7 +997,7 @@ namespace relational } virtual void - traverse_object (type& c); + traverse_object (type&); virtual void view_public_extra_pre (type&) @@ -1005,196 +1010,10 @@ namespace relational } virtual void - traverse_view (type& c) - { - string const& type (class_fq_name (c)); - - os << "// " << class_name (c) << endl - << "//" << endl; - - os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_view;" - << "};"; - - os << "template <>" << endl - << "class access::view_traits< " << type << " >" - << "{" - << "public:" << endl; - - view_public_extra_pre (c); - - // view_type & pointer_type - // - os << "typedef " << type << " view_type;" - << "typedef " << c.get<string> ("object-pointer") << " pointer_type;" - << endl; - - // image_type - // - image_type_->traverse (c); - - // - // Query. - // - - // query_base_type and query_columns (definition generated by class2). - // - os << "typedef " << db << "::query query_base_type;" - << "struct query_columns"; - - if (c.get<size_t> ("object-count") == 0) - os << "{" - << "};"; - else - os << ";" - << endl; - - // - // Functions. - // - - // grow () - // - if (generate_grow) - { - os << "static bool" << endl - << "grow (image_type&, " << truncated_vector << ");" - << endl; - } - - // bind (image_type) - // - os << "static void" << endl - << "bind (" << bind_vector << ", image_type&);" - << endl; - - // init (view, image) - // - os << "static void" << endl - << "init (view_type&, const image_type&, database*);" - << endl; - - // column_count - // - os << "static const std::size_t column_count = " << - column_count (c).total << "UL;" - << endl; - - // Statements. - // - view_query& vq (c.get<view_query> ("query")); - - if (vq.kind != view_query::runtime) - { - os << "static query_base_type" << endl - << "query_statement (const query_base_type&);" - << endl; - } - - // - // Functions. - // - - // callback () - // - os << "static void" << endl - << "callback (database&, view_type&, callback_event);" - << endl; - - // query () - // - os << "static result<view_type>" << endl - << "query (database&, const query_base_type&);" - << endl; - - view_public_extra_post (c); - - os << "};"; - } + traverse_view (type&); virtual void - traverse_composite (type& c) - { - string const& type (class_fq_name (c)); - - os << "// " << class_name (c) << endl - << "//" << endl; - - os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_composite;" - << "};"; - - os << "template <>" << endl - << "class access::composite_value_traits< " << type << " >" - << "{" - << "public:" << endl; - - // value_type - // - os << "typedef " << type << " value_type;" - << endl; - - // image_type - // - image_type_->traverse (c); - - // Containers. - // - { - instance<container_traits> t (c); - t->traverse (c); - } - - // grow () - // - if (generate_grow) - { - os << "static bool" << endl - << "grow (image_type&, " << truncated_vector << ");" - << endl; - } - - // bind (image_type) - // - os << "static void" << endl - << "bind (" << bind_vector << ", image_type&, " << - db << "::statement_kind);" - << endl; - - // init (image, value) - // - os << "static " << (generate_grow ? "bool" : "void") << endl - << "init (image_type&, const value_type&, " << - db << "::statement_kind);" - << endl; - - // init (value, image) - // - os << "static void" << endl - << "init (value_type&, const image_type&, database*);" - << endl; - - if (!has_a (c, test_container)) - { - // get_null (image) - // - os << "static bool" << endl - << "get_null (const image_type&);" - << endl; - - // set_null (image) - // - os << "static void" << endl - << "set_null (image_type&, " << db << "::statement_kind);" - << endl; - } - - os << "};"; - } + traverse_composite (type&); private: instance<image_type> image_type_; @@ -1306,7 +1125,8 @@ namespace relational } } - os << "struct access::view_traits< " << type << " >::query_columns"; + os << "struct access::view_traits_impl< " << type << ", id_" << + db << " >::query_columns"; if (obj_count > 1) { @@ -1329,19 +1149,20 @@ namespace relational << "//" << endl << "typedef" << endl << "odb::pointer_query_columns<" << endl - << " " << otype << "," << endl; + << " " << otype << "," << endl + << " id_" << db << "," << endl; if (alias && (polymorphic (o) || table.qualified () || i->alias != table.uname ())) { string tag (escape (i->alias + "_alias_tag")); - os << " odb::alias_traits< " << otype << ", " << - tag << " > >" << endl; + os << " odb::alias_traits< " << otype << ", id_" << db << + ", " << tag << " > >" << endl; } else - os << " odb::access::object_traits< " << otype << - " > >" << endl; + os << " odb::access::object_traits_impl< " << otype << + ", id_" << db << " > >" << endl; os << oname << ";" << endl; @@ -1370,18 +1191,20 @@ namespace relational os << ":" << endl << " odb::pointer_query_columns<" << endl - << " " << otype << "," << endl; + << " " << otype << "," << endl + << " id_" << db << "," << endl; if (alias && (polymorphic (o) || table.qualified () || vo->alias != table.uname ())) { string tag (escape (vo->alias + "_alias_tag")); - os << " odb::alias_traits< " << otype << ", " << - tag << " > >"; + os << " odb::alias_traits< " << otype << ", id_" << + db << ", " << tag << " > >"; } else - os << " odb::access::object_traits< " << otype << " > >"; + os << " odb::access::object_traits_impl< " << otype << + ", id_" << db << " > >"; os << "{" << "};"; @@ -1410,9 +1233,12 @@ namespace relational virtual void generate () { - os << "#include <odb/details/buffer.hxx>" << endl - << "#include <odb/details/unused.hxx>" << endl - << endl; + os << "#include <odb/details/buffer.hxx>" << endl; + + if (options.generate_query ()) + os << "#include <odb/details/shared-ptr.hxx>" << endl; + + os << endl; os << "#include <odb/" << db << "/version.hxx>" << endl << "#include <odb/" << db << "/forward.hxx>" << endl diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index 76c29f1..466e348 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -14,68 +14,6 @@ namespace relational namespace inline_ { // - // - struct callback_calls: traversal::class_, virtual context - { - typedef callback_calls base; - - callback_calls () - { - *this >> inherits_ >> *this; - } - - callback_calls (callback_calls const&) - : root_context (), //@@ -Wextra - context () - { - *this >> inherits_ >> *this; - } - - virtual void - traverse (type& c, bool constant) - { - const_ = constant; - traverse (c); - } - - virtual void - traverse (type& c) - { - bool obj (object (c)); - - // Ignore transient bases. - // - if (!(obj || view (c))) - return; - - if (c.count ("callback")) - { - string name (c.get<string> ("callback")); - - // In case of the const instance, we only generate the call if - // there is a const callback. - // - string const& type (class_fq_name (c)); - - if (const_) - { - if (c.count ("callback-const")) - os << "static_cast< const " << type << "& > (x)." << - name << " (e, db);"; - } - else - os << "static_cast< " << type << "& > (x)." << name << " (e, db);"; - } - else if (obj) - inherits (c); - } - - protected: - bool const_; - traversal::inherits inherits_; - }; - - // // get/set null (composite value only) // @@ -121,7 +59,8 @@ namespace relational virtual void traverse_composite (member_info& mi) { - string traits ("composite_value_traits< " + mi.fq_type () + " >"); + string traits ("composite_value_traits< " + mi.fq_type () + ", id_" + + db.string () + " >"); if (get_) os << "r = r && " << traits << "::get_null (" << @@ -145,7 +84,8 @@ namespace relational if (!composite (c)) return; - string traits ("composite_value_traits< " + class_fq_name (c) + " >"); + string traits ("composite_value_traits< " + class_fq_name (c) + + ", id_" + db.string () + " >"); // If the derived value type is readonly, then set will never be // called with sk == statement_update. @@ -209,7 +149,7 @@ namespace relational if (object (c)) traverse_object (c); - if (view (c)) + else if (view (c)) traverse_view (c); else if (composite (c)) traverse_composite (c); @@ -226,11 +166,11 @@ namespace relational traverse_object (type& c) { semantics::data_member* id (id_member (c)); - bool base_id (id ? &id->scope () != &c : false); // Comes from base. + bool base_id (id && &id->scope () != &c); // Comes from base. semantics::data_member* optimistic (context::optimistic (c)); - // Base class the contains the object id and version for optimistic + // Base class that contains the object id and version for optimistic // concurrency. // type* base ( @@ -244,7 +184,8 @@ namespace relational bool reuse_abst (abst && !poly); string const& type (class_fq_name (c)); - string traits ("access::object_traits< " + type + " >"); + string traits ("access::object_traits_impl< " + type + ", id_" + + db.string () + " >"); os << "// " << class_name (c) << endl << "//" << endl @@ -252,39 +193,6 @@ namespace relational object_extra (c); - // id (object_type) - // - if (id != 0 || !reuse_abst) - { - os << "inline" << endl - << traits << "::id_type" << endl - << traits << "::" << endl - << "id (const object_type&" << (id != 0 ? " o" : "") << ")" - << "{"; - - if (id != 0) - { - if (base_id) - os << "return object_traits< " << class_fq_name (*base) << - " >::id (o);"; - else - { - // Get the id using the accessor expression. If this is not - // a synthesized expression, then output its location for - // easier error tracking. - // - member_access& ma (id->get<member_access> ("get")); - - if (!ma.synthesized) - os << "// From " << location_string (ma.loc, true) << endl; - - os << "return " << ma.translate ("o") << ";"; - } - } - - os << "}"; - } - if (id != 0 && base_id) { if (!poly_derived) @@ -298,8 +206,8 @@ namespace relational << traits << "::" << endl << "id (const image_type& i)" << "{" - << "return object_traits< " << class_fq_name (*base) << - " >::id (i);" + << "return object_traits_impl< " << class_fq_name (*base) << + ", id_" << db << " >::id (i);" << "}"; } @@ -312,8 +220,8 @@ namespace relational << traits << "::" << endl << "version (const image_type& i)" << "{" - << "return object_traits< " << class_fq_name (*base) << - " >::version (i);" + << "return object_traits_impl< " << class_fq_name (*base) << + ", id_" << db << " >::version (i);" << "}"; } } @@ -325,8 +233,8 @@ namespace relational << "bind (" << bind_vector << " b, id_image_type& i" << (optimistic != 0 ? ", bool bv" : "") << ")" << "{" - << "object_traits< " << class_fq_name (*base) << - " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");" + << "object_traits_impl< " << class_fq_name (*base) << ", id_" << + db << " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");" << "}"; os << "inline" << endl @@ -334,8 +242,8 @@ namespace relational << "init (id_image_type& i, const id_type& id" << (optimistic != 0 ? ", const version_type* v" : "") << ")" << "{" - << "object_traits< " << class_fq_name (*base) << - " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");" + << "object_traits_impl< " << class_fq_name (*base) << ", id_" << + db << " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");" << "}"; } @@ -390,7 +298,6 @@ namespace relational os << "}"; } - // // The rest does not apply to reuse-abstract objects. // if (reuse_abst) @@ -410,31 +317,6 @@ namespace relational << "}"; } - // callback () - // - os << "inline" << endl - << "void " << traits << "::" << endl - << "callback (database& db, object_type& x, callback_event e)" - << endl - << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << "ODB_POTENTIALLY_UNUSED (x);" - << "ODB_POTENTIALLY_UNUSED (e);" - << endl; - callback_calls_->traverse (c, false); - os << "}"; - - os << "inline" << endl - << "void " << traits << "::" << endl - << "callback (database& db, const object_type& x, callback_event e)" - << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << "ODB_POTENTIALLY_UNUSED (x);" - << "ODB_POTENTIALLY_UNUSED (e);" - << endl; - callback_calls_->traverse (c, true); - os << "}"; - // load_() // if (id != 0 && !(poly_derived || has_a (c, test_container))) @@ -464,7 +346,7 @@ namespace relational << "{"; if (poly_derived) - os << "return object_traits<base_type>::root_image (*i.base);"; + os << "return base_traits::root_image (*i.base);"; else os << "return i;"; @@ -479,9 +361,7 @@ namespace relational << "{"; if (poly_derived) - os << "typedef object_traits<base_type> base_traits;" - << endl - << "details::unique_ptr<base_traits::image_type> p (" << endl + os << "details::unique_ptr<base_traits::image_type> p (" << endl << "base_traits::clone_image (*i.base));" << "image_type* c (new image_type (i));" << "c->base = p.release ();" @@ -499,9 +379,7 @@ namespace relational << "{"; if (poly_derived) - os << "typedef object_traits<base_type> base_traits;" - << endl - << "base_traits::image_type* b (d.base);" + os << "base_traits::image_type* b (d.base);" << "base_traits::copy_image (*b, *s.base);" << "d = s;" // Overwrites the base pointer. << "d.base = b;"; @@ -518,7 +396,7 @@ namespace relational << "{"; if (poly_derived) - os << "object_traits<base_type>::free_image (i->base);"; + os << "base_traits::free_image (i->base);"; os << "delete i;" << "}"; @@ -534,34 +412,22 @@ namespace relational traverse_view (type& c) { string const& type (class_fq_name (c)); - string traits ("access::view_traits< " + type + " >"); + string traits ("access::view_traits_impl< " + type + ", id_" + + db.string () + " >"); os << "// " << class_name (c) << endl << "//" << endl << endl; view_extra (c); - - // callback () - // - os << "inline" << endl - << "void " << traits << "::" << endl - << "callback (database& db, view_type& x, callback_event e)" - << endl - << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << "ODB_POTENTIALLY_UNUSED (x);" - << "ODB_POTENTIALLY_UNUSED (e);" - << endl; - callback_calls_->traverse (c, false); - os << "}"; } virtual void traverse_composite (type& c) { string const& type (class_fq_name (c)); - string traits ("access::composite_value_traits< " + type + " >"); + string traits ("access::composite_value_traits< " + type + ", id_" + + db.string () + " >"); os << "// " << class_name (c) << endl << "//" << endl @@ -602,8 +468,6 @@ namespace relational } private: - instance<callback_calls> callback_calls_; - instance<null_base> get_null_base_; traversal::inherits get_null_base_inherits_; instance<null_member> get_null_member_; diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx index b2f982a..d802e54 100644 --- a/odb/relational/mssql/common.cxx +++ b/odb/relational/mssql/common.cxx @@ -202,7 +202,8 @@ namespace relational void member_image_type:: traverse_composite (member_info& mi) { - type_ = "composite_value_traits< " + mi.fq_type () + " >::image_type"; + type_ = "composite_value_traits< " + mi.fq_type () + + ", id_mssql >::image_type"; } void member_image_type:: diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx index b8fa4c3..9d62b6b 100644 --- a/odb/relational/mysql/common.cxx +++ b/odb/relational/mysql/common.cxx @@ -168,7 +168,8 @@ namespace relational void member_image_type:: traverse_composite (member_info& mi) { - type_ = "composite_value_traits< " + mi.fq_type () + " >::image_type"; + type_ = "composite_value_traits< " + mi.fq_type () + + ", id_mysql >::image_type"; } void member_image_type:: diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 494abf5..48ae1f2 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -346,7 +346,7 @@ namespace relational traverse_composite (member_info& mi) { os << "if (composite_value_traits< " << mi.fq_type () << - " >::grow (" << endl + ", id_mysql >::grow (" << endl << "i." << mi.var << "value, t + " << index_ << "UL))" << "{" << "grew = true;" diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx index e9d8390..04bb0f8 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/relational/oracle/common.cxx @@ -188,7 +188,8 @@ namespace relational void member_image_type:: traverse_composite (member_info& mi) { - type_ = "composite_value_traits< " + mi.fq_type () + " >::image_type"; + type_ = "composite_value_traits< " + mi.fq_type () + + ", id_oracle >::image_type"; } void member_image_type:: diff --git a/odb/relational/pgsql/common.cxx b/odb/relational/pgsql/common.cxx index 3d7e01e..811269e 100644 --- a/odb/relational/pgsql/common.cxx +++ b/odb/relational/pgsql/common.cxx @@ -144,7 +144,8 @@ namespace relational void member_image_type:: traverse_composite (member_info& mi) { - type_ = "composite_value_traits< " + mi.fq_type () + " >::image_type"; + type_ = "composite_value_traits< " + mi.fq_type () + + ", id_pgsql >::image_type"; } void member_image_type:: diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index 23600d0..332e25b 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -295,7 +295,7 @@ namespace relational traverse_composite (member_info& mi) { os << "if (composite_value_traits< " << mi.fq_type () << - " >::grow (" << endl + ", id_pgsql >::grow (" << endl << "i." << mi.var << "value, t + " << index_ << "UL))" << "{" << "grew = true;" @@ -642,7 +642,7 @@ namespace relational string const& n (class_fq_name (c)); string const& fn (flat_name (n)); - string traits ("access::object_traits< " + n + " >"); + string traits ("access::object_traits_impl< " + n + ", id_pgsql >"); os << "const char " << traits << "::" << endl << "persist_statement_name[] = " << strlit (fn + "_persist") << ";" @@ -792,7 +792,7 @@ namespace relational { string const& n (class_fq_name (c)); string const& fn (flat_name (n)); - string traits ("access::view_traits< " + n + " >"); + string traits ("access::view_traits_impl< " + n + ", id_pgsql >"); os << "const char " << traits << "::" << endl << "query_statement_name[] = " << strlit (fn + "_query") << ";" @@ -800,14 +800,19 @@ namespace relational } virtual void - object_query_statement_ctor_args (type&) + object_query_statement_ctor_args (type&, string const& q, bool prep) { - os << "sts.connection ()," << endl - << "query_statement_name," << endl - << "query_statement + q.clause ()," << endl - << "q.parameter_types ()," << endl - << "q.parameter_count ()," << endl - << "q.parameters_binding ()," << endl + os << "sts.connection ()," << endl; + + if (prep) + os << "n," << endl; + else + os << "query_statement_name," << endl; + + os << "query_statement + " << q << ".clause ()," << endl + << q << ".parameter_types ()," << endl + << q << ".parameter_count ()," << endl + << q << ".parameters_binding ()," << endl << "imb"; } @@ -823,21 +828,27 @@ namespace relational } virtual void - view_query_statement_ctor_args (type&) + view_query_statement_ctor_args (type&, string const& q, bool prep) { - os << "sts.connection ()," << endl - << "query_statement_name," << endl - << "qs.clause ()," << endl - << "qs.parameter_types ()," << endl - << "qs.parameter_count ()," << endl - << "qs.parameters_binding ()," << endl + os << "sts.connection ()," << endl; + + if (prep) + os << "n," << endl; + else + os << "query_statement_name," << endl; + + os << q << ".clause ()," << endl + << q << ".parameter_types ()," << endl + << q << ".parameter_count ()," << endl + << q << ".parameters_binding ()," << endl << "imb"; } virtual void - post_query_ (type&) + post_query_ (type&, bool once_off) { - os << "st->deallocate ();"; + if (once_off) + os << "st->deallocate ();"; } }; entry<class_> class_entry_; diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index f000858..5100739 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -305,497 +305,14 @@ namespace relational throw operation_failed (); } - process_access (m, "get"); - process_access (m, "set"); - process_index (m); - } - - // - // Process member access expressions. - // - - enum found_type - { - found_none, - found_some, // Found something but keep looking for a better one. - found_best - }; - - // Check if a function is a suitable accessor for this member. - // - found_type - check_accessor (semantics::data_member& m, - tree f, - string const& n, - member_access& ma, - bool strict) - { - // Must be const. - // - if (!DECL_CONST_MEMFUNC_P (f)) - return found_none; - - // Accessor is a function with no arguments (other than 'this'). - // - if (DECL_CHAIN (DECL_ARGUMENTS (f)) != NULL_TREE) - return found_none; - - // Note that to get the return type we have to use - // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as - // suggested in the documentation. - // - tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); - int tc (TREE_CODE (r)); - - // In the strict mode make sure the function returns for non-array - // types a value or a (const) reference to the member type and for - // array types a (const) pointer to element type. In the lax mode - // we just check that the return value is not void. - // - if (strict) - { - semantics::type& t (utype (m)); - semantics::array* ar (dynamic_cast<semantics::array*> (&t)); - - if (ar != 0 && tc != POINTER_TYPE) - return found_none; - - tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r); - tree bt_mv (TYPE_MAIN_VARIANT (bt)); - - if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) - return found_none; - } - else if (r == void_type_node) - return found_none; - - cxx_tokens& e (ma.expr); - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); - e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); - - // See if it returns by value. - // - ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE); - - return found_best; - } - - // Check if a function is a suitable modifier for this member. - // - found_type - check_modifier (semantics::data_member& m, - tree f, - string const& n, - member_access& ma, - bool strict) - { - tree a (DECL_ARGUMENTS (f)); - a = DECL_CHAIN (a); // Skip this. - - // For a modifier, it can either be a function that returns a non- - // const reference (or non-const pointer, in case the member is an - // array) or a by-value modifier that sets a new value. If both are - // available, we prefer the former for efficiency. - // - cxx_tokens& e (ma.expr); - semantics::type& t (utype (m)); - semantics::array* ar (dynamic_cast<semantics::array*> (&t)); - - if (a == NULL_TREE) + if (m.count ("polymorphic-ref")) { - // Note that to get the return type we have to use - // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as - // suggested in the documentation. - // - tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); - int tc (TREE_CODE (r)); - - // By-reference modifier. Should return a reference or a pointer. - // - if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE)) - return found_none; - - // The base type should not be const and, in strict mode, should - // match the member type. - // - tree bt (TREE_TYPE (r)); - - if (CP_TYPE_CONST_P (bt)) - return found_none; - - tree bt_mv (TYPE_MAIN_VARIANT (bt)); - - if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) - return found_none; - - e.clear (); // Could contain by value modifier. - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); - e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); - - return found_best; + m.set ("not-null", true); + m.set ("deferred", false); + m.set ("on-delete", sema_rel::foreign_key::cascade); } - // Otherwise look for a by value modifier, which is a function - // with a single argument. - // - else if (DECL_CHAIN (a) == NULL_TREE) - { - // In the lax mode any function with a single argument works - // for us. And we don't care what it returns. - // - if (strict) - { - // In the strict mode make sure the argument matches the - // member. This is exactly the same logic as in accessor - // with regards to arrays, references, etc. - // - tree at (TREE_TYPE (a)); - int tc (TREE_CODE (at)); - - if (ar != 0 && tc != POINTER_TYPE) - return found_none; - - tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at); - tree bt_mv (TYPE_MAIN_VARIANT (bt)); - - if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) - return found_none; - } - - if (e.empty ()) - { - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); - e.push_back (cxx_token (0, CPP_QUERY)); - e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); - - // Continue searching in case there is version that returns a - // non-const reference which we prefer for efficiency. - // - return found_some; - } - else - return found_none; // We didn't find anything better. - } - - return found_none; - } - - void - process_access (semantics::data_member& m, std::string const& k) - { - bool virt (m.count ("virtual")); - - // Ignore certain special virtual members. - // - if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator"))) - return; - - char const* kind (k == "get" ? "accessor" : "modifier"); - semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ())); - - // If we don't have an access expression, try to come up with - // one. - // - if (!m.count (k)) - { - found_type found (found_none); - semantics::access const& a (m.named ().access ()); - member_access& ma (m.set (k, member_access (m.location (), true))); - - // If this member is not virtual and is either public or if we - // are a friend of this class, then go for the member directly. - // - if (!virt && (a == semantics::access::public_ || - c.get<bool> ("friend"))) - { - ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this")); - ma.expr.push_back (cxx_token (0, CPP_DOT)); - ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ())); - found = found_best; - } - - // Otherwise, try to find a suitable accessor/modifier. - // - - // First try the original name. If that doesn't produce anything, - // then try the public name. - // - bool t (k == "get" - ? options.accessor_regex_trace () - : options.modifier_regex_trace ()); - regex_mapping const& re ( - k == "get" ? accessor_regex : modifier_regex); - - for (unsigned short j (0); found != found_best && j != 2; ++j) - { - string b (j == 0 ? m.name () : public_name (m, false)); - - // Skip the second pass if original and public names are the same. - // - if (j == 1 && b == m.name ()) - continue; - - if (t) - cerr << kind << (j == 0 ? " original" : " public") - << " name '" << b << "'" << endl; - - for (regex_mapping::const_iterator i (re.begin ()); - found != found_best && i != re.end (); - ++i) - { - if (t) - cerr << "try: '" << i->regex () << "' : "; - - if (!i->match (b)) - { - if (t) - cerr << '-' << endl; - continue; - } - - string n (i->replace (b)); - - if (t) - cerr << "'" << n << "' : "; - - tree decl ( - lookup_qualified_name ( - c.tree_node (), get_identifier (n.c_str ()), false, false)); - - if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) - { - if (t) - cerr << '-' << endl; - continue; - } - - // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. - // - for (tree o (BASELINK_FUNCTIONS (decl)); - o != 0; - o = OVL_NEXT (o)) - { - tree f (OVL_CURRENT (o)); - - // We are only interested in public non-static member - // functions. Note that TREE_PUBLIC() returns something - // other than what we need. - // - if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) || - TREE_PRIVATE (f) || TREE_PROTECTED (f)) - continue; - - found_type r (k == "get" - ? check_accessor (m, f, n, ma, true) - : check_modifier (m, f, n, ma, true)); - - if (r != found_none) - { - // Update the location of the access expression to point - // to this function. - // - ma.loc = location (DECL_SOURCE_LOCATION (f)); - found = r; - } - } - - if (t) - cerr << (found != found_none ? '+' : '-') << endl; - } - } - - // If that didn't work then the generated code won't be able - // to access this member. - // - if (found == found_none) - { - location const& l (m.location ()); - - if (virt) - { - error (l) << "no suitable " << kind << " function could be " - << "automatically found for virtual data member '" - << m.name () << "'" << endl; - - info (l) << "use '#pragma db " << k << "' to explicitly " - << "specify the " << kind << " function or " - << "expression" << endl; - } - else - { - error (l) << "data member '" << m.name () << "' is " - << a.string () << " and no suitable " << kind - << " function could be automatically found" << endl; - info (l) << "consider making class 'odb::access' a friend of " - << "class '" << class_name (c) << "'" << endl; - - info (l) << "or use '#pragma db " << k << "' to explicitly " - << "specify the " << kind << " function or " - << "expression" << endl; - } - - throw operation_failed (); - } - } - - member_access& ma (m.get<member_access> (k)); - cxx_tokens& e (ma.expr); - - // If it is just a name, resolve it and convert to an appropriate - // expression. - // - if (e.size () == 1 && e.back ().type == CPP_NAME) - { - string n (e.back ().literal); - e.clear (); - - tree decl ( - lookup_qualified_name ( - c.tree_node (), get_identifier (n.c_str ()), false, false)); - - if (decl == error_mark_node) - { - error (ma.loc) << "unable to resolve data member or function " - << "name '" << n << "'" << endl; - throw operation_failed (); - } - - switch (TREE_CODE (decl)) - { - case FIELD_DECL: - { - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - break; - } - case BASELINK: - { - // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. - // - for (tree o (BASELINK_FUNCTIONS (decl)); - o != 0; - o = OVL_NEXT (o)) - { - tree f (OVL_CURRENT (o)); - - // We are only interested in non-static member functions. - // - if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f)) - continue; - - if ((k == "get" - ? check_accessor (m, f, n, ma, false) - : check_modifier (m, f, n, ma, false)) == found_best) - break; - } - - if (e.empty ()) - { - error (ma.loc) << "unable to find suitable " << kind - << " function '" << n << "'" << endl; - throw operation_failed (); - } - break; - } - default: - { - error (ma.loc) << "name '" << n << "' does not refer to a data " - << "member or function" << endl; - throw operation_failed (); - } - } - } - - // If there is no 'this' keyword, then add it as a prefix. - // - { - bool t (false); - for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i) - { - if (i->type == CPP_KEYWORD && i->literal == "this") - { - t = true; - break; - } - } - - if (!t) - { - e.insert (e.begin (), cxx_token (0, CPP_DOT)); - e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this")); - } - } - - // Check that there is no placeholder in the accessor expression. - // - if (k == "get" && ma.placeholder ()) - { - error (ma.loc) << "(?) placeholder in the accessor expression" - << endl; - throw operation_failed (); - } - - // Check that the member type is default-constructible if we - // have a by value modifier. - // - if (k == "set" && ma.placeholder ()) - { - semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m))); - - // Assume all other types are default-constructible. - // - if (c != 0) - { - // If this type is a class template instantiation, then make - // sure it is instantiated. While types used in real members - // will be instantiated, this is not necessarily the case for - // virtual members. Without the instantiation we won't be able - // to detect whether the type has the default ctor. - // - // It would have been cleaner to do it in post_process_pragmas() - // but there we don't yet know whether we need the default ctor. - // And it is a good idea not to require instantiability unless - // we really need it. - // - tree type (c->tree_node ()); - - if (!COMPLETE_TYPE_P (type) && - CLASSTYPE_TEMPLATE_INSTANTIATION (type)) - { - // Reset input location so that we get nice diagnostics in - // case of an error. Use the location of the virtual pragma. - // - location_t loc (m.get<location_t> ("virtual-location")); - input_location = loc; - - if (instantiate_class_template (type) == error_mark_node || - errorcount != 0 || - !COMPLETE_TYPE_P (type)) - { - error (loc) << "unable to instantiate virtual data member " << - "type" << endl; - throw operation_failed (); - } - } - - if (!c->default_ctor ()) - { - error (ma.loc) << "modifier expression requires member type " << - "to be default-constructible" << endl; - throw operation_failed (); - } - } - } + process_index (m); } // Convert index/unique specifiers to the index entry in the object. @@ -2351,44 +1868,7 @@ namespace relational struct class_: traversal::class_, context { class_ () - : std_string_ (0), std_string_hint_ (0), access_ (0) { - // Resolve the std::string type node. - // - using semantics::scope; - - for (scope::names_iterator_pair ip (unit.find ("std")); - ip.first != ip.second; ++ip.first) - { - if (scope* ns = dynamic_cast<scope*> (&ip.first->named ())) - { - scope::names_iterator_pair jp (ns->find ("string")); - - if (jp.first != jp.second) - { - std_string_ = dynamic_cast<semantics::type*> ( - &jp.first->named ()); - std_string_hint_ = &*jp.first; - break; - } - } - } - - assert (std_string_ != 0); // No std::string? - - // Resolve odb::access, if any. - // - tree odb = lookup_qualified_name ( - global_namespace, get_identifier ("odb"), false, false); - - if (odb != error_mark_node) - { - access_ = lookup_qualified_name ( - odb, get_identifier ("access"), true, false); - - access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0); - } - *this >> member_names_ >> member_; } @@ -2400,28 +1880,12 @@ namespace relational if (k == class_other) return; - // Check if odb::access is a friend of this class. - // - c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_)); - - // Assign pointer. - // - if (k == class_object || k == class_view) - assign_pointer (c); - - // Do some additional pre-processing. - // - if (k == class_object) - traverse_object_pre (c); - names (c); - // Do some additional post-processing. - // if (k == class_object) - traverse_object_post (c); + traverse_object (c); else if (k == class_view) - traverse_view_post (c); + traverse_view (c); } // @@ -2429,149 +1893,7 @@ namespace relational // virtual void - traverse_object_pre (type& c) - { - semantics::class_* poly_root (polymorphic (c)); - - // Determine whether it is a session object. - // - if (!c.count ("session")) - { - // If this is a derived class in a polymorphic hierarchy, - // then it should have the same session value as the root. - // - if (poly_root != 0 && poly_root != &c) - c.set ("session", session (*poly_root)); - else - { - // See if any of the namespaces containing this class specify - // the session value. - // - bool found (false); - for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) - { - using semantics::namespace_; - - namespace_* ns (dynamic_cast<namespace_*> (s)); - - if (ns == 0) - continue; // Some other scope. - - if (ns->extension ()) - ns = &ns->original (); - - if (ns->count ("session")) - { - c.set ("session", ns->get<bool> ("session")); - found = true; - break; - } - - if (ns->global_scope ()) - break; - } - - // If still not found, then use the default value. - // - if (!found) - c.set ("session", options.generate_session ()); - } - } - - if (session (c)) - features.session_object = true; - - if (poly_root != 0) - { - using namespace semantics; - - semantics::data_member& idm (*id_member (*poly_root)); - - if (poly_root != &c) - { - // If we are a derived class in the polymorphic persistent - // class hierarchy, then add a synthesized virtual pointer - // member that points back to the root. - // - path const& f (idm.file ()); - size_t l (idm.line ()), col (idm.column ()); - - semantics::data_member& m ( - unit.new_node<semantics::data_member> (f, l, col, tree (0))); - m.set ("virtual", true); - - // Make it the first member in the class. - // - node_position<type, scope::names_iterator> np (c, c.names_end ()); - unit.new_edge<semantics::names> ( - np, m, idm.name (), access::public_); - - // Use the raw pointer as this member's type. - // - if (!poly_root->pointed_p ()) - { - // Create the pointer type in the graph. The pointer node - // in GCC seems to always be present, even if not explicitly - // used in the translation unit. - // - tree t (poly_root->tree_node ()); - tree ptr (TYPE_POINTER_TO (t)); - assert (ptr != 0); - ptr = TYPE_MAIN_VARIANT (ptr); - pointer& p (unit.new_node<pointer> (f, l, col, ptr)); - unit.insert (ptr, p); - unit.new_edge<points> (p, *poly_root); - assert (poly_root->pointed_p ()); - } - - unit.new_edge<belongs> (m, poly_root->pointed ().pointer ()); - - m.set ("not-null", true); - m.set ("deferred", false); - m.set ("on-delete", sema_rel::foreign_key::cascade); - - // Mark it as a special kind of id. - // - m.set ("id", true); - m.set ("polymorphic-ref", true); - } - else - { - // If we are a root of the polymorphic persistent class hierarchy, - // then add a synthesized virtual member for the discriminator. - // Use the location of the polymorphic pragma as the location of - // this member. - // - location_t loc (c.get<location_t> ("polymorphic-location")); - semantics::data_member& m ( - unit.new_node<semantics::data_member> ( - path (LOCATION_FILE (loc)), - LOCATION_LINE (loc), - LOCATION_COLUMN (loc), - tree (0))); - m.set ("virtual", true); - - // Insert it after the id member (or first if this id comes - // from reuse-base). - // - node_position<type, scope::names_iterator> np ( - c, c.find (idm.named ())); - unit.new_edge<semantics::names> ( - np, m, "typeid_", access::public_); - - belongs& edge (unit.new_edge<belongs> (m, *std_string_)); - edge.hint (*std_string_hint_); - - m.set ("readonly", true); - m.set ("discriminator", true); - - c.set ("discriminator", &m); - } - } - } - - virtual void - traverse_object_post (type& c) + traverse_object (type& c) { // Process indexes. Here we need to do two things: resolve member // names to member paths and assign names to unnamed indexes. We @@ -2767,7 +2089,7 @@ namespace relational typedef vector<relationship> relationships; virtual void - traverse_view_post (type& c) + traverse_view (type& c) { bool has_q (c.count ("query")); bool has_o (c.count ("objects")); @@ -3154,417 +2476,11 @@ namespace relational view_object& pointee_; }; - void - assign_pointer (type& c) - { - location_t loc (0); // Pragma location, or 0 if not used. - - try - { - string ptr; - string const& type (class_fq_name (c)); - - tree decl (0); // Resolved template node. - string decl_name; // User-provided template name. - tree resolve_scope (0); // Scope in which we resolve names. - - class_pointer const* cp (0); - bool cp_template (false); - - if (c.count ("pointer")) - { - cp = &c.get<class_pointer> ("pointer"); - } - // If we are a derived type in polymorphic hierarchy, then use - // our root's pointer type by default. - // - else if (semantics::class_* r = polymorphic (c)) - { - if (&c != r && r->count ("pointer-template")) - cp = r->get<class_pointer const*> ("pointer-template"); - } - - if (cp != 0) - { - string const& p (cp->name); - - if (p == "*") - { - ptr = type + "*"; - cp_template = true; - } - else if (p[p.size () - 1] == '*') - ptr = p; - else if (p.find ('<') != string::npos) - { - // Template-id. - // - ptr = p; - decl_name.assign (p, 0, p.find ('<')); - } - else - { - // This is not a template-id. Resolve it and see if it is a - // template or a type. - // - decl = resolve_name (p, cp->scope, true); - int tc (TREE_CODE (decl)); - - if (tc == TYPE_DECL) - { - ptr = p; - - // This can be a typedef'ed alias for a TR1 template-id. - // - if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl))) - { - decl = TI_TEMPLATE (ti); // DECL_TEMPLATE - - // Get to the most general template declaration. - // - while (DECL_TEMPLATE_INFO (decl)) - decl = DECL_TI_TEMPLATE (decl); - } - else - decl = 0; // Not a template. - } - else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) - { - ptr = p + "< " + type + " >"; - decl_name = p; - cp_template = true; - } - else - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "does not name a type or a template" << endl; - - throw operation_failed (); - } - } - - // Resolve scope is the scope of the pragma. - // - resolve_scope = cp->scope; - loc = cp->loc; - } - else - { - // See if any of the namespaces containing this class specify - // a pointer. - // - for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) - { - using semantics::namespace_; - - namespace_* ns (dynamic_cast<namespace_*> (s)); - - if (ns == 0) - continue; // Some other scope. - - if (ns->extension ()) - ns = &ns->original (); - - if (!ns->count ("pointer")) - { - if (ns->global_scope ()) - break; - else - continue; - } - - cp = &ns->get<class_pointer> ("pointer"); - string const& p (cp->name); - - // Namespace-specified pointer can only be '*' or are template. - // - if (p == "*") - ptr = type + "*"; - else if (p[p.size () - 1] == '*') - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "at namespace level cannot be a raw pointer" << endl; - } - else if (p.find ('<') != string::npos) - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "at namespace level cannot be a template-id" << endl; - } - else - { - // Resolve this name and make sure it is a template. - // - decl = resolve_name (p, cp->scope, true); - int tc (TREE_CODE (decl)); - - if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) - { - ptr = p + "< " + type + " >"; - decl_name = p; - } - else - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "does not name a template" << endl; - } - } - - if (ptr.empty ()) - throw operation_failed (); - - cp_template = true; - - // Resolve scope is the scope of the pragma. - // - resolve_scope = cp->scope; - loc = cp->loc; - break; - } - - // Use the default pointer. - // - if (ptr.empty ()) - { - string const& p (options.default_pointer ()); - - if (p == "*") - ptr = type + "*"; - else - { - ptr = p + "< " + type + " >"; - decl_name = p; - } - - // Resolve scope is the scope of the class. - // - resolve_scope = c.scope ().tree_node (); - } - } - - // If this class is a root of a polymorphic hierarchy, then cache - // the pointer template so that we can use it for derived classes. - // - if (cp != 0 && cp_template && polymorphic (c) == &c) - c.set ("pointer-template", cp); - - // Check if we are using TR1. - // - if (decl != 0 || !decl_name.empty ()) - { - bool& tr1 (features.tr1_pointer); - bool& boost (features.boost_pointer); - - // First check the user-supplied name. - // - tr1 = tr1 - || decl_name.compare (0, 8, "std::tr1") == 0 - || decl_name.compare (0, 10, "::std::tr1") == 0; - - // If there was no match, also resolve the name since it can be - // a using-declaration for a TR1 template. - // - if (!tr1) - { - if (decl == 0) - decl = resolve_name (decl_name, resolve_scope, false); - - if (TREE_CODE (decl) != TEMPLATE_DECL || ! - DECL_CLASS_TEMPLATE_P (decl)) - { - // This is only checked for the --default-pointer option. - // - error (c.file (), c.line (), c.column ()) - << "name '" << decl_name << "' specified with the " - << "--default-pointer option does not name a class " - << "template" << endl; - - throw operation_failed (); - } - - string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); - - // In case of a boost TR1 implementation, we cannot distinguish - // between the boost:: and std::tr1:: usage since the latter is - // just a using-declaration for the former. - // - tr1 = tr1 - || n.compare (0, 8, "std::tr1") == 0 - || n.compare (0, 10, "::std::tr1") == 0; - - boost = boost - || n.compare (0, 17, "boost::shared_ptr") == 0 - || n.compare (0, 19, "::boost::shared_ptr") == 0; - } - } - - // Fully-qualify all the unqualified components of the name. - // - try - { - lex_.start (ptr); - ptr.clear (); - - string t; - bool punc (false); - bool scoped (false); - - for (cpp_ttype tt (lex_.next (t)); - tt != CPP_EOF; - tt = lex_.next (t)) - { - if (punc && tt > CPP_LAST_PUNCTUATOR) - ptr += ' '; - - punc = false; - - switch (static_cast<unsigned> (tt)) - { - case CPP_LESS: - { - ptr += "< "; - break; - } - case CPP_GREATER: - { - ptr += " >"; - break; - } - case CPP_COMMA: - { - ptr += ", "; - break; - } - case CPP_NAME: - { - // If the name was not preceeded with '::', look it - // up in the pragmas's scope and add the qualifer. - // - if (!scoped) - { - tree decl (resolve_name (t, resolve_scope, false)); - tree scope (CP_DECL_CONTEXT (decl)); - - if (scope != global_namespace) - { - ptr += "::"; - ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER); - } - - ptr += "::"; - } - - ptr += t; - punc = true; - break; - } - case CPP_KEYWORD: - case CPP_NUMBER: - { - ptr += t; - punc = true; - break; - } - default: - { - ptr += t; - break; - } - } - - scoped = (tt == CPP_SCOPE); - } - } - catch (cxx_lexer::invalid_input const&) - { - throw operation_failed (); - } - - c.set ("object-pointer", ptr); - } - catch (invalid_name const& ex) - { - if (loc != 0) - error (loc) - << "name '" << ex.name () << "' specified with db pragma " - << "pointer is invalid" << endl; - else - error (c.file (), c.line (), c.column ()) - << "name '" << ex.name () << "' specified with the " - << "--default-pointer option is invalid" << endl; - - - throw operation_failed (); - } - catch (unable_to_resolve const& ex) - { - if (loc != 0) - error (loc) - << "unable to resolve name '" << ex.name () << "' specified " - << "with db pragma pointer" << endl; - else - error (c.file (), c.line (), c.column ()) - << "unable to resolve name '" << ex.name () << "' specified " - << "with the --default-pointer option" << endl; - - throw operation_failed (); - } - } - - private: - struct invalid_name - { - invalid_name (string const& n): name_ (n) {} - - string const& - name () const {return name_;} - - private: - string name_; - }; - - typedef lookup::unable_to_resolve unable_to_resolve; - - tree - resolve_name (string const& qn, tree scope, bool is_type) - { - try - { - string tl; - tree tn; - cpp_ttype tt, ptt; - - nlex_.start (qn); - tt = nlex_.next (tl, &tn); - - string name; - return lookup::resolve_scoped_name ( - nlex_, tt, tl, tn, ptt, scope, name, is_type); - } - catch (cxx_lexer::invalid_input const&) - { - throw invalid_name (qn); - } - catch (lookup::invalid_name const&) - { - throw invalid_name (qn); - } - } - private: cxx_string_lexer lex_; - cxx_string_lexer nlex_; // Nested lexer. data_member member_; traversal::names member_names_; - - semantics::type* std_string_; - semantics::names* std_string_hint_; - - tree access_; // odb::access node. }; } diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index 56857f3..63ab860 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -864,7 +864,8 @@ namespace relational ++end; // Transform the range from [begin, end] to [begin, end). string const& type (class_fq_name (c)); - string traits ("access::object_traits< " + type + " >"); + string traits ("access::object_traits_impl< " + type + ", id_" + + db.string () + " >"); // create_schema () // @@ -941,7 +942,8 @@ namespace relational os << "static const schema_catalog_entry" << endl << "schema_catalog_entry_" << flat_name (type) << "_ (" << endl - << strlit (options.schema_name ()) << "," << endl + << "id_" << db << "," << endl + << strlit (options.schema_name ()[db]) << "," << endl << "&" << traits << "::create_schema);" << endl; } diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 5a02703..7516801 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -18,8 +18,8 @@ traverse_object (type& c) using semantics::data_member; data_member* id (id_member (c)); - bool auto_id (id ? id->count ("auto") : false); - bool base_id (id ? &id->scope () != &c : false); // Comes from base. + bool auto_id (id && auto_ (*id)); + bool base_id (id && &id->scope () != &c); // Comes from base. member_access* id_ma (id ? &id->get<member_access> ("get") : 0); bool has_ptr (has_a (c, test_pointer)); @@ -49,7 +49,8 @@ traverse_object (type& c) } string const& type (class_fq_name (c)); - string traits ("access::object_traits< " + type + " >"); + string traits ("access::object_traits_impl< " + type + ", id_" + + db.string () + " >"); column_count_type const& cc (column_count (c)); os << "// " << class_name (c) << endl @@ -187,7 +188,7 @@ traverse_object (type& c) << "//" << endl << "if (--d != 0)" << "{" - << "if (object_traits<base_type>::grow (*i.base, " << + << "if (base_traits::grow (*i.base, " << "t + " << cols << "UL" << (poly_base != poly_root ? ", d" : "") << "))" << endl << "i.base->version++;" @@ -268,7 +269,7 @@ traverse_object (type& c) os << "// " << class_name (*poly_base) << " base" << endl << "//" << endl << "if (sk == statement_select)" << endl - << "object_traits<base_type>::"; + << "base_traits::"; if (poly_base == poly_root) os << "bind (b + n, *i.base, sk);"; @@ -360,7 +361,7 @@ traverse_object (type& c) os << "// " << class_name (*poly_base) << " base" << endl << "//" << endl << "if (--d != 0)" << endl - << "object_traits<base_type>::init (o, *i.base, db" << + << "base_traits::init (o, *i.base, db" << (poly_base != poly_root ? ", d" : "") << ");" << endl; } @@ -450,7 +451,8 @@ traverse_object (type& c) << "typeid (" << type << ")," << endl; if (poly_derived) - os << "&object_traits< " << class_fq_name (*poly_base) << " >::info"; + os << "&object_traits_impl< " << class_fq_name (*poly_base) << + ", id_" << db << " >::info"; else os << "0"; @@ -463,7 +465,7 @@ traverse_object (type& c) os << "," << endl << strlit (string (n, 2, string::npos)) << "," << endl << "&odb::create_impl< " << type << " >," << endl - << "&odb::dispatch_impl< " << type << " >," << endl; + << "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl; if (poly_derived) os << "&statements_type::delayed_loader"; @@ -883,7 +885,7 @@ traverse_object (type& c) // hierarchy. // if (poly_derived) - os << "object_traits<base_type>::persist (db, obj, false, false);" + os << "base_traits::persist (db, obj, false, false);" << endl; os << "image_type& im (sts.image ());" @@ -1135,7 +1137,7 @@ traverse_object (type& c) // if (!readonly_base) { - os << "object_traits<base_type>::update (db, obj, false, false);" + os << "base_traits::update (db, obj, false, false);" << endl; } else @@ -1550,7 +1552,7 @@ traverse_object (type& c) // Call our base last (we erase polymorphic objects from base // to derived in order not to trigger cascading deletes). // - os << "object_traits<base_type>::erase (db, id, false, false);" + os << "base_traits::erase (db, id, false, false);" << endl; } @@ -1787,7 +1789,7 @@ traverse_object (type& c) // Call our base last (we erase polymorphic objects from base // to derived in order not to trigger cascading deletes). // - os << "object_traits<base_type>::erase (db, obj, false, false);" + os << "base_traits::erase (db, obj, false, false);" << endl; } @@ -2349,7 +2351,7 @@ traverse_object (type& c) if (poly_derived) os << "if (--d != 0)" << endl - << "object_traits<base_type>::load_ (sts.base_statements (), obj" << + << "base_traits::load_ (sts.base_statements (), obj" << (poly_base != poly_root ? ", d" : "") << ");" << endl; @@ -2562,60 +2564,6 @@ traverse_object (type& c) if (options.generate_query ()) { - // query () - // - os << "result< " << traits << "::object_type >" << endl - << traits << "::" << endl - << "query (database&, const query_base_type& q)" - << "{" - << "using namespace " << db << ";" - << "using odb::details::shared;" - << "using odb::details::shared_ptr;" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << endl - << "statements_type& sts (" << endl - << "conn.statement_cache ().find_object<object_type> ());" - << endl; - - // Rebind the image if necessary. - // - os << "image_type& im (sts.image ());" - << "binding& imb (sts.select_image_binding (" << - (poly_derived ? "depth" : "") << "));" - << endl; - - if (poly_derived) - { - os << "if (imb.version == 0 ||" << endl - << "check_version (sts.select_image_versions (), im))" - << "{" - << "bind (imb.bind, 0, 0, im, statement_select);" - << "update_version (sts.select_image_versions ()," << endl - << "im," << endl - << "sts.select_image_bindings ());" - << "}"; - } - else - { - os << "if (im.version != sts.select_image_version () ||" << endl - << "imb.version == 0)" - << "{" - << "bind (imb.bind, im, statement_select);" - << "sts.select_image_version (im.version);" - << "imb.version++;" - << "}"; - } - - os << "shared_ptr<select_statement> st (" << endl - << "new (shared) select_statement (" << endl; - object_query_statement_ctor_args (c); - os << "));" << endl - << "st->execute ();"; - - post_query_ (c); - char const* result_type; if (poly) result_type = "polymorphic_object_result_impl<object_type>"; @@ -2624,13 +2572,73 @@ traverse_object (type& c) else result_type = "no_id_object_result_impl<object_type>"; - os << endl - << "shared_ptr< odb::" << result_type << " > r (" << endl - << "new (shared) " << db << "::" << result_type << " (" << endl - << "q, st, sts));" - << endl - << "return result<object_type> (r);" - << "}"; + // Unprepared. + // + if (!options.omit_unprepared ()) + { + // query () + // + os << "result< " << traits << "::object_type >" << endl + << traits << "::" << endl + << "query (database&, const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object<object_type> ());" + << endl; + + // Rebind the image if necessary. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.select_image_binding (" << + (poly_derived ? "depth" : "") << "));" + << endl; + + if (poly_derived) + { + os << "if (imb.version == 0 ||" << endl + << "check_version (sts.select_image_versions (), im))" + << "{" + << "bind (imb.bind, 0, 0, im, statement_select);" + << "update_version (sts.select_image_versions ()," << endl + << "im," << endl + << "sts.select_image_bindings ());" + << "}"; + } + else + { + os << "if (im.version != sts.select_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_select);" + << "sts.select_image_version (im.version);" + << "imb.version++;" + << "}"; + } + + os << "q.init_parameters ();" + << "shared_ptr<select_statement> st (" << endl + << "new (shared) select_statement (" << endl; + object_query_statement_ctor_args (c, "q", false); + os << "));" << endl + << "st->execute ();"; + + post_query_ (c, true); + + os << endl + << "shared_ptr< odb::" << result_type << " > r (" << endl + << "new (shared) " << db << "::" << result_type << " (" << endl + << "q, st, sts));" + << endl + << "return result<object_type> (r);" + << "}"; + } // erase_query // @@ -2642,12 +2650,142 @@ traverse_object (type& c) << db << "::connection& conn (" << endl << db << "::transaction::current ().connection ());" << endl + << "q.init_parameters ();" << "delete_statement st (" << endl; object_erase_query_statement_ctor_args (c); os << ");" << endl << "return st.execute ();" << "}"; + + // Prepared. Very similar to unprepared but has some annoying variations + // that make it difficult to factor out something common. + // + if (options.generate_prepared ()) + { + os << "odb::details::shared_ptr<prepared_query_impl>" << endl + << traits << "::" << endl + << "prepare_query (connection& c, const char* n, " << + "const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::connection& conn (" << endl + << "static_cast<" << db << "::connection&> (c));" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object<object_type> ());" + << endl; + + // Rebind the image if necessary. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.select_image_binding (" << + (poly_derived ? "depth" : "") << "));" + << endl; + + if (poly_derived) + { + os << "if (imb.version == 0 ||" << endl + << "check_version (sts.select_image_versions (), im))" + << "{" + << "bind (imb.bind, 0, 0, im, statement_select);" + << "update_version (sts.select_image_versions ()," << endl + << "im," << endl + << "sts.select_image_bindings ());" + << "}"; + } + else + { + os << "if (im.version != sts.select_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_select);" + << "sts.select_image_version (im.version);" + << "imb.version++;" + << "}"; + } + + os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl + << "new (shared) " << db << "::prepared_query_impl (conn));" + << "r->name = n;" + << "r->execute = &execute_query;" + << "r->query = q;" + << "r->stmt.reset (" << endl + << "new (shared) select_statement (" << endl; + object_query_statement_ctor_args (c, "r->query", true); + os << "));" + << endl + << "return r;" + << "}"; + + os << "odb::details::shared_ptr<result_impl>" << endl + << traits << "::" << endl + << "execute_query (prepared_query_impl& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::prepared_query_impl& pq (" << endl + << "static_cast<" << db << "::prepared_query_impl&> (q));" + << "shared_ptr<select_statement> st (" << endl + << "odb::details::inc_ref (" << endl + << "static_cast<select_statement*> (pq.stmt.get ())));" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "// The connection used by the current transaction and the" << endl + << "// one used to prepare this statement must be the same." << endl + << "//" << endl + << "assert (&conn == &st->connection ());" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object<object_type> ());" + << endl; + + // Rebind the image if necessary. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.select_image_binding (" << + (poly_derived ? "depth" : "") << "));" + << endl; + + if (poly_derived) + { + os << "if (imb.version == 0 ||" << endl + << "check_version (sts.select_image_versions (), im))" + << "{" + << "bind (imb.bind, 0, 0, im, statement_select);" + << "update_version (sts.select_image_versions ()," << endl + << "im," << endl + << "sts.select_image_bindings ());" + << "}"; + } + else + { + os << "if (im.version != sts.select_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_select);" + << "sts.select_image_version (im.version);" + << "imb.version++;" + << "}"; + } + + os << "pq.query.init_parameters ();" + << "st->execute ();"; + post_query_ (c, false); + + os << endl + << "return shared_ptr<result_impl> (" << endl + << "new (shared) " << db << "::" << result_type << " (" << endl + << "pq.query, st, sts));" + << "}"; + } } if (embedded_schema) @@ -2658,7 +2796,8 @@ void relational::source::class_:: traverse_view (type& c) { string const& type (class_fq_name (c)); - string traits ("access::view_traits< " + type + " >"); + string traits ("access::view_traits_impl< " + type + ", id_" + + db.string () + " >"); os << "// " << class_name (c) << endl << "//" << endl @@ -3391,54 +3530,157 @@ traverse_view (type& c) << "}"; } - // query () + // Unprepared. // - os << "result< " << traits << "::view_type >" << endl - << traits << "::" << endl - << "query (database&, const query_base_type& q)" - << "{" - << "using namespace " << db << ";" - << "using odb::details::shared;" - << "using odb::details::shared_ptr;" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << endl - << "view_statements< view_type >& sts (" << endl - << "conn.statement_cache ().find_view<view_type> ());" - << endl - << "image_type& im (sts.image ());" - << "binding& imb (sts.image_binding ());" - << endl - << "if (im.version != sts.image_version () || imb.version == 0)" - << "{" - << "bind (imb.bind, im);" - << "sts.image_version (im.version);" - << "imb.version++;" - << "}"; + if (!options.omit_unprepared ()) + { + os << "result< " << traits << "::view_type >" << endl + << traits << "::" << endl + << "query (database&, const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_view<view_type> ());" + << endl + << "image_type& im (sts.image ());" + << "binding& imb (sts.image_binding ());" + << endl + << "if (im.version != sts.image_version () || imb.version == 0)" + << "{" + << "bind (imb.bind, im);" + << "sts.image_version (im.version);" + << "imb.version++;" + << "}"; - if (vq.kind == view_query::runtime) - os << "const query_base_type& qs (q);"; - else - os << "const query_base_type& qs (query_statement (q));"; + if (vq.kind == view_query::runtime) + os << "const query_base_type& qs (q);"; + else + os << "const query_base_type& qs (query_statement (q));"; - os << "shared_ptr<select_statement> st (" << endl - << "new (shared) select_statement (" << endl; + os << "qs.init_parameters ();" + << "shared_ptr<select_statement> st (" << endl + << "new (shared) select_statement (" << endl; + view_query_statement_ctor_args (c, "qs", false); + os << "));" << endl + << "st->execute ();"; - view_query_statement_ctor_args (c); + post_query_ (c, true); - os << "));" << endl - << "st->execute ();"; + os << endl + << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl + << "new (shared) " << db << "::view_result_impl<view_type> (" << endl + << "qs, st, sts));" + << endl + << "return result<view_type> (r);" + << "}"; + } - post_query_ (c); + // Prepared. Very similar to unprepared but has some annoying variations + // that make it difficult to factor out something common. + // + if (options.generate_prepared ()) + { + os << "odb::details::shared_ptr<prepared_query_impl>" << endl + << traits << "::" << endl + << "prepare_query (connection& c, const char* n, " << + "const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::connection& conn (" << endl + << "static_cast<" << db << "::connection&> (c));" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_view<view_type> ());" + << endl; - os << endl - << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl - << "new (shared) " << db << "::view_result_impl<view_type> (" << endl - << "qs, st, sts));" - << endl - << "return result<view_type> (r);" - << "}"; + // Rebind the image if necessary. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.image_binding ());" + << endl + << "if (im.version != sts.image_version () || imb.version == 0)" + << "{" + << "bind (imb.bind, im);" + << "sts.image_version (im.version);" + << "imb.version++;" + << "}"; + + os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl + << "new (shared) " << db << "::prepared_query_impl (conn));" + << "r->name = n;" + << "r->execute = &execute_query;"; + + if (vq.kind == view_query::runtime) + os << "r->query = q;"; + else + os << "r->query = query_statement (q);"; + + os << "r->stmt.reset (" << endl + << "new (shared) select_statement (" << endl; + view_query_statement_ctor_args (c, "r->query", true); + os << "));" + << endl + << "return r;" + << "}"; + + os << "odb::details::shared_ptr<result_impl>" << endl + << traits << "::" << endl + << "execute_query (prepared_query_impl& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::prepared_query_impl& pq (" << endl + << "static_cast<" << db << "::prepared_query_impl&> (q));" + << "shared_ptr<select_statement> st (" << endl + << "odb::details::inc_ref (" << endl + << "static_cast<select_statement*> (pq.stmt.get ())));" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "// The connection used by the current transaction and the" << endl + << "// one used to prepare this statement must be the same." << endl + << "//" << endl + << "assert (&conn == &st->connection ());" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_view<view_type> ());" + << endl; + + // Rebind the image if necessary. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.image_binding ());" + << endl + << "if (im.version != sts.image_version () || imb.version == 0)" + << "{" + << "bind (imb.bind, im);" + << "sts.image_version (im.version);" + << "imb.version++;" + << "}"; + + os << "pq.query.init_parameters ();" + << "st->execute ();"; + + post_query_ (c, false); + + os << endl + << "return shared_ptr<result_impl> (" << endl + << "new (shared) " << db << "::view_result_impl<view_type> (" << endl + << "pq.query, st, sts));" + << "}"; + } } namespace relational diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index b73cd01..8d57b1b 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -937,8 +937,9 @@ namespace relational virtual void traverse_composite (member_info& mi) { - os << "composite_value_traits< " << mi.fq_type () << - " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; + os << "composite_value_traits< " << mi.fq_type () << ", id_" << + db << " >::bind (" << endl + << "b + n, " << arg << "." << mi.var << "value, sk);"; } protected: @@ -973,8 +974,12 @@ namespace relational os << "if (sk != statement_update)" << "{"; - os << (obj ? "object" : "composite_value") << "_traits< " << - class_fq_name (c) << " >::bind (b + n, i, sk);"; + if (obj) + os << "object_traits_impl< "; + else + os << "composite_value_traits< "; + + os << class_fq_name (c) << ", id_" << db << " >::bind (b + n, i, sk);"; column_count_type const& cc (column_count (c)); @@ -1067,8 +1072,15 @@ namespace relational os << "// " << class_name (c) << " base" << endl << "//" << endl; - os << "if (" << (obj ? "object" : "composite_value") << "_traits< " << - class_fq_name (c) << " >::grow (i, t + " << index_ << "UL))" << endl + os << "if ("; + + if (obj) + os << "object_traits_impl< "; + else + os << "composite_value_traits< "; + + os << class_fq_name (c) << ", id_" << db << " >::grow (" << endl + << "i, t + " << index_ << "UL))" << endl << "grew = true;" << endl; @@ -1232,10 +1244,11 @@ namespace relational if (null (mi.m, key_prefix_) && mi.wrapper->template get<bool> ("wrapper-null-handler")) { - os << "if (wrapper_traits< " + wt + " >::get_null (" << + os << "if (wrapper_traits< " << wt << " >::get_null (" << member << "))" << endl - << "composite_value_traits< " + mi.fq_type () + " >::" << - "set_null (i." << mi.var << "value, sk);" + << "composite_value_traits< " << mi.fq_type () << ", id_" << + db << " >::set_null (" << endl + << "i." << mi.var << "value, sk);" << "else" << "{"; } @@ -1307,7 +1320,8 @@ namespace relational } if (comp) - traits = "composite_value_traits< " + type + " >"; + traits = "composite_value_traits< " + type + ", id_" + + db.string () + " >"; else { db_type_id = member_database_type_id_->database_type_id (mi.m); @@ -1403,8 +1417,12 @@ namespace relational if (generate_grow) os << "if ("; - os << (obj ? "object" : "composite_value") << "_traits< " << - class_fq_name (c) << " >::init (i, o, sk)"; + if (obj) + os << "object_traits_impl< "; + else + os << "composite_value_traits< "; + + os << class_fq_name (c) << ", id_" << db << " >::init (i, o, sk)"; if (generate_grow) os << ")" << endl @@ -1571,9 +1589,10 @@ namespace relational if (null (mi.m, key_prefix_) && mi.wrapper->template get<bool> ("wrapper-null-handler")) { - os << "if (composite_value_traits< " + mi.fq_type () + " >::" << - "get_null (i." << mi.var << "value))" << endl - << "wrapper_traits< " + wt + " >::set_null (" << member + ");" + os << "if (composite_value_traits< " << mi.fq_type () << + ", id_" << db << " >::get_null (" << endl + << "i." << mi.var << "value))" << endl + << "wrapper_traits< " << wt << " >::set_null (" << member + ");" << "else" << endl; } @@ -1595,8 +1614,9 @@ namespace relational os << "if ("; if (comp) - os << "composite_value_traits< " + type + " >::get_null (i." << - mi.var << "value)"; + os << "composite_value_traits< " << type << ", id_" << db << + " >::get_null (" << endl + << "i." << mi.var << "value)"; else get_null (mi); @@ -1618,7 +1638,8 @@ namespace relational type = mi.fq_type (); if (comp) - traits = "composite_value_traits< " + type + " >"; + traits = "composite_value_traits< " + type + ", id_" + + db.string () + " >"; else { db_type_id = member_database_type_id_->database_type_id (mi.m); @@ -1645,7 +1666,8 @@ namespace relational semantics::type& pt (member_utype (mi.m, key_prefix_)); if (lazy_pointer (pt)) - os << member << " = ptr_traits::pointer_type (*db, id);"; + os << member << " = ptr_traits::pointer_type (" << endl + << "*static_cast<" << db << "::database*> (db), id);"; else { os << "// If a compiler error points to the line below, then" << endl @@ -1653,7 +1675,8 @@ namespace relational << "// cannot be initialized from an object pointer." << endl << "//" << endl << member << " = ptr_traits::pointer_type (" << endl - << "db->load< obj_traits::object_type > (id));"; + << "static_cast<" << db << "::database*> (db)->load<" << endl + << " obj_traits::object_type > (id));"; // If we are loading into an eager weak pointer, make sure there // is someone else holding a strong pointer to it (normally a @@ -1730,9 +1753,14 @@ namespace relational return; os << "// " << class_name (c) << " base" << endl - << "//" << endl - << (obj ? "object" : "composite_value") << "_traits< " << - class_fq_name (c) << " >::init (o, i, db);" + << "//" << endl; + + if (obj) + os << "object_traits_impl< "; + else + os << "composite_value_traits< "; + + os << class_fq_name (c) << ", id_" << db << " >::init (o, i, db);" << endl; } }; @@ -1750,12 +1778,11 @@ namespace relational false), c_ (c) { - string const& type (class_fq_name (c)); + scope_ = object (c) + ? "access::object_traits_impl< " + : "access::composite_value_traits< "; - if (object (c)) - scope_ = "access::object_traits< " + type + " >"; - else - scope_ = "access::composite_value_traits< " + type + " >"; + scope_ += class_fq_name (c) + ", id_" + db.string () + " >"; } // Unless the database system can execute several interleaving @@ -3381,7 +3408,7 @@ namespace relational // virtual void - post_query_ (type&) + post_query_ (type&, bool /*once_off*/) { } @@ -3400,11 +3427,13 @@ namespace relational } virtual void - object_query_statement_ctor_args (type&) + object_query_statement_ctor_args (type&, + std::string const& q, + bool /*prepared*/) { - os << "sts.connection ()," << endl - << "query_statement + q.clause ()," << endl - << "q.parameters_binding ()," << endl + os << "conn," << endl + << "query_statement + " << q << ".clause ()," << endl + << q << ".parameters_binding ()," << endl << "imb"; } @@ -3429,11 +3458,13 @@ namespace relational } virtual void - view_query_statement_ctor_args (type&) + view_query_statement_ctor_args (type&, + string const& q, + bool /*prepared*/) { - os << "sts.connection ()," << endl - << "qs.clause ()," << endl - << "qs.parameters_binding ()," << endl + os << "conn," << endl + << q << ".clause ()," << endl + << q << ".parameters_binding ()," << endl << "imb"; } @@ -3469,7 +3500,8 @@ namespace relational traverse_composite (type& c) { string const& type (class_fq_name (c)); - string traits ("access::composite_value_traits< " + type + " >"); + string traits ("access::composite_value_traits< " + type + ", id_" + + db.string () + " >"); os << "// " << class_name (c) << endl << "//" << endl @@ -3637,9 +3669,6 @@ namespace relational if (embedded_schema) os << "#include <odb/schema-catalog-impl.hxx>" << endl; - if (options.generate_query ()) - os << "#include <odb/details/shared-ptr.hxx>" << endl; - os << endl; os << "#include <odb/" << db << "/traits.hxx>" << endl @@ -3666,6 +3695,9 @@ namespace relational if (options.generate_query ()) { + if (options.generate_prepared ()) + os << "#include <odb/" << db << "/prepared-query.hxx>" << endl; + if (features.simple_object) os << "#include <odb/" << db << "/simple-object-result.hxx>" << endl; diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx index 7164ef3..601b01d 100644 --- a/odb/relational/sqlite/common.cxx +++ b/odb/relational/sqlite/common.cxx @@ -78,7 +78,8 @@ namespace relational void member_image_type:: traverse_composite (member_info& mi) { - type_ = "composite_value_traits< " + mi.fq_type () + " >::image_type"; + type_ = "composite_value_traits< " + mi.fq_type () + + ", id_sqlite >::image_type"; } void member_image_type:: diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 03467cc..731afba 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -123,7 +123,7 @@ namespace relational traverse_composite (member_info& mi) { os << "if (composite_value_traits< " << mi.fq_type () << - " >::grow (" << endl + ", id_sqlite >::grow (" << endl << "i." << mi.var << "value, t + " << index_ << "UL))" << "{" << "grew = true;" diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index 1269406..9d14571 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -21,11 +21,228 @@ namespace relational // Pass 2. // + struct data_member2: traversal::data_member, context + { + data_member2 (bool& valid) + : valid_ (valid) + { + } + + virtual void + traverse (type& m) + { + if (transient (m)) + return; + + if (null (m)) + { + if (semantics::class_* c = composite_wrapper (utype (m))) + { + if (has_a (*c, test_container)) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: composite member containing containers cannot " + << "be null" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () + << ": info: composite value type is defined here" << endl; + + valid_ = false; + } + } + } + } + + bool& valid_; + }; + + struct object_no_id_members: object_members_base + { + object_no_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + if (inverse (m)) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: inverse object pointer member '" << member_prefix_ + << m.name () << "' in an object without an object id" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in an object without an object id" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct object data member. + }; + + struct composite_id_members: object_members_base + { + composite_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: object pointer member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; + + valid_ = false; + } + + virtual void + traverse_simple (semantics::data_member& m) + { + if (readonly (member_path_, member_scope_)) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: readonly member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct composite member. + }; + + struct view_members: object_members_base + { + view_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_simple (semantics::data_member& m) + { + if (object_pointer (utype (m))) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is an object pointer" << endl; + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain object pointers" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is a container" << endl; + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain containers" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct view data member. + }; + struct class2: traversal::class_, context { class2 (bool& valid) - : valid_ (valid) + : valid_ (valid), + data_member_ (valid), + object_no_id_members_ (valid), + composite_id_members_ (valid), + view_members_ (valid) { + *this >> data_member_names_ >> data_member_; } virtual void @@ -56,6 +273,66 @@ namespace relational virtual void traverse_object (type& c) { + semantics::data_member* id (id_member (c)); + + if (id != 0) + { + if (semantics::class_* cm = composite_wrapper (utype (*id))) + { + // Composite id cannot be auto. + // + if (auto_ (*id)) + { + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: composite id cannot be automatically assigned" + << endl; + + valid_ = false; + } + + // Make sure we don't have any containers or pointers in this + // composite value type. + // + if (valid_) + { + composite_id_members_.traverse (*cm); + + if (!valid_) + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + } + + // Check that the composite value type is default-constructible. + // + if (!cm->default_ctor ()) + { + os << cm->file () << ":" << cm->line () << ":" << cm->column () + << ": error: composite value type that is used as object id " + << "is not default-constructible" << endl; + + os << cm->file () << ":" << cm->line () << ":" << cm->column () + << ": info: provide default constructor for this value type" + << endl; + + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + + valid_ = false; + } + } + } + else + { + if (!abstract (c)) + { + // Make sure we don't have any containers or inverse pointers. + // + object_no_id_members_.traverse (c); + } + } + + names (c); + // Validate indexes. // { @@ -90,17 +367,30 @@ namespace relational } virtual void - traverse_view (type&) + traverse_view (type& c) { + // Make sure we don't have any containers or object pointers. + // + view_members_.traverse (c); + + names (c); } virtual void - traverse_composite (type&) + traverse_composite (type& c) { + names (c); } public: bool& valid_; + + data_member2 data_member_; + traversal::names data_member_names_; + + object_no_id_members object_no_id_members_; + composite_id_members composite_id_members_; + view_members view_members_; }; } diff --git a/odb/validator.cxx b/odb/validator.cxx index aba82a5..5e99114 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -821,233 +821,13 @@ namespace // Pass 2. // - struct data_member2: traversal::data_member, context - { - data_member2 (bool& valid) - : valid_ (valid) - { - } - - virtual void - traverse (type& m) - { - if (transient (m)) - return; - - if (null (m)) - { - if (semantics::class_* c = composite_wrapper (utype (m))) - { - if (has_a (*c, test_container)) - { - os << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: composite member containing containers cannot " - << "be null" << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ":" - << " info: composite value type is defined here" << endl; - - valid_ = false; - } - } - } - } - - bool& valid_; - }; - - struct object_no_id_members: object_members_base - { - object_no_id_members (bool& valid) - : object_members_base (false, false, true), valid_ (valid), dm_ (0) - { - } - - virtual void - traverse_pointer (semantics::data_member& m, semantics::class_&) - { - if (inverse (m)) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: inverse object pointer member '" << member_prefix_ - << m.name () << "' in an object without an object id" << endl; - - valid_ = false; - } - } - - virtual void - traverse_container (semantics::data_member& m, semantics::type&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: container member '" << member_prefix_ << m.name () - << "' in an object without an object id" << endl; - - valid_ = false; - } - - virtual void - traverse_composite (semantics::data_member* m, semantics::class_& c) - { - semantics::data_member* old_dm (dm_); - - if (dm_ == 0) - dm_ = m; - - object_members_base::traverse_composite (m, c); - - dm_ = old_dm; - } - - private: - bool& valid_; - semantics::data_member* dm_; // Direct object data member. - }; - - struct composite_id_members: object_members_base - { - composite_id_members (bool& valid) - : object_members_base (false, false, true), valid_ (valid), dm_ (0) - { - } - - virtual void - traverse_pointer (semantics::data_member& m, semantics::class_&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: object pointer member '" << member_prefix_ << m.name () - << "' in a composite value type that is used as an object id" << endl; - - valid_ = false; - } - - virtual void - traverse_simple (semantics::data_member& m) - { - if (readonly (member_path_, member_scope_)) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: readonly member '" << member_prefix_ << m.name () - << "' in a composite value type that is used as an object id" - << endl; - - valid_ = false; - } - } - - virtual void - traverse_container (semantics::data_member& m, semantics::type&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: container member '" << member_prefix_ << m.name () - << "' in a composite value type that is used as an object id" << endl; - - valid_ = false; - } - - virtual void - traverse_composite (semantics::data_member* m, semantics::class_& c) - { - semantics::data_member* old_dm (dm_); - - if (dm_ == 0) - dm_ = m; - - object_members_base::traverse_composite (m, c); - - dm_ = old_dm; - } - - private: - bool& valid_; - semantics::data_member* dm_; // Direct composite member. - }; - - struct view_members: object_members_base - { - view_members (bool& valid) - : object_members_base (false, false, true), valid_ (valid), dm_ (0) - { - } - - virtual void - traverse_simple (semantics::data_member& m) - { - if (object_pointer (utype (m))) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is an object pointer" << endl; - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain object pointers" << endl; - - valid_ = false; - } - } - - virtual void - traverse_container (semantics::data_member& m, semantics::type&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is a container" << endl; - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain containers" << endl; - - valid_ = false; - } - - virtual void - traverse_composite (semantics::data_member* m, semantics::class_& c) - { - semantics::data_member* old_dm (dm_); - - if (dm_ == 0) - dm_ = m; - - object_members_base::traverse_composite (m, c); - - dm_ = old_dm; - } - - private: - bool& valid_; - semantics::data_member* dm_; // Direct view data member. - }; - - // - // struct class2: traversal::class_, context { class2 (bool& valid) - : valid_ (valid), - data_member_ (valid), - object_no_id_members_ (valid), - composite_id_members_ (valid), - view_members_ (valid) + : valid_ (valid), has_lt_operator_ (0) { - *this >> data_member_names_ >> data_member_; - - // Find the has_lt_operator function template.. + // Find the has_lt_operator function template. // - has_lt_operator_ = 0; - tree odb ( lookup_qualified_name ( global_namespace, get_identifier ("odb"), false, false)); @@ -1098,148 +878,79 @@ namespace virtual void traverse_object (type& c) { - semantics::data_member* id (id_member (c)); - - if (id != 0) + if (semantics::data_member* id = id_member (c)) { - if (semantics::class_* cm = composite_wrapper (utype (*id))) - { - // Composite id cannot be auto. - // - if (auto_ (*id)) - { - os << id->file () << ":" << id->line () << ":" << id->column () - << ": error: composite id cannot be automatically assigned" - << endl; + semantics::type& t (utype (*id)); - valid_ = false; - } + // If this is a session object, make sure that the id type can + // be compared. + // + if (session (c) && has_lt_operator_ != 0) + { + tree args (make_tree_vec (1)); + TREE_VEC_ELT (args, 0) = t.tree_node (); - // Make sure we don't have any containers or pointers in this - // composite value type. - // - if (valid_) - { - composite_id_members_.traverse (*cm); + tree inst ( + instantiate_template ( + has_lt_operator_, args, tf_none)); - if (!valid_) - os << id->file () << ":" << id->line () << ":" << id->column () - << ": info: composite id is defined here" << endl; - } + bool v (inst != error_mark_node); - // Check that the composite value type is default-constructible. - // - if (!cm->default_ctor ()) + if (v && + DECL_TEMPLATE_INSTANTIATION (inst) && + !DECL_TEMPLATE_INSTANTIATED (inst)) { - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": error: composite value type that is used as object id " - << "is not default-constructible" << endl; - - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": info: provide default constructor for this value type" - << endl; + // Instantiate this function template to see if the value type + // provides operator<. Unfortunately, GCC instantiate_decl() + // does not provide any control over the diagnostics it issues + // in case of an error. To work around this, we are going to + // temporarily redirect diagnostics to /dev/null, which is + // where asm_out_file points to (see plugin.cxx). + // + int ec (errorcount); + FILE* s (global_dc->printer->buffer->stream); + global_dc->printer->buffer->stream = asm_out_file; - os << id->file () << ":" << id->line () << ":" << id->column () - << ": info: composite id is defined here" << endl; + instantiate_decl (inst, false, false); - valid_ = false; + global_dc->printer->buffer->stream = s; + v = (ec == errorcount); } - // If this is a session object, make sure that the composite - // value can be compared. - // - if (session (c) && has_lt_operator_ != 0) + if (!v) { - tree args (make_tree_vec (1)); - TREE_VEC_ELT (args, 0) = cm->tree_node (); + os << t.file () << ":" << t.line () << ":" << t.column () + << ": error: value type that is used as object id in " + << "persistent class with session support does not define " + << "the less than (<) comparison" << endl; - tree inst ( - instantiate_template ( - has_lt_operator_, args, tf_none)); + os << t.file () << ":" << t.line () << ":" << t.column () + << ": info: provide operator< for this value type" << endl; - bool v (inst != error_mark_node); - - if (v && - DECL_TEMPLATE_INSTANTIATION (inst) && - !DECL_TEMPLATE_INSTANTIATED (inst)) - { - // Instantiate this function template to see if the value type - // provides operator<. Unfortunately, GCC instantiate_decl() - // does not provide any control over the diagnostics it issues - // in case of an error. To work around this, we are going to - // temporarily redirect diagnostics to /dev/null, which is - // where asm_out_file points to (see plugin.cxx). - // - int ec (errorcount); - FILE* s (global_dc->printer->buffer->stream); - global_dc->printer->buffer->stream = asm_out_file; - - instantiate_decl (inst, false, false); - - global_dc->printer->buffer->stream = s; - v = (ec == errorcount); - } - - if (!v) - { - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": error: composite value type that is used as object id " - << "in persistent class with session support does not " - << "define the less than (<) comparison" - << endl; - - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": info: provide operator< for this value type" << endl; - - os << id->file () << ":" << id->line () << ":" << id->column () - << ": info: composite id is defined here" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: id member is defined here" << endl; - os << c.file () << ":" << c.line () << ":" << c.column () - << ": info: persistent class is defined here" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () + << ": info: persistent class is defined here" << endl; - valid_ = false; - } + valid_ = false; } } } - else - { - if (!abstract (c)) - { - // Make sure we don't have any containers or inverse pointers. - // - object_no_id_members_.traverse (c); - } - } - - names (c); } virtual void - traverse_view (type& c) + traverse_view (type&) { - // Make sure we don't have any containers or object pointers. - // - view_members_.traverse (c); - - names (c); } virtual void - traverse_composite (type& c) + traverse_composite (type&) { - names (c); } bool& valid_; tree has_lt_operator_; - - data_member2 data_member_; - traversal::names data_member_names_; - - object_no_id_members object_no_id_members_; - composite_id_members composite_id_members_; - view_members view_members_; }; } @@ -1251,18 +962,38 @@ validate (options const& ops, unsigned short pass) { bool valid (true); + database db (ops.database ()[0]); // Validate options. // if (ops.generate_schema_only () && (ops.schema_format ().size () != 1 || - *ops.schema_format ().begin () != schema_format::sql)) + *ops.schema_format ()[db].begin () != schema_format::sql)) { cerr << "error: --generate-schema-only is only valid when generating " << "schema as a standalone SQL file" << endl; valid = false; } + // Multi-database support options. + // + if (ops.multi_database () == multi_database::dynamic && + ops.default_database_specified () && + ops.default_database () != database::common) + { + cerr << "error: when dynamic multi-database support is used, the " << + "default database can only be 'common'" << endl; + valid = false; + } + + if (db == database::common && + ops.multi_database () == multi_database::disabled) + { + cerr << "error: 'common' database is only valid with multi-database " << + "support enabled" << endl; + valid = false; + } + if (!valid) throw failed (); @@ -1319,13 +1050,29 @@ validate (options const& ops, if (!valid) throw failed (); - try + switch (db) { - relational::validator v; - v.validate (ops, f, u, p, pass); - } - catch (relational::validator::failed const&) - { - throw failed (); + case database::common: + { + break; + } + case database::mssql: + case database::mysql: + case database::oracle: + case database::pgsql: + case database::sqlite: + { + try + { + relational::validator v; + v.validate (ops, f, u, p, pass); + } + catch (relational::validator::failed const&) + { + throw failed (); + } + + break; + } } } diff --git a/odb/version.hxx b/odb/version.hxx index 93ca6b5..0987c85 100644 --- a/odb/version.hxx +++ b/odb/version.hxx @@ -24,12 +24,12 @@ // ODB interface version: minor, major, and alpha/beta versions. // -#define ODB_VERSION 20100 -#define ODB_VERSION_STR "2.1" +#define ODB_VERSION 20101 +#define ODB_VERSION_STR "2.2.a1" // ODB compiler version: interface version plus the bugfix version. // -#define ODB_COMPILER_VERSION 2010100 -#define ODB_COMPILER_VERSION_STR "2.1.1" +#define ODB_COMPILER_VERSION 2019901 +#define ODB_COMPILER_VERSION_STR "2.2.0.a1" #endif // ODB_VERSION_HXX @@ -1 +1 @@ -2.1.1 +2.2.0.a1 |