diff options
Diffstat (limited to 'odb/odb/relational/oracle')
-rw-r--r-- | odb/odb/relational/oracle/common.cxx | 522 | ||||
-rw-r--r-- | odb/odb/relational/oracle/common.hxx | 203 | ||||
-rw-r--r-- | odb/odb/relational/oracle/context.cxx | 795 | ||||
-rw-r--r-- | odb/odb/relational/oracle/context.hxx | 188 | ||||
-rw-r--r-- | odb/odb/relational/oracle/header.cxx | 230 | ||||
-rw-r--r-- | odb/odb/relational/oracle/inline.cxx | 42 | ||||
-rw-r--r-- | odb/odb/relational/oracle/model.cxx | 64 | ||||
-rw-r--r-- | odb/odb/relational/oracle/schema.cxx | 696 | ||||
-rw-r--r-- | odb/odb/relational/oracle/source.cxx | 646 |
9 files changed, 3386 insertions, 0 deletions
diff --git a/odb/odb/relational/oracle/common.cxx b/odb/odb/relational/oracle/common.cxx new file mode 100644 index 0000000..7caafc9 --- /dev/null +++ b/odb/odb/relational/oracle/common.cxx @@ -0,0 +1,522 @@ +// file : odb/relational/oracle/common.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cassert> + +#include <odb/relational/oracle/common.hxx> + +using namespace std; + +namespace relational +{ + namespace oracle + { + // + // 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) + { + // Numeric types. + // + case sql_type::NUMBER: + { + const sql_type& st (*mi.st); + + if (st.prec) + { + unsigned short r (st.prec_value); + + if (!st.scale) + { + if (r <= 10) + traverse_int32 (mi); + // Only OCI versions 11.2 and later support insertion and + // extraction into a 64 bit integer. + // + else if ( + (options.oracle_client_version () >= oracle_version (11, 2)) && + (r <= 19 || (r == 20 && unsigned_integer (mi.t)))) + traverse_int64 (mi); + else + traverse_big_int (mi); + } + else + { + // We can calculate the decimal exponent of the normalised + // floating point equivalent of the fixed point number using + // e = p - s, where p is the precision, s is the scale, and + // e the exponent. We can then use this to determine whether + // or not a value of Oracle SQL type NUMBER can be completely + // stored in the native floating point type. + // + + // The maximum decimal precision of a float is 7 significant + // digits. The minimum and maximum decimal exponents + // representable by a float are -37 and 38 respectively. + // + if (r <= 7) + { + int e = r - st.scale_value; + + if (e >= -37 && e <= 38) + traverse_float (mi); + else + traverse_double (mi); + } + + // The maximum decimal precision of a double is 15 significant + // digits. The minimum and maximum decimal exponent representable + // by a double exceeds that of the Oracle NUMBER type. + // + else if (r <= 15) + traverse_double (mi); + else + traverse_big_float (mi); + } + } + else + // If there is no precision, then this is a floating-point number. + // + traverse_double (mi); + + break; + } + case sql_type::FLOAT: + { + // We map FLOAT types based exclusively on their binary precision + // seeing that in 99% of cases it is the precision that is the + // limiting factor and not the exponent. + // + if (mi.st->prec_value <= 24) + traverse_float (mi); + else if (mi.st->prec_value <= 53) + traverse_double (mi); + else + traverse_big_float (mi); + + break; + } + case sql_type::BINARY_FLOAT: + { + traverse_float (mi); + break; + } + case sql_type::BINARY_DOUBLE: + { + traverse_double (mi); + break; + } + // Data-time types. + // + case sql_type::DATE: + { + traverse_date (mi); + break; + } + case sql_type::TIMESTAMP: + { + traverse_timestamp (mi); + break; + } + case sql_type::INTERVAL_YM: + { + traverse_interval_ym (mi); + break; + } + case sql_type::INTERVAL_DS: + { + traverse_interval_ds (mi); + break; + } + // String and binary types. + // + case sql_type::CHAR: + case sql_type::NCHAR: + case sql_type::VARCHAR2: + case sql_type::NVARCHAR2: + case sql_type::RAW: + { + traverse_string (mi); + break; + } + case sql_type::BLOB: + case sql_type::CLOB: + case sql_type::NCLOB: + { + traverse_lob (mi); + break; + } + case sql_type::invalid: + { + assert (false); + break; + } + } + } + + // + // member_image_type + // + + 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_oracle >::image_type"; + } + + void member_image_type:: + traverse_int32 (member_info& mi) + { + if (unsigned_integer (mi.t)) + type_ = "unsigned int"; + else + type_ = "int"; + } + + void member_image_type:: + traverse_int64 (member_info& mi) + { + if (unsigned_integer (mi.t)) + type_ = "unsigned long long"; + else + type_ = "long long"; + } + + void member_image_type:: + traverse_big_int (member_info&) + { + type_ = "char*"; + } + + void member_image_type:: + traverse_float (member_info&) + { + type_ = "float"; + } + + void member_image_type:: + traverse_double (member_info&) + { + type_ = "double"; + } + + void member_image_type:: + traverse_big_float (member_info&) + { + type_ = "char*"; + } + + void member_image_type:: + traverse_date (member_info&) + { + type_ = "char*"; + } + + void member_image_type:: + traverse_timestamp (member_info&) + { + type_ = "oracle::datetime"; + } + + void member_image_type:: + traverse_interval_ym (member_info&) + { + type_ = "oracle::interval_ym"; + } + + void member_image_type:: + traverse_interval_ds (member_info&) + { + type_ = "oracle::interval_ds"; + } + + void member_image_type:: + traverse_string (member_info&) + { + type_ = "char*"; + } + + void member_image_type:: + traverse_lob (member_info&) + { + type_ = "oracle::lob_callback"; + } + + entry<member_image_type> member_image_type_; + + // + // member_database_type + // + + namespace + { + const char* string_bin_database_id[] = + { + "id_string", // CHAR + "id_nstring", // NCHAR + "id_string", // VARCHAR2 + "id_nstring", // NVARCHAR2 + "id_raw" // RAW + }; + + const char* lob_database_id[] = + { + "id_blob", + "id_clob", + "id_nclob" + }; + } + + 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_int32 (member_info&) + { + type_id_ = "oracle::id_int32"; + } + + void member_database_type_id:: + traverse_int64 (member_info&) + { + type_id_ = "oracle::id_int64"; + } + + void member_database_type_id:: + traverse_big_int (member_info&) + { + type_id_ = "oracle::id_big_int"; + } + + void member_database_type_id:: + traverse_float (member_info&) + { + type_id_ = "oracle::id_float"; + } + + void member_database_type_id:: + traverse_double (member_info&) + { + type_id_ = "oracle::id_double"; + } + + void member_database_type_id:: + traverse_big_float (member_info&) + { + type_id_ = "oracle::id_big_float"; + } + + void member_database_type_id:: + traverse_date (member_info&) + { + type_id_ = "oracle::id_date"; + } + + void member_database_type_id:: + traverse_timestamp (member_info&) + { + type_id_ = "oracle::id_timestamp"; + } + + void member_database_type_id:: + traverse_interval_ym (member_info&) + { + type_id_ = "oracle::id_interval_ym"; + } + + void member_database_type_id:: + traverse_interval_ds (member_info&) + { + type_id_ = "oracle::id_interval_ds"; + } + + void member_database_type_id:: + traverse_string (member_info& mi) + { + type_id_ = string ("oracle::") + + string_bin_database_id[mi.st->type - sql_type::CHAR]; + } + + void member_database_type_id:: + traverse_lob (member_info& mi) + { + type_id_ = string ("oracle::") + + lob_database_id[mi.st->type - sql_type::BLOB]; + } + + 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) {} + + void + column_ctor (string const& type, string const& name, string const& base) + { + os << name << " ("; + + if (multi_dynamic) + os << "odb::query_column< " << type << " >& qc," << endl; + + os << "const char* t," << endl + << "const char* c," << endl + << "const char* conv," << endl + << "unsigned short p = 0xFFF," << endl + << "short s = 0xFFF)" << endl + << " : " << base << " (" << (multi_dynamic ? "qc, " : "") << + "t, c, conv, p, s)" + << "{" + << "}"; + } + + virtual void + column_ctor_args_extra (semantics::data_member& m) + { + // For some types we need to pass precision and scale. + // + sql_type const& st (parse_sql_type (column_type (), m)); + + switch (st.type) + { + case sql_type::NUMBER: + { + if (st.prec) + { + os << ", " << st.prec_value; + + if (st.scale) + os << ", " << st.scale_value; + } + break; + } + case sql_type::FLOAT: + { + os << ", " << st.prec_value; + break; + } + case sql_type::TIMESTAMP: + { + os << ", " << st.prec_value; + break; + } + case sql_type::INTERVAL_YM: + { + os << ", " << st.prec_value; + break; + } + case sql_type::INTERVAL_DS: + { + // INTERVAL DAY TO SECOND has two precisions. + // + os << ", " << st.prec_value << ", " << st.scale_value; + break; + } + case sql_type::CHAR: + case sql_type::NCHAR: + case sql_type::VARCHAR2: + case sql_type::NVARCHAR2: + case sql_type::RAW: + { + // The same logic as in header.cxx. + // + size_t n (st.prec ? st.prec_value : 1); + + if (!st.byte_semantics) + n *= 4; + + if (st.type == sql_type::VARCHAR2 || + st.type == sql_type::NVARCHAR2) + n = n > 4000 ? 4000 : n; + else + n = n > 2000 ? 2000 : n; + + os << ", " << n; + break; + } + default: + { + break; + } + } + } + + 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/oracle/common.hxx b/odb/odb/relational/oracle/common.hxx new file mode 100644 index 0000000..1958aab --- /dev/null +++ b/odb/odb/relational/oracle/common.hxx @@ -0,0 +1,203 @@ +// file : odb/relational/oracle/common.hxx +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_RELATIONAL_ORACLE_COMMON_HXX +#define ODB_RELATIONAL_ORACLE_COMMON_HXX + +#include <odb/relational/common.hxx> +#include <odb/relational/oracle/context.hxx> + +namespace relational +{ + namespace oracle + { + 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 oracle 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_int32 (member_info&) + { + } + + virtual void + traverse_int64 (member_info&) + { + } + + virtual void + traverse_big_int (member_info&) + { + } + + virtual void + traverse_float (member_info&) + { + } + + virtual void + traverse_double (member_info&) + { + } + + virtual void + traverse_big_float (member_info&) + { + } + + virtual void + traverse_date (member_info&) + { + } + + virtual void + traverse_timestamp (member_info&) + { + } + + virtual void + traverse_interval_ym (member_info&) + { + } + + virtual void + traverse_interval_ds (member_info&) + { + } + + virtual void + traverse_string (member_info&) + { + } + + virtual void + traverse_lob (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_int32 (member_info&); + + virtual void + traverse_int64 (member_info&); + + virtual void + traverse_big_int (member_info&); + + virtual void + traverse_float (member_info&); + + virtual void + traverse_double (member_info&); + + virtual void + traverse_big_float (member_info&); + + virtual void + traverse_date (member_info&); + + virtual void + traverse_timestamp (member_info&); + + virtual void + traverse_interval_ym (member_info&); + + virtual void + traverse_interval_ds (member_info&); + + virtual void + traverse_string (member_info&); + + virtual void + traverse_lob (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_int32 (member_info&); + + virtual void + traverse_int64 (member_info&); + + virtual void + traverse_big_int (member_info&); + + virtual void + traverse_float (member_info&); + + virtual void + traverse_double (member_info&); + + virtual void + traverse_big_float (member_info&); + + virtual void + traverse_date (member_info&); + + virtual void + traverse_timestamp (member_info&); + + virtual void + traverse_interval_ym (member_info&); + + virtual void + traverse_interval_ds (member_info&); + + virtual void + traverse_string (member_info&); + + virtual void + traverse_lob (member_info&); + + private: + string type_id_; + }; + } +} +#endif // ODB_RELATIONAL_ORACLE_COMMON_HXX diff --git a/odb/odb/relational/oracle/context.cxx b/odb/odb/relational/oracle/context.cxx new file mode 100644 index 0000000..12ce0aa --- /dev/null +++ b/odb/odb/relational/oracle/context.cxx @@ -0,0 +1,795 @@ +// file : odb/relational/oracle/context.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cassert> +#include <sstream> + +#include <odb/sql-token.hxx> +#include <odb/sql-lexer.hxx> + +#include <odb/relational/oracle/context.hxx> + +using namespace std; + +namespace relational +{ + namespace oracle + { + 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", "NUMBER(1)", 0, false}, + + {"char", "CHAR(1)", 0, false}, + {"signed char", "NUMBER(3)", 0, false}, + {"unsigned char", "NUMBER(3)", 0, false}, + + {"short int", "NUMBER(5)", 0, false}, + {"short unsigned int", "NUMBER(5)", 0, false}, + + {"int", "NUMBER(10)", 0, false}, + {"unsigned int", "NUMBER(10)", 0, false}, + + {"long int", "NUMBER(19)", 0, false}, + {"long unsigned int", "NUMBER(20)", 0, false}, + + {"long long int", "NUMBER(19)", 0, false}, + {"long long unsigned int", "NUMBER(20)", 0, false}, + + {"float", "BINARY_FLOAT", 0, false}, + {"double", "BINARY_DOUBLE", 0, false}, + + // Oracle treats empty VARCHAR2 (and NVARCHAR2) strings as NULL. + // + {"::std::string", "VARCHAR2(512)", 0, true}, + + {"::size_t", "NUMBER(20)", 0, false}, + {"::std::size_t", "NUMBER(20)", 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 = false; + need_alias_as = false; + insert_send_auto_id = false; + delay_freeing_statement_result = false; + need_image_clone = true; + generate_bulk = true; + global_index = true; + global_fkey = true; + data_->bind_vector_ = "oracle::bind*"; + + // 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_) + { + } + + 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; + + if (f) + f = false; + else + r += '.'; + + r += '"'; + r.append (*i, 0, 30); // Max identifier length is 30. + r += '"'; + } + + 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 = "VARCHAR2"; + n--; + } + + // Oracle VARCHAR2 limit is 4000 bytes. Since there are no good + // alternatives (CLOB?), let the user specify the mapping. + // + if (n > 4000) + return ""; + + // Allow empty VARCHAR2 values. + // + if (null != 0 && r == "VARCHAR2") + *null = true; + + ostringstream ostr; + ostr << n; + r += '('; + r += ostr.str (); + r += ')'; + } + } + + return r; + } + + bool context:: + unsigned_integer (semantics::type& t) + { + semantics::type* wt (wrapper (t)); + const string& s ((wt == 0 ? t : utype (*wt)).name ()); + + return s == "bool" || + s == "unsigned char" || + s == "short unsigned int" || + s == "unsigned int" || + s == "long unsigned int" || + s == "long long unsigned int"; + } + + qname context:: + sequence_name (qname const& table) + { + string n; + + if (options.sequence_suffix ().count (db) != 0) + n = table.uname () + options.sequence_suffix ()[db]; + else + n = compose_name (table.uname (), "seq"); + + n = transform_name (n, sql_name_sequence); + + qname r (table.qualifier ()); + r.append (n); + return r; + } + + // + // SQL type parsing. + // + + 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: + // + // CHARACTER VARYING (VARCHAR2) + // CHAR VARYING (VARCHAR2) + // NATIONAL CHARACTER (NCHAR) + // NATIONAL CHAR (NCHAR) + // NCHAR VARYING (NVARCHAR2) + // NATIONAL CHARACTER VARYING (NVARCHAR2) + // NATIONAL CHAR VARYING (NVARCHAR2) + // NCHAR VARYING (NVARCHAR2) + // DOUBLE PRECISION (FLOAT(126)) + // INTERVAL YEAR TO MONTH + // INTERVAL DAY TO SECOND + // + enum state + { + parse_identifier, + parse_prec, + parse_done + }; + + state s (parse_identifier); + string prefix; + sql_token t (l.next ()); + + while (t.type () != sql_token::t_eos) + { + sql_token::token_type tt (t.type ()); + + switch (s) + { + case parse_identifier: + { + if (tt == sql_token::t_identifier) + { + string const& id (context::upcase (t.identifier ())); + + // + // Numeric types. + // + if ((id == "NUMBER") && prefix.empty ()) + { + // If NUMBER has no precision/scale, then it is a floating- + // point number. We indicate this by having no precision. + // + r.type = sql_type::NUMBER; + s = parse_prec; + } + else if ((id == "DEC" || id == "DECIMAL" || id == "NUMERIC") + && prefix.empty ()) + { + // DEC, DECIMAL, and NUMERIC are equivalent to NUMBER in + // all ways except that they may not represent a floating + // point number. The scale defaults to zero. + // + r.type = sql_type::NUMBER; + s = parse_prec; + } + else if ((id == "INT" || id == "INTEGER" || id == "SMALLINT") + && prefix.empty ()) + { + // INT, INTEGER, and SMALLINT map to NUMBER(38). They may not + // have precision or scale explicitly specified. + // + r.type = sql_type::NUMBER; + r.prec = true; + r.prec_value = 38; + + s = parse_done; + } + // + // Floating point types. + // + else if (id == "FLOAT" && prefix.empty ()) + { + r.type = sql_type::FLOAT; + r.prec = true; + r.prec_value = 126; + + s = parse_prec; + } + else if (id == "DOUBLE" && prefix.empty ()) + { + prefix = id; + } + else if (id == "PRECISION" && prefix == "DOUBLE") + { + r.type = sql_type::FLOAT; + r.prec = true; + r.prec_value = 126; + + s = parse_done; + } + else if (id == "REAL" && prefix.empty ()) + { + r.type = sql_type::FLOAT; + r.prec = true; + r.prec_value = 63; + + s = parse_done; + } + else if (id == "BINARY_FLOAT" && prefix.empty ()) + { + r.type = sql_type::BINARY_FLOAT; + s = parse_done; + } + else if (id == "BINARY_DOUBLE" && prefix.empty ()) + { + r.type = sql_type::BINARY_DOUBLE; + s = parse_done; + } + // + // Date-time types. + // + else if (id == "DATE" && prefix.empty ()) + { + r.type = sql_type::DATE; + s = parse_done; + } + else if (id == "TIMESTAMP" && prefix.empty ()) + { + prefix = id; + } + else if (id == "INTERVAL" && prefix.empty ()) + { + prefix = id; + } + else if (id == "YEAR" && prefix == "INTERVAL") + { + prefix += " "; + prefix += id; + + r.prec = true; + r.prec_value = 2; + s = parse_prec; + } + else if (id == "DAY" && prefix == "INTERVAL") + { + prefix += " "; + prefix += id; + + r.prec = true; + r.prec_value = 2; + s = parse_prec; + } + else if (id == "TO" && + (prefix == "INTERVAL YEAR" || + prefix == "INTERVAL DAY")) + { + prefix += " "; + prefix += id; + } + else if (id == "MONTH" && prefix == "INTERVAL YEAR TO") + { + r.type = sql_type::INTERVAL_YM; + s = parse_done; + } + else if (id == "SECOND" && prefix == "INTERVAL DAY TO") + { + r.type = sql_type::INTERVAL_DS; + + // Store seconds precision in scale since prec holds + // the days precision. + // + r.scale = true; + r.scale_value = 6; + s = parse_prec; + } + // + // Timestamp with time zone (not supported). + // + else if (id == "WITH" && prefix == "TIMESTAMP") + { + prefix += " "; + prefix += id; + } + else if (id == "TIME" && + (prefix == "TIMESTAMP WITH" || + prefix == "TIMESTAMP WITH LOCAL")) + { + prefix += " "; + prefix += id; + } + else if (id == "LOCAL" && prefix == "TIMESTAMP WITH") + { + prefix += " "; + prefix += id; + } + else if (id == "ZONE" && + (prefix == "TIMESTAMP WITH LOCAL TIME" || + prefix == "TIMESTAMP WITH TIME")) + { + return error (ct, "Oracle timestamps with time zones are " + "not currently supported"); + } + // + // String and binary types. + // + else if (id == "CHAR") + { + prefix += prefix.empty () ? "" : " "; + prefix += id; + } + else if (id == "CHARACTER") + { + prefix += prefix.empty () ? "" : " "; + prefix += id; + } + else if (id == "NCHAR") + { + prefix += prefix.empty () ? "" : " "; + prefix += id; + } + else if (id == "VARCHAR" || id == "VARCHAR2") + { + // VARCHAR is currently mapped to VARCHAR2 in Oracle server. + // However, this may change in future versions. + // + r.type = sql_type::VARCHAR2; + r.byte_semantics = true; + s = parse_prec; + } + else if (id == "NVARCHAR2") + { + r.type = sql_type::NVARCHAR2; + r.byte_semantics = false; + s = parse_prec; + } + else if (id == "VARYING") + { + // VARYING always appears at the end of an identifier. + // + if (prefix == "CHAR" || prefix == "CHARACTER") + { + r.type = sql_type::VARCHAR2; + r.byte_semantics = true; + } + else if (prefix == "NCHAR" || + prefix == "NATIONAL CHAR" || + prefix == "NATIONAL CHARACTER") + { + r.type = sql_type::NVARCHAR2; + r.byte_semantics = false; + } + + s = parse_prec; + } + else if (id == "NATIONAL" && prefix.empty ()) + { + prefix = id; + } + else if (id == "RAW" && prefix.empty ()) + { + r.type = sql_type::RAW; + s = parse_prec; + } + // + // LOB types. + // + else if (id == "BLOB" && prefix.empty ()) + { + r.type = sql_type::BLOB; + s = parse_done; + } + else if (id == "CLOB" && prefix.empty ()) + { + r.type = sql_type::CLOB; + s = parse_done; + } + else if (id == "NCLOB" && prefix.empty ()) + { + r.type = sql_type::NCLOB; + s = parse_done; + } + // + // LONG types. + // + else if (id == "LONG") + return error (ct, "Oracle LONG types are not supported"); + else + return error (ct, "unknown Oracle type '" + + t.identifier () + "'"); + + t = l.next (); + continue; + } + else if (!prefix.empty ()) + { + // Some prefixes can also be type names if not followed + // by the actual type name. + // + + if (prefix == "CHAR" || prefix == "CHARACTER") + { + r.type = sql_type::CHAR; + r.byte_semantics = true; + r.prec = true; + r.prec_value = 1; + } + else if (prefix == "NCHAR" || + prefix == "NATIONAL CHAR" || + prefix == "NATIONAL CHARACTER") + { + r.type = sql_type::NCHAR; + r.byte_semantics = false; + r.prec = true; + r.prec_value = 1; + } + else if (prefix == "TIMESTAMP") + { + r.type = sql_type::TIMESTAMP; + r.prec = true; + r.prec_value = 6; + } + else + return error (ct, "incomplete Oracle type declaration: '" + + prefix + "'"); + + // All of the possible types handled in this block can take + // an optional precision specifier. Set the state and fall + // through to the parse_prec handler. + // + s = parse_prec; + } + else + { + assert (r.type == sql_type::invalid); + return error (ct, "unexepected '" + t.literal () + + "' in Oracle type declaration"); + } + } + // Fall through. + case parse_prec: + { + if (t.punctuation () == sql_token::p_lparen) + { + t = l.next (); + + if (t.type () != sql_token::t_int_lit) + { + return error (ct, "integer size/precision expected in " + "Oracle type declaration"); + } + + // Parse the precision. + // + { + unsigned short v; + istringstream is (t.literal ()); + + if (!(is >> v && is.eof ())) + { + return error (ct, "invalid prec value '" + t.literal () + + "' in Oracle type declaration"); + } + + // Store seconds precision in scale since prec holds + // the days precision for INTERVAL DAY TO SECOND. + // + if (r.type == sql_type::INTERVAL_DS) + { + r.scale = true; + r.scale_value = static_cast<short> (v); + } + else + { + r.prec = true; + r.prec_value = v; + } + + t = l.next (); + } + + // Parse the scale if present. + // + if (t.punctuation () == sql_token::p_comma) + { + // Scale can only be specified for NUMBER. + // + if (r.type != sql_type::NUMBER) + { + return error (ct, "invalid scale in Oracle type " + "declaration"); + } + + t = l.next (); + + if (t.type () != sql_token::t_int_lit) + { + return error (ct, "integer scale expected in Oracle type " + "declaration"); + } + + short v; + istringstream is (t.literal ()); + + if (!(is >> v && is.eof ())) + { + return error (ct, "invalid scale value '" + t.literal () + + "' in Oracle type declaration"); + } + + r.scale = true; + r.scale_value = v; + + t = l.next (); + } + else if (t.type () == sql_token::t_identifier) + { + const string& id (context::upcase (t.identifier ())); + + if (id == "CHAR") + r.byte_semantics = false; + else if (id != "BYTE") + { + return error (ct, "invalid keyword '" + t.literal () + + "' in Oracle type declaration"); + } + + t = l.next (); + } + + if (t.punctuation () != sql_token::p_rparen) + { + return error (ct, "expected ')' in Oracle type declaration"); + } + else + t = l.next (); + } + + s = r.type == sql_type::invalid ? parse_identifier : parse_done; + continue; + } + case parse_done: + { + return error (ct, "unexepected '" + t.literal () + "' in Oracle " + "type declaration"); + break; + } + } + } + + // Some prefixes can also be type names if not followed by the actual + // type name. + // + if (r.type == sql_type::invalid) + { + if (!prefix.empty ()) + { + if (prefix == "CHAR" || prefix == "CHARACTER") + { + r.type = sql_type::CHAR; + r.byte_semantics = true; + r.prec = true; + r.prec_value = 1; + } + else if (prefix == "NCHAR" || + prefix == "NATIONAL CHAR" || + prefix == "NATIONAL CHARACTER") + { + r.type = sql_type::NCHAR; + r.byte_semantics = false; + r.prec = true; + r.prec_value = 1; + } + else if (prefix == "TIMESTAMP") + { + r.type = sql_type::TIMESTAMP; + r.prec = true; + r.prec_value = 6; + } + else + return error (ct, "incomplete Oracle type declaration: '" + + prefix + "'"); + } + else + return error (ct, "invalid Oracle type declaration"); + } + + return r; + } + catch (sql_lexer::invalid_input const& e) + { + return error (ct, "invalid Oracle type declaration: " + e.message); + } + } + } +} diff --git a/odb/odb/relational/oracle/context.hxx b/odb/odb/relational/oracle/context.hxx new file mode 100644 index 0000000..6c55853 --- /dev/null +++ b/odb/odb/relational/oracle/context.hxx @@ -0,0 +1,188 @@ +// file : odb/relational/oracle/context.hxx +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_RELATIONAL_ORACLE_CONTEXT_HXX +#define ODB_RELATIONAL_ORACLE_CONTEXT_HXX + +#include <map> + +#include <odb/relational/context.hxx> + +namespace relational +{ + namespace oracle + { + struct sql_type + { + // Keep the order in each block of types. + // + enum core_type + { + // Numeric types. + // + NUMBER, + FLOAT, + + // Floating point types. + // + BINARY_FLOAT, + BINARY_DOUBLE, + + // Date-time types. + // + DATE, + TIMESTAMP, + INTERVAL_YM, + INTERVAL_DS, + + // String and binary types. + // + CHAR, + NCHAR, + VARCHAR2, + NVARCHAR2, + RAW, + + // LOB types. + // + BLOB, + CLOB, + NCLOB, + + // Invalid type. + // + invalid + }; + + sql_type () : + type (invalid), prec (false), scale (false), byte_semantics (true) + { + } + + core_type type; + + bool prec; + unsigned short prec_value; // Oracle max value is 4000. + + bool scale; + short scale_value; // Oracle min value is -84. Max value is 127. + + bool byte_semantics; + + // 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: + // If necessary, unwraps. + // + static bool + unsigned_integer (semantics::type&); + + public: + // Construct sequence name from a given table name. + // + qname + sequence_name (qname const& table); + + protected: + virtual string const& + convert_expr (string const&, semantics::data_member&, bool); + + virtual string + quote_id_impl (qname const&) 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_ORACLE_CONTEXT_HXX diff --git a/odb/odb/relational/oracle/header.cxx b/odb/odb/relational/oracle/header.cxx new file mode 100644 index 0000000..bf50bb2 --- /dev/null +++ b/odb/odb/relational/oracle/header.cxx @@ -0,0 +1,230 @@ +// file : odb/relational/oracle/header.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <odb/relational/header.hxx> + +#include <odb/relational/oracle/common.hxx> +#include <odb/relational/oracle/context.hxx> + +namespace relational +{ + namespace oracle + { + namespace header + { + namespace relational = relational::header; + + struct image_type: relational::image_type, context + { + image_type (base const& x): base (x) {}; + + virtual void + image_extra (type& c) + { + if (!(composite (c) || (abstract (c) && !polymorphic (c)))) + { + type* poly_root (polymorphic (c)); + + // If this is a polymorphic type, only add callback to the root. + // + if (poly_root == 0 || poly_root == &c) + { + bool gc (options.generate_query ()); + + if (gc) + os << "oracle::change_callback change_callback_;" + << endl; + + os << "oracle::change_callback*" << endl + << "change_callback ()" + << "{"; + + if (gc) + os << "return &change_callback_;"; + else + os << "return 0;"; + + os << "}"; + } + } + } + }; + entry<image_type> image_type_; + + 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_int32 (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_int64 (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_big_int (member_info& mi) + { + // Each significant base-100 digit requires a byte of storage + // in the manitissa. The default precision is 38 decimal digits, + // which is equivalent to 19 base-100 digits. + // + size_t n (19); + + if (mi.st->prec) + n = mi.st->prec_value / 2 + mi.st->prec_value % 2; + + // We require an additional byte for each of the exponent and + // negative value terminator values. + // + n += 2; + + os << "char " << mi.var << "value[" << n << "];" + << "ub2 " << mi.var << "size;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_float (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_double (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_big_float (member_info& mi) + { + // big_float is mapped to the OCI type SQLT_NUM, which requires 21 + // bytes of storage. + // + os << "char " << mi.var << "value[21];" + << "ub2 " << mi.var << "size;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_date (member_info& mi) + { + os << "char " << mi.var << "value[7];" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_timestamp (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_interval_ym (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_interval_ds (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_string (member_info& mi) + { + size_t n (mi.st->prec ? mi.st->prec_value : 1); + + // National characters can be either UTF-8 or UTF-16 encoded, + // both of which have a maximum character encoding size of 4 + // bytes. Database character set can also be UTF-8 so if the + // size is specified in characters, then conservatively assume + // each character can take up to 4 bytes. + // + if (!mi.st->byte_semantics) // N*CHAR always has CHAR semantics. + n *= 4; + + if (mi.st->type == sql_type::VARCHAR2 || + mi.st->type == sql_type::NVARCHAR2) + n = n > 4000 ? 4000 : n; + else + n = n > 2000 ? 2000 : n; + + os << "char " << mi.var << "value[" << n << "];" + << "ub2 " << mi.var << "size;" + << "sb2 " << mi.var << "indicator;" + << endl; + } + + virtual void + traverse_lob (member_info& mi) + { + os << "mutable " << image_type << " " << mi.var << "callback;" + << "sb2 " << mi.var << "indicator;" + << "oracle::lob " << mi.var << "lob;" + << endl; + } + }; + entry<image_member> image_member_; + + struct class1: relational::class1 + { + class1 (base const& x): base (x) {} + + virtual void + object_public_extra_pre (type& c) + { + bool abst (abstract (c)); + + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + if (poly_derived || (abst && !poly)) + 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; + } + } + }; + entry<class1> class1_entry_; + } + } +} diff --git a/odb/odb/relational/oracle/inline.cxx b/odb/odb/relational/oracle/inline.cxx new file mode 100644 index 0000000..1b6d606 --- /dev/null +++ b/odb/odb/relational/oracle/inline.cxx @@ -0,0 +1,42 @@ +// file : odb/relational/oracle/inline.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <odb/relational/inline.hxx> + +#include <odb/relational/oracle/common.hxx> +#include <odb/relational/oracle/context.hxx> + +using namespace std; + +namespace relational +{ + namespace oracle + { + 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 << "indicator == -1;"; + else + os << "i." << mi.var << "indicator = -1;"; + } + }; + entry<null_member> null_member_; + } + } +} diff --git a/odb/odb/relational/oracle/model.cxx b/odb/odb/relational/oracle/model.cxx new file mode 100644 index 0000000..b65e201 --- /dev/null +++ b/odb/odb/relational/oracle/model.cxx @@ -0,0 +1,64 @@ +// file : odb/relational/oracle/model.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <sstream> + +#include <odb/relational/model.hxx> + +#include <odb/relational/oracle/common.hxx> +#include <odb/relational/oracle/context.hxx> + +using namespace std; + +namespace relational +{ + namespace oracle + { + namespace model + { + namespace relational = relational::model; + + struct object_columns: relational::object_columns, context + { + object_columns (base const& x): base (x) {} + + virtual string + default_enum (semantics::data_member& m, tree en, string const&) + { + // Make sure the column is mapped to Oracle NUMBER. + // + sql_type const& t (parse_sql_type (column_type (), m, false)); + if (t.type != sql_type::NUMBER) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to Oracle NUMBER" << 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 (); + } + + virtual void + primary_key (sema_rel::primary_key& pk) + { + if (pk.auto_ ()) + pk.extra ()["sequence"] = sequence_name (table_.name ()).string (); + } + }; + entry<object_columns> object_columns_; + } + } +} diff --git a/odb/odb/relational/oracle/schema.cxx b/odb/odb/relational/oracle/schema.cxx new file mode 100644 index 0000000..75100b1 --- /dev/null +++ b/odb/odb/relational/oracle/schema.cxx @@ -0,0 +1,696 @@ +// file : odb/relational/oracle/schema.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <map> +#include <utility> // pair + +#include <odb/diagnostics.hxx> + +#include <odb/relational/schema.hxx> + +#include <odb/relational/oracle/common.hxx> +#include <odb/relational/oracle/context.hxx> + +using namespace std; + +namespace relational +{ + namespace oracle + { + namespace schema + { + namespace relational = relational::schema; + using relational::table_set; + + struct sql_emitter: relational::sql_emitter + { + sql_emitter (const base& x): base (x) {} + + virtual void + line (const std::string& l) + { + // SQLPlus doesn't like empty line in the middle of a statement. + // + if (!l.empty ()) + { + base::line (l); + last_ = l; + } + } + + virtual void + post () + { + if (!first_) // Ignore empty statements. + { + if (last_ == "END;") + os << endl + << '/' << endl + << endl; + + else + os << ';' << endl + << endl; + } + } + + private: + string last_; + }; + entry<sql_emitter> sql_emitter_; + + // + // File. + // + + struct sql_file: relational::sql_file, context + { + sql_file (const base& x): base (x) {} + + virtual void + prologue () + { + // Quiet down SQLPlus and make sure it exits with an error + // code if there is an error. + // + os << "SET FEEDBACK OFF;" << endl + << "WHENEVER SQLERROR EXIT FAILURE;" << endl + << "WHENEVER OSERROR EXIT FAILURE;" << endl + << endl; + } + + virtual void + epilogue () + { + os << "EXIT;" << endl; + } + }; + entry<sql_file> sql_file_; + + // + // Drop. + // + + struct drop_column: relational::drop_column, context + { + drop_column (base const& x): base (x) {} + + virtual void + traverse (sema_rel::drop_column& dc) + { + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + os << quote_id (dc.name ()); + } + }; + entry<drop_column> drop_column_; + + struct drop_foreign_key: relational::drop_foreign_key, context + { + drop_foreign_key (base const& x): base (x) {} + + virtual void + traverse (sema_rel::drop_foreign_key& dfk) + { + os << endl; + drop (dfk); + } + }; + entry<drop_foreign_key> drop_foreign_key_; + + struct drop_index: relational::drop_index, context + { + drop_index (base const& x): base (x) {} + + virtual string + name (sema_rel::index& in) + { + // In Oracle, index names can be qualified with the schema. + // + sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ())); + sema_rel::qname n (t.name ().qualifier ()); + n.append (in.name ()); + return quote_id (n); + } + }; + entry<drop_index> drop_index_; + + struct drop_table: relational::drop_table, context + { + drop_table (base const& x): base (x) {} + + virtual void + drop (sema_rel::table& t, bool migration) + { + using sema_rel::primary_key; + + sema_rel::table::names_iterator i (t.find ("")); // Special name. + primary_key* pk (i != t.names_end () + ? &dynamic_cast<primary_key&> (i->nameable ()) + : 0); + + string qt (quote_id (t.name ())); + string qs (pk != 0 && pk->auto_ () + ? quote_id (qname::from_string (pk->extra ()["sequence"])) + : ""); + + if (migration) + { + pre_statement (); + os << "DROP TABLE " << qt << endl; + post_statement (); + + // Drop the sequence if we have auto primary key. + // + if (!qs.empty ()) + { + pre_statement (); + os << "DROP SEQUENCE " << qs << endl; + post_statement (); + } + } + else + { + // Oracle has no IF EXISTS conditional for dropping objects. The + // PL/SQL approach below seems to be the least error-prone and the + // most widely used of the alternatives. + // + pre_statement (); + os << "BEGIN" << endl + << " BEGIN" << endl + << " EXECUTE IMMEDIATE 'DROP TABLE " << qt << " CASCADE " << + "CONSTRAINTS';" << endl + << " EXCEPTION" << endl + << " WHEN OTHERS THEN" << endl + << " IF SQLCODE != -942 THEN RAISE; END IF;" << endl + << " END;" << endl; + + // Drop the sequence if we have auto primary key. + // + if (!qs.empty ()) + { + os << " BEGIN" << endl + << " EXECUTE IMMEDIATE 'DROP SEQUENCE " << qs << + "';" << endl + << " EXCEPTION" << endl + << " WHEN OTHERS THEN" << endl + << " IF SQLCODE != -2289 THEN RAISE; END IF;" << endl + << " END;" << endl; + } + + os << "END;" << endl; + post_statement (); + } + } + + 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; + + drop (t, migration); + } + }; + entry<drop_table> drop_table_; + + // + // Create. + // + static sema_rel::uname + truncate (location const& l, const char* kind, sema_rel::uname n, bool w) + { + if (n.size () > 30) + { + if (w) + warn (l) << kind << " name '" << n << "' is longer than 30 " + << "characters and will be truncated" << endl; + + n.resize (30); + } + + return n; + } + + static sema_rel::qname + truncate (location const& l, + const char* kind, + sema_rel::qname const& n, + bool w) + { + // Don't bother verifying the schema name since that is + // specified explicitly and in a single place. + // + qname r (n.qualifier ()); + r.append (truncate (l, kind, n.uname (), w)); + return r; + } + + template <typename N> + struct scope + { + typedef std::map<N, pair<N, location> > map; + + scope (const char* k, const char* p, bool w) + : kind_ (k), prag_ (p), warn_ (w) {} + + void + check (location const& l, N const& n) + { + N tn (truncate (l, kind_, n, warn_)); + + pair<typename map::iterator, bool> r ( + map_.insert (make_pair (tn, make_pair (n, l)))); + + if (r.second) + return; + + error (l) << kind_ << " name '" << tn << "' conflicts with an " + << "already defined " << kind_ << " name" << endl; + + if (tn != n) + info (l) << kind_ << " name '" << tn << "' is truncated '" + << n << "'" << endl; + + N const& n1 (r.first->second.first); + location const& l1 (r.first->second.second); + + info (l1) << "conflicting " << kind_ << " is defined here" << endl; + + if (tn != n) + info (l1) << "conflicting " << kind_ << " name '" << tn + << "' is truncated '" << n1 << "'" << endl; + + info (l) << "use #pragma db " << prag_ << " to change one of " + << "the names" << endl; + + throw operation_failed (); + } + + void + clear () {map_.clear ();} + + const char* kind_; + const char* prag_; + bool warn_; + map map_; + }; + + struct scopes + { + scopes (bool warn) + : tables ("table", "table", warn), + fkeys ("foreign key", "column", warn), // Change column name. + indexes ("index", "index", warn), + sequences ("sequence", "table", warn), // Change table name. + columns ("column", "column", warn) {} + + // In Oracle, all these entities are in their own name spaces, + // as in an index and a foreign key with the same name do not + // conflict. + // + scope<sema_rel::qname> tables; + scope<sema_rel::uname> fkeys; // Global but can't have schema. + scope<sema_rel::qname> indexes; + scope<sema_rel::qname> sequences; + scope<sema_rel::uname> columns; + }; + + struct create_column: relational::create_column, context + { + create_column (base const& x): base (x) {} + + virtual void + traverse (sema_rel::column& c) + { + // Check name trunction and conflicts. + // + if (scopes* s = static_cast<scopes*> (context::extra)) + s->columns.check (c.get<location> ("cxx-location"), c.name ()); + + base::traverse (c); + } + + virtual void + traverse (sema_rel::add_column& ac) + { + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + create (ac); + } + + virtual void + constraints (sema_rel::column& c, sema_rel::primary_key* pk) + { + // Oracle wants DEFAULT before NULL even though we can end + // up with mouthfulls like DEFAULT NULL NULL. + // + if (!c.default_ ().empty ()) + os << " DEFAULT " << c.default_ (); + + null (c); + + // If this is a single-column primary key, generate it inline. + // + if (pk != 0 && pk->contains_size () == 1) + primary_key (); + + if (pk != 0 && pk->auto_ ()) + auto_ (*pk); + } + }; + entry<create_column> create_column_; + + struct create_foreign_key: relational::create_foreign_key, context + { + create_foreign_key (base const& x): base (x) {} + + virtual void + traverse_create (sema_rel::foreign_key& fk) + { + // Check name trunction and conflicts. + // + if (scopes* s = static_cast<scopes*> (context::extra)) + s->fkeys.check (fk.get<location> ("cxx-location"), fk.name ()); + + base::traverse_create (fk); + } + + virtual void + traverse_add (sema_rel::foreign_key& fk) + { + // Check name trunction and conflicts. + // + if (scopes* s = static_cast<scopes*> (context::extra)) + s->fkeys.check (fk.get<location> ("cxx-location"), fk.name ()); + + os << endl + << " ADD CONSTRAINT "; + create (fk); + } + }; + entry<create_foreign_key> create_foreign_key_; + + struct create_index: relational::create_index, context + { + create_index (base const& x): base (x) {} + + virtual string + name (sema_rel::index& in) + { + // In Oracle, index names can be qualified with the schema. + // + sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ())); + sema_rel::qname n (t.name ().qualifier ()); + n.append (in.name ()); + + // Check name trunction and conflicts. + // + if (scopes* s = static_cast<scopes*> (context::extra)) + s->indexes.check (in.get<location> ("cxx-location"), n); + + return quote_id (n); + } + }; + entry<create_index> create_index_; + + struct create_table: relational::create_table, context + { + create_table (base const& x): base (x) {} + + void + traverse (sema_rel::table& t) + { + // Check name trunction and conflicts. + // + if (scopes* s = static_cast<scopes*> (context::extra)) + { + if (pass_ == 1) + { + s->tables.check (t.get<location> ("cxx-location"), t.name ()); + s->columns.clear (); + } + } + + base::traverse (t); + + if (pass_ == 1) + { + // Create the sequence if we have auto primary key. + // + using sema_rel::primary_key; + + sema_rel::table::names_iterator i (t.find ("")); // Special name. + primary_key* pk (i != t.names_end () + ? &dynamic_cast<primary_key&> (i->nameable ()) + : 0); + + if (pk != 0 && pk->auto_ ()) + { + // Already qualified with the table's schema, if any. + // + sema_rel::qname n ( + qname::from_string (pk->extra ()["sequence"])); + + if (scopes* s = static_cast<scopes*> (context::extra)) + s->sequences.check (pk->get<location> ("cxx-location"), n); + + pre_statement (); + os_ << "CREATE SEQUENCE " << quote_id (n) << endl + << " START WITH 1 INCREMENT BY 1" << endl; + post_statement (); + } + } + } + }; + entry<create_table> create_table_; + + struct create_model: relational::create_model, context + { + create_model (base const& x): base (x) {} + + void + traverse (sema_rel::model& m) + { + scopes s (options.oracle_warn_truncation ()); + context::extra = &s; + base::traverse (m); + context::extra = 0; + } + }; + entry<create_model> create_model_; + + // + // Alter. + // + + struct alter_column: relational::alter_column, context + { + alter_column (base const& x): base (x) {} + + virtual void + traverse (sema_rel::column& c) + { + // Relax (NULL) in pre and tighten (NOT NULL) in post. + // + if (pre_ != c.null ()) + return; + + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + os << quote_id (c.name ()) << (c.null () ? " NULL" : " NOT NULL"); + } + }; + entry<alter_column> alter_column_; + + struct alter_table_pre: relational::alter_table_pre, context + { + alter_table_pre (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // Oracle can only alter certain kinds of things together but + // grouped one at a time. + // + if (check<sema_rel::drop_foreign_key> (at)) + { + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()); + + instance<drop_foreign_key> dfc (*this); + trav_rel::unames n (*dfc); + names (at, n); + os << endl; + + post_statement (); + } + + if (check<sema_rel::add_column> (at)) + { + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()) << endl + << " ADD ("; + + instance<create_column> cc (*this); + trav_rel::unames n (*cc); + names (at, n); + os << ")" << endl; + + post_statement (); + } + + if (check_alter_column_null (at, true)) + { + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()) << endl + << " MODIFY ("; + + bool tl (true); // (Im)perfect forwarding. + instance<alter_column> ac (*this, tl); + trav_rel::unames n (*ac); + names (at, n); + os << ")" << endl; + + post_statement (); + } + } + }; + entry<alter_table_pre> alter_table_pre_; + + struct alter_table_post: relational::alter_table_post, context + { + alter_table_post (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // Oracle can only alter certain kinds of things together but + // grouped one at a time. + // + if (check<sema_rel::drop_column> (at)) + { + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()) << endl + << " DROP ("; + + instance<drop_column> dc (*this); + trav_rel::unames n (*dc); + names (at, n); + os << ")" << endl; + + post_statement (); + } + + if (check_alter_column_null (at, false)) + { + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()) << endl + << " MODIFY ("; + + bool fl (false); // (Im)perfect forwarding. + instance<alter_column> ac (*this, fl); + trav_rel::unames n (*ac); + names (at, n); + os << ")" << endl; + + post_statement (); + } + + if (check<sema_rel::add_foreign_key> (at)) + { + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()); + + instance<create_foreign_key> cfc (*this); + trav_rel::unames n (*cfc); + names (at, n); + os << endl; + + post_statement (); + } + } + }; + entry<alter_table_post> alter_table_post_; + + // + // Schema version table. + // + + struct version_table: relational::version_table, context + { + version_table (base const& x) + : base (x) + { + // If the schema name is empty, replace it with a single space + // to workaround the VARCHAR2 empty/NULL issue. + // + if (qs_ == "''") + qs_ = "' '"; + } + + virtual void + create_table () + { + pre_statement (); + + os << "BEGIN" << endl + << " EXECUTE IMMEDIATE 'CREATE TABLE " << qt_ << " (" << endl + << " " << qn_ << " VARCHAR2(512) NOT NULL PRIMARY KEY," << endl + << " " << qv_ << " NUMBER(20) NOT NULL," << endl + << " " << qm_ << " NUMBER(1) NOT NULL)';" << endl + << "EXCEPTION" << endl + << " WHEN OTHERS THEN" << endl + << " IF SQLCODE != -955 THEN RAISE; END IF;" << endl + << "END;" << endl; + + post_statement (); + } + + virtual void + create (sema_rel::version v) + { + pre_statement (); + + os << "MERGE INTO " << qt_ << " USING DUAL ON (" << qn_ << " = " << + qs_ << ")" << endl + << " WHEN NOT MATCHED THEN INSERT (" << endl + << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl + << " VALUES (" << qs_ << ", " << v << ", 0)" << endl; + + post_statement (); + } + }; + entry<version_table> version_table_; + } + } +} diff --git a/odb/odb/relational/oracle/source.cxx b/odb/odb/relational/oracle/source.cxx new file mode 100644 index 0000000..adf9864 --- /dev/null +++ b/odb/odb/relational/oracle/source.cxx @@ -0,0 +1,646 @@ +// file : odb/relational/oracle/source.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <odb/relational/source.hxx> + +#include <odb/relational/oracle/common.hxx> +#include <odb/relational/oracle/context.hxx> + +using namespace std; + +namespace relational +{ + namespace oracle + { + namespace source + { + namespace relational = relational::source; + + struct query_parameters: relational::query_parameters, context + { + 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 quote_id (sequence_name (table_)) + ".nextval"; + } + + private: + size_t i_; + }; + entry<query_parameters> query_parameters_; + + namespace + { + const char* string_buffer_types[] = + { + "oracle::bind::string", // CHAR + "oracle::bind::nstring", // NCHAR + "oracle::bind::string", // VARCHAR2 + "oracle::bind::nstring", // NVARCHAR2 + "oracle::bind::raw" // RAW + }; + + const char* lob_buffer_types[] = + { + "oracle::bind::blob", + "oracle::bind::clob", + "oracle::bind::nclob" + }; + } + + // + // 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_int32 (member_info& mi) + { + os << b << ".type = oracle::bind::" << + (unsigned_integer (mi.t) ? "uinteger" : "integer") << ";" + << b << ".buffer = &" << arg << "." << mi.var << "value;" + << b << ".capacity = 4;" + << b << ".size = 0;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_int64 (member_info& mi) + { + os << b << ".type = oracle::bind::" << + (unsigned_integer (mi.t) ? "uinteger" : "integer") << ";" + << b << ".buffer= &" << arg << "." << mi.var << "value;" + << b << ".capacity = 8;" + << b << ".size = 0;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_big_int (member_info& mi) + { + os << b << ".type = oracle::bind::number;" + << b << ".buffer = " << arg << "." << mi.var << "value;" + << b << ".capacity = static_cast<ub4> (sizeof (" << arg << + "." << mi.var << "value));" + << b << ".size = &" << arg << "." << mi.var << "size;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_float (member_info& mi) + { + os << b << ".type = oracle::bind::binary_float;" + << b << ".buffer= &" << arg << "." << mi.var << "value;" + << b << ".capacity = 4;" + << b << ".size = 0;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_double (member_info& mi) + { + os << b << ".type = oracle::bind::binary_double;" + << b << ".buffer= &" << arg << "." << mi.var << "value;" + << b << ".capacity = 8;" + << b << ".size = 0;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_big_float (member_info& mi) + { + os << b << ".type = oracle::bind::number;" + << b << ".buffer = " << arg << "." << mi.var << "value;" + << b << ".capacity = static_cast<ub4> (sizeof (" << arg << "." << + mi.var << "value));" + << b << ".size = &" << arg << "." << mi.var << "size;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_date (member_info& mi) + { + os << b << ".type = oracle::bind::date;" + << b << ".buffer = " << arg << "." << mi.var << "value;" + << b << ".capacity = static_cast<ub4> (sizeof (" << arg << "." << + mi.var << "value));" + << b << ".size = 0;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_timestamp (member_info& mi) + { + os << b << ".type = oracle::bind::timestamp;" + << b << ".buffer = &" << arg << "." << mi.var << "value;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_interval_ym (member_info& mi) + { + os << b << ".type = oracle::bind::interval_ym;" + << b << ".buffer = &" << arg << "." << mi.var << "value;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_interval_ds (member_info& mi) + { + os << b << ".type = oracle::bind::interval_ds;" + << b << ".buffer = &" << arg << "." << mi.var << "value;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_string (member_info& mi) + { + os << b << ".type = " << + string_buffer_types[mi.st->type - sql_type::CHAR] << ";" + << b << ".buffer = " << arg << "." << mi.var << "value;" + << b << ".capacity = static_cast<ub4> (sizeof (" << arg << + "." << mi.var << "value));" + << b << ".size = &" << arg << "." << mi.var << "size;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;"; + } + + virtual void + traverse_lob (member_info& mi) + { + os << b << ".type = " << + lob_buffer_types[mi.st->type - sql_type::BLOB] << ";" + << b << ".buffer = &" << arg << "." << mi.var << "lob;" + << b << ".indicator = &" << arg << "." << mi.var << "indicator;" + << b << ".callback = &" << arg << "." << mi.var << "callback;" + << endl; + } + }; + entry<bind_member> bind_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 + check_accessor (member_info& mi, member_access& ma) + { + // We cannot use accessors that return by-value for LOB + // members. + // + if ((mi.st->type == sql_type::BLOB || + mi.st->type == sql_type::CLOB || + mi.st->type == sql_type::NCLOB) && + ma.by_value) + { + error (ma.loc) << "accessor returning a value cannot be used " + << "for a data member of Oracle LOB type" << endl; + info (ma.loc) << "accessor returning a const reference is required" + << endl; + info (mi.m.location ()) << "data member is defined here" << endl; + throw operation_failed (); + } + } + + virtual void + set_null (member_info& mi) + { + os << "i." << mi.var << "indicator = -1;"; + } + + virtual void + traverse_int32 (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_int64 (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_big_int (member_info& mi) + { + os << "std::size_t size (0);" + << traits << "::set_image (" << endl + << "i." << mi.var << "value," << endl + << "size," << endl + << "is_null," << endl + << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" + << "i." << mi.var << "size = static_cast<ub2> (size);"; + } + + virtual void + traverse_float (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_double (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_big_float (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 << "indicator = is_null ? -1 : 0;" + << "i." << mi.var << "size = static_cast<ub2> (size);"; + } + + virtual void + traverse_date (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_timestamp (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_interval_ym (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_interval_ds (member_info& mi) + { + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + + virtual void + traverse_string (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 << "indicator = is_null ? -1 : 0;" + << "i." << mi.var << "size = static_cast<ub2> (size);"; + } + + virtual void + traverse_lob (member_info& mi) + { + os << "i." << mi.var << "lob.position = 0;" + << traits << "::set_image (" << endl + << "i." << mi.var << "callback.callback.param," << endl + << "i." << mi.var << "callback.context.param," << endl + << "is_null," << endl + << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; + } + }; + 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 << "indicator == -1"; + } + + virtual void + check_modifier (member_info& mi, member_access& ma) + { + // We cannot use by-value modifier for LOB members. + // + if ((mi.st->type == sql_type::BLOB || + mi.st->type == sql_type::CLOB || + mi.st->type == sql_type::NCLOB) && + ma.placeholder ()) + { + error (ma.loc) << "modifier accepting a value cannot be used " + << "for a data member of Oracle LOB type" << endl; + info (ma.loc) << "modifier returning a non-const reference is " + << "required" << endl; + info (mi.m.location ()) << "data member is defined here" << endl; + throw operation_failed (); + } + } + + virtual void + traverse_int32 (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_int64 (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_big_int (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "size," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_float (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_double (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_big_float (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "size," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_date (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_timestamp (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_interval_ym (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + + virtual void + traverse_interval_ds (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "i." << mi.var << "indicator == -1);" + << 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 << "indicator == -1);" + << endl; + } + + virtual void + traverse_lob (member_info& mi) + { + os << traits << "::set_value (" << endl + << member << "," << endl + << "i." << mi.var << "callback.callback.result," << endl + << "i." << mi.var << "callback.context.result," << endl + << "i." << mi.var << "indicator == -1);" + << endl; + } + }; + entry<init_value_member> init_value_member_; + + struct container_traits: relational::container_traits, context + { + container_traits (base const& x): base (x) {} + + virtual void + cache_result (string const&) + { + // Caching is not necessary since Oracle can execute several + // interleaving statements. + // + } + + virtual void + init_value_extra () + { + os << "sts.select_statement ().stream_result ();" + << endl; + } + }; + entry<container_traits> container_traits_; + + struct section_traits: relational::section_traits, context + { + section_traits (base const& x): base (x) {} + + virtual void + init_value_extra () + { + os << "st.stream_result ();"; + } + }; + entry<section_traits> section_traits_; + + struct class_: relational::class_, context + { + class_ (base const& x): base (x) {} + + virtual void + init_image_pre (type& c) + { + if (options.generate_query () && + !(composite (c) || (abstract (c) && !polymorphic (c)))) + { + type* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + if (poly_derived) + os << "{" + << "root_traits::image_type& ri (root_image (i));" + << endl; + + string i (poly_derived ? "ri" : "i"); + + os << "if (" << i << ".change_callback_.callback != 0)" << endl + << "(" << i << ".change_callback_.callback) (" << + i << ".change_callback_.context);"; + + if (poly_derived) + os << "}"; + else + os << endl; + } + } + + virtual void + init_value_extra () + { + os << "st.stream_result ();"; + } + + virtual string + persist_statement_extra (type& c, + relational::query_parameters& qp, + 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)) + { + semantics::data_member& idb (*id->back ()); + + const string& name (column_qname (*id)); + const string& type (column_type (idb)); + + r = "RETURNING " + convert_from (name, type, idb) + + " INTO " + qp.next (idb, name, type); + } + } + + return r; + } + + virtual string + select_trailer (type& c) + { + view_query const& vq (c.get<view_query> ("query")); + + if (vq.for_update && vq.distinct) + { + error (vq.loc) + << "Oracle does not support FOR UPDATE with DISTINCT" << endl; + throw operation_failed (); + } + + return base::select_trailer (c); + } + }; + entry<class_> class_entry_; + } + } +} |