aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--odb/pgsql/statement.cxx328
-rw-r--r--odb/pgsql/statement.hxx111
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_;
};
}
}