summaryrefslogtreecommitdiff
path: root/odb/relational/oracle/schema.cxx
diff options
context:
space:
mode:
authorConstantin Michael <constantin@codesynthesis.com>2011-10-06 09:33:42 +0200
committerConstantin Michael <constantin@codesynthesis.com>2011-10-21 11:47:12 +0200
commit9ecea067585434633b907cc621f8a069e30d34cf (patch)
tree70a256328173fa5f413ab6b0e70fbc3f07b172ee /odb/relational/oracle/schema.cxx
parentcd6c6ffa22c3179756218aa718c309bebe3e4a42 (diff)
Add Oracle schema implementation
Diffstat (limited to 'odb/relational/oracle/schema.cxx')
-rw-r--r--odb/relational/oracle/schema.cxx279
1 files changed, 279 insertions, 0 deletions
diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx
new file mode 100644
index 0000000..db0b882
--- /dev/null
+++ b/odb/relational/oracle/schema.cxx
@@ -0,0 +1,279 @@
+// file : odb/relational/oracle/schema.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+
+ //
+ // Drop.
+ //
+
+ struct drop_common: virtual relational::drop_common
+ {
+ virtual void
+ drop_table (string const& table)
+ {
+ // 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.
+ //
+ os << "BEGIN" << endl
+ << " BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'DROP TABLE " << quote_id (table) <<
+ "';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -942 THEN" << endl
+ << " RAISE;" << endl
+ << " END IF;" << endl
+ << " END;"
+ << " BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'DROP SEQUENCE " <<
+ quote_id (table + "_seq") << "';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -2289 THEN" << endl
+ << " RAISE;" << endl
+ << " END IF;" << endl
+ << " END;" << endl
+ << " EXECUTE IMMEDIATE 'DROP TRIGGER " <<
+ quote_id (table + "_auto_id_trig") << "';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -4080 THEN" << endl
+ << " RAISE;" << endl
+ << " END IF;" << endl
+ << " END;" << endl
+ << "END;" << endl;
+ }
+ };
+
+ struct member_drop: relational::member_drop, drop_common
+ {
+ member_drop (base const& x): base (x) {}
+ };
+ entry<member_drop> member_drop_;
+
+ struct class_drop: relational::class_drop, drop_common
+ {
+ class_drop (base const& x): base (x) {}
+ };
+ entry<class_drop> class_drop_;
+
+ //
+ // Create.
+ //
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual void
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to Oracle NUMBER.
+ //
+ sql_type t (column_sql_type (m));
+
+ 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)));
+
+ if (e.enum_ ().unsigned_ ())
+ os << " DEFAULT " << e.value ();
+ else
+ os << " DEFAULT " << static_cast<long long> (e.value ());
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ struct object_columns_references:
+ object_columns_base, relational::common, context
+ {
+ object_columns_references (emitter& e,
+ ostream& os,
+ string const& table,
+ string const& prefix = string ())
+ : relational::common (e, os),
+ table_ (table),
+ prefix_ (prefix)
+ {
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ if (inverse (m))
+ return false;
+
+ if (semantics::class_* c = object_pointer (member_type (m, prefix_)))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (table_) << endl
+ << " ADD FOREIGN KEY (" << quote_id (name) << ")" << endl
+ << " REFERENCES " << table_qname (*c) << endl
+ << " DEFERRABLE INITIALLY DEFERRED" << endl;
+
+ post_statement ();
+ }
+ else if (prefix_ == "id")
+ {
+ semantics::class_& c (*context::top_object);
+
+ pre_statement ();
+
+ // We don't need INITIALLY DEFERRED here since the object row
+ // must exist before any container row.
+ //
+ os << "ALTER TABLE " << quote_id (table_) << endl
+ << " ADD FOREIGN KEY (" << quote_id (name) << ")" << endl
+ << " REFERENCES " << table_qname (c) << endl
+ << " ON DELETE CASCADE" << endl;
+
+ post_statement ();
+ }
+
+ return true;
+ }
+
+ private:
+ string table_;
+ string prefix_;
+ };
+
+ struct member_create: object_members_base, context
+ {
+ member_create (emitter& e, ostream& os, relational::tables& tables)
+ : object_members_base (false, true, false),
+ e_ (e),
+ os_ (os),
+ tables_ (tables)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& t)
+ {
+ using semantics::type;
+ using semantics::data_member;
+
+ // Ignore inverse containers of object pointers.
+ //
+ if (inverse (m, "value"))
+ return;
+
+ string const& name (table_name (m, table_prefix_));
+
+ if (tables_.count (name))
+ return;
+
+ type& vt (container_vt (t));
+
+ // object_id
+ //
+ {
+ object_columns_references ocr (e_, os_, name, "id");
+ string id_name (column_name (m, "id", "object_id"));
+ ocr.traverse_column (m, id_name, true);
+ }
+
+ // value
+ //
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ {
+ object_columns_references ocr (e_, os_, name);
+ ocr.traverse (m, *cvt, "value", "value");
+ }
+ else
+ {
+ object_columns_references ocr (e_, os_, name, "value");
+ string const& value_name (column_name (m, "value", "value"));
+ ocr.traverse_column (m, value_name, true);
+ }
+
+ tables_.insert (name);
+ }
+
+ private:
+ emitter& e_;
+ ostream& os_;
+ relational::tables& tables_;
+ };
+
+ struct class_create: relational::class_create, context
+ {
+ class_create (base const& x): base (x) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ if (pass_ != 2)
+ {
+ base::traverse (c);
+ return;
+ }
+
+ if (c.file () != unit.file ())
+ return;
+
+ if (!object (c) || abstract (c))
+ return;
+
+ string const& name (table_name (c));
+
+ if (tables_[pass_].count (name))
+ return;
+
+ if (id_member (c)->count ("auto"))
+ {
+ string seq_name (quote_id (name + "_seq"));
+
+ os_ << "CREATE SEQUENCE " << seq_name << endl
+ << " START WITH 1 INCREMENT BY 1;" << endl;
+
+ os_ << "CREATE TRIGGER " <<
+ quote_id (name + "_auto_id_trig") << endl
+ << " BEFORE INSERT ON " << quote_id (name) << endl
+ << " FOR EACH ROW" << endl
+ << "BEGIN" << endl
+ << " SELECT " << seq_name <<
+ ".nextval INTO :new.id FROM DUAL;" << endl
+ << "END;";
+ }
+
+
+ object_columns_references ocr (e_, os_, name);
+ ocr.traverse (c);
+
+ tables_[pass_].insert (name);
+
+ member_create mc (e_, os_, tables_[pass_]);
+ mc.traverse (c);
+ }
+ };
+ entry<class_create> class_create_;
+ }
+ }
+}