diff options
Diffstat (limited to 'odb/sqlite/statement.cxx')
-rw-r--r-- | odb/sqlite/statement.cxx | 296 |
1 files changed, 293 insertions, 3 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 ())); + } } } |