diff options
Diffstat (limited to 'odb/odb/relational/pgsql')
-rw-r--r-- | odb/odb/relational/pgsql/common.cxx | 351 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/common.hxx | 159 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/context.cxx | 786 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/context.hxx | 192 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/header.cxx | 285 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/inline.cxx | 42 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/model.cxx | 101 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/schema.cxx | 266 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/source.cxx | 1140 |
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_; + } + } +} |