aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-03-22 15:49:33 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-03-22 15:49:33 +0200
commit878a448f86ddd60e5eb7778e88edd965003ea480 (patch)
treea0efeef1d7fb9af58d9d73a1cb8aa39f6179b586
parentb1a6710e7d396538617550f1d00b4db2575485c7 (diff)
Implement the rest of statements
-rw-r--r--odb/sqlite/statement.cxx296
-rw-r--r--odb/sqlite/statement.hxx144
2 files changed, 435 insertions, 5 deletions
diff --git a/odb/sqlite/statement.cxx b/odb/sqlite/statement.cxx
index e7d197b..417a746 100644
--- a/odb/sqlite/statement.cxx
+++ b/odb/sqlite/statement.cxx
@@ -3,6 +3,11 @@
// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
// license : GNU GPL v2; see accompanying LICENSE file
+#include <cstring> // std::memcpy
+#include <cassert>
+
+#include <odb/exceptions.hxx> // object_not_persistent
+
#include <odb/sqlite/statement.hxx>
#include <odb/sqlite/connection.hxx>
#include <odb/sqlite/error.hxx>
@@ -17,6 +22,12 @@ namespace odb
//
statement::
+ ~statement ()
+ {
+ sqlite3_finalize (stmt_);
+ }
+
+ statement::
statement (connection& conn, const string& s)
: conn_ (conn)
{
@@ -46,12 +57,124 @@ namespace odb
}
}
+ void statement::
+ bind_param (const bind* p, size_t n, size_t start_param)
+ {
+ int e (SQLITE_OK);
+ start_param++; // SQLite parameters are counted from 1.
+ for (size_t i (0); e == SQLITE_OK && i < n; ++i)
+ {
+ const bind& b (p[i]);
+ int j (static_cast<int> (i + start_param));
- statement::
- ~statement ()
+ if (*b.is_null)
+ {
+ e = sqlite3_bind_null (stmt_, j);
+ continue;
+ }
+
+ switch (b.type)
+ {
+ case bind::integer:
+ {
+ long long v (*static_cast<long long*> (b.buffer));
+ e = sqlite3_bind_int64 (stmt_, j, static_cast<sqlite3_int64> (v));
+ break;
+ }
+ case bind::real:
+ {
+ double v (*static_cast<double*> (b.buffer));
+ e = sqlite3_bind_double (stmt_, j, v);
+ break;
+ }
+ case bind::text:
+ {
+ e = sqlite3_bind_text (stmt_,
+ j,
+ static_cast<const char*> (b.buffer),
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::blob:
+ {
+ e = sqlite3_bind_blob (stmt_,
+ j,
+ b.buffer,
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ }
+ }
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+ }
+
+ bool statement::
+ bind_result (const bind* p, size_t n, bool truncated)
{
- sqlite3_finalize (stmt_);
+ bool r (true);
+
+ for (size_t i (0); i < n; ++i)
+ {
+ const bind& b (p[i]);
+ int j (static_cast<int> (i));
+
+ if (truncated && !*b.truncated)
+ continue;
+
+ *b.truncated = false;
+
+ // Check for NULL unless we are reloading a truncated result.
+ //
+ if (!truncated)
+ {
+ *b.is_null = sqlite3_column_type (stmt_, j) != SQLITE_NULL;
+
+ if (*b.is_null)
+ continue;
+ }
+
+ switch (b.type)
+ {
+ case bind::integer:
+ {
+ *static_cast<long long*> (b.buffer) =
+ static_cast<long long> (sqlite3_column_int64 (stmt_, j));
+ break;
+ }
+ case bind::real:
+ {
+ *static_cast<double*> (b.buffer) =
+ sqlite3_column_double (stmt_, j);
+ break;
+ }
+ case bind::text:
+ case bind::blob:
+ {
+ *b.size = static_cast<size_t> (sqlite3_column_bytes (stmt_, j));
+
+ if (*b.size > b.capacity)
+ {
+ *b.truncated = true;
+ r = false;
+ continue;
+ }
+
+ const void* d (b.type == bind::text
+ ? sqlite3_column_text (stmt_, j)
+ : sqlite3_column_blob (stmt_, j));
+
+ memcpy (b.buffer, d, *b.size);
+ break;
+ }
+ }
+ }
+
+ return r;
}
// simple_statement
@@ -95,5 +218,172 @@ namespace odb
return r;
}
+
+ // select_statement
+ //
+
+ select_statement::
+ select_statement (connection& conn,
+ const string& s,
+ binding& cond,
+ binding& data)
+ : statement (conn, s), cond_ (cond), data_ (data)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ done_ = false;
+
+ if (int e = sqlite3_reset (stmt_))
+ translate_error (e, conn_);
+
+ bind_param (cond_.bind, cond_.count);
+ }
+
+ bool select_statement::
+ next ()
+ {
+ if (!done_)
+ {
+ int e (sqlite3_step (stmt_));
+
+ switch (e)
+ {
+ case SQLITE_DONE:
+ {
+ done_ = true;
+ break;
+ }
+ case SQLITE_ROW:
+ {
+ break;
+ }
+ default:
+ {
+ translate_error (e, conn_);
+ }
+ }
+ }
+
+ return !done_;
+ }
+
+ select_statement::result select_statement::
+ load ()
+ {
+ if (done_)
+ return no_data;
+
+ return bind_result (data_.bind, data_.count) ? success : truncated;
+ }
+
+ void select_statement::
+ reload ()
+ {
+ assert (!done_);
+
+ if (!bind_result (data_.bind, data_.count, true))
+ assert (false);
+ }
+
+ // insert_statement
+ //
+
+ insert_statement::
+ insert_statement (connection& conn, const string& s, binding& data)
+ : statement (conn, s), data_ (data)
+ {
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ if (int e = sqlite3_reset (stmt_))
+ translate_error (e, conn_);
+
+ bind_param (data_.bind, data_.count);
+
+ int e (sqlite3_step (stmt_));
+
+ if (e != SQLITE_DONE)
+ {
+ // SQLITE_CONSTRAINT error code covers more than just a duplicate
+ // primary key. Unfortunately, there is nothing more precise that
+ // we can use (even sqlite3_errmsg() returns generic "constraint
+ // failed").
+ //
+ if (e == SQLITE_CONSTRAINT)
+ return false;
+ else
+ translate_error (e, conn_);
+ }
+
+ return true;
+ }
+
+ unsigned long long insert_statement::
+ id ()
+ {
+ return static_cast<unsigned long long> (
+ sqlite3_last_insert_rowid (conn_.handle ()));
+ }
+
+ // update_statement
+ //
+
+ update_statement::
+ update_statement (connection& conn,
+ const string& s,
+ binding& cond,
+ binding& data)
+ : statement (conn, s), cond_ (cond), data_ (data)
+ {
+ }
+
+ void update_statement::
+ execute ()
+ {
+ if (int e = sqlite3_reset (stmt_))
+ translate_error (e, conn_);
+
+ bind_param (data_.bind, data_.count);
+ bind_param (cond_.bind, cond_.count, data_.count);
+
+ int e (sqlite3_step (stmt_));
+
+ if (e != SQLITE_DONE)
+ translate_error (e, conn_);
+
+ if (sqlite3_changes (conn_.handle ()) == 0)
+ throw object_not_persistent ();
+ }
+
+ // delete_statement
+ //
+
+ delete_statement::
+ delete_statement (connection& conn, const string& s, binding& cond)
+ : statement (conn, s), cond_ (cond)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ if (int e = sqlite3_reset (stmt_))
+ translate_error (e, conn_);
+
+ bind_param (cond_.bind, cond_.count);
+
+ int e (sqlite3_step (stmt_));
+
+ if (e != SQLITE_DONE)
+ translate_error (e, conn_);
+
+ return static_cast<unsigned long long> (
+ sqlite3_changes (conn_.handle ()));
+ }
}
}
diff --git a/odb/sqlite/statement.hxx b/odb/sqlite/statement.hxx
index 3c5ea1c..967ff3d 100644
--- a/odb/sqlite/statement.hxx
+++ b/odb/sqlite/statement.hxx
@@ -17,8 +17,7 @@
#include <odb/details/shared-ptr.hxx>
#include <odb/sqlite/version.hxx>
-//@@ #include <odb/sqlite/binding.hxx>
-
+#include <odb/sqlite/binding.hxx>
#include <odb/sqlite/details/export.hxx>
namespace odb
@@ -37,6 +36,17 @@ namespace odb
statement (connection&, const std::string& statement);
statement (connection&, const char* statement, std::size_t n);
+ void
+ bind_param (const bind*, std::size_t count, std::size_t start_param = 0);
+
+ // Extract row columns into the bound buffers. 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.
+ //
+ bool
+ bind_result (const bind*, std::size_t count, bool truncated = false);
+
protected:
connection& conn_;
sqlite3_stmt* stmt_;
@@ -58,6 +68,136 @@ namespace odb
private:
bool result_set_;
};
+
+ class LIBODB_SQLITE_EXPORT select_statement: public statement
+ {
+ public:
+ select_statement (connection& conn,
+ const std::string& statement,
+ binding& 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 ()
+ {
+ return next () ? load () : no_data;
+ }
+
+ // Reload truncated columns into bound buffers.
+ //
+ void
+ refetch ()
+ {
+ reload ();
+ }
+
+ // Free the result set.
+ //
+ void
+ free_result ()
+ {
+ }
+
+ // More fine-grained SQLite-specific interface that splits fetch()
+ // into next() and load().
+ //
+ public:
+ // Return false if there is no more rows.
+ //
+ bool
+ next ();
+
+ result
+ load ();
+
+ void
+ reload ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ bool done_;
+ binding& cond_;
+ binding& data_;
+ };
+
+ class LIBODB_SQLITE_EXPORT insert_statement: public statement
+ {
+ public:
+ insert_statement (connection& conn,
+ const std::string& statement,
+ binding& data);
+
+ // 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&);
+
+ private:
+ binding& data_;
+ };
+
+ class LIBODB_SQLITE_EXPORT update_statement: public statement
+ {
+ public:
+ 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&);
+
+ private:
+ binding& cond_;
+ binding& data_;
+ };
+
+ class LIBODB_SQLITE_EXPORT delete_statement: public statement
+ {
+ public:
+ 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&);
+
+ private:
+ binding& cond_;
+ };
}
}