From 0ac0cd1b4993f5d8aa00057fdc9eb58ca6450615 Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Wed, 7 Sep 2011 15:09:58 +0200 Subject: Add implementations for insert, update, delete, and select statements --- odb/oracle/makefile | 1 + odb/oracle/statement.cxx | 444 +++++++++++++++++++++++++++++++++++++++++++++++ odb/oracle/statement.hxx | 182 +++++++++++++++++++ 3 files changed, 627 insertions(+) create mode 100644 odb/oracle/statement.cxx create mode 100644 odb/oracle/statement.hxx diff --git a/odb/oracle/makefile b/odb/oracle/makefile index 29f6a15..7f16e5d 100644 --- a/odb/oracle/makefile +++ b/odb/oracle/makefile @@ -13,6 +13,7 @@ connection-factory.cxx \ database.cxx \ error.cxx \ exceptions.cxx \ +statement.cxx \ transaction.cxx \ transaction-impl.cxx diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx new file mode 100644 index 0000000..a88aa06 --- /dev/null +++ b/odb/oracle/statement.cxx @@ -0,0 +1,444 @@ +// file : odb/oracle/statement.cxx +// author : Constantin Michael +// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +#include + +#if OCI_MAJOR_VERSION >= 11 && OCI_MINOR_VERSION >= 2 +# define ODB_ORACLE_USE_64_BIT_ID +#endif + +#include // object_not_persistent + +#include +#include +#include + +using namespace std; + +namespace odb +{ + namespace oracle + { + // + // statement + // + + statement:: + statement (connection& conn, const string& s) + : conn_ (conn) + { + OCIStmt* handle (0); + + sword r (OCIStmtPrepare2 (conn_.handle (), + &handle, + conn_.error_handle (), + reinterpret_cast (s.c_str ()), + static_cast (s.size ()), + 0, + 0, + OCI_NTV_SYNTAX, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + stmt_.reset (handle, OCI_DEFAULT, conn_.error_handle ()); + } + + void statement:: + bind_param (bind* b, size_t c) + { + for (size_t i (0); i < c; ++i) + { + OCIBind* h (0); + + sword r (OCIBindByPos (stmt_, + &h, + conn_.error_handle (), + i, + b[i].buffer, + b[i].capacity, + b[i].type, + b[i].indicator, + b[i].size, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + } + } + + void statement:: + bind_result (bind* b, size_t c) + { + for (size_t i (0); i < c; ++i) + { + OCIDefine* h (0); + + sword r (OCIDefineByPos (stmt_, + &h, + conn_.error_handle (), + i, + b[i].buffer, + b[i].capacity, + b[i].type, + b[i].indicator, + b[i].size, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + } + } + + statement:: + ~statement () + { + } + + // + // select_statement + // + + select_statement:: + ~select_statement () + { + } + + select_statement:: + select_statement (connection& conn, + const string& s, + binding& cond, + binding& data) + : statement (conn, s), + end_ (false), + rows_ (0) + { + bind_param (cond.bind, cond.count); + bind_result (data.bind, data.count); + } + + void select_statement:: + execute () + { + if (!end_) + free_result (); + + // @@ Retrieve a single row into the already bound output buffers + // as an optimization? This will avoid multiple server round-trips + // in the case of a single object load. + // + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + conn_.error_handle (), + 0, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + } + + select_statement::result select_statement:: + fetch () + { + sword r (OCIStmtFetch2 (stmt_, + conn_.error_handle (), + 1, + OCI_FETCH_NEXT, + 0, + OCI_DEFAULT)); + + switch (r) + { + case OCI_SUCCESS: + { + ++rows_; + return success; + } + case OCI_NO_DATA: + { + end_ = true; + return no_data; + } + default: + { + translate_error (conn_.error_handle (), r); + return no_data; // Never reached. + } + } + } + + + void select_statement:: + free_result () + { + end_ = true; + rows_ = 0; + + sword r (OCIStmtFetch2 (stmt_, + conn_.error_handle (), + 0, + OCI_FETCH_NEXT, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + } + + // + // insert_statement + // + + static sb4 + returning_in_cb (void* context, + OCIBind*, // bind + ub4, // iter + ub4, // index + void** buffer, + ub4* len, + ub1* piece, + void** indicator) + { + typedef insert_statement::id_bind_type bind; + + bind& b (*reinterpret_cast (context)); + b.ind = -1; + + *buffer = 0; + *len = 0; + *piece = OCI_ONE_PIECE; + *reinterpret_cast (indicator) = &b.ind; + + return OCI_CONTINUE; + } + + static sb4 + returning_out_cb (void* context, + OCIBind*, // bind + ub4, // iter + ub4, // index + void** buffer, + ub4** len, + ub1*, // piece + void** ind, + ub2** rcode) + { + typedef insert_statement::id_bind_type bind; + + bind& b (*reinterpret_cast (context)); + +#ifdef ODB_ORACLE_USE_64_BIT_ID + *buffer = &b.id.long_long_; + **len = sizeof (b.id.long_long_); +#else + *buffer = &b.id.int_; + **len = sizeof (b.id.int_); +#endif + + *ind = reinterpret_cast (&b.ind); + *rcode = 0; + + return OCI_CONTINUE; + } + + insert_statement:: + ~insert_statement () + { + } + + insert_statement:: + insert_statement (connection& conn, + const string& s, + binding& data, + bool returning) + : statement (conn, s) + { + bind_param (data.bind, data.count); + + if (returning) + { + OCIBind* h (0); + + sword r (OCIBindByPos (stmt_, + &h, + conn_.error_handle (), + data.count, + 0, +#ifdef ODB_ORACLE_USE_64_BIT_ID + sizeof (unsigned long long), +#else + sizeof (unsigned int), +#endif + SQLT_UIN, + 0, + 0, + 0, + 0, + 0, + OCI_DATA_AT_EXEC)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + r = OCIBindDynamic (h, + conn_.error_handle (), + reinterpret_cast (&id_bind_), + &returning_in_cb, + reinterpret_cast (&id_bind_), + &returning_out_cb); + } + } + + bool insert_statement:: + execute () + { + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + conn_.error_handle (), + 1, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + ub4 row_count (0); + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &row_count, + 0, + OCI_ATTR_ROW_COUNT, + conn_.error_handle ()); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + // The value of the OCI_ATTR_ROW_COUNT attribute associated with an + // INSERT statment represents the number of rows inserted. + // + return row_count != 0; + } + + unsigned long long insert_statement:: + id () + { +#ifdef ODB_ORACLE_USE_64_BIT_ID + return id_bind_.id.long_long_; +#else + return id_bind_.id.int_; +#endif + } + + // + // update_statement + // + + update_statement:: + ~update_statement () + { + } + + update_statement:: + update_statement (connection& conn, + const string& s, + binding& cond, + binding& data) + : statement (conn, s) + { + bind_param (data.bind, cond.count + data.count); + } + + void update_statement:: + execute () + { + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + conn_.error_handle (), + 1, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + ub4 row_count (0); + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &row_count, + 0, + OCI_ATTR_ROW_COUNT, + conn_.error_handle ()); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + // The value of the OCI_ATTR_ROW_COUNT attribute associated with an + // UPDATE statment represents the number of matching rows found. Zero + // indicates no match. + // + if (row_count == 0) + throw object_not_persistent (); + } + + // + // delete_statement + // + + delete_statement:: + ~delete_statement () + { + } + + delete_statement:: + delete_statement (connection& conn, + const string& s, + binding& cond) + : statement (conn, s) + { + bind_param (cond.bind, cond.count); + } + + unsigned long long delete_statement:: + execute () + { + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + conn_.error_handle (), + 1, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + ub4 row_count (0); + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &row_count, + 0, + OCI_ATTR_ROW_COUNT, + conn_.error_handle ()); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + return static_cast (row_count); + } + } +} diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx new file mode 100644 index 0000000..d59f3a6 --- /dev/null +++ b/odb/oracle/statement.hxx @@ -0,0 +1,182 @@ +// file : odb/oracle/statement.hxx +// author : Constantin Michael +// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_STATEMENT_HXX +#define ODB_ORACLE_STATEMENT_HXX + +#include + +#include +#include // std::size_t + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT statement: public details::shared_base + { + public: + virtual + ~statement () = 0; + + protected: + statement (connection&, const std::string& statement); + + // Bind output parameters to this statement. This function must + // only be called once. Multiple calls to it will result in memory leaks + // due to lost OCIBind resources. + // + void + bind_param (bind*, std::size_t count); + + // Bind input parameters to this statement. This function must + // only be called once. Multiple calls to it will result in memory leaks + // due to lost OCIDefine resources. + // + void + bind_result (bind*, std::size_t count); + + protected: + connection& conn_; + auto_handle stmt_; + }; + + class LIBODB_ORACLE_EXPORT select_statement: public statement + { + public: + virtual + ~select_statement (); + + select_statement (connection& conn, + const std::string& statement, + binding& cond, + binding& data); + enum result + { + success, + no_data + }; + + void + execute (); + + // Number of rows already fetched. + // + std::size_t + fetched () const + { + return rows_; + } + + result + fetch (); + + void + free_result (); + + private: + select_statement (const select_statement&); + select_statement& operator= (const select_statement&); + + private: + bool end_; + std::size_t rows_; + }; + + class LIBODB_ORACLE_EXPORT insert_statement: public statement + { + public: + virtual + ~insert_statement (); + + insert_statement (connection& conn, + const std::string& statement, + binding& data, + bool returning); + + // Return true if successful and false if the row is a duplicate. + // All other errors are reported by throwing exceptions. + // + bool + execute (); + + unsigned long long + id (); + + private: + insert_statement (const insert_statement&); + insert_statement& operator= (const insert_statement&); + + public: + struct id_bind_type + { + union + { + unsigned int int_; + unsigned long long long_long_; + } id; + + sb2 ind; + }; + + private: + id_bind_type id_bind_; + }; + + class LIBODB_ORACLE_EXPORT update_statement: public statement + { + public: + virtual + ~update_statement (); + + // Asssumes that cond.bind is a suffix of data.bind. + // + update_statement (connection& conn, + const std::string& statement, + binding& cond, + binding& data); + void + execute (); + + private: + update_statement (const update_statement&); + update_statement& operator= (const update_statement&); + }; + + class LIBODB_ORACLE_EXPORT delete_statement: public statement + { + public: + virtual + ~delete_statement (); + + delete_statement (connection& conn, + const std::string& statement, + binding& cond); + + unsigned long long + execute (); + + private: + delete_statement (const delete_statement&); + delete_statement& operator= (const delete_statement&); + }; + } +} + +#include + +#endif // ODB_ORACLE_STATEMENT_HXX -- cgit v1.1