finalize common blob test & remove backend-specific ones

This commit is contained in:
Robert Adam
2023-01-07 18:40:42 +01:00
parent de996e9298
commit ae75990cb2
7 changed files with 179 additions and 637 deletions
+177 -79
View File
@@ -6378,119 +6378,217 @@ TEST_CASE_METHOD(common_tests, "BLOB", "[core][blob]")
if (!tableCreator.get())
{
try
{
soci::blob blob(sql);
FAIL("BLOB creation should throw, if backend doesn't support BLOBs");
} catch (const soci_error &)
{
// Throwing is expected if the backend doesn't support BLOBs
}
try
{
soci::blob blob(sql);
FAIL("BLOB creation should throw, if backend doesn't support BLOBs");
} catch (const soci_error &)
{
// Throwing is expected if the backend doesn't support BLOBs
}
WARN("BLOB type not supported by the database, skipping the test.");
return;
}
const char dummy_data[] = "abcdefghijklmnopüqrstuvwxyz";
const char dummy_data[] = "abcdefghijklmnopüqrstuvwxyz";
// Cross-DB usage of BLOBs is only possible if the entire lifetime of the blob object
// is covered in an active transaction.
soci::transaction transaction(sql);
{
SECTION("Read-access on default-constructed blob")
{
soci::blob blob(sql);
// Cross-DB usage of BLOBs is only possible if the entire lifetime of the blob object
// is covered in an active transaction.
soci::transaction transaction(sql);
{
SECTION("Read-access on default-constructed blob")
{
soci::blob blob(sql);
CHECK(blob.get_len() == 0);
CHECK(blob.get_len() == 0);
char buf[5];
std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf));
char buf[5];
std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf));
// There should be no data that could be read
CHECK(read_bytes == 0);
// There should be no data that could be read
CHECK(read_bytes == 0);
// Reading from any offset other than zero is invalid
CHECK_THROWS_AS(blob.read_from_start(buf, sizeof(buf), 1), soci_error);
}
SECTION("Write-access on default-constructed blob")
{
soci::blob blob(sql);
// Reading from any offset other than zero is invalid
CHECK_THROWS_AS(blob.read_from_start(buf, sizeof(buf), 1), soci_error);
}
SECTION("BLOB I/O")
{
soci::blob blob(sql);
std::size_t written_bytes = blob.write_from_start(dummy_data, 5);
std::size_t written_bytes = blob.write_from_start(dummy_data, 5);
CHECK(written_bytes == 5);
CHECK(blob.get_len() == 5);
CHECK(written_bytes == 5);
CHECK(blob.get_len() == 5);
char buf[5];
char buf[5];
static_assert(sizeof(buf) <= sizeof(dummy_data), "Underlying assumption violated");
std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf));
std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf));
CHECK(read_bytes == 5);
CHECK(read_bytes == sizeof(buf));
for (std::size_t i = 0; i < sizeof(buf); ++i)
{
CHECK(buf[i] == dummy_data[i]);
}
for (std::size_t i = 0; i < sizeof(buf); ++i)
{
CHECK(buf[i] == dummy_data[i]);
}
written_bytes = blob.append(dummy_data + 5, 3);
written_bytes = blob.append(dummy_data + 5, 3);
CHECK(written_bytes == 3);
CHECK(blob.get_len() == 8);
CHECK(written_bytes == 3);
CHECK(blob.get_len() == 8);
read_bytes = blob.read_from_start(buf, sizeof(buf), 3);
read_bytes = blob.read_from_start(buf, sizeof(buf), 3);
CHECK(read_bytes == 5);
CHECK(read_bytes == 5);
for (std::size_t i = 0; i < sizeof(buf); ++i)
{
CHECK(buf[i] == dummy_data[i + 3]);
}
for (std::size_t i = 0; i < sizeof(buf); ++i)
{
CHECK(buf[i] == dummy_data[i + 3]);
}
blob.trim(2);
blob.trim(2);
CHECK(blob.get_len() == 2);
CHECK(blob.get_len() == 2);
read_bytes = blob.read_from_start(buf, sizeof(buf));
read_bytes = blob.read_from_start(buf, sizeof(buf));
CHECK(read_bytes == 2);
CHECK(read_bytes == 2);
for (std::size_t i = 0; i < read_bytes; ++i)
{
CHECK(buf[i] == dummy_data[i]);
}
for (std::size_t i = 0; i < read_bytes; ++i)
{
CHECK(buf[i] == dummy_data[i]);
}
// Reading from an offset >= the current length of the blob is invalid
CHECK_THROWS_AS(blob.read_from_start(buf, sizeof(buf), blob.get_len()), soci_error);
// Reading from an offset >= the current length of the blob is invalid
CHECK_THROWS_AS(blob.read_from_start(buf, sizeof(buf), blob.get_len()), soci_error);
written_bytes = blob.append("z", 1);
CHECK(written_bytes == 1);
CHECK(blob.get_len() == 3);
written_bytes = blob.append("z", 1);
CHECK(written_bytes == 1);
CHECK(blob.get_len() == 3);
read_bytes = blob.read_from_start(buf, 1, 2);
read_bytes = blob.read_from_start(buf, 1, 2);
CHECK(read_bytes == 1);
CHECK(buf[0] == 'z');
CHECK(read_bytes == 1);
CHECK(buf[0] == 'z');
// Writing more than one position beyond the blob is invalid
// (Writing exactly one position beyond is the same as appending)
CHECK_THROWS_AS(blob.write_from_start(dummy_data, 2, blob.get_len() + 1), soci_error);
}
SECTION("Inserting/Reading default-constructed blob")
{
soci::blob input_blob(sql);
// Writing more than one position beyond the blob is invalid
// (Writing exactly one position beyond is the same as appending)
CHECK_THROWS_AS(blob.write_from_start(dummy_data, 2, blob.get_len() + 1), soci_error);
}
SECTION("Inserting/Reading default-constructed blob")
{
soci::blob input_blob(sql);
sql << "insert into soci_test (id, b) values(5, :b)", soci::use(input_blob);
sql << "insert into soci_test (id, b) values(5, :b)", soci::use(input_blob);
soci::blob output_blob(sql);
soci::indicator ind;
soci::blob output_blob(sql);
soci::indicator ind;
sql << "select b from soci_test where id = 5", soci::into(output_blob, ind);
sql << "select b from soci_test where id = 5", soci::into(output_blob, ind);
CHECK(ind == soci::i_ok);
CHECK(output_blob.get_len() == 0);
}
}
transaction.rollback();
CHECK(ind == soci::i_ok);
CHECK(output_blob.get_len() == 0);
}
SECTION("Ensure reading into blob overwrites previous contents")
{
soci::blob blob(sql);
blob.write_from_start("hello kitty", 10);
CHECK(blob.get_len() == 10);
soci::blob write_blob(sql);
write_blob.write_from_start("test", 4);
sql << "insert into soci_test (id, b) values (5, :b)", soci::use(write_blob);
sql << "select b from soci_test where id = 5", soci::into(blob);
CHECK(blob.get_len() == 4);
char buf[5];
std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf));
CHECK(read_bytes == 4);
CHECK(buf[0] == 't');
CHECK(buf[1] == 'e');
CHECK(buf[2] == 's');
CHECK(buf[3] == 't');
}
SECTION("Blob-DB interaction")
{
soci::blob write_blob(sql);
static_assert(sizeof(dummy_data) >= 10, "Underlying assumption violated");
write_blob.write_from_start(dummy_data, 10);
const int first_id = 42;
// Write and retrieve blob from/into database
sql << "insert into soci_test (id, b) values(:id, :b)", soci::use(first_id), soci::use(write_blob);
soci::blob read_blob(sql);
sql << "select b from soci_test where id = :id", soci::use(first_id), soci::into(read_blob);
CHECK(sql.got_data());
CHECK(read_blob.get_len() == write_blob.get_len());
char buf[15];
std::size_t bytes_read = read_blob.read_from_start(buf, sizeof(buf));
CHECK(bytes_read == read_blob.get_len());
CHECK(bytes_read == 10);
for (std::size_t i = 0; i < bytes_read; ++i) {
CHECK(buf[i] == dummy_data[i]);
}
// Update original blob and insert new db-entry (must not change previous entry)
const int second_id = first_id + 1;
write_blob.trim(0);
static_assert(sizeof(dummy_data) >= 15 + 5, "Underlying assumption violated");
write_blob.write_from_start(dummy_data + 15, 5);
sql << "insert into soci_test (id, b) values (:id, :b)", soci::use(second_id), soci::use(write_blob);
// First, check that the original entry has not been changed
sql << "select b from soci_test where id = :id", soci::use(first_id), soci::into(read_blob);
CHECK(read_blob.get_len() == 10);
// Then check new entry can be read
sql << "select b from soci_test where id = :id", soci::use(second_id), soci::into(read_blob);
bytes_read = read_blob.read_from_start(buf, sizeof(buf));
CHECK(bytes_read == read_blob.get_len());
CHECK(bytes_read == 5);
for (std::size_t i = 0; i < bytes_read; ++i) {
CHECK(buf[i] == dummy_data[i + 15]);
}
}
SECTION("Binary data")
{
const std::uint8_t binary_data[12] = {0, 1, 2, 3, 4, 5, 6, 7, 22, 255, 250 };
soci::blob write_blob(sql);
std::size_t bytes_written = write_blob.write_from_start(reinterpret_cast<const char *>(binary_data), sizeof(binary_data));
CHECK(bytes_written == sizeof(binary_data));
sql << "insert into soci_test (id, b) values (1, :b)", soci::use(write_blob);
soci::blob read_blob(sql);
sql << "select b from soci_test where id = 1", soci::into(read_blob);
CHECK(read_blob.get_len() == sizeof(binary_data));
std::uint8_t buf[20];
std::size_t bytes_read = read_blob.read_from_start(reinterpret_cast<char *>(buf), sizeof(buf));
CHECK(bytes_read == sizeof(binary_data));
for (std::size_t i = 0; i < sizeof(binary_data); ++i) {
CHECK(buf[i] == binary_data[i]);
}
}
}
transaction.rollback();
}
TEST_CASE_METHOD(common_tests, "Logger", "[core][log]")
-146
View File
@@ -500,152 +500,6 @@ TEST_CASE("Firebird bulk operations", "[firebird][bulk]")
sql << "drop table test6";
}
// blob test
TEST_CASE("Firebird blobs", "[firebird][blob]")
{
soci::session sql(backEnd, connectString);
try
{
sql << "drop table test7";
}
catch (std::runtime_error &)
{} // ignore if error
sql << "create table test7(id integer, img blob)";
sql.commit();
sql.begin();
{
// Read from default-constructed BLOB
blob b(sql);
CHECK(b.get_len() == 0);
char buf[5];
std::size_t read = b.read_from_start(buf, 5);
CHECK(read == 0);
}
{
// verify empty blob
blob b(sql);
indicator ind;
sql << "insert into test7(id, img) values(1,?)", use(b);
sql << "select img from test7 where id = 1", into(b, ind);
CHECK(ind == i_ok);
CHECK(b.get_len() == 0);
sql << "delete from test7";
}
{
// create a new blob
blob b(sql);
char str1[] = "Hello";
b.write_from_start(str1, strlen(str1));
char str2[20];
std::size_t i = b.read_from_start(str2, 2, 3);
str2[i] = '\0';
CHECK(str2[0] == 'l');
CHECK(str2[1] == 'o');
CHECK(str2[2] == '\0');
char str3[] = ", Firebird!";
b.append(str3, strlen(str3));
sql << "insert into test7(id, img) values(1,?)", use(b);
}
{
// read & update blob
blob b(sql);
sql << "select img from test7 where id = 1", into(b);
std::vector<char> text(b.get_len());
b.read_from_start(&text[0], b.get_len());
CHECK(strncmp(&text[0], "Hello, Firebird!", b.get_len()) == 0);
char str1[] = "FIREBIRD";
b.write_from_start(str1, strlen(str1), 7);
// after modification blob must be written to database
sql << "update test7 set img=? where id=1", use(b);
}
{
// read blob from database, modify and write to another record
blob b(sql);
sql << "select img from test7 where id = 1", into(b);
std::vector<char> text(b.get_len());
b.read_from_start(&text[0], b.get_len());
char str1[] = "HELLO";
b.write_from_start(str1, strlen(str1));
b.read_from_start(&text[0], b.get_len());
CHECK(strncmp(&text[0], "HELLO, FIREBIRD!", b.get_len()) == 0);
b.trim(5);
sql << "insert into test7(id, img) values(2,?)", use(b);
}
{
blob b(sql);
statement st = (sql.prepare << "select img from test7", into(b));
st.execute();
st.fetch();
std::vector<char> text(b.get_len());
b.read_from_start(&text[0], b.get_len());
CHECK(strncmp(&text[0], "Hello, FIREBIRD!", b.get_len()) == 0);
st.fetch();
text.resize(b.get_len());
b.read_from_start(&text[0], b.get_len());
CHECK(strncmp(&text[0], "HELLO", b.get_len()) == 0);
}
{
// delete blob
blob b(sql);
indicator ind=i_null;
sql << "update test7 set img=? where id = 1", use(b, ind);
sql << "select img from test7 where id = 2", into(b, ind);
CHECK(ind==i_ok);
sql << "select img from test7 where id = 1", into(b, ind);
CHECK(ind==i_null);
}
{
//create large blob
const int blobSize = 65536; //max segment size is 65535(unsigned short)
std::vector<char> data(blobSize);
blob b(sql);
b.write_from_start(data.data(), blobSize);
sql << "insert into test7(id, img) values(3,?)", use(b);
//now read blob back from database and make sure it has correct content and size
blob br(sql);
sql << "select img from test7 where id = 3", into(br);
std::vector<char> data2(br.get_len());
if(br.get_len()>0)
br.read_from_start(data2.data(), br.get_len());
CHECK(data == data2);
}
sql << "drop table test7";
}
// named parameters
TEST_CASE("Firebird named parameters", "[firebird][named-params]")
{
-98
View File
@@ -605,104 +605,6 @@ TEST_CASE("MySQL function call", "[mysql][function]")
sql << "select concat(@day,' ',@mm,' ',@year)", into(r);
}
// BLOB test
struct blob_table_creator : public table_creator_base
{
blob_table_creator(soci::session & sql)
: table_creator_base(sql)
{
sql <<
"create table soci_test ("
" id integer,"
" img blob"
")";
}
};
TEST_CASE("MySQL blob", "[mysql][blob]")
{
soci::session sql(backEnd, connectString);
blob_table_creator tableCreator(sql);
const char buf[] = "abcdefghijklmnopqrstuvwxyz";
{
// Read from default-constructed BLOB
blob b(sql);
CHECK(b.get_len() == 0);
char buf2[5];
std::size_t read = b.read_from_start(buf2, 5);
CHECK(read == 0);
}
{
// empty, default-constructed BLOB
blob b(sql);
indicator ind;
sql << "insert into soci_test(id, img) values(1, :img)", use(b);
sql << "select img from soci_test where id = 1", into(b, ind);
CHECK(ind == i_ok);
CHECK(b.get_len() == 0);
sql << "delete from soci_test where id = 1";
}
{
// Create new BLOB
blob b(sql);
b.write_from_start(buf, sizeof(buf));
char substr[20];
std::size_t i = b.read_from_start(substr, 3);
substr[i] = '\0';
CHECK(substr[0] == buf[0]);
CHECK(substr[1] == buf[1]);
CHECK(substr[2] == buf[2]);
CHECK(substr[3] == '\0');
sql << "insert into soci_test(id, img) values(7, :img)", use(b);
}
{
// Append to BLOB
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == sizeof(buf));
b.append(buf, sizeof(buf));
CHECK(b.get_len() == 2 * sizeof(buf));
sql << "update soci_test set img = :img where id = 7", use(b);
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
char buf2[100];
b.read_from_start(buf2, 10);
CHECK(std::strncmp(buf2, "abcdefghij", 10) == 0);
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
b.trim(sizeof(buf));
CHECK(b.get_len() == sizeof(buf));
sql << "update soci_test set img = :img where id = 7", use(b);
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == sizeof(buf));
}
}
struct double_value_table_creator : table_creator_base
{
double_value_table_creator(soci::session & sql)
+1 -1
View File
@@ -52,7 +52,7 @@ struct table_creator_for_get_affected_rows : table_creator_base
struct table_creator_for_blob : public tests::table_creator_base
{
table_creator_for_blob(soci::session & sql)
: tests::table_creator_base(sql)
: tests::table_creator_base(sql)
{
sql << "create table soci_test(id integer, b blob)";
}
+1 -89
View File
@@ -102,95 +102,7 @@ TEST_CASE("Oracle explicit calls", "[oracle]")
CHECK(i == 7);
}
// DDL + blob test
struct blob_table_creator : public table_creator_base
{
blob_table_creator(soci::session & sql)
: table_creator_base(sql)
{
sql <<
"create table soci_test ("
" id number(10) not null,"
" img blob"
")";
}
};
TEST_CASE("Oracle blob", "[oracle][blob]")
{
soci::session sql(backEnd, connectString);
soci::transaction transaction(sql);
blob_table_creator tableCreator(sql);
char buf[] = "abcdefghijklmnopqrstuvwxyz";
sql << "insert into soci_test (id, img) values (7, empty_blob())";
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 0);
// note: blob offsets start from 1
b.write_from_start(buf, sizeof(buf));
CHECK(b.get_len() == sizeof(buf));
b.trim(10);
CHECK(b.get_len() == 10);
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
//assert(b.get_len() == sizeof(buf) + 10);
CHECK(b.get_len() == 10);
char buf2[100];
b.read_from_start(buf2, 10);
CHECK(strncmp(buf2, "abcdefghij", 10) == 0);
}
{
// Read from default-constructed BLOB
blob b(sql);
CHECK(b.get_len() == 0);
char buf[5];
std::size_t read = b.read_from_start(buf, 5);
CHECK(read == 0);
}
{
// empty, default-constructed BLOB
blob b(sql);
indicator ind;
sql << "insert into soci_test(id, img) values(1, :img)", use(b);
sql << "select img from soci_test where id = 1", into(b, ind);
CHECK(ind == i_ok);
CHECK(b.get_len() == 0);
sql << "delete from soci_test where id = 1";
}
{
// Create new BLOB
blob b(sql);
b.write_from_start(buf, sizeof(buf));
char substr[20];
std::size_t i = b.read_from_start(substr, 3);
substr[i] = '\0';
CHECK(substr[0] == buf[0]);
CHECK(substr[1] == buf[1]);
CHECK(substr[2] == buf[2]);
CHECK(substr[3] == '\0');
sql << "insert into soci_test(id, img) values(7, :img)", use(b);
}
transaction.commit();
}
// DDL test
// nested statement test
// (the same syntax is used for output cursors in PL/SQL)
-104
View File
@@ -229,110 +229,6 @@ TEST_CASE("PostgreSQL function call", "[postgresql][function]")
}
}
// BLOB test
struct blob_table_creator : public table_creator_base
{
blob_table_creator(soci::session & sql)
: table_creator_base(sql)
{
sql <<
"create table soci_test ("
" id integer,"
" img oid"
")";
}
};
TEST_CASE("PostgreSQL blob", "[postgresql][blob]")
{
soci::session sql(backEnd, connectString);
blob_table_creator tableCreator(sql);
const char buf[] = "abcdefghijklmnopqrstuvwxyz";
// in PostgreSQL, BLOB operations must be within transaction block
transaction tr(sql);
{
// Read from default-constructed BLOB
blob b(sql);
CHECK(b.get_len() == 0);
char buf2[5];
std::size_t read = b.read_from_start(buf2, 5);
CHECK(read == 0);
}
{
// empty, default-constructed BLOB
blob b(sql);
indicator ind;
sql << "insert into soci_test(id, img) values(1, :img)", use(b);
sql << "select img from soci_test where id = 1", into(b, ind);
CHECK(ind == i_ok);
CHECK(b.get_len() == 0);
sql << "delete from soci_test where id = 1";
}
{
// Create new BLOB
blob b(sql);
b.write_from_start(buf, sizeof(buf));
char substr[20];
std::size_t i = b.read_from_start(substr, 3);
substr[i] = '\0';
CHECK(substr[0] == buf[0]);
CHECK(substr[1] == buf[1]);
CHECK(substr[2] == buf[2]);
CHECK(substr[3] == '\0');
sql << "insert into soci_test(id, img) values(7, :img)", use(b);
}
{
// Append to BLOB
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == sizeof(buf));
b.append(buf, sizeof(buf));
CHECK(b.get_len() == 2 * sizeof(buf));
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
char buf2[100];
b.read_from_start(buf2, 10);
CHECK(std::strncmp(buf2, "abcdefghij", 10) == 0);
}
#if PG_VERSION_NUM >= 80003
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
b.trim(sizeof(buf));
CHECK(b.get_len() == sizeof(buf));
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == sizeof(buf));
}
#endif
// Destroy BLOB
unsigned long oid;
sql << "select img from soci_test where id = 7", into(oid);
sql << "select lo_unlink(" << oid << ")";
}
struct longlong_table_creator : table_creator_base
{
longlong_table_creator(soci::session & sql)
-120
View File
@@ -256,126 +256,6 @@ TEST_CASE("SQLite get_last_insert_id escapes table name",
CHECK(val == 0);
}
// BLOB test
struct blob_table_creator : public table_creator_base
{
blob_table_creator(soci::session & sql)
: table_creator_base(sql)
{
sql <<
"create table soci_test ("
" id integer,"
" img blob"
")";
}
};
TEST_CASE("SQLite blob", "[sqlite][blob]")
{
soci::session sql(backEnd, connectString);
blob_table_creator tableCreator(sql);
char buf[] = "abcdefghijklmnopqrstuvwxyz";
{
// Read from default-constructed BLOB
blob b(sql);
CHECK(b.get_len() == 0);
char buf2[5];
std::size_t read = b.read_from_start(buf2, 5);
CHECK(read == 0);
}
{
// empty, default-constructed BLOB
blob b(sql);
indicator ind;
sql << "insert into soci_test(id, img) values(1, :img)", use(b);
sql << "select img from soci_test where id = 1", into(b, ind);
CHECK(ind == i_ok);
CHECK(b.get_len() == 0);
sql << "delete from soci_test where id = 1";
}
{
// Create new BLOB
blob b(sql);
b.write_from_start(buf, sizeof(buf));
char substr[20];
std::size_t i = b.read_from_start(substr, 3);
substr[i] = '\0';
CHECK(substr[0] == buf[0]);
CHECK(substr[1] == buf[1]);
CHECK(substr[2] == buf[2]);
CHECK(substr[3] == '\0');
sql << "insert into soci_test(id, img) values(7, :img)", use(b);
}
{
// Append to BLOB
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == sizeof(buf));
b.append(buf, sizeof(buf));
CHECK(b.get_len() == 2 * sizeof(buf));
sql << "update soci_test set img = :img where id = 7", use(b);
}
{
// Read from BLOB as fetched from DB
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
char buf2[100];
b.read_from_start(buf2, 10);
CHECK(std::strncmp(buf2, "abcdefghij", 10) == 0);
}
{
// Trim BLOB
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
b.trim(0);
CHECK(b.get_len() == 0);
sql << "update soci_test set img = :img where id = 7", use(b);
}
{
blob b(sql);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == 0);
b.write_from_start(buf, sizeof(buf));
CHECK(b.get_len() == sizeof(buf));
sql << "update soci_test set img=? where id = 7", use(b);
b.append(buf, sizeof(buf));
CHECK(b.get_len() == 2 * sizeof(buf));
sql << "insert into soci_test(id, img) values(8, ?)", use(b);
}
{
blob b(sql);
sql << "select img from soci_test where id = 8", into(b);
CHECK(b.get_len() == 2 * sizeof(buf));
char buf2[100];
b.read_from_start(buf2, 10);
CHECK(std::strncmp(buf2, "abcdefghij", 10) == 0);
sql << "select img from soci_test where id = 7", into(b);
CHECK(b.get_len() == sizeof(buf));
}
}
// This test was put in to fix a problem that occurs when there are both
// into and use elements in the same query and one of them (into) binds
// to a vector object.