summaryrefslogtreecommitdiff
path: root/libodb-oracle/odb/oracle/database.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libodb-oracle/odb/oracle/database.cxx')
-rw-r--r--libodb-oracle/odb/oracle/database.cxx352
1 files changed, 352 insertions, 0 deletions
diff --git a/libodb-oracle/odb/oracle/database.cxx b/libodb-oracle/odb/oracle/database.cxx
new file mode 100644
index 0000000..3b720be
--- /dev/null
+++ b/libodb-oracle/odb/oracle/database.cxx
@@ -0,0 +1,352 @@
+// file : odb/oracle/database.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <sstream>
+
+#include <odb/oracle/traits.hxx>
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/transaction.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/error.hxx>
+
+#include <odb/oracle/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ ub2 charset,
+ ub2 ncharset,
+ OCIEnv* environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_oracle),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ port_ (0),
+ charset_ (charset),
+ ncharset_ (ncharset),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ if (environment_ == 0)
+ {
+ sword s (OCIEnvNlsCreate (&environment_,
+ OCI_THREADED,
+ 0, 0, 0, 0, 0, 0,
+ charset,
+ ncharset));
+
+ if (s == OCI_ERROR)
+ translate_error (environment_);
+
+ auto_environment_.reset (environment_);
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& service,
+ const string& host,
+ unsigned int port,
+ ub2 charset,
+ ub2 ncharset,
+ OCIEnv* environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_oracle),
+ user_ (user),
+ password_ (password),
+ service_ (service),
+ host_ (host),
+ port_ (port),
+ charset_ (charset),
+ ncharset_ (ncharset),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ if (environment_ == 0)
+ {
+ sword s (OCIEnvNlsCreate (&environment_,
+ OCI_THREADED,
+ 0, 0, 0, 0, 0, 0,
+ charset,
+ ncharset));
+
+ if (s == OCI_ERROR)
+ translate_error (environment_);
+
+ auto_environment_.reset (environment_);
+ }
+
+ ostringstream ss;
+
+ if (!host.empty ())
+ {
+ ss << "//" << host_;
+
+ if (port != 0)
+ ss << ":" << port;
+ }
+
+ if (!service_.empty ())
+ {
+ if (!host.empty ())
+ ss << "/" << service_;
+ else
+ ss << service_;
+ }
+
+ db_ = ss.str ();
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ ub2 charset,
+ ub2 ncharset,
+ OCIEnv* environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_oracle),
+ port_ (0),
+ charset_ (charset),
+ ncharset_ (ncharset),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ if (environment_ == 0)
+ {
+ sword s (OCIEnvNlsCreate (&environment_,
+ OCI_THREADED,
+ 0, 0, 0, 0, 0, 0,
+ charset,
+ ncharset));
+
+ if (s == OCI_ERROR)
+ translate_error (environment_);
+
+ auto_environment_.reset (environment_);
+ }
+
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ if (ops.user_specified ())
+ user_ = ops.user ();
+
+ if (ops.password_specified ())
+ password_ = ops.password ();
+
+ if (ops.database_specified ())
+ {
+ if (ops.host_specified () ||
+ ops.port_specified () ||
+ ops.service_specified ())
+
+ throw cli_exception ("--host, --port, or --service options "
+ "cannot be specified together with "
+ "--database option");
+ db_ = ops.database ();
+ }
+ else
+ {
+ bool host_present (false);
+ ostringstream oss;
+
+ if (ops.host_specified () && !ops.host ().empty ())
+ {
+ host_present = true;
+
+ host_ = ops.host ();
+ oss << "//" << host_;
+
+ if (ops.port_specified ())
+ {
+ port_ = ops.port ();
+
+ if (port_ != 0)
+ oss << ":" << port_;
+ }
+ }
+
+ if (ops.service_specified () && !ops.service ().empty ())
+ {
+ service_ = ops.service ();
+
+ if (host_present)
+ oss << "/" << service_;
+ else
+ oss << service_;
+ }
+
+ db_ = oss.str ();
+ }
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ database::
+ ~database ()
+ {
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "\"schema_version\"";
+
+ text += " WHERE \"name\" = :1";
+
+ // Bind parameters and results. If the schema name is empty, replace
+ // it with a single space to workaround the VARCHAR2 empty/NULL issue.
+ //
+ string n (name.empty () ? string (" ") : name);
+ ub2 psize[1] = {static_cast<ub2> (n.size ())};
+ sb2 pind[1] = {0};
+ bind pbind[1] = {{bind::string,
+ const_cast<char*> (n.c_str ()),
+ &psize[0],
+ psize[0],
+ &pind[0],
+ 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ char version[12];
+ unsigned int migration;
+ ub2 rsize[1];
+ sb2 rind[2];
+ bind rbind[2] = {
+ {bind::number,
+ version,
+ &rsize[0],
+ static_cast<ub4> (sizeof (version)),
+ &rind[0],
+ 0},
+
+ {bind::uinteger, &migration, 0, 4, &rind[1], 0}
+ };
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, then OCI will start an implicit one
+ // but only if we try to modify anything. Since our statement is read-
+ // only, we can run without a transaction.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ oracle::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text,
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ value_traits<unsigned long long, id_big_int>::set_value (
+ svi.version, version, rsize[0], rind[0] == -1);
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table.
+ //
+ if (e.size () != 0 && e.begin ()->error () == 942)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}