From 4deb25a98c3324ed2e9e3e486ca15e10e755cb00 Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Tue, 24 May 2011 16:37:08 +0200 Subject: Add select_statement and binding adapters. Correct version usage --- odb/pgsql/statement.cxx | 328 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 301 insertions(+), 27 deletions(-) (limited to 'odb/pgsql/statement.cxx') 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 #include #include -#include #include 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 (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 (*b.size); + } + } + } + + bool statement:: + bind_result (bind* p, + size_t count, + PGresult* result, + size_t row, + bool truncated) + { + bool r (true); + + assert (static_cast (PQnfields (result)) == count); + + for (int i (0); i < static_cast (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 (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 ( + PQgetlength (result, static_cast (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 (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 (*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 (++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 (*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 (*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 (*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 ()); -- cgit v1.1