diff options
-rw-r--r-- | odb/sqlite/statement.cxx | 296 | ||||
-rw-r--r-- | odb/sqlite/statement.hxx | 144 |
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_; + }; } } |