summaryrefslogtreecommitdiff
path: root/odb/odb/relational/pgsql
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/relational/pgsql')
-rw-r--r--odb/odb/relational/pgsql/common.cxx351
-rw-r--r--odb/odb/relational/pgsql/common.hxx159
-rw-r--r--odb/odb/relational/pgsql/context.cxx786
-rw-r--r--odb/odb/relational/pgsql/context.hxx192
-rw-r--r--odb/odb/relational/pgsql/header.cxx285
-rw-r--r--odb/odb/relational/pgsql/inline.cxx42
-rw-r--r--odb/odb/relational/pgsql/model.cxx101
-rw-r--r--odb/odb/relational/pgsql/schema.cxx266
-rw-r--r--odb/odb/relational/pgsql/source.cxx1140
9 files changed, 3322 insertions, 0 deletions
diff --git a/odb/odb/relational/pgsql/common.cxx b/odb/odb/relational/pgsql/common.cxx
new file mode 100644
index 0000000..6a59954
--- /dev/null
+++ b/odb/odb/relational/pgsql/common.cxx
@@ -0,0 +1,351 @@
+// file : odb/relational/pgsql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Integral types.
+ //
+ case sql_type::BOOLEAN:
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Float types.
+ //
+ case sql_type::REAL:
+ case sql_type::DOUBLE:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::NUMERIC:
+ {
+ traverse_numeric (mi);
+ break;
+ }
+
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ case sql_type::TIME:
+ case sql_type::TIMESTAMP:
+ {
+ traverse_date_time (mi);
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::TEXT:
+ case sql_type::BYTEA:
+ {
+ traverse_string (mi);
+ break;
+ }
+ case sql_type::BIT:
+ {
+ traverse_bit (mi);
+ break;
+ }
+ case sql_type::VARBIT:
+ {
+ traverse_varbit (mi);
+ break;
+ }
+ // Other types.
+ //
+ case sql_type::UUID:
+ {
+ traverse_uuid (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ namespace
+ {
+ const char* integer_types[] =
+ {
+ "bool",
+ "short",
+ "int",
+ "long long"
+ };
+
+ const char* float_types[] =
+ {
+ "float",
+ "double"
+ };
+
+ const char* date_time_types[] =
+ {
+ "int",
+ "long long",
+ "long long"
+ };
+ }
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_pgsql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ type_ += integer_types[mi.st->type - sql_type::BOOLEAN];
+ }
+
+ void member_image_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = float_types[mi.st->type - sql_type::REAL];
+ }
+
+ void member_image_type::
+ traverse_numeric (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_date_time (member_info& mi)
+ {
+ type_ = date_time_types[mi.st->type - sql_type::DATE];
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ void member_image_type::
+ traverse_varbit (member_info&)
+ {
+ type_ = "details::ubuffer";
+ }
+
+ void member_image_type::
+ traverse_uuid (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ namespace
+ {
+ const char* integer_database_id[] =
+ {
+ "id_boolean",
+ "id_smallint",
+ "id_integer",
+ "id_bigint"
+ };
+
+ const char* float_database_id[] =
+ {
+ "id_real",
+ "id_double"
+ };
+
+ const char* date_time_database_id[] =
+ {
+ "id_date",
+ "id_time",
+ "id_timestamp"
+ };
+
+ const char* char_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_string", // VARCHAR
+ "id_string", // TEXT,
+ "id_bytea" // BYTEA
+ };
+ }
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ integer_database_id[mi.st->type - sql_type::BOOLEAN];
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ float_database_id[mi.st->type - sql_type::REAL];
+ }
+
+ void member_database_type_id::
+ traverse_numeric (member_info&)
+ {
+ type_id_ = "pgsql::id_numeric";
+ }
+
+ void member_database_type_id::
+ traverse_date_time (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ date_time_database_id[mi.st->type - sql_type::DATE];
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ char_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_bit (member_info&)
+ {
+ type_id_ = "pgsql::id_bit";
+ }
+
+ void member_database_type_id::
+ traverse_varbit (member_info&)
+ {
+ type_id_ = "pgsql::id_varbit";
+ }
+
+ void member_database_type_id::
+ traverse_uuid (member_info&)
+ {
+ type_id_ = "pgsql::id_uuid";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/pgsql/common.hxx b/odb/odb/relational/pgsql/common.hxx
new file mode 100644
index 0000000..1d383bf
--- /dev/null
+++ b/odb/odb/relational/pgsql/common.hxx
@@ -0,0 +1,159 @@
+// file : odb/relational/pgsql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PGSQL_COMMON_HXX
+#define ODB_RELATIONAL_PGSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the pgsql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_numeric (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_varbit (member_info&);
+
+ virtual void
+ traverse_uuid (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_numeric (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_varbit (member_info&);
+
+ virtual void
+ traverse_uuid (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_PGSQL_COMMON_HXX
diff --git a/odb/odb/relational/pgsql/context.cxx b/odb/odb/relational/pgsql/context.cxx
new file mode 100644
index 0000000..7f99f5d
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.cxx
@@ -0,0 +1,786 @@
+// file : odb/relational/pgsql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BOOLEAN", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "SMALLINT", 0, false},
+ {"unsigned char", "SMALLINT", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT", 0, false},
+
+ {"int", "INTEGER", 0, false},
+ {"unsigned int", "INTEGER", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT", 0, false},
+
+ {"float", "REAL", 0, false},
+ {"double", "DOUBLE PRECISION", 0, false},
+
+ {"::std::string", "TEXT", 0, false},
+
+ {"::size_t", "BIGINT", 0, false},
+ {"::std::size_t", "BIGINT", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = true;
+ global_index = true;
+ global_fkey = false;
+ data_->bind_vector_ = "pgsql::bind*";
+ data_->truncated_vector_ = "bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("pgsql-grow"))
+ r_ = c.get<bool> ("pgsql-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("pgsql-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("pgsql-grow"))
+ return c.get<bool> ("pgsql-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ if (i->size () > 63)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r += *i;
+ r += '"';
+ }
+
+ return r;
+ }
+
+ string context::
+ statement_name (string const& type, string const& name, semantics::node& n)
+ {
+ // Put the type first so that in the case of truncation it
+ // remains thus lowering the chance of a clash.
+ //
+ string r (type);
+ r += '_';
+ r += name;
+
+ r = transform_name (r, sql_name_statement);
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ // Note that we have to do it in addition to the above since this
+ // name doesn't go through quote_id().
+ //
+ if (r.size () > 63)
+ {
+ location const& l (n.location ());
+
+ warn (l) << "prepared statement name '" << r << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ info (l) << "consider shortening the corresponding namespace "
+ << "name, class name, or data member name" << endl;
+
+ info (l) << "or shortening the statement name itself using the "
+ << "--statement-regex option" << endl;
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR(";
+ else
+ {
+ r = "VARCHAR(";
+ n--;
+ }
+
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // BIT VARYING (VARBIT)
+ // CHARACTER VARYING (VARRCHAR)
+ // DOUBLE PRECISION (DOUBLE)
+ // TIME WITH TIME ZONE (not currently supported)
+ // TIMESTAMP WITH TIME ZONE (not currently supported)
+ //
+
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_suffix,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+ bool flt (false);
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ if (id == "BIT" ||
+ id == "CHARACTER" ||
+ id == "DOUBLE")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ s = parse_name;
+ }
+ // Fall through.
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (context::upcase (t.identifier ()));
+
+ //
+ // Numeric types.
+ //
+ if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::BOOLEAN;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "INT" ||
+ id == "INTEGER" ||
+ id == "INT4")
+ {
+ r.type = sql_type::INTEGER;
+ }
+ else if (id == "BIGINT")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "REAL" || id == "FLOAT4")
+ {
+ r.type = sql_type::REAL;
+ }
+ else if ((id == "PRECISION" && prefix == "DOUBLE") ||
+ id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "FLOAT")
+ {
+ // Assign a type only once we know the precision of the
+ // float.
+ //
+ flt = true;
+ }
+ else if (id == "NUMERIC" || id == "DECIMAL")
+ {
+ r.type = sql_type::NUMERIC;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "TIMETZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "TIMESTAMPTZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "CHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "VARYING")
+ {
+ if (prefix == "BIT")
+ r.type = sql_type::VARBIT;
+ else if (prefix == "CHARACTER")
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BYTEA")
+ {
+ r.type = sql_type::BYTEA;
+ }
+ else if (id == "VARBIT")
+ {
+ r.type = sql_type::VARBIT;
+ }
+ //
+ // Other types.
+ //
+ else if (id == "UUID")
+ {
+ r.type = sql_type::UUID;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "expected PostgreSQL type name");
+ }
+
+ s = parse_range;
+ }
+ // Fall through.
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer range expected in PostgreSQL "
+ "type declaration");
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in PostgreSQL type declaration");
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ l.next ();
+ t = l.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in PostgreSQL type "
+ "declaration");
+ }
+
+ s = parse_suffix;
+ continue;
+ }
+
+ s = parse_suffix;
+ }
+ // Fall through.
+ case parse_suffix:
+ {
+ if (r.type == sql_type::TIME || r.type == sql_type::TIMESTAMP)
+ {
+ string const& id1 (context::upcase (t.identifier ()));
+
+ if (id1 == "WITH")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id2 (context::upcase (t.identifier ()));
+
+ if (id2 == "TIME")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id3 (context::upcase (t.identifier ()));
+
+ if (id3 == "ZONE")
+ {
+ // This code shall not fall through.
+ //
+ return error (ct, "PostgreSQL time zones are not "
+ "currently supported");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "unknown PostgreSQL type");
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (flt)
+ {
+ r.type = r.range && r.range_value < 25 ?
+ sql_type::REAL :
+ sql_type::DOUBLE;
+ }
+
+ if (r.type == sql_type::invalid)
+ return error (ct, "incomplete PostgreSQL type declaration");
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid PostgreSQL type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/context.hxx b/odb/odb/relational/pgsql/context.hxx
new file mode 100644
index 0000000..64e0b1a
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.hxx
@@ -0,0 +1,192 @@
+// file : odb/relational/pgsql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ BOOLEAN,
+ SMALLINT,
+ INTEGER,
+ BIGINT,
+
+ // Float types.
+ //
+ REAL,
+ DOUBLE,
+ NUMERIC,
+
+ // Data-time types.
+ //
+ DATE,
+ TIME,
+ TIMESTAMP,
+
+ // String and binary types.
+ //
+ CHAR,
+ VARCHAR,
+ TEXT,
+ BYTEA,
+ BIT,
+ VARBIT,
+
+ // Other types.
+ //
+ UUID,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () : type (invalid), range (false) {}
+
+ core_type type;
+
+ // VARBIT maximum length is 2^31 - 1 bit. String types can hold a
+ // maximum of 1GB of data.
+ //
+ bool range;
+ unsigned int range_value;
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ public:
+ // Construct statement name from a given type and name.
+ //
+ string
+ statement_name (string const& type,
+ string const& name,
+ semantics::node&);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_PGSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/pgsql/header.cxx b/odb/odb/relational/pgsql/header.cxx
new file mode 100644
index 0000000..c3efc3e
--- /dev/null
+++ b/odb/odb/relational/pgsql/header.cxx
@@ -0,0 +1,285 @@
+// file : odb/relational/pgsql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct class1: relational::class1
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_post (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ // Statement names.
+ //
+ os << "static const char persist_statement_name[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ os << "static const char* const find_statement_names[" <<
+ (abst ? "1" : "depth") << "];";
+ else
+ os << "static const char find_statement_name[];";
+
+ if (poly && !poly_derived)
+ os << "static const char find_discriminator_statement_name[];";
+
+ if (update_columns != 0)
+ os << "static const char update_statement_name[];";
+
+ os << "static const char erase_statement_name[];";
+
+ if (optimistic != 0)
+ os << "static const char optimistic_erase_statement_name[];";
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ os << "static const char query_statement_name[];"
+ << "static const char erase_query_statement_name[];";
+
+ os << endl;
+
+ // Statement types.
+ //
+ os << "static const unsigned int persist_statement_types[];";
+
+ if (id != 0)
+ {
+ os << "static const unsigned int find_statement_types[];";
+
+ if (update_columns != 0)
+ os << "static const unsigned int update_statement_types[];";
+
+ if (optimistic != 0)
+ os << "static const unsigned int " <<
+ "optimistic_erase_statement_types[];";
+ }
+
+ os << endl;
+
+ if (poly_derived)
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ // Statement names.
+ //
+ os << "static const char query_statement_name[];"
+ << endl;
+ }
+ };
+ entry<class1> class1_entry_;
+
+ struct container_traits: relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_public_extra_pre (semantics::data_member& m,
+ semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ bool smart (!inverse (m, "value") && !unordered (m) &&
+ container_smart (t));
+
+ // Container statement names.
+ //
+ os << "static const char select_name[];"
+ << "static const char insert_name[];";
+
+ if (smart)
+ os << "static const char update_name[];";
+
+ os << "static const char delete_name[];"
+ << endl;
+
+ // Container statement types.
+ //
+ os << "static const unsigned int insert_types[];";
+
+ if (smart)
+ os << "static const unsigned int update_types[];"
+ << "static const unsigned int delete_types[];";
+
+ os << endl;
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_post (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Statement names.
+ //
+ if (load || load_opt)
+ os << "static const char select_name[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_name[];"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ os << "static const unsigned int update_types[];";
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 1000 digits not counting
+ // '-' and '.'.
+ //
+
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Additional 4 bytes at the beginning of the array specify
+ // the number of significant bits in the image. This number
+ // is stored in network byte order.
+ //
+ unsigned int n (4 + mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ // UUID is a 16-byte sequence.
+ //
+ os << "unsigned char " << mi.var << "value[16];"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/inline.cxx b/odb/odb/relational/pgsql/inline.cxx
new file mode 100644
index 0000000..08688c3
--- /dev/null
+++ b/odb/odb/relational/pgsql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/pgsql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = true;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/model.cxx b/odb/odb/relational/pgsql/model.cxx
new file mode 100644
index 0000000..092f8bb
--- /dev/null
+++ b/odb/odb/relational/pgsql/model.cxx
@@ -0,0 +1,101 @@
+// file : odb/relational/pgsql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ base::traverse_object (c);
+
+ if (context::top_object == &c)
+ {
+ // Make sure that the auto id type is INTEGER or BIGINT.
+ //
+ if (pkey_ != 0 && pkey_->auto_ ())
+ {
+ // Should be a single column.
+ //
+ sema_rel::column& c (pkey_->contains_begin ()->column ());
+
+ // This should never fail since we have already parsed this.
+ //
+ sql_type const& t (parse_sql_type (c.type ()));
+
+ if (t.type != sql_type::INTEGER && t.type != sql_type::BIGINT)
+ {
+ location const& l (c.get<location> ("cxx-location"));
+ error (l) << "automatically assigned object id must map "
+ << "to PostgreSQL INTEGER or BIGINT" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ return v ? "TRUE" : "FALSE";
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to an integer type.
+ //
+ switch (parse_sql_type (column_type (), m, false).type)
+ {
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to PostgreSQL integer type" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/schema.cxx b/odb/odb/relational/pgsql/schema.cxx
new file mode 100644
index 0000000..b9c3f2e
--- /dev/null
+++ b/odb/odb/relational/pgsql/schema.cxx
@@ -0,0 +1,266 @@
+// file : odb/relational/pgsql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ //
+ // Drop.
+ //
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ pre_statement ();
+ os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
+ quote_id (t.name ()) << " CASCADE" << endl;
+ post_statement ();
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ type (sema_rel::column& c, bool auto_)
+ {
+ if (auto_)
+ {
+ // This should never fail since we have already parsed this.
+ //
+ sql_type const& t (parse_sql_type (c.type ()));
+
+ // The model creation code makes sure it is one of these type.
+ //
+ if (t.type == sql_type::INTEGER)
+ os << "SERIAL";
+ else if (t.type == sql_type::BIGINT)
+ os << "BIGSERIAL";
+ }
+ else
+ base::type (c, auto_);
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ deferrable (sema_rel::deferrable d)
+ {
+ os << endl
+ << " INITIALLY " << d;
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ {
+ // Handle the CONCURRENTLY keyword.
+ //
+ string const& t (in.type ());
+
+ if (t == "concurrently" || t == "CONCURRENTLY")
+ {
+ os << "INDEX " << t;
+ }
+ else
+ {
+ size_t p (t.rfind (' '));
+ string s (t, (p != string::npos ? p + 1 : 0), string::npos);
+
+ if (s == "concurrently" || s == "CONCURRENTLY")
+ os << string (t, 0, p) << " INDEX " << s;
+ else
+ os << t << " INDEX";
+ }
+ }
+ else
+ os << "INDEX";
+
+ os << " " << name (in) << endl
+ << " ON " << table_name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << " (";
+ columns (in);
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+ };
+ entry<create_index> create_index_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::column& c)
+ {
+ os << quote_id (c.name ()) << " " <<
+ (c.null () ? "DROP" : "SET") << " NOT NULL";
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ // PostgreSQL prior to 9.1 doesn't support IF NOT EXISTS in
+ // CREATE TABLE. We also cannot use IF-ELSE construct in plain
+ // SQL. To make it at least work for a single schema, we are
+ // going to drop the schema version table after the DROP
+ // statements and then unconditionally create it after CREATE.
+ //
+ virtual void
+ create_table ()
+ {
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BOOLEAN NOT NULL)" << endl;
+
+ post_statement ();
+ }
+ }
+
+ virtual void
+ drop ()
+ {
+ pre_statement ();
+
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ os << "DELETE FROM " << qt_ << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+ else
+ os << "DROP TABLE IF EXISTS " << qt_ << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ {
+ os << "INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " SELECT " << qs_ << ", " << v << ", FALSE" << endl
+ << " WHERE NOT EXISTS (" << endl
+ << " SELECT 1 FROM " << qt_ << " WHERE " << qn_ << " = " <<
+ qs_ << ")" << endl;
+ }
+ else
+ {
+ os << "CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BOOLEAN NOT NULL)" << endl;
+
+ post_statement ();
+ pre_statement ();
+
+ os << "INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", FALSE)" << endl;
+ }
+
+ post_statement ();
+ }
+
+ virtual void
+ migrate_pre (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qv_ << " = " << v << ", " << qm_ << " = TRUE" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ migrate_post ()
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qm_ << " = FALSE" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/source.cxx b/odb/odb/relational/pgsql/source.cxx
new file mode 100644
index 0000000..b881e48
--- /dev/null
+++ b/odb/odb/relational/pgsql/source.cxx
@@ -0,0 +1,1140 @@
+// file : odb/relational/pgsql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x), i_ (0) {}
+
+ virtual string
+ next (semantics::data_member&, const string&, const string&)
+ {
+ ostringstream ss;
+ ss << "$" << ++i_;
+
+ return ss.str ();
+ }
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "DEFAULT";
+ }
+
+ private:
+ size_t i_;
+ };
+ entry<query_parameters> query_parameters_;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "pgsql::bind::boolean_",
+ "pgsql::bind::smallint",
+ "pgsql::bind::integer",
+ "pgsql::bind::bigint"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "pgsql::bind::real",
+ "pgsql::bind::double_"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "pgsql::bind::text", // CHAR
+ "pgsql::bind::text", // VARCHAR
+ "pgsql::bind::text", // TEXT
+ "pgsql::bind::bytea" // BYTEA
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "pgsql::bind::date",
+ "pgsql::bind::time",
+ "pgsql::bind::timestamp"
+ };
+
+ const char* oids[] =
+ {
+ "pgsql::bool_oid", // BOOLEAN
+ "pgsql::int2_oid", // SMALLINT
+ "pgsql::int4_oid", // INTEGER
+ "pgsql::int8_oid", // BIGINT
+ "pgsql::float4_oid", // REAL
+ "pgsql::float8_oid", // DOUBLE
+ "pgsql::numeric_oid", // NUMERIC
+ "pgsql::date_oid", // DATE
+ "pgsql::time_oid", // TIME
+ "pgsql::timestamp_oid", // TIMESTAMP
+ "pgsql::text_oid", // CHAR
+ "pgsql::text_oid", // VARCHAR
+ "pgsql::text_oid", // TEXT
+ "pgsql::bytea_oid", // BYTEA
+ "pgsql::bit_oid", // BIT
+ "pgsql::varbit_oid", // VARBIT
+ "pgsql::uuid_oid" // UUID
+ };
+ }
+
+ struct statement_oids: object_columns_base, context
+ {
+ statement_oids (statement_kind sk,
+ bool first = true,
+ object_section* section = 0)
+ : object_columns_base (first, column_prefix (), section), sk_ (sk)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (!(inverse (m, key_prefix_) && sk_ != statement_select))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const&,
+ bool first)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (id ())
+ {
+ if (sk_ == statement_update ||
+ (sk_ == statement_insert && auto_ (m)))
+ return false;
+ }
+
+ if (sk_ == statement_update &&
+ readonly (member_path_, member_scope_))
+ return false;
+
+ if ((sk_ == statement_insert || sk_ == statement_update) &&
+ version (m))
+ return false;
+
+ if (!first)
+ os << ',' << endl;
+
+ os << oids[parse_sql_type (column_type (), m).type];
+
+ return true;
+ }
+
+ private:
+ statement_kind sk_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BOOLEAN] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".type = " <<
+ float_buffer_types[mi.st->type - sql_type::REAL] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::numeric;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::bit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = sizeof (" << arg << "." << mi.var << "value);"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::varbit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::uuid;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Presented as byte.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ // Presented as bytea.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ if (p == persist_after_values)
+ {
+ data_member_path* id (id_member (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // Top-level auto id.
+ //
+ if (id != 0 && !poly_derived && auto_ (*id))
+ r = "RETURNING " +
+ convert_from (column_qname (*id), *id->back ());
+ }
+
+ return r;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::object_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "persist_statement_name[] = " <<
+ strlit (statement_name ("persist", fn, c)) << ";"
+ << endl;
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ os << "const char* const " << traits << "::" << endl
+ << "find_statement_names[] ="
+ << "{";
+
+ for (size_t i (0), n (abst ? 1 : polymorphic_depth (c));
+ i < n;
+ ++i)
+ {
+ if (i != 0)
+ os << "," << endl;
+
+ ostringstream ostr;
+ ostr << "find_" << i;
+ os << strlit (statement_name (ostr.str (), fn, c));
+ }
+
+ os << "};";
+ }
+ else
+ os << "const char " << traits << "::" << endl
+ << "find_statement_name[] = " <<
+ strlit (statement_name ("find", fn, c)) << ";"
+ << endl;
+
+ if (poly && !poly_derived)
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement_name[] = " <<
+ strlit (statement_name ("find_discriminator", fn, c)) << ";"
+ << endl;
+
+ if (update_columns != 0)
+ os << "const char " << traits << "::" << endl
+ << "update_statement_name[] = " <<
+ strlit (statement_name ("update", fn, c)) << ";"
+ << endl;
+
+ os << "const char " << traits << "::" << endl
+ << "erase_statement_name[] = " <<
+ strlit (statement_name ("erase", fn, c)) << ";"
+ << endl;
+
+ if (optimistic != 0)
+ os << "const char " << traits << "::" << endl
+ << "optimistic_erase_statement_name[] = " <<
+ strlit (statement_name ("erase_optimistic", fn, c)) << ";"
+ << endl;
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ {
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl
+ << "const char " << traits << "::" << endl
+ << "erase_query_statement_name[] = " <<
+ strlit (statement_name ("erase_query", fn, c)) << ";"
+ << endl;
+ }
+
+ // Statement types.
+ //
+
+ // persist_statement_types.
+ //
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "persist_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_insert);
+ st.traverse (c);
+
+ // Empty array is not portable. So add a dummy member if we
+ // are not sending anything with the insert statement.
+ //
+ if (cc.total == cc.inverse + cc.optimistic_managed +
+ (id != 0 && !poly_derived && auto_ (*id) ? cc.id : 0))
+ os << "0";
+
+ os << "};";
+ }
+
+ // find_statement_types.
+ //
+ if (id != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "find_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_select, true);
+ st.traverse (*id);
+
+ os << "};";
+ }
+
+ // update_statement_types.
+ //
+ if (id != 0 && update_columns != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "update_statement_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &main_section);
+ st.traverse (c);
+ }
+
+ // Not the same as update_columns.
+ //
+ bool first (cc.total == cc.id + cc.inverse + cc.readonly +
+ cc.separate_update + cc.optimistic_managed);
+
+ statement_oids st (statement_where, first);
+ st.traverse (*id);
+
+ if (optimistic != 0)
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+
+ if (id != 0 && optimistic != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "optimistic_erase_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_where);
+ st.traverse (*id);
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+ }
+
+ virtual void
+ extra_statement_cache_extra_args (bool c, bool s)
+ {
+ bool u (c || s);
+
+ os << "," << endl
+ << db << "::native_binding&" << (u ? " idn" : "") << "," << endl
+ << "const unsigned int*" << (u ? " idt" : "");
+ }
+
+ virtual void
+ view_extra (type& c)
+ {
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::view_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl;
+ }
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "erase_query_statement_name," << endl
+ << "text," << endl
+ << "q.parameter_types ()," << endl
+ << "q.parameter_count ()," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ post_query_ (type&, bool once_off)
+ {
+ if (once_off)
+ os << "st->deallocate ();";
+ }
+ };
+ entry<class_> class_entry_;
+
+ struct container_traits : relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_extra (semantics::data_member& m, semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ container_kind_type ck (container_kind (t));
+
+ string const& pn (public_name (m));
+ string scope (scope_ + "::" + flat_prefix_ + pn + "_traits");
+
+ data_member_path* imp (inverse (m, "value"));
+ bool inv (imp != 0);
+
+ bool smart (!inv && !unordered (m) && container_smart (t));
+
+ // Statment names.
+ //
+
+ // Prefix top-object name to avoid conflicts with inherited
+ // member statement names.
+ //
+ string fn (
+ flat_name (
+ class_fq_name (*top_object) + "_" + flat_prefix_ + pn));
+
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, m)) << ";"
+ << endl
+ << "const char " << scope << "::" << endl
+ << "insert_name[] = " <<
+ strlit (statement_name ("insert", fn, m)) << ";"
+ << endl;
+
+ if (smart)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, m)) << ";"
+ << endl;
+
+ os << "const char " << scope << "::" << endl
+ << "delete_name[] = " <<
+ strlit (statement_name ("delete", fn, m)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+
+ semantics::type& vt (container_vt (m));
+ semantics::type& idt (container_idt (m));
+
+ // insert statement types.
+ //
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "insert_types[] ="
+ << "{";
+
+ if (!inv)
+ {
+ statement_oids so (statement_insert);
+
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ so.traverse (m, container_kt (m), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ so.traverse (m, vt, "value", "value");
+ }
+ else
+ // MSVC does not allow zero length arrays or uninitialized
+ // non-extern const values.
+ //
+ os << "0";
+
+ os << "};";
+ }
+
+ // update statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ // Use insert instead of update to include read-only members.
+ //
+ statement_oids so (statement_insert);
+ so.traverse (m, vt, "value", "value");
+ }
+
+ statement_oids so (statement_where, false);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+
+ // delete statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "delete_types[] ="
+ << "{";
+
+ statement_oids so (statement_where);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits : relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_extra (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ semantics::data_member* opt (optimistic (c_));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ string name (public_name (*s.member));
+ string scope (scope_ + "::" + name + "_traits");
+
+ // Statment names.
+ //
+
+ // Prefix object name to avoid conflicts with inherited member
+ // statement names.
+ //
+ string fn (flat_name (class_fq_name (c_) + "_" + name));
+
+ if (load || load_opt)
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, *s.member)) << ";"
+ << endl;
+
+ if (update || update_opt)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, *s.member)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &s);
+ st.traverse (c_);
+ }
+
+ statement_oids st (statement_where, !update);
+ st.traverse (*id_member (c_));
+
+ if (s.optimistic ()) // Note: not update_opt.
+ st.traverse (*opt);
+
+ os << "};";
+ }
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct container_cache_init_members:
+ relational::container_cache_init_members
+ {
+ container_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<container_cache_init_members> container_cache_init_members_;
+
+ struct section_cache_init_members:
+ relational::section_cache_init_members
+ {
+ section_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<section_cache_init_members> section_cache_init_members_;
+ }
+ }
+}