Ferenc Szontágh
2024-07-01 abf49b44cc47f39d6cceb83866f915bc03b7d900
include/qtl_sqlite.hpp
@@ -10,1123 +10,1124 @@
namespace qtl
{
namespace sqlite
{
   namespace sqlite
   {
class error : public std::exception
{
public:
   explicit error(int error_code) : m_errno(error_code)
   {
      m_errmsg=sqlite3_errstr(error_code);
   }
   explicit error(sqlite3* db)
   {
      m_errno=sqlite3_errcode(db);
      m_errmsg=sqlite3_errmsg(db);
   }
   error(const error& src) = default;
   virtual ~error() throw() { }
   virtual const char* what() const throw() override { return m_errmsg; }
   int code() const throw() { return m_errno; }
private:
   int m_errno;
   const char* m_errmsg;
};
class statement final
{
public:
   statement() : m_stmt(NULL), m_fetch_result(SQLITE_OK) { }
   statement(const statement&) = delete;
   statement(statement&& src)
      : m_stmt(src.m_stmt), m_fetch_result(src.m_fetch_result),
      m_tail_text(std::forward<std::string>(src.m_tail_text))
   {
      src.m_stmt=NULL;
      src.m_fetch_result=SQLITE_OK;
   }
   statement& operator=(const statement&) = delete;
   statement& operator=(statement&& src)
   {
      if(this!=&src)
      class error : public std::exception
      {
         m_stmt=src.m_stmt;
         m_fetch_result=src.m_fetch_result;
         m_tail_text=std::forward<std::string>(src.m_tail_text);
         src.m_stmt=NULL;
         src.m_fetch_result=SQLITE_OK;
      }
      return *this;
   }
   ~statement()
   {
      close();
   }
      public:
         explicit error(int error_code) : m_errno(error_code)
         {
            m_errmsg = sqlite3_errstr(error_code);
         }
         explicit error(sqlite3 *db)
         {
            m_errno = sqlite3_errcode(db);
            m_errmsg = sqlite3_errmsg(db);
         }
         error(const error &src) = default;
         virtual ~error() throw() {}
         virtual const char *what() const throw() override { return m_errmsg; }
         int code() const throw() { return m_errno; }
   void open(sqlite3* db, const char* query_text, size_t text_length=-1)
   {
      const char* tail=NULL;
      close();
      verify_error(sqlite3_prepare_v2(db, query_text, (int)text_length, &m_stmt, &tail));
      if(tail!=NULL)
      {
         if(text_length==-1)
            m_tail_text.assign(tail);
         else
            m_tail_text.assign(tail, query_text+text_length);
      }
      else
         m_tail_text.clear();
   }
      private:
         int m_errno;
         const char *m_errmsg;
      };
   void close()
   {
      if(m_stmt)
      class statement final
      {
         sqlite3_finalize(m_stmt);
         m_stmt=NULL;
      }
   }
      public:
         statement() : m_stmt(NULL), m_fetch_result(SQLITE_OK) {}
         statement(const statement &) = delete;
         statement(statement &&src)
            : m_stmt(src.m_stmt), m_fetch_result(src.m_fetch_result),
              m_tail_text(std::forward<std::string>(src.m_tail_text))
         {
            src.m_stmt = NULL;
            src.m_fetch_result = SQLITE_OK;
         }
         statement &operator=(const statement &) = delete;
         statement &operator=(statement &&src)
         {
            if (this != &src)
            {
               m_stmt = src.m_stmt;
               m_fetch_result = src.m_fetch_result;
               m_tail_text = std::forward<std::string>(src.m_tail_text);
               src.m_stmt = NULL;
               src.m_fetch_result = SQLITE_OK;
            }
            return *this;
         }
         ~statement()
         {
            close();
         }
   void bind_param(int index, int value)
   {
      verify_error(sqlite3_bind_int(m_stmt, index+1, value));
   }
   void bind_param(int index, int64_t value)
   {
      verify_error(sqlite3_bind_int64(m_stmt, index+1, value));
   }
   void bind_param(int index, double value)
   {
      verify_error(sqlite3_bind_double(m_stmt, index+1, value));
   }
   void bind_param(int index, const char* value, size_t n=-1)
   {
      if(value)
         verify_error(sqlite3_bind_text(m_stmt, index+1, value, (int)n, NULL));
      else
         verify_error(sqlite3_bind_null(m_stmt, index+1));
   }
   void bind_param(int index, const wchar_t* value, size_t n=-1)
   {
      if(value)
         verify_error(sqlite3_bind_text16(m_stmt, index+1, value, (int)n, NULL));
      else
         verify_error(sqlite3_bind_null(m_stmt, index+1));
   }
   void bind_param(int index, const std::string& value)
   {
      bind_param(index, value.data(), value.size());
   }
   void bind_param(int index, const std::wstring& value)
   {
      bind_param(index, value.data(), value.size());
   }
   void bind_param(int index, const const_blob_data& value)
   {
      if(value.size)
      {
         if(value.data)
            verify_error(sqlite3_bind_blob(m_stmt, index+1, value.data, (int)value.size, NULL));
         else
            verify_error(sqlite3_bind_zeroblob(m_stmt, index+1, (int)value.size));
      }
      else
         verify_error(sqlite3_bind_null(m_stmt, index+1));
   }
   void bind_zero_blob(int index, int n)
   {
      verify_error(sqlite3_bind_zeroblob(m_stmt, index+1, (int)n));
   }
   //void bind_zero_blob(int index, sqlite3_uint64 n)
   //{
   //   verify_error(sqlite3_bind_zeroblob64(m_stmt, index+1, (int)n));
   //}
   void bind_param(int index, qtl::null)
   {
      verify_error(sqlite3_bind_null(m_stmt, index+1));
   }
   void bind_param(int index, std::nullptr_t)
   {
      verify_error(sqlite3_bind_null(m_stmt, index+1));
   }
   int get_parameter_count() const
   {
      return sqlite3_bind_parameter_count(m_stmt);
   }
   const char* get_parameter_name(int i) const
   {
      return sqlite3_bind_parameter_name(m_stmt, i);
   }
   int get_parameter_index(const char* param_name) const
   {
      return sqlite3_bind_parameter_index(m_stmt, param_name);
   }
         void open(sqlite3 *db, const char *query_text, size_t text_length = -1)
         {
            const char *tail = NULL;
            close();
            verify_error(sqlite3_prepare_v2(db, query_text, (int)text_length, &m_stmt, &tail));
            if (tail != NULL)
            {
               if (text_length == -1)
                  m_tail_text.assign(tail);
               else
                  m_tail_text.assign(tail, query_text + text_length);
            }
            else
               m_tail_text.clear();
         }
   template<class Type>
   void bind_field(size_t index, Type&& value)
   {
      get_value((int)index, std::forward<Type>(value));
   }
   template<class Type>
   void bind_field(size_t index, qtl::indicator<Type>&& value)
   {
      int type=get_column_type(index);
      value.length=0;
      value.is_truncated=false;
      qtl::bind_field(*this, index, value.data);
      if(type==SQLITE_NULL)
      {
         value.is_null=true;
      }
      else
      {
         value.is_null=false;
         if(type==SQLITE_TEXT || type==SQLITE_BLOB)
            value.length=sqlite3_column_bytes(m_stmt, index);
      }
   }
   void bind_field(size_t index, char* value, size_t length)
   {
      size_t col_length=get_column_length((int)index);
      if(col_length>0)
         strncpy(value, (const char*)sqlite3_column_text(m_stmt, (int)index), std::min(length, col_length+1));
      else
         memset(value, 0, length*sizeof(char));
   }
   void bind_field(size_t index, wchar_t* value, size_t length)
   {
      size_t col_length=sqlite3_column_bytes16(m_stmt, (int)index);
      if(col_length>0)
         wcsncpy(value, (const wchar_t*)sqlite3_column_text16(m_stmt, (int)index), std::min(length, col_length+1));
      else
         memset(value, 0, length*sizeof(wchar_t));
   }
   template<size_t N>
   void bind_field(size_t index, char (&&value)[N])
   {
      bind_field(index, value, N);
   }
   template<size_t N>
   void bind_field(size_t index, wchar_t (&&value)[N])
   {
      bind_field(index, value, N);
   }
   template<size_t N>
   void bind_field(size_t index, std::array<char, N>&& value)
   {
      bind_field(index, value.data(), value.size());
   }
   template<size_t N>
   void bind_field(size_t index, std::array<wchar_t, N>&& value)
   {
      bind_field(index, value.data(), value.size());
   }
         void close()
         {
            if (m_stmt)
            {
               sqlite3_finalize(m_stmt);
               m_stmt = NULL;
            }
         }
         void bind_param(int index, int value)
         {
            verify_error(sqlite3_bind_int(m_stmt, index + 1, value));
         }
         void bind_param(int index, int64_t value)
         {
            verify_error(sqlite3_bind_int64(m_stmt, index + 1, value));
         }
         void bind_param(int index, double value)
         {
            verify_error(sqlite3_bind_double(m_stmt, index + 1, value));
         }
         void bind_param(int index, const char *value, size_t n = -1)
         {
            if (value)
               verify_error(sqlite3_bind_text(m_stmt, index + 1, value, (int)n, NULL));
            else
               verify_error(sqlite3_bind_null(m_stmt, index + 1));
         }
         void bind_param(int index, const wchar_t *value, size_t n = -1)
         {
            if (value)
               verify_error(sqlite3_bind_text16(m_stmt, index + 1, value, (int)n, NULL));
            else
               verify_error(sqlite3_bind_null(m_stmt, index + 1));
         }
         void bind_param(int index, const std::string &value)
         {
            bind_param(index, value.data(), value.size());
         }
         void bind_param(int index, const std::wstring &value)
         {
            bind_param(index, value.data(), value.size());
         }
         void bind_param(int index, const const_blob_data &value)
         {
            if (value.size)
            {
               if (value.data)
                  verify_error(sqlite3_bind_blob(m_stmt, index + 1, value.data, (int)value.size, NULL));
               else
                  verify_error(sqlite3_bind_zeroblob(m_stmt, index + 1, (int)value.size));
            }
            else
               verify_error(sqlite3_bind_null(m_stmt, index + 1));
         }
         void bind_zero_blob(int index, int n)
         {
            verify_error(sqlite3_bind_zeroblob(m_stmt, index + 1, (int)n));
         }
         // void bind_zero_blob(int index, sqlite3_uint64 n)
         //{
         //   verify_error(sqlite3_bind_zeroblob64(m_stmt, index+1, (int)n));
         // }
         void bind_param(int index, qtl::null)
         {
            verify_error(sqlite3_bind_null(m_stmt, index + 1));
         }
         void bind_param(int index, std::nullptr_t)
         {
            verify_error(sqlite3_bind_null(m_stmt, index + 1));
         }
         int get_parameter_count() const
         {
            return sqlite3_bind_parameter_count(m_stmt);
         }
         const char *get_parameter_name(int i) const
         {
            return sqlite3_bind_parameter_name(m_stmt, i);
         }
         int get_parameter_index(const char *param_name) const
         {
            return sqlite3_bind_parameter_index(m_stmt, param_name);
         }
         template <class Type>
         void bind_field(size_t index, Type &&value)
         {
            get_value((int)index, std::forward<Type>(value));
         }
         template <class Type>
         void bind_field(size_t index, qtl::indicator<Type> &&value)
         {
            int type = get_column_type(index);
            value.length = 0;
            value.is_truncated = false;
            qtl::bind_field(*this, index, value.data);
            if (type == SQLITE_NULL)
            {
               value.is_null = true;
            }
            else
            {
               value.is_null = false;
               if (type == SQLITE_TEXT || type == SQLITE_BLOB)
                  value.length = sqlite3_column_bytes(m_stmt, index);
            }
         }
         void bind_field(size_t index, char *value, size_t length)
         {
            size_t col_length = get_column_length((int)index);
            if (col_length > 0)
               strncpy(value, (const char *)sqlite3_column_text(m_stmt, (int)index), std::min(length, col_length + 1));
            else
               memset(value, 0, length * sizeof(char));
         }
         void bind_field(size_t index, wchar_t *value, size_t length)
         {
            size_t col_length = sqlite3_column_bytes16(m_stmt, (int)index);
            if (col_length > 0)
               wcsncpy(value, (const wchar_t *)sqlite3_column_text16(m_stmt, (int)index), std::min(length, col_length + 1));
            else
               memset(value, 0, length * sizeof(wchar_t));
         }
         template <size_t N>
         void bind_field(size_t index, char (&&value)[N])
         {
            bind_field(index, value, N);
         }
         template <size_t N>
         void bind_field(size_t index, wchar_t (&&value)[N])
         {
            bind_field(index, value, N);
         }
         template <size_t N>
         void bind_field(size_t index, std::array<char, N> &&value)
         {
            bind_field(index, value.data(), value.size());
         }
         template <size_t N>
         void bind_field(size_t index, std::array<wchar_t, N> &&value)
         {
            bind_field(index, value.data(), value.size());
         }
#ifdef _QTL_ENABLE_CPP17
   template<typename T>
   inline void bind_field(size_t index, std::optional<T>&& value)
   {
      int type = get_column_type(index);
      if (type == SQLITE_NULL)
      {
         value.reset();
      }
      else
      {
         qtl::bind_field(*this, index, *value);
      }
   }
         template <typename T>
         inline void bind_field(size_t index, std::optional<T> &&value)
         {
            int type = get_column_type(index);
            if (type == SQLITE_NULL)
            {
               value.reset();
            }
            else
            {
               qtl::bind_field(*this, index, *value);
            }
         }
   inline void bind_field(size_t index, std::any&& value)
   {
      int type = get_column_type(index);
      switch(type)
      {
      case SQLITE_NULL:
         value.reset();
      case SQLITE_INTEGER:
         value = sqlite3_column_int64(m_stmt, index);
         break;
      case SQLITE_FLOAT:
         value = sqlite3_column_double(m_stmt, index);
         break;
      case SQLITE_TEXT:
         value.emplace<std::string_view>((const char*)sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
         break;
      case SQLITE_BLOB:
         value.emplace<const_blob_data>(sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
         break;
      default:
         throw sqlite::error(SQLITE_MISMATCH);
      }
   }
         inline void bind_field(size_t index, std::any &&value)
         {
            int type = get_column_type(index);
            switch (type)
            {
            case SQLITE_NULL:
               value.reset();
            case SQLITE_INTEGER:
               value = sqlite3_column_int64(m_stmt, index);
               break;
            case SQLITE_FLOAT:
               value = sqlite3_column_double(m_stmt, index);
               break;
            case SQLITE_TEXT:
               value.emplace<std::string_view>((const char *)sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
               break;
            case SQLITE_BLOB:
               value.emplace<const_blob_data>(sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
               break;
            default:
               throw sqlite::error(SQLITE_MISMATCH);
            }
         }
#endif // C++17
   size_t find_field(const char* name) const
   {
      size_t count=get_column_count();
      for(size_t i=0; i!=count; i++)
      {
         if(strcmp(get_column_name(i), name)==0)
            return i;
      }
      return -1;
   }
   bool fetch()
   {
      m_fetch_result=sqlite3_step(m_stmt);
      switch(m_fetch_result)
      {
      case SQLITE_ROW:
         return true;
      case SQLITE_DONE:
         return false;
      default:
         throw sqlite::error(m_fetch_result);
      }
   }
   int get_column_count() const
   {
      return sqlite3_column_count(m_stmt);
   }
   const char* get_column_name(int col) const
   {
      return sqlite3_column_name(m_stmt, col);
   }
   void get_value(int col, int&& value) const
   {
      value=sqlite3_column_int(m_stmt, col);
   }
   void get_value(int col, int64_t&& value) const
   {
      value=sqlite3_column_int64(m_stmt, col);
   }
   void get_value(int col, double&& value) const
   {
      value=sqlite3_column_double(m_stmt, col);
   }
   const unsigned char* get_value(int col) const
   {
      return sqlite3_column_text(m_stmt, col);
   }
   template<typename CharT>
   const CharT* get_text_value(int col) const;
   template<typename T>
   void get_value(int col, qtl::bind_string_helper<T>&& value) const
   {
      typedef typename qtl::bind_string_helper<T>::char_type char_type;
      int bytes=sqlite3_column_bytes(m_stmt, col);
      if(bytes>0)
         value.assign(get_text_value<char_type>(col), bytes/sizeof(char_type));
      else
         value.clear();
   }
   void get_value(int col, const_blob_data&& value) const
   {
      value.data=sqlite3_column_blob(m_stmt, col);
      value.size=sqlite3_column_bytes(m_stmt, col);
   }
   void get_value(int col, blob_data&& value) const
   {
      const void* data=sqlite3_column_blob(m_stmt, col);
      size_t size=sqlite3_column_bytes(m_stmt, col);
      if(value.size<size)
         throw std::out_of_range("no enough buffer to receive blob data.");
      memcpy(value.data, data, size);
      value.size=size;
   }
   void get_value(int col, std::ostream&& value) const
   {
      const void* data=sqlite3_column_blob(m_stmt, col);
      size_t size=sqlite3_column_bytes(m_stmt, col);
      if(size>0)
         value.write((const char*)data, size);
   }
   int get_column_length(int col) const
   {
      return sqlite3_column_bytes(m_stmt, col);
   }
   int get_column_type(int col) const
   {
      return sqlite3_column_type(m_stmt, col);
   }
   void clear_bindings()
   {
      sqlite3_clear_bindings(m_stmt);
   }
   void reset()
   {
      sqlite3_reset(m_stmt);
   }
   template<typename Types>
   void execute(const Types& params)
   {
      unsigned long count=get_parameter_count();
      if(count>0)
      {
         qtl::bind_params(*this, params);
      }
      fetch();
   }
   template<typename Types>
   bool fetch(Types&& values)
   {
      bool result=false;
      if(m_fetch_result==SQLITE_OK)
         fetch();
      if(m_fetch_result==SQLITE_ROW)
      {
         result=true;
         qtl::bind_record(*this, std::forward<Types>(values));
         m_fetch_result=SQLITE_OK;
      }
      return result;
   }
   bool next_result()
   {
      sqlite3* db=sqlite3_db_handle(m_stmt);
      int count=0;
      do
      {
         trim_string(m_tail_text, " \t\r\n");
         if(!m_tail_text.empty())
         size_t find_field(const char *name) const
         {
            open(db, m_tail_text.data(), m_tail_text.size());
            count=sqlite3_column_count(m_stmt);
            m_fetch_result=SQLITE_OK;
            size_t count = get_column_count();
            for (size_t i = 0; i != count; i++)
            {
               if (strcmp(get_column_name(i), name) == 0)
                  return i;
            }
            return -1;
         }
      }while(!m_tail_text.empty() && count==0);
      return count>0;;
   }
   int affetced_rows() const
   {
      sqlite3* db=sqlite3_db_handle(m_stmt);
      return db ? sqlite3_changes(db) : 0;
   }
         bool fetch()
         {
            m_fetch_result = sqlite3_step(m_stmt);
            switch (m_fetch_result)
            {
            case SQLITE_ROW:
               return true;
            case SQLITE_DONE:
               return false;
            default:
               throw sqlite::error(m_fetch_result);
            }
         }
         int get_column_count() const
         {
            return sqlite3_column_count(m_stmt);
         }
         const char *get_column_name(int col) const
         {
            return sqlite3_column_name(m_stmt, col);
         }
         void get_value(int col, int &&value) const
         {
            value = sqlite3_column_int(m_stmt, col);
         }
         void get_value(int col, int64_t &&value) const
         {
            value = sqlite3_column_int64(m_stmt, col);
         }
         void get_value(int col, double &&value) const
         {
            value = sqlite3_column_double(m_stmt, col);
         }
         const unsigned char *get_value(int col) const
         {
            return sqlite3_column_text(m_stmt, col);
         }
   int64_t insert_id() const
   {
      sqlite3* db=sqlite3_db_handle(m_stmt);
      return db ? sqlite3_last_insert_rowid(db) : 0;
   }
         template <typename CharT>
         const CharT *get_text_value(int col) const;
protected:
   sqlite3_stmt* m_stmt;
   std::string m_tail_text;
   int m_fetch_result;
   void verify_error(int e)
   {
      if(e!=SQLITE_OK) throw error(e);
   }
};
         template <typename T>
         void get_value(int col, qtl::bind_string_helper<T> &&value) const
         {
            typedef typename qtl::bind_string_helper<T>::char_type char_type;
            int bytes = sqlite3_column_bytes(m_stmt, col);
            if (bytes > 0)
               value.assign(get_text_value<char_type>(col), bytes / sizeof(char_type));
            else
               value.clear();
         }
         void get_value(int col, const_blob_data &&value) const
         {
            value.data = sqlite3_column_blob(m_stmt, col);
            value.size = sqlite3_column_bytes(m_stmt, col);
         }
         void get_value(int col, blob_data &&value) const
         {
            const void *data = sqlite3_column_blob(m_stmt, col);
            size_t size = sqlite3_column_bytes(m_stmt, col);
            if (value.size < size)
               throw std::out_of_range("no enough buffer to receive blob data.");
            memcpy(value.data, data, size);
            value.size = size;
         }
         void get_value(int col, std::ostream &&value) const
         {
            const void *data = sqlite3_column_blob(m_stmt, col);
            size_t size = sqlite3_column_bytes(m_stmt, col);
            if (size > 0)
               value.write((const char *)data, size);
         }
class database final : public qtl::base_database<database, statement>
{
public:
   typedef sqlite::error exception_type;
         int get_column_length(int col) const
         {
            return sqlite3_column_bytes(m_stmt, col);
         }
         int get_column_type(int col) const
         {
            return sqlite3_column_type(m_stmt, col);
         }
         void clear_bindings()
         {
            sqlite3_clear_bindings(m_stmt);
         }
         void reset()
         {
            sqlite3_reset(m_stmt);
         }
   database() : m_db(NULL) { }
   ~database() { close(); }
   database(const database&) = delete;
   database(database&& src)
   {
      m_db=src.m_db;
      src.m_db=NULL;
   }
   database& operator=(const database&) = delete;
   database& operator=(database&& src)
   {
      if(this!=&src)
         template <typename Types>
         void execute(const Types &params)
         {
            unsigned long count = get_parameter_count();
            if (count > 0)
            {
               qtl::bind_params(*this, params);
            }
            fetch();
         }
         template <typename Types>
         bool fetch(Types &&values)
         {
            bool result = false;
            if (m_fetch_result == SQLITE_OK)
               fetch();
            if (m_fetch_result == SQLITE_ROW)
            {
               result = true;
               qtl::bind_record(*this, std::forward<Types>(values));
               m_fetch_result = SQLITE_OK;
            }
            return result;
         }
         bool next_result()
         {
            sqlite3 *db = sqlite3_db_handle(m_stmt);
            int count = 0;
            do
            {
               trim_string(m_tail_text, " \t\r\n");
               if (!m_tail_text.empty())
               {
                  open(db, m_tail_text.data(), m_tail_text.size());
                  count = sqlite3_column_count(m_stmt);
                  m_fetch_result = SQLITE_OK;
               }
            } while (!m_tail_text.empty() && count == 0);
            return count > 0;
            ;
         }
         int affetced_rows() const
         {
            sqlite3 *db = sqlite3_db_handle(m_stmt);
            return db ? sqlite3_changes(db) : 0;
         }
         int64_t insert_id() const
         {
            sqlite3 *db = sqlite3_db_handle(m_stmt);
            return db ? sqlite3_last_insert_rowid(db) : 0;
         }
      protected:
         sqlite3_stmt *m_stmt;
         std::string m_tail_text;
         int m_fetch_result;
         void verify_error(int e)
         {
            if (e != SQLITE_OK)
               throw error(e);
         }
      };
      class database final : public qtl::base_database<database, statement>
      {
         close();
         m_db=src.m_db;
         src.m_db=NULL;
      }
      return *this;
   }
      public:
         typedef sqlite::error exception_type;
   void open(const char *filename, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
   {
      int result=sqlite3_open_v2(filename, &m_db, flags, NULL);
      if(result!=SQLITE_OK)
         throw sqlite::error(result);
   }
   void open(const wchar_t *filename)
   {
      int result=sqlite3_open16(filename, &m_db);
      if(result!=SQLITE_OK)
         throw sqlite::error(result);
   }
   void close()
   {
      if(m_db)
      {
         sqlite3_close_v2(m_db);
         m_db=NULL;
      }
   }
         database() : m_db(NULL) {}
         ~database() { close(); }
         database(const database &) = delete;
         database(database &&src)
         {
            m_db = src.m_db;
            src.m_db = NULL;
         }
         database &operator=(const database &) = delete;
         database &operator=(database &&src)
         {
            if (this != &src)
            {
               close();
               m_db = src.m_db;
               src.m_db = NULL;
            }
            return *this;
         }
   statement open_command(const char* query_text, size_t text_length)
   {
      statement stmt;
      stmt.open(handle(), query_text, text_length);
      return stmt;
   }
   statement open_command(const char* query_text)
   {
      return open_command(query_text, strlen(query_text));
   }
   statement open_command(const std::string& query_text)
   {
      return open_command(query_text.data(), query_text.length());
   }
         void open(const char *filename, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
         {
            int result = sqlite3_open_v2(filename, &m_db, flags, NULL);
            if (result != SQLITE_OK)
               throw sqlite::error(result);
         }
         void open(const wchar_t *filename)
         {
            int result = sqlite3_open16(filename, &m_db);
            if (result != SQLITE_OK)
               throw sqlite::error(result);
         }
         void close()
         {
            if (m_db)
            {
               sqlite3_close_v2(m_db);
               m_db = NULL;
            }
         }
   void simple_execute(const char* lpszSql)
   {
      int result=sqlite3_exec(m_db, lpszSql, NULL, NULL, NULL);
      if(result!=SQLITE_OK)
         throw sqlite::error(result);
   }
         statement open_command(const char *query_text, size_t text_length)
         {
            statement stmt;
            stmt.open(handle(), query_text, text_length);
            return stmt;
         }
         statement open_command(const char *query_text)
         {
            return open_command(query_text, strlen(query_text));
         }
         statement open_command(const std::string &query_text)
         {
            return open_command(query_text.data(), query_text.length());
         }
   void begin_transaction()
   {
      simple_execute("BEGIN TRANSACTION");
   }
   void commit()
   {
      simple_execute("COMMIT TRANSACTION");
   }
   void rollback()
   {
      simple_execute("ROLLBACK TRANSACTION");
   }
         void simple_execute(const char *lpszSql)
         {
            int result = sqlite3_exec(m_db, lpszSql, NULL, NULL, NULL);
            if (result != SQLITE_OK)
               throw sqlite::error(result);
         }
   bool is_alive()
   {
         void begin_transaction()
         {
            simple_execute("BEGIN TRANSACTION");
         }
         void commit()
         {
            simple_execute("COMMIT TRANSACTION");
         }
         void rollback()
         {
            simple_execute("ROLLBACK TRANSACTION");
         }
         bool is_alive()
         {
#ifdef _WIN32
      return true;
            return true;
#else
      int has_moved=0;
      int result=sqlite3_file_control(m_db, NULL, SQLITE_FCNTL_HAS_MOVED, &has_moved);
      if(result!=SQLITE_OK)
         throw sqlite::error(result);
      return has_moved==0;
            int has_moved = 0;
            int result = sqlite3_file_control(m_db, NULL, SQLITE_FCNTL_HAS_MOVED, &has_moved);
            if (result != SQLITE_OK)
               throw sqlite::error(result);
            return has_moved == 0;
#endif //_WIN32
   }
   const char* errmsg() const { return sqlite3_errmsg(m_db); }
   int error() const { return sqlite3_errcode(m_db); }
   uint64_t insert_id() { return sqlite3_last_insert_rowid(m_db); }
   sqlite3* handle() { return m_db; }
protected:
   sqlite3* m_db;
};
// stream for blob field
class blobbuf : public std::streambuf
{
public:
   blobbuf()
   {
      init();
   }
   blobbuf(const blobbuf&) = delete;
   blobbuf(blobbuf&& src) : std::streambuf(std::move(src))
   {
      init();
      swap(src);
   }
   virtual ~blobbuf()
   {
      if(m_blob)
      {
         close();
      }
   }
   blobbuf& operator=(const blobbuf&) = delete;
   blobbuf& operator=(blobbuf&& src)
   {
      if(this!=&src)
      {
         reset_back();
         close();
         swap(src);
      }
      return *this;
   }
   void swap( blobbuf& other )
   {
      std::swap(m_blob, other.m_blob);
      std::swap(m_inbuf, other.m_inbuf);
      std::swap(m_outbuf, other.m_outbuf);
      std::swap(m_size, other.m_size);
      std::swap(m_inpos, other.m_inpos);
      std::swap(m_outpos, other.m_outpos);
      std::streambuf::swap(other);
      std::swap(m_back_char, other.m_back_char);
      if(eback() == &other.m_back_char)
         set_back();
      else
         reset_back();
      if(other.eback()==&m_back_char)
         other.set_back();
      else
         other.reset_back();
   }
   static void init_blob(database& db, const char* table, const char* column, int64_t row, int length)
   {
      statement stmt;
      std::ostringstream oss;
      oss<< "UPDATE " << table << " SET " << column << "=? WHERE rowid=?";
      stmt.open(db.handle(), oss.str().data());
      stmt.bind_zero_blob(0, length);
      stmt.bind_param(1, row);
      stmt.fetch();
   }
   static void init_blob(database& db, const std::string& table, const std::string& column, int64_t row, int length)
   {
      return init_blob(db, table.c_str(), column.c_str(), row, length);
   }
   bool is_open() const { return m_blob!=nullptr; }
   blobbuf* open(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode, const char* dbname="main")
   {
      int flags=0;
      if(mode&std::ios_base::out) flags=1;
      if(sqlite3_blob_open(db.handle(), dbname, table, column, row, flags, &m_blob)==SQLITE_OK)
      {
         m_size=sqlite3_blob_bytes(m_blob)/sizeof(char);
         // prepare buffer
         size_t bufsize=std::min<size_t>(default_buffer_size, m_size);
         if(mode&std::ios_base::in)
         {
            m_inbuf.resize(bufsize);
            m_inpos=0;
            setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
         }
         if(mode&std::ios_base::out)
         const char *errmsg() const { return sqlite3_errmsg(m_db); }
         int error() const { return sqlite3_errcode(m_db); }
         uint64_t insert_id() { return sqlite3_last_insert_rowid(m_db); }
         sqlite3 *handle() { return m_db; }
      protected:
         sqlite3 *m_db;
      };
      // stream for blob field
      class blobbuf : public std::streambuf
      {
      public:
         blobbuf()
         {
            m_outbuf.resize(bufsize);
            m_outpos=0;
            setp(m_outbuf.data(), m_outbuf.data()+bufsize);
            init();
         }
      }
      return this;
   }
   blobbuf* open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode, const char* dbname="main")
   {
      return open(db, table.c_str(), column.c_str(), row, mode, dbname);
   }
   blobbuf* close()
   {
      if(m_blob==nullptr)
         return nullptr;
      overflow();
      sqlite3_blob_close(m_blob);
      init();
      return this;
   }
   std::streamoff size() const { return std::streamoff(m_size); }
   void flush()
   {
      if(m_blob)
         overflow();
   }
protected:
   enum { default_buffer_size = 4096 };
   virtual pos_type seekoff( off_type off, std::ios_base::seekdir dir,
      std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ) override
   {
      if(!is_open())
         return pos_type(off_type(-1));
      pos_type pos=0;
      if(which&std::ios_base::out)
      {
         pos=seekoff(m_outpos, off, dir);
      }
      else if(which&std::ios_base::in)
      {
         pos=seekoff(m_inpos, off, dir);
      }
      return seekpos(pos, which);
   }
   virtual pos_type seekpos( pos_type pos,
      std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
   {
      if(!is_open())
         return pos_type(off_type(-1));
      if(pos>=m_size)
         return pos_type(off_type(-1));
      if(which&std::ios_base::out)
      {
         if(pos<m_outpos || pos>=m_outpos+off_type(egptr()-pbase()))
         blobbuf(const blobbuf &) = delete;
         blobbuf(blobbuf &&src) : std::streambuf(std::move(src))
         {
            init();
            swap(src);
         }
         virtual ~blobbuf()
         {
            if (m_blob)
            {
               close();
            }
         }
         blobbuf &operator=(const blobbuf &) = delete;
         blobbuf &operator=(blobbuf &&src)
         {
            if (this != &src)
            {
               reset_back();
               close();
               swap(src);
            }
            return *this;
         }
         void swap(blobbuf &other)
         {
            std::swap(m_blob, other.m_blob);
            std::swap(m_inbuf, other.m_inbuf);
            std::swap(m_outbuf, other.m_outbuf);
            std::swap(m_size, other.m_size);
            std::swap(m_inpos, other.m_inpos);
            std::swap(m_outpos, other.m_outpos);
            std::streambuf::swap(other);
            std::swap(m_back_char, other.m_back_char);
            if (eback() == &other.m_back_char)
               set_back();
            else
               reset_back();
            if (other.eback() == &m_back_char)
               other.set_back();
            else
               other.reset_back();
         }
         static void init_blob(database &db, const char *table, const char *column, int64_t row, int length)
         {
            statement stmt;
            std::ostringstream oss;
            oss << "UPDATE " << table << " SET " << column << "=? WHERE rowid=?";
            stmt.open(db.handle(), oss.str().data());
            stmt.bind_zero_blob(0, length);
            stmt.bind_param(1, row);
            stmt.fetch();
         }
         static void init_blob(database &db, const std::string &table, const std::string &column, int64_t row, int length)
         {
            return init_blob(db, table.c_str(), column.c_str(), row, length);
         }
         bool is_open() const { return m_blob != nullptr; }
         blobbuf *open(database &db, const char *table, const char *column, sqlite3_int64 row,
                    std::ios_base::openmode mode, const char *dbname = "main")
         {
            int flags = 0;
            if (mode & std::ios_base::out)
               flags = 1;
            if (sqlite3_blob_open(db.handle(), dbname, table, column, row, flags, &m_blob) == SQLITE_OK)
            {
               m_size = sqlite3_blob_bytes(m_blob) / sizeof(char);
               // prepare buffer
               size_t bufsize = std::min<size_t>(default_buffer_size, m_size);
               if (mode & std::ios_base::in)
               {
                  m_inbuf.resize(bufsize);
                  m_inpos = 0;
                  setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
               }
               if (mode & std::ios_base::out)
               {
                  m_outbuf.resize(bufsize);
                  m_outpos = 0;
                  setp(m_outbuf.data(), m_outbuf.data() + bufsize);
               }
            }
            return this;
         }
         blobbuf *open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                    std::ios_base::openmode mode, const char *dbname = "main")
         {
            return open(db, table.c_str(), column.c_str(), row, mode, dbname);
         }
         blobbuf *close()
         {
            if (m_blob == nullptr)
               return nullptr;
            overflow();
            m_outpos=pos;
            setp(m_outbuf.data(), m_outbuf.data()+m_outbuf.size());
            sqlite3_blob_close(m_blob);
            init();
            return this;
         }
         else
         std::streamoff size() const { return std::streamoff(m_size); }
         void flush()
         {
            pbump(off_type(pos-pabs()));
            if (m_blob)
               overflow();
         }
      }
      else if(which&std::ios_base::in)
      {
         if(pos<m_inpos || pos>=m_inpos+off_type(epptr()-eback()))
      protected:
         enum
         {
            m_inpos=pos;
            setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
         }
         else
            default_buffer_size = 4096
         };
         virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
                            std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
         {
            gbump(off_type(pos-gabs()));
            if (!is_open())
               return pos_type(off_type(-1));
            pos_type pos = 0;
            if (which & std::ios_base::out)
            {
               pos = seekoff(m_outpos, off, dir);
            }
            else if (which & std::ios_base::in)
            {
               pos = seekoff(m_inpos, off, dir);
            }
            return seekpos(pos, which);
         }
      }
      return pos;
   }
   virtual std::streamsize showmanyc() override
   {
      return m_size-pabs();
   }
   //reads characters from the associated input sequence to the get area
   virtual int_type underflow() override
   {
      if(!is_open())
         return traits_type::eof();
      if(pptr()>pbase())
         overflow();
      off_type count=egptr()-eback();
      pos_type next_pos=0;
      if(count==0 && eback()==m_inbuf.data())
      {
         setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data()+m_inbuf.size());
         count=m_inbuf.size();
      }
      else
      {
         next_pos=m_inpos+pos_type(count);
      }
      if(next_pos>=m_size)
         return traits_type::eof();
      count=std::min(count, m_size-next_pos);
      m_inpos = next_pos;
      if(sqlite3_blob_read(m_blob, eback(), count, m_inpos)!=SQLITE_OK)
         return traits_type::eof();
      setg(eback(), eback(), eback()+count);
      return traits_type::to_int_type(*gptr());
   }
   /*//reads characters from the associated input sequence to the get area and advances the next pointer
   virtual int_type uflow() override
   {
   }*/
   //writes characters to the associated output sequence from the put area
   virtual int_type overflow( int_type ch = traits_type::eof() ) override
   {
      if(!is_open())
         return traits_type::eof();
      if(pptr()!=pbase())
      {
         size_t count = pptr()-pbase();
         if(sqlite3_blob_write(m_blob, pbase(), count, m_outpos)!=SQLITE_OK)
            return traits_type::eof();
         auto intersection = interval_intersection(m_inpos, egptr()-eback(), m_outpos, epptr()-pbase());
         if(intersection.first!=intersection.second)
         virtual pos_type seekpos(pos_type pos,
                            std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
         {
            commit(intersection.first, intersection.second);
            if (!is_open())
               return pos_type(off_type(-1));
            if (pos >= m_size)
               return pos_type(off_type(-1));
            if (which & std::ios_base::out)
            {
               if (pos < m_outpos || pos >= m_outpos + off_type(egptr() - pbase()))
               {
                  overflow();
                  m_outpos = pos;
                  setp(m_outbuf.data(), m_outbuf.data() + m_outbuf.size());
               }
               else
               {
                  pbump(off_type(pos - pabs()));
               }
            }
            else if (which & std::ios_base::in)
            {
               if (pos < m_inpos || pos >= m_inpos + off_type(epptr() - eback()))
               {
                  m_inpos = pos;
                  setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
               }
               else
               {
                  gbump(off_type(pos - gabs()));
               }
            }
            return pos;
         }
         m_outpos+=count;
         setp(pbase(), epptr());
      }
      if(!traits_type::eq_int_type(ch, traits_type::eof()))
      {
         char_type c = traits_type::to_char_type(ch);
         if(m_outpos>=m_size)
            return traits_type::eof();
         if(sqlite3_blob_write(m_blob, &c, 1, m_outpos)!=SQLITE_OK)
            return traits_type::eof();
         auto intersection = interval_intersection(m_inpos, egptr()-eback(), m_outpos, 1);
         if(intersection.first!=intersection.second)
         virtual std::streamsize showmanyc() override
         {
            eback()[intersection.first-m_inpos]=c;
            return m_size - pabs();
         }
         m_outpos+=1;
      }
      return ch;
   }
   virtual int_type pbackfail( int_type c = traits_type::eof() ) override
   {
      if (gptr() == 0
         || gptr() <= eback()
         || (!traits_type::eq_int_type(traits_type::eof(), c)
         && !traits_type::eq(traits_type::to_char_type(c), gptr()[-1])))
         // reads characters from the associated input sequence to the get area
         virtual int_type underflow() override
         {
            if (!is_open())
               return traits_type::eof();
            if (pptr() > pbase())
               overflow();
            off_type count = egptr() - eback();
            pos_type next_pos = 0;
            if (count == 0 && eback() == m_inbuf.data())
            {
               setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data() + m_inbuf.size());
               count = m_inbuf.size();
            }
            else
            {
               next_pos = m_inpos + pos_type(count);
            }
            if (next_pos >= m_size)
               return traits_type::eof();
            count = std::min(count, m_size - next_pos);
            m_inpos = next_pos;
            if (sqlite3_blob_read(m_blob, eback(), count, m_inpos) != SQLITE_OK)
               return traits_type::eof();
            setg(eback(), eback(), eback() + count);
            return traits_type::to_int_type(*gptr());
         }
         /*//reads characters from the associated input sequence to the get area and advances the next pointer
         virtual int_type uflow() override
         {
         }*/
         // writes characters to the associated output sequence from the put area
         virtual int_type overflow(int_type ch = traits_type::eof()) override
         {
            if (!is_open())
               return traits_type::eof();
            if (pptr() != pbase())
            {
               size_t count = pptr() - pbase();
               if (sqlite3_blob_write(m_blob, pbase(), count, m_outpos) != SQLITE_OK)
                  return traits_type::eof();
               auto intersection = interval_intersection(m_inpos, egptr() - eback(), m_outpos, epptr() - pbase());
               if (intersection.first != intersection.second)
               {
                  commit(intersection.first, intersection.second);
               }
               m_outpos += count;
               setp(pbase(), epptr());
            }
            if (!traits_type::eq_int_type(ch, traits_type::eof()))
            {
               char_type c = traits_type::to_char_type(ch);
               if (m_outpos >= m_size)
                  return traits_type::eof();
               if (sqlite3_blob_write(m_blob, &c, 1, m_outpos) != SQLITE_OK)
                  return traits_type::eof();
               auto intersection = interval_intersection(m_inpos, egptr() - eback(), m_outpos, 1);
               if (intersection.first != intersection.second)
               {
                  eback()[intersection.first - m_inpos] = c;
               }
               m_outpos += 1;
            }
            return ch;
         }
         virtual int_type pbackfail(int_type c = traits_type::eof()) override
         {
            if (gptr() == 0 || gptr() <= eback() || (!traits_type::eq_int_type(traits_type::eof(), c) && !traits_type::eq(traits_type::to_char_type(c), gptr()[-1])))
            {
               return (traits_type::eof()); // can't put back, fail
            }
            else
            { // back up one position and store put-back character
               gbump(-1);
               if (!traits_type::eq_int_type(traits_type::eof(), c))
                  *gptr() = traits_type::to_char_type(c);
               return (traits_type::not_eof(c));
            }
         }
      private:
         sqlite3_blob *m_blob;
         std::vector<char> m_inbuf;
         std::vector<char> m_outbuf;
         pos_type m_size;
         pos_type m_inpos;  // position in the input sequence
         pos_type m_outpos; // position in the output sequence
         void init()
         {
            m_blob = nullptr;
            m_size = 0;
            m_inpos = m_outpos = 0;
            m_set_eback = m_set_egptr = nullptr;
            m_back_char = 0;
            setg(nullptr, nullptr, nullptr);
            setp(nullptr, nullptr);
         }
         off_type seekoff(off_type position, off_type off, std::ios_base::seekdir dir)
         {
            off_type result = 0;
            switch (dir)
            {
            case std::ios_base::beg:
               result = off;
               break;
            case std::ios_base::cur:
               result = position + off;
               break;
            case std::ios_base::end:
               result = m_size - off;
               break;
            }
            if (result > m_size)
               result = m_size;
            return result;
         }
         void reset_back()
         { // restore buffer after putback
            if (this->eback() == &m_back_char)
               this->setg(m_set_eback, m_set_eback, m_set_egptr);
         }
         void set_back()
         { // set up putback area
            if (this->eback() != &m_back_char)
            { // save current get buffer
               m_set_eback = this->eback();
               m_set_egptr = this->egptr();
            }
            this->setg(&m_back_char, &m_back_char, &m_back_char + 1);
         }
         char_type *m_set_eback{nullptr}; // saves eback() during one-element putback
         char_type *m_set_egptr{nullptr}; // saves egptr()
         char_type m_back_char{0};
         void move_data(blobbuf &other)
         {
            m_blob = other.m_blob;
            other.m_blob = nullptr;
            m_size = other.m_size;
            other.m_size = 0;
            m_inpos = other.m_inpos;
            other.m_inpos = 0;
            m_outpos = other.m_outpos;
            other.m_outpos = 0;
         }
         static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, pos_type last1, pos_type first2, pos_type last2)
         {
            if (first1 > first2)
            {
               std::swap(first1, first2);
               std::swap(last1, last2);
            }
            if (first2 < last1)
               return std::make_pair(first2, std::min(last1, last2));
            else
               return std::make_pair(0, 0);
         }
         static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, off_type count1, pos_type first2, off_type count2)
         {
            return interval_intersection(first1, first1 + count1, first2, first2 + count2);
         }
         void commit(off_type first, off_type last)
         {
            char_type *src = pbase() + (first - m_outpos);
            char_type *dest = eback() + (first - m_inpos);
            memmove(dest, src, last - first);
         }
         pos_type gabs() const // absolute offset of input pointer in blob field
         {
            return m_inpos + off_type(gptr() - eback());
         }
         pos_type pabs() const // absolute offset of output pointer in blob field
         {
            return m_outpos + off_type(pptr() - pbase());
         }
      };
      class iblobstream : public std::istream
      {
         return (traits_type::eof());   // can't put back, fail
      }
      else
      {   // back up one position and store put-back character
         gbump(-1);
         if (!traits_type::eq_int_type(traits_type::eof(), c))
            *gptr() = traits_type::to_char_type(c);
         return (traits_type::not_eof(c));
      }
   }
      public:
         iblobstream() : std::istream(&m_buffer) {}
         iblobstream(database &db, const char *table, const char *column, sqlite3_int64 row,
                  std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            : std::istream(&m_buffer)
         {
            open(db, table, column, row, mode, dbname);
         }
         iblobstream(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                  std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            : std::istream(&m_buffer)
         {
            open(db, table, column, row, mode, dbname);
         }
         iblobstream(const iblobstream &) = delete;
         iblobstream(iblobstream &&src) : std::istream(&m_buffer), m_buffer(std::move(src.m_buffer)) {}
private:
   sqlite3_blob* m_blob;
   std::vector<char> m_inbuf;
   std::vector<char> m_outbuf;
   pos_type m_size;
   pos_type m_inpos;   //position in the input sequence
   pos_type m_outpos;   //position in the output sequence
         iblobstream &operator=(const iblobstream &) = delete;
         iblobstream &operator=(iblobstream &&src)
         {
            m_buffer.operator=(std::move(src.m_buffer));
         }
   void init()
   {
      m_blob=nullptr;
      m_size=0;
      m_inpos=m_outpos=0;
      m_set_eback=m_set_egptr=nullptr;
      m_back_char=0;
      setg(nullptr, nullptr, nullptr);
      setp(nullptr, nullptr);
   }
         bool is_open() const { return m_buffer.is_open(); }
   off_type seekoff(off_type position, off_type off, std::ios_base::seekdir dir)
   {
      off_type result=0;
      switch(dir)
         void open(database &db, const char *table, const char *column, sqlite3_int64 row,
                 std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
         {
            if (m_buffer.open(db, table, column, row, mode | std::ios_base::in, dbname) == nullptr)
               this->setstate(std::ios_base::failbit);
            else
               this->clear();
         }
         void open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                 std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
         {
            open(db, table.c_str(), column.c_str(), row, mode, dbname);
         }
         void close()
         {
            if (m_buffer.close() == nullptr)
               this->setstate(std::ios_base::failbit);
         }
         blobbuf *rdbuf() const
         {
            return const_cast<blobbuf *>(&m_buffer);
         }
         std::streamoff blob_size() const { return m_buffer.size(); }
      private:
         blobbuf m_buffer;
      };
      class oblobstream : public std::ostream
      {
      case std::ios_base::beg:
         result=off;
         break;
      case std::ios_base::cur:
         result=position+off;
         break;
      case std::ios_base::end:
         result=m_size-off;
         break;
      }
      if(result>m_size)
         result=m_size;
      return result;
   }
      public:
         oblobstream() : std::ostream(&m_buffer) {}
         oblobstream(database &db, const char *table, const char *column, sqlite3_int64 row,
                  std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            : std::ostream(&m_buffer)
         {
            open(db, table, column, row, mode, dbname);
         }
         oblobstream(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                  std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            : std::ostream(&m_buffer)
         {
            open(db, table, column, row, mode, dbname);
         }
         oblobstream(const oblobstream &) = delete;
         oblobstream(oblobstream &&src) : std::ostream(&m_buffer), m_buffer(std::move(src.m_buffer)) {}
   void reset_back()
   {   // restore buffer after putback
      if (this->eback() == &m_back_char)
         this->setg(m_set_eback, m_set_eback, m_set_egptr);
   }
         oblobstream &operator=(const oblobstream &) = delete;
         oblobstream &operator=(oblobstream &&src)
         {
            m_buffer.operator=(std::move(src.m_buffer));
         }
   void set_back()
   {   // set up putback area
      if (this->eback() != &m_back_char)
      {   // save current get buffer
         m_set_eback = this->eback();
         m_set_egptr = this->egptr();
      }
      this->setg(&m_back_char, &m_back_char, &m_back_char + 1);
   }
         bool is_open() const { return m_buffer.is_open(); }
   char_type *m_set_eback { nullptr };   // saves eback() during one-element putback
   char_type *m_set_egptr { nullptr };   // saves egptr()
   char_type m_back_char { 0 };
         void open(database &db, const char *table, const char *column, sqlite3_int64 row,
                 std::ios_base::openmode mode = std::ios_base::out, const char *dbname = "main")
         {
            if (m_buffer.open(db, table, column, row, mode | std::ios_base::out, dbname) == nullptr)
               this->setstate(std::ios_base::failbit);
            else
               this->clear();
         }
         void open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                 std::ios_base::openmode mode = std::ios_base::out, const char *dbname = "main")
         {
            open(db, table.c_str(), column.c_str(), row, mode, dbname);
         }
   void move_data(blobbuf& other)
   {
      m_blob=other.m_blob;
      other.m_blob=nullptr;
      m_size=other.m_size;
      other.m_size=0;
      m_inpos=other.m_inpos;
      other.m_inpos=0;
      m_outpos=other.m_outpos;
      other.m_outpos=0;
   }
         void close()
         {
            if (m_buffer.close() == nullptr)
               this->setstate(std::ios_base::failbit);
         }
   static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, pos_type last1, pos_type first2, pos_type last2)
   {
      if(first1>first2)
         blobbuf *rdbuf() const
         {
            return const_cast<blobbuf *>(&m_buffer);
         }
         std::streamoff blob_size() const { return m_buffer.size(); }
      private:
         blobbuf m_buffer;
      };
      class blobstream : public std::iostream
      {
         std::swap(first1, first2);
         std::swap(last1, last2);
      public:
         blobstream() : std::iostream(&m_buffer) {}
         blobstream(database &db, const char *table, const char *column, sqlite3_int64 row,
                  std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            : std::iostream(&m_buffer)
         {
            open(db, table, column, row, mode, dbname);
         }
         blobstream(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                  std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            : std::iostream(&m_buffer)
         {
            open(db, table, column, row, mode, dbname);
         }
         blobstream(const blobstream &) = delete;
         blobstream(blobstream &&src) : std::iostream(&m_buffer), m_buffer(std::move(src.m_buffer)) {}
         blobstream &operator=(const blobstream &) = delete;
         blobstream &operator=(blobstream &&src)
         {
            m_buffer.operator=(std::move(src.m_buffer));
         }
         bool is_open() const { return m_buffer.is_open(); }
         void open(database &db, const char *table, const char *column, sqlite3_int64 row,
                 std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out, const char *dbname = "main")
         {
            if (m_buffer.open(db, table, column, row, mode | std::ios_base::in | std::ios_base::out, dbname) == nullptr)
               this->setstate(std::ios_base::failbit);
            else
               this->clear();
         }
         void open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                 std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out, const char *dbname = "main")
         {
            open(db, table.c_str(), column.c_str(), row, mode, dbname);
         }
         void close()
         {
            if (m_buffer.close() == nullptr)
               this->setstate(std::ios_base::failbit);
         }
         blobbuf *rdbuf() const
         {
            return const_cast<blobbuf *>(&m_buffer);
         }
         std::streamoff blob_size() const { return m_buffer.size(); }
      private:
         blobbuf m_buffer;
      };
      typedef qtl::transaction<database> transaction;
      template <typename Record>
      using query_iterator = qtl::query_iterator<statement, Record>;
      template <typename Record>
      using query_result = qtl::query_result<statement, Record>;
      template <typename Params>
      inline statement &operator<<(statement &stmt, const Params &params)
      {
         stmt.reset();
         stmt.execute(params);
         return stmt;
      }
      if(first2<last1)
         return std::make_pair(first2, std::min(last1, last2));
      else
         return std::make_pair(0, 0);
      template <>
      inline const char *statement::get_text_value<char>(int col) const
      {
         return (const char *)sqlite3_column_text(m_stmt, col);
      }
      template <>
      inline const wchar_t *statement::get_text_value<wchar_t>(int col) const
      {
         return (const wchar_t *)sqlite3_column_text16(m_stmt, col);
      }
   }
   static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, off_type count1, pos_type first2, off_type count2)
   {
      return interval_intersection(first1, first1+count1, first2, first2+count2);
   }
   void commit(off_type first, off_type last)
   {
      char_type* src= pbase()+(first-m_outpos);
      char_type* dest = eback()+(first-m_inpos);
      memmove(dest, src, last-first);
   }
   pos_type gabs() const // absolute offset of input pointer in blob field
   {
      return m_inpos+off_type(gptr()-eback());
   }
   pos_type pabs() const // absolute offset of output pointer in blob field
   {
      return m_outpos+off_type(pptr()-pbase());
   }
};
class iblobstream : public std::istream
{
public:
   iblobstream() : std::istream(&m_buffer) { }
   iblobstream(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
      : std::istream(&m_buffer)
   {
      open(db, table, column, row, mode, dbname);
   }
   iblobstream(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
      : std::istream(&m_buffer)
   {
      open(db, table, column, row, mode, dbname);
   }
   iblobstream(const iblobstream&) = delete;
   iblobstream(iblobstream&& src) : std::istream(&m_buffer), m_buffer(std::move(src.m_buffer)) { }
   iblobstream& operator=(const iblobstream&) = delete;
   iblobstream& operator=(iblobstream&& src)
   {
      m_buffer.operator =(std::move(src.m_buffer));
   }
   bool is_open() const { return m_buffer.is_open(); }
   void open(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
   {
      if(m_buffer.open(db, table, column, row, mode|std::ios_base::in, dbname)==nullptr)
         this->setstate(std::ios_base::failbit);
      else
         this->clear();
   }
   void open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
   {
      open(db, table.c_str(), column.c_str(), row, mode, dbname);
   }
   void close()
   {
      if(m_buffer.close()==nullptr)
         this->setstate(std::ios_base::failbit);
   }
   blobbuf* rdbuf() const
   {
      return const_cast<blobbuf*>(&m_buffer);
   }
   std::streamoff blob_size() const { return m_buffer.size(); }
private:
   blobbuf m_buffer;
};
class oblobstream : public std::ostream
{
public:
   oblobstream() : std::ostream(&m_buffer) { }
   oblobstream(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
      : std::ostream(&m_buffer)
   {
      open(db, table, column, row, mode, dbname);
   }
   oblobstream(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
      : std::ostream(&m_buffer)
   {
      open(db, table, column, row, mode, dbname);
   }
   oblobstream(const oblobstream&) = delete;
   oblobstream(oblobstream&& src) : std::ostream(&m_buffer), m_buffer(std::move(src.m_buffer)) { }
   oblobstream& operator=(const oblobstream&) = delete;
   oblobstream& operator=(oblobstream&& src)
   {
      m_buffer.operator =(std::move(src.m_buffer));
   }
   bool is_open() const { return m_buffer.is_open(); }
   void open(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::out, const char* dbname="main")
   {
      if(m_buffer.open(db, table, column, row, mode|std::ios_base::out, dbname)==nullptr)
         this->setstate(std::ios_base::failbit);
      else
         this->clear();
   }
   void open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::out, const char* dbname="main")
   {
      open(db, table.c_str(), column.c_str(), row, mode, dbname);
   }
   void close()
   {
      if(m_buffer.close()==nullptr)
         this->setstate(std::ios_base::failbit);
   }
   blobbuf* rdbuf() const
   {
      return const_cast<blobbuf*>(&m_buffer);
   }
   std::streamoff blob_size() const { return m_buffer.size(); }
private:
   blobbuf m_buffer;
};
class blobstream : public std::iostream
{
public:
   blobstream() : std::iostream(&m_buffer) { }
   blobstream(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
      : std::iostream(&m_buffer)
   {
      open(db, table, column, row, mode, dbname);
   }
   blobstream(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
      : std::iostream(&m_buffer)
   {
      open(db, table, column, row, mode, dbname);
   }
   blobstream(const blobstream&) = delete;
   blobstream(blobstream&& src) : std::iostream(&m_buffer), m_buffer(std::move(src.m_buffer)) { }
   blobstream& operator=(const blobstream&) = delete;
   blobstream& operator=(blobstream&& src)
   {
      m_buffer.operator =(std::move(src.m_buffer));
   }
   bool is_open() const { return m_buffer.is_open(); }
   void open(database& db, const char* table, const char* column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out, const char* dbname="main")
   {
      if(m_buffer.open(db, table, column, row, mode|std::ios_base::in|std::ios_base::out, dbname)==nullptr)
         this->setstate(std::ios_base::failbit);
      else
         this->clear();
   }
   void open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
      std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out, const char* dbname="main")
   {
      open(db, table.c_str(), column.c_str(), row, mode, dbname);
   }
   void close()
   {
      if(m_buffer.close()==nullptr)
         this->setstate(std::ios_base::failbit);
   }
   blobbuf* rdbuf() const
   {
      return const_cast<blobbuf*>(&m_buffer);
   }
   std::streamoff blob_size() const { return m_buffer.size(); }
private:
   blobbuf m_buffer;
};
typedef qtl::transaction<database> transaction;
template<typename Record>
using query_iterator = qtl::query_iterator<statement, Record>;
template<typename Record>
using query_result = qtl::query_result<statement, Record>;
template<typename Params>
inline statement& operator<<(statement& stmt, const Params& params)
{
   stmt.reset();
   stmt.execute(params);
   return stmt;
}
template<>
inline const char* statement::get_text_value<char>(int col) const
{
   return (const char*)sqlite3_column_text(m_stmt, col);
}
template<>
inline const wchar_t* statement::get_text_value<wchar_t>(int col) const
{
   return (const wchar_t*)sqlite3_column_text16(m_stmt, col);
}
}
}