From 55b36b8297ef9aac9e4ccc7b98f8649534ee0ac1 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 14 Aug 2014 09:37:06 +0200 Subject: Implement bulk database operation support for Oracle and SQL Server --- odb/relational/source.cxx | 409 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 405 insertions(+), 4 deletions(-) (limited to 'odb/relational/source.cxx') diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index db288f6..6c8204c 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -125,6 +125,7 @@ traverse_object (type& c) << db << "::connection&" << (containers || sections ? " c" : "") << "," << endl << "image_type&" << (sections ? " im" : "") << "," << endl + << "id_image_type&" << (sections ? " idim" : "") << "," << endl << db << "::binding&" << (containers || sections ? " id" : "") << "," << endl << db << "::binding&" << (sections ? " idv" : ""); @@ -174,10 +175,27 @@ traverse_object (type& c) // Functions (abstract and concrete). // - // id (image_type) + // id(), version() // if (!poly_derived && id != 0 && !base_id) { + // id (id_image_type) + // + if (auto_id) + { + os << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const id_image_type& i)" + << "{" + << db << "::database* db (0);" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl + << "id_type id;"; + init_id_value_member_id_image_->traverse (*id); + os << "return id;" + << "}"; + } + // id (image) // if (options.generate_query ()) @@ -210,7 +228,7 @@ traverse_object (type& c) } } - // discriminator() + // discriminator(image) // if (poly && !poly_derived) { @@ -1235,6 +1253,29 @@ traverse_object (type& c) << "imb.version++;" << "}"; + // Bind id image since that's where the returned id and/or version will + // be extracted. + // + if (!poly_derived) + { + bool bv (opt != 0 && optimistic_insert_bind_version (*opt)); + if (bv || auto_id) + { + os << "{" + << "id_image_type& i (sts.id_image ());" + << "binding& b (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || b.version == 0)" + << "{" + << "bind (b.bind, i);" + << "sts.id_image_version (i.version);" + << "b.version++;"; + if (opt != 0) + os << "sts.optimistic_id_image_binding ().version++;"; + os << "}" + << "}"; + } + } + os << "insert_statement& st (sts.persist_statement ());" << "if (!st.execute ())" << endl << "throw object_already_persistent ();" @@ -1248,7 +1289,7 @@ traverse_object (type& c) os << "// From " << location_string (ma.loc, true) << endl; if (ma.placeholder ()) - os << ma.translate ("obj", "static_cast< id_type > (st.id ())") << ";" + os << ma.translate ("obj", "id (sts.id_image ())") << ";" << endl; else { @@ -1265,7 +1306,7 @@ traverse_object (type& c) if (cast) os << ")"; - os << " = static_cast< id_type > (st.id ());" + os << " = id (sts.id_image ());" << endl; } } @@ -1408,6 +1449,173 @@ traverse_object (type& c) os << "}"; + // persist() bulk + // + if (c.count ("bulk-persist")) + { + os << "void " << traits << "::" << endl + << "persist (database& db," << endl + << (auto_id ? "" : "const ") << "object_type** objs," << endl + << "std::size_t n," << endl + << "multiple_exceptions& mex)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl + << "using namespace " << db << ";" + << endl; + + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());"; + + if (versioned || + uss.count (user_sections::count_new | + user_sections::count_all | + user_sections::count_versioned_only) != 0) + os << "const schema_version_migration& svm (" << + "sts.version_migration (" << schema_name << "));"; + + os << endl; + + os << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "const object_type& obj (*objs[i]);" + << "callback (db, obj, callback_event::pre_persist);" + //@@ assumption: generate_grow is false + //@@ assumption: insert_send_auto_id is false + << "init (sts.image (i), obj, statement_insert" << + (versioned ? ", svm" : "") << ");" + << "}"; + + //@@ assumption: generate_grow is false + os << "binding& imb (sts.insert_image_binding ());" + << "if (imb.version == 0)" + << "{" + << "bind (imb.bind, sts.image (), statement_insert" << + (versioned ? ", svm" : "") << ");" + << "imb.version++;" + << "}"; + + // Bind id image since that's where the returned id and/or version will + // be extracted. + // + bool bv (opt != 0 && optimistic_insert_bind_version (*opt)); + if (bv || auto_id) + { + os << "binding& idb (sts.id_image_binding ());" + //@@ assumption: generate_grow is false + << "if (idb.version == 0)" + << "{" + << "bind (idb.bind, sts.id_image ());" + << "idb.version++;"; + if (opt != 0) + os << "sts.optimistic_id_image_binding ().version++;"; + os << "}"; + } + + os << "insert_statement& st (sts.persist_statement ());" + << "n = st.execute (n, mex);" // Actual number of rows attempted. + << endl; + + os << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "bool r (st.result (i));" // Sets current in mex. + << endl + << "if (mex[i] != 0)" << endl // Pending exception for this position. + << "continue;" + << endl + << "if (!r)" + << "{" + << "mex.insert (i, object_already_persistent ());" + << "continue;" + << "}" + << "if (mex.fatal ())" << endl // Don't do any extra work. + << "continue;" + << endl + << (auto_id ? "" : "const ") << "object_type& obj (*objs[i]);" + << endl; + + // Extract auto id. + // + if (auto_id) + { + member_access& ma (id->get ("set")); + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + if (ma.placeholder ()) + os << ma.translate ("obj", "id (sts.id_image (i))") << ";" + << endl; + else + { + // If this member is const and we have a synthesized direct access, + // then cast away constness. Otherwise, we assume that the user- + // provided expression handles this. + // + bool cast (ma.direct () && const_type (id->type ())); + if (cast) + os << "const_cast< id_type& > (" << endl; + + os << ma.translate ("obj"); + + if (cast) + os << ")"; + + os << " = id (sts.id_image (i));" + << endl; + } + } + + // Reset sections: loaded, unchanged. + // + for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i) + { + // Skip special sections. + // + if (i->special == user_section::special_version) + continue; + + data_member& m (*i->member); + + // If the section is soft- added or deleted, check the version. + // + unsigned long long av (added (m)); + unsigned long long dv (deleted (m)); + if (av != 0 || dv != 0) + { + os << "if ("; + + if (av != 0) + os << "svm >= schema_version_migration (" << av << "ULL, true)"; + + if (av != 0 && dv != 0) + os << " &&" << endl; + + if (dv != 0) + os << "svm <= schema_version_migration (" << dv << "ULL, true)"; + + os << ")" << endl; + } + + // Section access is always by reference. + // + member_access& ma (m.get ("get")); + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + os << ma.translate ("obj") << ".reset (true, false);" + << endl; + } + + os << "callback (db," << endl + << (auto_id ? "static_cast (obj)," : "obj,") << endl + << "callback_event::post_persist);" + << "}" // for + << "}"; // persist () + } + // update () // if (id != 0 && (!readonly || poly)) @@ -2071,6 +2279,110 @@ traverse_object (type& c) os << "}"; } + // update () bulk + // + if (id != 0 && !readonly && c.count ("bulk-update")) + { + os << "void " << traits << "::" << endl + << "update (database& db," << endl + << "const object_type** objs," << endl + << "std::size_t n," << endl + << "multiple_exceptions& mex)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl + << "using namespace " << db << ";" + << "using " << db << "::update_statement;" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());"; + + if (versioned) + os << "const schema_version_migration& svm (" << + "sts.version_migration (" << schema_name << "));"; + + os << endl + << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "const object_type& obj (*objs[i]);" + << "callback (db, obj, callback_event::pre_update);"; + + if (!id_ma->synthesized) + os << "// From " << location_string (id_ma->loc, true) << endl; + + os << "const id_type& id (" << endl + << id_ma->translate ("obj") << ");"; + + os << "init (sts.id_image (i), id);" + //@@ assumption: generate_grow false + << "init (sts.image (i), obj, statement_update);" + << "}"; + + // Update bindings. + // + os << "binding& idb (sts.id_image_binding ());" + << "binding& imb (sts.update_image_binding ());" + << endl; + + //@@ assumption: generate_grow false + // + os << "bool u (false);" // Avoid incrementing version twice. + << "if (imb.version == 0)" + << "{" + << "bind (imb.bind, sts.image (), statement_update" << + (versioned ? ", svm" : "") << ");" + << "imb.version++;" + << "u = true;" + << "}"; + + //@@ assumption: generate_grow false + // + os << "if (idb.version == 0)" + << "{" + << "bind (idb.bind, sts.id_image ());" + << "idb.version++;"; + if (opt != 0) + os << "sts.optimistic_id_image_binding ().version++;"; + os << endl + << "if (!u)" << endl + << "imb.version++;" + << "}"; + + // We don't need the versioned check here since the statement cannot + // be empty; otherwise it would have been an empty object which is + // illegal. + // + os << "update_statement& st (sts.update_statement ());" + << "n = st.execute (n, mex);"; // Actual number of rows attempted. + + os << endl + << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "unsigned long long r (st.result (i));" // Also sets current in mex. + << endl + << "if (mex[i] != 0)" << endl // Pending exception from result(). + << "continue;" + << endl + << "if (r != 1)" + << "{" + << "mex.insert (i," << endl + //@@ assumption: result_unknown + << "(r == update_statement::result_unknown)," << endl + << "object_not_persistent ());" + << "continue;" + << "}" + << "if (mex.fatal ())" << endl // Don't do any extra work. + << "continue;" + << endl + << "const object_type& obj (*objs[i]);" + << "callback (db, obj, callback_event::post_update);" + << "pointer_cache_traits::update (db, obj);" + << "}" // for + << "}"; // update() + } + // erase (id) // size_t erase_containers (has_a (c, test_straight_container)); @@ -2204,6 +2516,62 @@ traverse_object (type& c) os << "}"; } + // erase (id) bulk + // + if (id != 0 && c.count ("bulk-erase")) + { + os << "std::size_t " << traits << "::" << endl + << "erase (database& db," << endl + << "const id_type** ids," << endl + << "std::size_t n," << endl + << "multiple_exceptions& mex)" + << "{" + << "using namespace " << db << ";" + << endl + << "ODB_POTENTIALLY_UNUSED (db);" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl + << "for (std::size_t i (0); i != n; ++i)" << endl + << "init (sts.id_image (i), *ids[i]);" + << endl + << "binding& idb (sts.id_image_binding ());" + //@@ assumption: generate_grow false + << "if (idb.version == 0)" + << "{" + << "bind (idb.bind, sts.id_image ());" + << "idb.version++;" + << (opt != 0 ? "sts.optimistic_id_image_binding ().version++;" : "") + << "}" + << "delete_statement& st (sts.erase_statement ());" + << "n = st.execute (n, mex);" // Set to actual number of rows attempted. + << endl + << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "unsigned long long r (st.result (i));" // Sets current in mex. + << endl + << "if (mex[i] != 0)" << endl // Pending exception from result(). + << "continue;" + << endl + << "if (r != 1)" + << "{" + << "mex.insert (i," << endl + //@@ assumption: result_unknown + << "(r == delete_statement::result_unknown)," << endl + << "object_not_persistent ());" + << "continue;" + << "}" + << "if (mex.fatal ())" << endl // Don't do any extra work. + << "continue;" + << "pointer_cache_traits::erase (db, *ids[i]);" + << "}" // for + << "return n;" + << "}"; // erase() + } + // erase (object) // bool erase_smart_containers (has_a (c, test_smart_container)); @@ -2516,6 +2884,39 @@ traverse_object (type& c) os << "}"; } + // erase (object) bulk + // + if (id != 0 && c.count ("bulk-erase")) + { + os << "void " << traits << "::" << endl + << "erase (database& db," << endl + << "const object_type** objs," << endl + << "std::size_t n," << endl + << "multiple_exceptions& mex)" + << "{" + << "id_type a[batch];" + << "const id_type* p[batch];" + << endl + << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "const object_type& obj (*objs[i]);" + << "callback (db, obj, callback_event::pre_erase);" + << "a[i] = id (obj);" + << "p[i] = a + i;" + << "}" + << "n = erase (db, p, n, mex);" + << endl + << "if (mex.fatal ())" << endl // Don't do any extra work. + << "return;" + << endl + << "for (std::size_t i (0); i != n; ++i)" + << "{" + << "if (mex[i] == 0)" << endl // No pending exception. + << "callback (db, *objs[i], callback_event::post_erase);" + << "}" // for + << "}"; // erase() + } + // find (id) // if (id != 0 && c.default_ctor ()) -- cgit v1.1