diff options
-rw-r--r-- | odb/pgsql/statement.cxx | 328 | ||||
-rw-r--r-- | odb/pgsql/statement.hxx | 111 |
2 files changed, 404 insertions, 35 deletions
diff --git a/odb/pgsql/statement.cxx b/odb/pgsql/statement.cxx index 35b2bb6..5a9bc28 100644 --- a/odb/pgsql/statement.cxx +++ b/odb/pgsql/statement.cxx @@ -11,7 +11,6 @@ #include <odb/pgsql/statement.hxx> #include <odb/pgsql/connection.hxx> #include <odb/pgsql/transaction.hxx> -#include <odb/pgsql/result-ptr.hxx> #include <odb/pgsql/error.hxx> using namespace std; @@ -57,6 +56,231 @@ namespace odb translate_error (conn_, r.get ()); } + void statement:: + bind_param (native_binding& n, const bind* p, size_t c) + { + assert (n.count == c); + + for (size_t i (0); i < c; ++i) + { + const bind& b (p[i]); + + if (b.is_null != 0 && *b.is_null) + { + n.values[i] = 0; + continue; + } + + n.values[i] = reinterpret_cast<char*> (b.buffer); + + // Use text format for numeric/decimal types and binary format + // for all others. + // + if (b.type == bind::numeric) + n.formats[i] = 0; + else + { + n.formats[i] = 1; + n.lengths[i] = static_cast<int> (*b.size); + } + } + } + + bool statement:: + bind_result (bind* p, + size_t count, + PGresult* result, + size_t row, + bool truncated) + { + bool r (true); + + assert (static_cast<size_t> (PQnfields (result)) == count); + + for (int i (0); i < static_cast<int> (count); ++i) + { + const bind& b (p[i]); + + if (truncated && (b.truncated == 0 || !*b.truncated)) + continue; + + if (b.truncated != 0) + *b.truncated = false; + + // Check for NULL unless we are reloading a truncated result. + // + if (!truncated) + { + *b.is_null = PQgetisnull (result, static_cast<int> (row), i) == 1; + + if (*b.is_null) + continue; + } + + // @@ Revise this. + // + size_t buffer_len; + + switch (b.type) + { + case bind::smallint: + { + buffer_len = 2; + break; + } + case bind::integer: + { + buffer_len = 4; + break; + } + case bind::bigint: + { + buffer_len = 8; + break; + } + case bind::real: + { + buffer_len = 4; + break; + } + case bind::dbl: + { + buffer_len = 8; + break; + } + case bind::numeric: + case bind::text: + case bind::bytea: + default: + { + buffer_len = static_cast<size_t> ( + PQgetlength (result, static_cast<int> (row), i)); + + *b.size = buffer_len; + + if (b.capacity < *b.size) + { + if (b.truncated) + *b.truncated = true; + + r = false; + continue; + } + + break; + } + }; + + memcpy (b.buffer, + PQgetvalue (result, static_cast<int> (row), i), + buffer_len); + } + + return r; + } + + // + // select_statement + // + + select_statement:: + ~select_statement () + { + } + + select_statement:: + select_statement (connection& conn, + const std::string& name, + const std::string& stmt, + const Oid* types, + std::size_t types_count, + binding& cond, + native_binding& native_cond, + binding& data) + : statement (conn, name, stmt, types, types_count), + cond_ (cond), + native_cond_ (native_cond), + data_ (data) + { + } + + void select_statement:: + execute () + { + result_.reset (); + + if (cond_.version != native_cond_.version) + { + bind_param (native_cond_, cond_.bind, cond_.count); + + native_cond_.version = cond_.version; + } + else + { + for (size_t i (0); i < cond_.count; ++i) + native_cond_.lengths[i] = static_cast<int> (*cond_.bind[i].size); + } + + result_.reset (PQexecPrepared (conn_.handle (), + name_.c_str (), + native_cond_.count, + native_cond_.values, + native_cond_.lengths, + native_cond_.formats, + 1)); + + PGresult* h (result_.get ()); + + if (!is_good_result (h)) + translate_error (conn_, h); + + // Clear the result if it is empty so that fetch () and refetch () + // behave correctly. + // + else if (PQntuples (h) <= 0) + result_.reset (); + + current_row_ = 0; + } + + select_statement::result select_statement:: + fetch () + { + PGresult* h (result_.get ()); + + if (h == 0) + return no_data; + + if (bind_result (data_.bind, data_.count, h, current_row_)) + { + if (PQntuples (h) <= static_cast<int> (++current_row_)) + result_.reset (); + + return success; + } + + return truncated; + } + + void select_statement:: + refetch () + { + assert (result_.get () != 0); + + if (!bind_result (data_.bind, + data_.count, + result_.get (), + current_row_, + true)) + assert (false); + } + + void select_statement:: + free_result () + { + result_.reset (); + } + // // insert_statement // @@ -72,36 +296,50 @@ namespace odb const string& stmt, const Oid* types, size_t types_count, - native_binding& data) + binding& data, + native_binding& native_data) : statement (conn, name, stmt, types, types_count), - data_ (data) + data_ (data), + native_data_ (native_data) { } bool insert_statement:: execute () { - result_ptr r (PQexecPrepared (conn_.handle (), - name_.c_str (), - data_.count, - data_.values, - data_.lengths, - data_.formats, - 1)); - PGresult* h (r.get ()); + if (data_.version != native_data_.version) + { + bind_param (native_data_, data_.bind, data_.count); + + native_data_.version = data_.version; + } + else + { + for (size_t i (0); i < data_.count; ++i) + native_data_.lengths[i] = static_cast<int> (*data_.bind[i].size); + } + + result_ptr r1 (PQexecPrepared (conn_.handle (), + name_.c_str (), + native_data_.count, + native_data_.values, + native_data_.lengths, + native_data_.formats, + 1)); + PGresult* h1 (r1.get ()); ExecStatusType stat; - if (!is_good_result (h, &stat)) + if (!is_good_result (h1, &stat)) { if (PGRES_FATAL_ERROR == stat) { - string s (PQresultErrorField (h, PG_DIAG_SQLSTATE)); + string s (PQresultErrorField (h1, PG_DIAG_SQLSTATE)); if (s == "23505") return false; } - translate_error (conn_, h); + translate_error (conn_, h1); } oid_ = PQoidValue (h); @@ -133,23 +371,45 @@ namespace odb const string& stmt, const Oid* types, size_t types_count, - native_binding& cond, - native_binding& data) + binding& cond, + native_binding& native_cond, + binding& data, + native_binding& native_data) : statement (conn, name, stmt, types, types_count), cond_ (cond), - data_ (data) + native_cond_ (native_cond), + data_ (data), + native_data_ (native_data) { } void update_statement:: execute () { + if (cond_.version != native_cond_.version || + data_.version != native_data_.version) + { + // We assume that cond_.bind is a suffix of data_.bind. + // + bind_param (native_data_, data_.bind, data_.count); + + native_cond_.version = cond_.version; + native_data_.version = data_.version; + } + else + { + // We assume that cond_.bind is a suffix of data_.bind. + // + for (size_t i (0); i < data_.count; ++i) + native_data_.lengths[i] = static_cast<int> (*data_.bind[i].size); + } + result_ptr r (PQexecPrepared (conn_.handle (), name_.c_str (), - cond_.count, - cond_.values, - cond_.lengths, - cond_.formats, + native_data_.count, + native_data_.values, + native_data_.lengths, + native_data_.formats, 1)); PGresult* h (r.get ()); @@ -179,21 +439,35 @@ namespace odb const string& stmt, const Oid* types, size_t types_count, - native_binding& cond) + binding& cond, + native_binding& native_cond) : statement (conn, name, stmt, types, types_count), - cond_ (cond) + cond_ (cond), + native_cond_ (native_cond) { } unsigned long long delete_statement:: execute () { + if (cond_.version != native_cond_.version) + { + bind_param (native_cond_, cond_.bind, cond_.count); + + native_cond_.version = cond_.version; + } + else + { + for (size_t i (0); i < cond_.count; ++i) + native_cond_.lengths[i] = static_cast<int> (*cond_.bind[i].size); + } + result_ptr r (PQexecPrepared (conn_.handle (), name_.c_str (), - cond_.count, - cond_.values, - cond_.lengths, - cond_.formats, + native_cond_.count, + native_cond_.values, + native_cond_.lengths, + native_cond_.formats, 1)); PGresult* h (r.get ()); diff --git a/odb/pgsql/statement.hxx b/odb/pgsql/statement.hxx index 0315f05..c66ff9b 100644 --- a/odb/pgsql/statement.hxx +++ b/odb/pgsql/statement.hxx @@ -17,6 +17,7 @@ #include <odb/pgsql/version.hxx> #include <odb/pgsql/binding.hxx> +#include <odb/pgsql/result-ptr.hxx> #include <odb/pgsql/details/export.hxx> @@ -32,6 +33,12 @@ namespace odb virtual ~statement () = 0; + // @@ Check section 30.5 of manual for description of + // how to cancel queries in progress. + // + // virtual void + // cancel (); + protected: statement (connection&, const std::string& name, @@ -40,10 +47,87 @@ namespace odb std::size_t types_count); protected: + // Adapt an ODB binding to a native PostgreSQL parameter binding. + // + static void + bind_param (native_binding&, + const bind*, + std::size_t count); + + // Populate an ODB binding given a PostgreSQL result. If the truncated + // argument is true, then only truncated columns are extracted. Return + // true if all the data was extracted successfully and false if one or + // more columns were truncated. + // + static bool + bind_result (bind*, + std::size_t count, + PGresult*, + std::size_t row, + bool truncated = false); + + protected: connection& conn_; std::string name_; }; + class LIBODB_PGSQL_EXPORT select_statement: public statement + { + public: + virtual + ~select_statement (); + + select_statement (connection& conn, + const std::string& name, + const std::string& stmt, + const Oid* types, + std::size_t types_count, + binding& cond, + native_binding& native_cond, + binding& data); + + // Common select interface expected by the generated code. + // + public: + enum result + { + success, + no_data, + truncated + }; + + void + execute (); + + // Load next row columns into bound buffers. + // + result + fetch (); + + // Reload truncated columns into bound buffers. + // + void + refetch (); + + // Free the result set. + // + void + free_result (); + + private: + select_statement (const select_statement&); + select_statement& operator= (const select_statement&); + + private: + binding& cond_; + native_binding& native_cond_; + + binding& data_; + + result_ptr result_; + std::size_t current_row_; + }; + class LIBODB_PGSQL_EXPORT insert_statement: public statement { public: @@ -55,7 +139,8 @@ namespace odb const std::string& stmt, const Oid* types, std::size_t types_count, - native_binding& data); + binding& data, + native_binding& native_data); // Return true if successful and false if the row is a duplicate. // All other errors are reported by throwing exceptions. @@ -71,7 +156,9 @@ namespace odb insert_statement& operator= (const insert_statement&); private: - native_binding& data_; + binding& data_; + native_binding& native_data_; + Oid oid_; }; @@ -90,8 +177,11 @@ namespace odb const std::string& stmt, const Oid* types, std::size_t types_count, - native_binding& cond, - native_binding& data); + binding& cond, + native_binding& native_cond, + binding& data, + native_binding& native_data); + void execute (); @@ -100,8 +190,11 @@ namespace odb update_statement& operator= (const update_statement&); private: - native_binding& cond_; - native_binding& data_; + binding& cond_; + native_binding& native_cond_; + + binding& data_; + native_binding& native_data_; }; class LIBODB_PGSQL_EXPORT delete_statement: public statement @@ -115,7 +208,8 @@ namespace odb const std::string& stmt, const Oid* types, std::size_t types_count, - native_binding& cond); + binding& cond, + native_binding& native_cond); unsigned long long execute (); @@ -125,7 +219,8 @@ namespace odb delete_statement& operator= (const delete_statement&); private: - native_binding& cond_; + binding& cond_; + native_binding& native_cond_; }; } } |