mirror of
https://github.com/pocoproject/poco.git
synced 2025-02-21 06:37:42 +01:00
* Unit test for bug #2755. * Removed condition to set buffer length to 0. * Fixes to unit tests for LONGBLOB/TEXT data type. * Adjusted buffer sizes to accommodate LONGBLOBs. Co-authored-by: Hector Toledo Soto <hsoto@transperfect.com>
This commit is contained in:
parent
6ec184dfca
commit
b1b97b9640
@ -323,6 +323,8 @@ public:
|
||||
private:
|
||||
bool realExtractFixed(std::size_t pos, enum_field_types type, void* buffer, bool isUnsigned = false);
|
||||
|
||||
bool extractLongLOB(std::size_t pos);
|
||||
|
||||
// Prevent VC8 warning "operator= could not be generated"
|
||||
Extractor& operator=(const Extractor&);
|
||||
|
||||
|
@ -40,6 +40,9 @@ class ResultMetadata
|
||||
/// MySQL result metadata
|
||||
{
|
||||
public:
|
||||
~ResultMetadata();
|
||||
/// Destroys the ResultMetadata.
|
||||
|
||||
void reset();
|
||||
/// Resets the metadata.
|
||||
|
||||
@ -64,10 +67,13 @@ public:
|
||||
bool isNull(std::size_t pos) const;
|
||||
/// Returns true if value at pos is null.
|
||||
|
||||
void adjustColumnSizeToFit(std::size_t pos);
|
||||
/// Expands the size allocated for column to fit the length of the data.
|
||||
|
||||
private:
|
||||
std::vector<MetaColumn> _columns;
|
||||
std::vector<MYSQL_BIND> _row;
|
||||
std::vector<char> _buffer;
|
||||
std::vector<char*> _buffer;
|
||||
std::vector<unsigned long> _lengths;
|
||||
std::vector<my_boolv> _isNull; // using char instead of bool to avoid std::vector<bool> disaster
|
||||
};
|
||||
|
@ -147,6 +147,9 @@ bool Extractor::extract(std::size_t pos, Poco::Data::BLOB& val)
|
||||
if (_metadata.metaColumn(static_cast<Poco::UInt32>(pos)).type() != Poco::Data::MetaColumn::FDT_BLOB)
|
||||
throw MySQLException("Extractor: not a blob");
|
||||
|
||||
if (_metadata.metaColumn(static_cast<Poco::UInt32>(pos)).length() == 0 && !extractLongLOB(pos))
|
||||
return false;
|
||||
|
||||
val.assignRaw(_metadata.rawData(pos), _metadata.length(pos));
|
||||
return true;
|
||||
}
|
||||
@ -163,6 +166,9 @@ bool Extractor::extract(std::size_t pos, Poco::Data::CLOB& val)
|
||||
if (_metadata.metaColumn(static_cast<Poco::UInt32>(pos)).type() != Poco::Data::MetaColumn::FDT_BLOB)
|
||||
throw MySQLException("Extractor: not a blob");
|
||||
|
||||
if (_metadata.metaColumn(static_cast<Poco::UInt32>(pos)).length() == 0 && !extractLongLOB(pos))
|
||||
return false;
|
||||
|
||||
val.assignRaw(reinterpret_cast<const char*>(_metadata.rawData(pos)), _metadata.length(pos));
|
||||
return true;
|
||||
}
|
||||
@ -263,6 +269,22 @@ bool Extractor::realExtractFixed(std::size_t pos, enum_field_types type, void* b
|
||||
return isNull == 0;
|
||||
}
|
||||
|
||||
bool Extractor::extractLongLOB(std::size_t pos)
|
||||
{
|
||||
// Large LOBs (LONGBLOB and LONGTEXT) are fetched
|
||||
// with a zero-length buffer to avoid allocating
|
||||
// huge amounts of memory. Therefore, when extracting
|
||||
// the buffers need to be adjusted.
|
||||
|
||||
_metadata.adjustColumnSizeToFit(pos);
|
||||
|
||||
MYSQL_BIND* row = _metadata.row();
|
||||
if (!_stmt.fetchColumn(pos, &row[pos]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Not implemented
|
||||
|
@ -140,6 +140,13 @@ namespace Data {
|
||||
namespace MySQL {
|
||||
|
||||
|
||||
ResultMetadata::~ResultMetadata()
|
||||
{
|
||||
for (std::vector<char*>::iterator it = _buffer.begin(); it != _buffer.end(); ++it)
|
||||
std::free(*it);
|
||||
}
|
||||
|
||||
|
||||
void ResultMetadata::reset()
|
||||
{
|
||||
_columns.resize(0);
|
||||
@ -165,7 +172,6 @@ void ResultMetadata::init(MYSQL_STMT* stmt)
|
||||
std::size_t count = mysql_num_fields(h);
|
||||
MYSQL_FIELD* fields = mysql_fetch_fields(h);
|
||||
|
||||
std::size_t commonSize = 0;
|
||||
_columns.reserve(count);
|
||||
|
||||
for (std::size_t i = 0; i < count; i++)
|
||||
@ -181,29 +187,24 @@ void ResultMetadata::init(MYSQL_STMT* stmt)
|
||||
0, // TODO: precision
|
||||
!IS_NOT_NULL(fields[i].flags) // nullable
|
||||
));
|
||||
|
||||
commonSize += _columns[i].length();
|
||||
}
|
||||
|
||||
_buffer.resize(commonSize);
|
||||
_buffer.resize(count);
|
||||
_row.resize(count);
|
||||
_lengths.resize(count);
|
||||
_isNull.resize(count);
|
||||
|
||||
std::size_t offset = 0;
|
||||
|
||||
for (std::size_t i = 0; i < count; i++)
|
||||
{
|
||||
std::memset(&_row[i], 0, sizeof(MYSQL_BIND));
|
||||
unsigned int len = static_cast<unsigned int>(_columns[i].length());
|
||||
_buffer[i] = (char*) std::calloc(len, sizeof(char));
|
||||
_row[i].buffer_type = fields[i].type;
|
||||
_row[i].buffer_length = len;
|
||||
_row[i].buffer = (len > 0) ? (&_buffer[0] + offset) : 0;
|
||||
_row[i].buffer = _buffer[i];
|
||||
_row[i].length = &_lengths[i];
|
||||
_row[i].is_null = reinterpret_cast<my_bool*>(&_isNull[i]); // workaround to make it work with both MySQL 8 and earlier
|
||||
_row[i].is_unsigned = (fields[i].flags & UNSIGNED_FLAG) > 0;
|
||||
|
||||
offset += _row[i].buffer_length;
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,4 +245,13 @@ bool ResultMetadata::isNull(std::size_t pos) const
|
||||
}
|
||||
|
||||
|
||||
void ResultMetadata::adjustColumnSizeToFit(std::size_t pos)
|
||||
{
|
||||
std::free(_buffer[pos]);
|
||||
_buffer[pos] = (char*) std::calloc(_lengths[pos], sizeof(char));
|
||||
_row[pos].buffer = _buffer[pos];
|
||||
_row[pos].buffer_length = _lengths[pos];
|
||||
}
|
||||
|
||||
|
||||
} } } // namespace Poco::Data::MySQL
|
||||
|
@ -469,6 +469,15 @@ void MySQLTest::testBLOBStmt()
|
||||
}
|
||||
|
||||
|
||||
void MySQLTest::testLongBLOB()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
recreatePersonLongBLOBTable();
|
||||
_pExecutor->longBlob();
|
||||
}
|
||||
|
||||
|
||||
void MySQLTest::testUnsignedInts()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
@ -752,6 +761,15 @@ void MySQLTest::recreatePersonTimeTable()
|
||||
}
|
||||
|
||||
|
||||
void MySQLTest::recreatePersonLongBLOBTable()
|
||||
{
|
||||
dropTable("Person");
|
||||
try { *_pSession << "CREATE TABLE Person (LastName VARCHAR(30), FirstName VARCHAR(30), Address VARCHAR(30), Biography LONGTEXT)", now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail ("recreatePersonLongBLOBTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail ("recreatePersonLongBLOBTable()"); }
|
||||
}
|
||||
|
||||
|
||||
void MySQLTest::recreateIntsTable()
|
||||
{
|
||||
dropTable("Strings");
|
||||
@ -919,6 +937,7 @@ CppUnit::Test* MySQLTest::suite()
|
||||
CppUnit_addTest(pSuite, MySQLTest, testDateTime);
|
||||
//CppUnit_addTest(pSuite, MySQLTest, testBLOB);
|
||||
CppUnit_addTest(pSuite, MySQLTest, testBLOBStmt);
|
||||
CppUnit_addTest(pSuite, MySQLTest, testLongBLOB);
|
||||
CppUnit_addTest(pSuite, MySQLTest, testUnsignedInts);
|
||||
CppUnit_addTest(pSuite, MySQLTest, testFloat);
|
||||
CppUnit_addTest(pSuite, MySQLTest, testDouble);
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
void testDateTime();
|
||||
void testBLOB();
|
||||
void testBLOBStmt();
|
||||
void testLongBLOB();
|
||||
|
||||
void testUnsignedInts();
|
||||
void testFloat();
|
||||
@ -117,6 +118,7 @@ private:
|
||||
void recreatePersonDateTimeTable();
|
||||
void recreatePersonDateTable();
|
||||
void recreatePersonTimeTable();
|
||||
void recreatePersonLongBLOBTable();
|
||||
void recreateStringsTable();
|
||||
void recreateIntsTable();
|
||||
void recreateUnsignedIntsTable();
|
||||
|
@ -1435,6 +1435,32 @@ void SQLExecutor::blobStmt()
|
||||
}
|
||||
|
||||
|
||||
void SQLExecutor::longBlob()
|
||||
{
|
||||
std::string funct = "longBlob()";
|
||||
std::string lastName("lastname");
|
||||
std::string firstName("firstname");
|
||||
std::string address("Address");
|
||||
Poco::Data::CLOB biography("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 123);
|
||||
|
||||
int count = 0;
|
||||
Statement ins = (*_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(biography));
|
||||
ins.execute();
|
||||
try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
|
||||
assertTrue (count == 1);
|
||||
|
||||
Poco::Data::CLOB res;
|
||||
poco_assert (res.size() == 0);
|
||||
Statement stmt = (*_pSession << "SELECT Biography FROM Person", into(res));
|
||||
try { stmt.execute(); }
|
||||
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
|
||||
poco_assert (res == biography);
|
||||
}
|
||||
|
||||
|
||||
void SQLExecutor::tuples()
|
||||
{
|
||||
typedef Tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> TupleType;
|
||||
|
@ -83,6 +83,7 @@ public:
|
||||
void dateTime();
|
||||
void date();
|
||||
void time();
|
||||
void longBlob();
|
||||
void unsignedInts();
|
||||
void floats();
|
||||
void doubles();
|
||||
|
Loading…
x
Reference in New Issue
Block a user