From f046bd8361574f0823001bc06ad00032d7192ba8 Mon Sep 17 00:00:00 2001
From: znone <glyc@sina.com.cn>
Date: Tue, 14 May 2019 13:35:14 +0000
Subject: [PATCH] 提供以标准流的方式直接操纵MySQL的BLOB字段 修正一些错误

---
 include/qtl_sqlite.hpp |    9 
 test/TestMysql.cpp     |   74 ++++++
 include/qtl_common.hpp |    8 
 include/qtl_mysql.hpp  |  272 ++++++++++++++++++++++++++-
 test/TestMysql.h       |    2 
 LICENSE                |  204 ++++++++++++++++++++
 README.md              |   28 ++
 7 files changed, 569 insertions(+), 28 deletions(-)

diff --git a/LICENSE b/LICENSE
index c106f0b..388f73e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,207 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+-------------------------------------------------------------------------------
+
 Copyright (c) 2017 znone
 
 Anti 996 License Version 1.0 (Draft)
diff --git a/README.md b/README.md
index fdbfb59..e89d4d2 100644
--- a/README.md
+++ b/README.md
@@ -115,15 +115,16 @@
 1. 为你的字符串类型,对 qtl::bind_string_helper 实现一个专门化。如果该字符串类型有符合标准库字符串语义的以下成员函数,可以跳过这一步:assign,clear,resize,data,size;
 2. 为你的字符串类型,对 qtl::bind_field 实现一个专门化;
 
-因为QT的QString有兼容标准库的成员函数,所以绑定到QString只需要一步:
+因为 QT 的 QByteArray 有兼容标准库的成员函数,所以绑定到 QByteArray 只需要一步:
+一般数据库不提供到 QChar/QString 的绑定,所以只能先用 QByteArray 接收数据,然后转换为 QString。
 
 ```C++
 namespace qtl
 {
 	template<typename Command>
-	inline void bind_field(Command& command, size_t index, QString&& value)
+	inline void bind_field(Command& command, size_t index, QByteArray&& value)
 	{
-		command.bind_field(index, bind_string(std::forward<QString>(value)));
+		command.bind_field(index, bind_string(std::forward<QByteArray>(value)));
 	}
 }
 
@@ -186,8 +187,14 @@
 | float | float |
 | double | double |
 | char<br>varchar | const char*<br>std::string |
-| blob<br>binary<br>text | qtl::const_blob_data<br>std::istream |
+| blob<br>binary<br>text | qtl::const_blob_data<br>std::istream<br>qtl::mysql::blob_writer |
 | date<br>time<br>datetime<br/>timestamp | qtl::mysql::time |
+
+blob_writer是一个函数,它的定义如下:
+```C++
+typedef std::function<void(std::ostream&)> blob_writer;
+```
+该函数通过std::ostream类型的参数向BLOB字段写入数据。因为MySQL API的限制,该流基本只能向前移动,并不建议对该流随意调整写入位置。
 
 ### MySQL的字段数据绑定
 
@@ -199,10 +206,19 @@
 | bigint | int64_t<br/>uint64_t |
 | float | float |
 | double | double |
-| char<br>varchar | char[N]<br>std::array&lt;char, N&gt;<br>std::string |
-| blob<br>binary<br>text | qtl::blob_data<br>std::ostream |
+| char<br>varchar | char[N]<br>std::array&lt;char, N&gt;<br>std::string<br>std::istream
+| blob<br>binary<br>text | qtl::blob_data<br>std::ostream<br>qtl::mysql::blobbuf
 | date<br>time<br>datetime<br>timestamp | qtl::mysql::time |
 
+可以通过qtl::mysql::blobbuf读取BLOB字段的数据:
+```C++
+void read_blob(qtl::mysql::blobbuf& buf) {
+	istream s(&buf);
+	...
+};
+```
+因为MySQL API的限制,该流只能向前移动,并不建议对该流随意调整读取位置。
+
 ### MySQL相关的C++类
 - qtl::mysql::database
 表示一个MySQL的数据库连接,程序主要通过这个类操纵数据库。
diff --git a/include/qtl_common.hpp b/include/qtl_common.hpp
index 6388a17..db7c607 100644
--- a/include/qtl_common.hpp
+++ b/include/qtl_common.hpp
@@ -80,18 +80,18 @@
 	typedef typename string_type::value_type char_type;
 	bind_string_helper(string_type&& value) : m_value(std::forward<string_type>(value)) { }
 	bind_string_helper(const bind_string_helper& src)
-		: m_value(std::forward<std::string>(src.m_value))
+		: m_value(std::forward<StringT>(src.m_value))
 	{
 	}
 	bind_string_helper(bind_string_helper&& src)
-		: m_value(std::forward<std::string>(src.m_value))
+		: m_value(std::forward<StringT>(src.m_value))
 	{
 	}
 	bind_string_helper& operator=(const bind_string_helper& src)
 	{
 		if (this != &src)
 		{
-			m_value = std::forward<std::string>(src.m_value);
+			m_value = std::forward<StringT>(src.m_value);
 		}
 		return *this;
 	}
@@ -99,7 +99,7 @@
 	{
 		if (this != &src)
 		{
-			m_value = std::forward<std::string>(src.m_value);
+			m_value = std::forward<StringT>(src.m_value);
 		}
 		return *this;
 	}
diff --git a/include/qtl_mysql.hpp b/include/qtl_mysql.hpp
index 70cef52..ffa55a4 100644
--- a/include/qtl_mysql.hpp
+++ b/include/qtl_mysql.hpp
@@ -20,7 +20,7 @@
 namespace mysql
 {
 
-#if MYSQL_VERSION_ID >=80000
+#if LIBMYSQL_VERSION_ID >=80000
 typedef bool my_bool;
 #endif //MySQL 8
 
@@ -200,7 +200,6 @@
 public:
 	error() : m_error(0) { }
 	error(unsigned int err, const char* errmsg) : m_error(err), m_errmsg(errmsg) { }
-	explicit error(unsigned int err) : m_error(err), m_errmsg(err_msg(err)) { }
 	explicit error(statement& stmt);
 	explicit error(database& db);
 	error(const error& src) = default;
@@ -210,13 +209,239 @@
 private:
 	unsigned int m_error;
 	std::string m_errmsg;
-
-#if MYSQL_VERSION_ID < 80000
-	static const char* err_msg(int err) { return ER(err); }
-#else
-	static const char* err_msg(int err) { return ER_CLIENT(err); }
-#endif
 };
+
+class blobbuf : public std::streambuf
+{
+public:
+	blobbuf() : m_stmt(nullptr), m_field(0) 
+	{
+	}
+	blobbuf(const blobbuf&) = default;
+	blobbuf& operator=(const blobbuf&) = default;
+	virtual ~blobbuf() 
+	{ 
+		overflow();
+	}
+
+	void open(MYSQL_STMT* stmt, int field, const binder& b, std::ios_base::openmode mode)
+	{
+		if (m_stmt && m_field)
+		{
+			overflow();
+		}
+
+		assert(stmt != nullptr);
+		m_stmt = stmt;
+		m_field = field;
+		m_binder = b;
+		size_t bufsize;
+		if (b.length) m_size = *b.length;
+		if (m_size > 0)
+			bufsize = std::min<size_t>(blob_buffer_size, m_size);
+		else
+			bufsize = blob_buffer_size;
+		if (mode&std::ios_base::in)
+		{
+			m_buf.resize(bufsize);
+			m_pos = 0;
+			setg(m_buf.data(), m_buf.data(), m_buf.data());
+		}
+		else if (mode&std::ios_base::out)
+		{
+			m_buf.resize(bufsize);
+			m_pos = 0;
+			setp(m_buf.data(), m_buf.data() + bufsize);
+		}
+
+	}
+
+protected:
+	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 (which&std::ios_base::in)
+		{
+			pos_type pos = 0;
+			pos = seekoff(m_pos, off, dir);
+			return seekpos(pos, which);
+		}
+		return std::streambuf::seekoff(off, dir, which);
+	}
+
+	virtual pos_type seekpos(pos_type pos,
+		std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
+	{
+		if (pos >= m_size)
+			return pos_type(off_type(-1));
+
+		if (which&std::ios_base::out)
+		{
+			if (pos < m_pos || pos >= m_pos + off_type(egptr() - pbase()))
+			{
+				overflow();
+				m_pos = pos;
+				setp(m_buf.data(), m_buf.data() + m_buf.size());
+			}
+			else
+			{
+				pbump(off_type(pos - pabs()));
+			}
+		}
+		else if (which&std::ios_base::in)
+		{
+			if (pos < m_pos || pos >= m_pos + off_type(epptr() - eback()))
+			{
+				m_pos = pos;
+				setg(m_buf.data(), m_buf.data(), m_buf.data());
+			}
+			else
+			{
+				gbump(off_type(pos - gabs()));
+			}
+		}
+		return pos;
+	}
+
+	virtual std::streamsize showmanyc() override
+	{
+		return m_size - pabs();
+	}
+
+	virtual int_type underflow() override
+	{
+		if (pptr() > pbase())
+			overflow();
+
+		off_type count = egptr() - eback();
+		pos_type next_pos=0;
+		if (count == 0 && eback() == m_buf.data())
+		{
+			setg(m_buf.data(), m_buf.data(), m_buf.data() + m_buf.size());
+			count = m_buf.size();
+		}
+		else
+		{
+			next_pos = m_pos + pos_type(count);
+		}
+		if (next_pos >= m_size)
+			return traits_type::eof();
+
+		count = std::min(count, m_size - next_pos);
+		m_binder.buffer = m_buf.data();
+		m_binder.buffer_length = count;
+		m_pos = next_pos;
+		int ret = mysql_stmt_fetch_column(m_stmt, &m_binder,  m_field, m_pos);
+		switch (ret)
+		{
+		case 0:
+			count = std::min(m_binder.buffer_length,  *m_binder.length);
+			setg(eback(), eback(), eback() + count);
+			return traits_type::to_int_type(*gptr());
+		case CR_NO_DATA:
+			return traits_type::eof();
+		default:
+			throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
+		}
+	}
+
+	virtual int_type overflow(int_type ch = traits_type::eof()) override
+	{
+		if (pptr() != pbase())
+		{
+			size_t count = pptr() - pbase();
+			int ret = mysql_stmt_send_long_data(m_stmt, m_field, pbase(), count);
+			if (ret != 0)
+				throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
+
+			//auto intersection = interval_intersection(m_pos, egptr() - eback(), m_pos, epptr() - pbase());
+			//if (intersection.first != intersection.second)
+			//{
+			//	commit(intersection.first, intersection.second);
+			//}
+
+			m_pos += 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_pos >= m_size)
+				return traits_type::eof();
+			int ret = mysql_stmt_send_long_data(m_stmt, m_field, &c, 1);
+			if (ret != 0)
+				throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
+
+			//auto intersection = interval_intersection(m_pos, egptr() - eback(), m_pos, 1);
+			//if (intersection.first != intersection.second)
+			//{
+			//	eback()[intersection.first - m_pos] = c;
+			//}
+			m_pos += 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:
+	MYSQL_STMT* m_stmt;
+	binder m_binder;
+	int m_field;
+	std::vector<char> m_buf;
+	pos_type m_size;
+	pos_type m_pos;	//position in the input sequence
+
+	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;
+		}
+		if (result > m_size)
+			result = m_size;
+		return result;
+	}
+
+	pos_type gabs() const // absolute offset of input pointer in blob field
+	{
+		return m_pos + off_type(gptr() - eback());
+	}
+
+	pos_type pabs() const // absolute offset of output pointer in blob field
+	{
+		return m_pos + off_type(pptr() - pbase());
+	}
+};
+
+typedef std::function<void(std::ostream&)> blob_writer;
 
 class statement final
 {
@@ -336,6 +561,17 @@
 		};
 	}
 
+	void bind_param(size_t index, const blob_writer& param)
+	{
+		m_binders[index].bind(NULL, 0, MYSQL_TYPE_LONG_BLOB);
+		m_binderAddins[index].m_after_fetch = [this, index, &param](const binder& b) {
+			blobbuf buf;
+			buf.open(m_stmt, index, b, std::ios::out);
+			std::ostream s(&buf);
+			param(s);
+		};
+	}
+
 	template<class Param>
 	void bind_param(size_t index, const Param& param)
 	{
@@ -396,7 +632,7 @@
 				if(*b.is_null) value.clear();
 				else value.truncate(*b.length);
 			};
-			m_binders[index].bind(data, field->length, field->type);
+			m_binders[index].bind((void*)data, field->length, field->type);
 		}
 	}
 
@@ -404,7 +640,9 @@
 	{
 		if(m_result)
 		{
-			m_binders[index].bind(NULL, 0, MYSQL_TYPE_LONG_BLOB);
+			MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index);
+			assert(IS_LONGDATA(field->type));
+			m_binders[index].bind(NULL, 0, field->type);
 			m_binderAddins[index].m_after_fetch=[this, index, &value](const binder& b) {
 				unsigned long readed=0;
 				std::array<char, blob_buffer_size> buffer;
@@ -424,6 +662,20 @@
 		}
 	}
 
+	void bind_field(size_t index, blobbuf&& value)
+	{
+		if (m_result)
+		{
+			MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index);
+			assert(IS_LONGDATA(field->type));
+			m_binders[index].bind(NULL, 0, field->type);
+			m_binderAddins[index].m_after_fetch = [this, index, &value](const binder& b) {
+				if (*b.is_null) return;
+				value.open(m_stmt, index, b, std::ios::in);
+			};
+		}
+	}
+
 	template<typename Type>
 	void bind_field(size_t index, indicator<Type>&& value)
 	{
diff --git a/include/qtl_sqlite.hpp b/include/qtl_sqlite.hpp
index ded2e57..157c82f 100644
--- a/include/qtl_sqlite.hpp
+++ b/include/qtl_sqlite.hpp
@@ -686,7 +686,7 @@
 			overflow();
 
 		off_type count=egptr()-eback();
-		pos_type next_pos;
+		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());
@@ -700,9 +700,9 @@
 			return traits_type::eof();
 
 		count=std::min(count, m_size-next_pos);
-		if(sqlite3_blob_read(m_blob, eback(), count, next_pos)!=SQLITE_OK)
+		m_inpos = next_pos;
+		if(sqlite3_blob_read(m_blob, eback(), count, m_inpos)!=SQLITE_OK)
 			return traits_type::eof();
-		m_inpos=next_pos;
 		setg(eback(), eback(), eback()+count);
 		return traits_type::to_int_type(*gptr());
 	}
@@ -796,10 +796,13 @@
 		{
 		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;
diff --git a/test/TestMysql.cpp b/test/TestMysql.cpp
index d7dda0b..98ead07 100644
--- a/test/TestMysql.cpp
+++ b/test/TestMysql.cpp
@@ -30,9 +30,9 @@
 	template<>
 	inline void bind_record<qtl::mysql::statement, TestMysqlRecord>(qtl::mysql::statement& command, TestMysqlRecord&& v)
 	{
-		qtl::bind_field(command, 0, v.id);
+		qtl::bind_field(command, static_cast<size_t>(0), v.id);
 		qtl::bind_field(command, 1, v.name);
-		qtl::bind_field(command, 3, v.create_time);
+		qtl::bind_field(command, 2, v.create_time);
 	}
 }
 
@@ -48,6 +48,8 @@
 	TEST_ADD(TestMysql::test_iterator)
 	TEST_ADD(TestMysql::test_insert_blob)
 	TEST_ADD(TestMysql::test_select_blob)
+	//TEST_ADD(TestMysql::test_insert_stream)
+	//TEST_ADD(TestMysql::test_fetch_stream)
 }
 
 inline void TestMysql::connect(qtl::mysql::database& db)
@@ -81,19 +83,19 @@
 
 	try
 	{
-		db.query("select * from test where id=?", id, 
+		db.query("select * from test where id=?", 0, id, 
 			[](const qtl::indicator<uint32_t>& id, const std::string& name, const qtl::mysql::time& create_time) {
 				printf("ID=\"%d\", Name=\"%s\"\n",
 					id.data, name.data());
 		});
 
-		db.query("select * from test where id=?", id,
+		db.query("select * from test where id=?", 0, id,
 			[](const TestMysqlRecord& record) {
 				printf("ID=\"%d\", Name=\"%s\"\n",
 					record.id, record.name);
 		});
 
-		db.query("select * from test where id=?", id, 
+		db.query("select * from test where id=?", 0, id, 
 			&TestMysqlRecord::print);
 	}
 	catch(qtl::mysql::error& e)
@@ -254,6 +256,68 @@
 	}
 }
 
+void TestMysql::test_insert_stream()
+{
+	qtl::mysql::database db;
+	connect(db);
+
+	try
+	{
+		qtl::mysql::blob_writer writer = [](std::ostream& s) {
+			for (size_t i = 0; i != 100; i++)
+			{
+				s << i << ": ";
+				for (size_t j = 0; j <= i; j++)
+					s << char('a' + j % 26);
+				s << endl;
+				for (size_t j = 0; j <= i; j++)
+					s << '-';
+				s << endl;
+			}
+		};
+		id = db.insert("INSERT INTO test_stream (Data) values(?)",
+				writer);
+	}
+	catch (qtl::mysql::error& e)
+	{
+		ASSERT_EXCEPTION(e);
+	}
+}
+
+void TestMysql::test_fetch_stream()
+{
+	qtl::mysql::database db;
+	connect(db);
+
+	try
+	{
+		db.query("SELECT Data from test_stream", [](qtl::mysql::blobbuf& buf) {
+			istream s(&buf);
+			string str;
+			while (!s.eof())
+			{
+				getline(s, str);
+				cout << str << endl;
+			}
+			s.clear(ios_base::goodbit | ios_base::eofbit);
+			s.seekg(0, ios::beg);
+			if (s.good())
+			{
+				cout << "again:" << endl;
+				while (!s.eof())
+				{
+					getline(s, str);
+					cout << str << endl;
+				}
+			}
+		});
+	}
+	catch (qtl::mysql::error& e)
+	{
+		ASSERT_EXCEPTION(e);
+	}
+}
+
 void TestMysql::get_md5(std::istream& is, unsigned char* result)
 {
 	std::array<char, 64*1024> buffer;
diff --git a/test/TestMysql.h b/test/TestMysql.h
index e4af3c1..3f0e1da 100644
--- a/test/TestMysql.h
+++ b/test/TestMysql.h
@@ -26,6 +26,8 @@
 	void test_iterator();
 	void test_insert_blob();
 	void test_select_blob();
+	void test_insert_stream();
+	void test_fetch_stream();
 
 private:
 	uint32_t id;

--
Gitblit v1.9.3