summaryrefslogtreecommitdiff
path: root/odb/odb/relational/oracle
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/relational/oracle')
-rw-r--r--odb/odb/relational/oracle/common.cxx522
-rw-r--r--odb/odb/relational/oracle/common.hxx203
-rw-r--r--odb/odb/relational/oracle/context.cxx795
-rw-r--r--odb/odb/relational/oracle/context.hxx188
-rw-r--r--odb/odb/relational/oracle/header.cxx230
-rw-r--r--odb/odb/relational/oracle/inline.cxx42
-rw-r--r--odb/odb/relational/oracle/model.cxx64
-rw-r--r--odb/odb/relational/oracle/schema.cxx696
-rw-r--r--odb/odb/relational/oracle/source.cxx646
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_;
+ }
+ }
+}