SQLite multiple statement handling

This commit is contained in:
Aleksandar Fabijanic 2008-01-21 00:56:52 +00:00
parent 1f47c0df2f
commit 5e1605823b
24 changed files with 441 additions and 232 deletions

View File

@ -66,8 +66,6 @@ class ODBC_API ODBCStatementImpl: public Poco::Data::StatementImpl
/// Implements statement functionality needed for ODBC
{
public:
typedef std::vector<ODBCMetaColumn*> ColumnPtrVec;
ODBCStatementImpl(SessionImpl& rSession);
/// Creates the ODBCStatementImpl.
@ -96,8 +94,12 @@ protected:
bool canBind() const;
/// Returns true if a valid statement is set and we can bind.
void compileImpl();
/// Compiles the statement, doesn't bind yet
bool compileImpl();
/// Compiles the statement, doesn't bind yet.
/// Does nothing if the statement has already been compiled.
/// In this implementation, batch statements are compiled in a single step.
/// Therefore, this function always return false indicating no need for
/// subsequent compilation.
void bindImpl();
/// Binds all parameters and executes the statement.
@ -119,6 +121,8 @@ private:
typedef std::vector<PreparationPtr> PreparationVec;
typedef Poco::SharedPtr<Extractor> ExtractorPtr;
typedef std::vector<ExtractorPtr> ExtractorVec;
typedef std::vector<ODBCMetaColumn*> ColumnPtrVec;
typedef std::vector<ColumnPtrVec> ColumnPtrVecVec;
static const std::string INVALID_CURSOR_STATE;
@ -167,9 +171,10 @@ private:
ExtractorVec _extractors;
bool _stepCalled;
int _nextResponse;
ColumnPtrVec _columnPtrs;
ColumnPtrVecVec _columnPtrs;
bool _prepared;
mutable Poco::UInt32 _affectedRowCount;
bool _compiled;
};

View File

@ -101,17 +101,11 @@ void Binder::freeMemory()
StringMap::iterator itStr = _strings.begin();
StringMap::iterator itStrEnd = _strings.end();
for(; itStr != itStrEnd; ++itStr)
{
if (itStr->first) std::free(itStr->first);
}
for(; itStr != itStrEnd; ++itStr) std::free(itStr->first);
CharPtrVec::iterator itChr = _charPtrs.begin();
CharPtrVec::iterator endChr = _charPtrs.end();
for (; itChr != endChr; ++itChr)
{
if (*itChr) std::free(*itChr);
}
for (; itChr != endChr; ++itChr) std::free(*itChr);
BoolPtrVec::iterator itBool = _boolPtrs.begin();
BoolPtrVec::iterator endBool = _boolPtrs.end();

View File

@ -65,21 +65,30 @@ ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession):
_stepCalled(false),
_nextResponse(0),
_prepared(false),
_affectedRowCount(0)
_affectedRowCount(0),
_compiled(false)
{
}
ODBCStatementImpl::~ODBCStatementImpl()
{
ColumnPtrVec::iterator it = _columnPtrs.begin();
ColumnPtrVec::iterator itEnd = _columnPtrs.end();
for(; it != itEnd; ++it) delete *it;
ColumnPtrVecVec::iterator it = _columnPtrs.begin();
ColumnPtrVecVec::iterator end = _columnPtrs.end();
for(; it != end; ++it)
{
ColumnPtrVec::iterator itC = it->begin();
ColumnPtrVec::iterator endC = it->end();
for(; itC != endC; ++itC) delete *itC;
}
}
void ODBCStatementImpl::compileImpl()
bool ODBCStatementImpl::compileImpl()
{
if (_compiled) return false;
clear();
_stepCalled = false;
_nextResponse = 0;
@ -111,6 +120,9 @@ void ODBCStatementImpl::compileImpl()
makeInternalExtractors();
doPrepare();
_compiled = true;
return false;
}
@ -302,9 +314,8 @@ bool ODBCStatementImpl::hasNext()
if (!nextRowReady())
{
try { activateNextDataSet(); }
catch (NoDataException&)
{ return false; }
if (hasMoreDataSets()) activateNextDataSet();
else return false;
if (SQL_NO_DATA == SQLMoreResults(_stmt))
return false;
@ -418,9 +429,12 @@ void ODBCStatementImpl::checkError(SQLRETURN rc, const std::string& msg)
void ODBCStatementImpl::fillColumns()
{
Poco::UInt32 colCount = columnsReturned();
Poco::UInt32 curDataSet = currentDataSet();
if (curDataSet >= _columnPtrs.size())
_columnPtrs.resize(curDataSet + 1);
for (int i = 0; i < colCount; ++i)
_columnPtrs.push_back(new ODBCMetaColumn(_stmt, i));
_columnPtrs[curDataSet].push_back(new ODBCMetaColumn(_stmt, i));
}
@ -435,12 +449,15 @@ bool ODBCStatementImpl::isStoredProcedure() const
const MetaColumn& ODBCStatementImpl::metaColumn(Poco::UInt32 pos) const
{
std::size_t sz = _columnPtrs.size();
Poco::UInt32 curDataSet = currentDataSet();
poco_assert_dbg (curDataSet < _columnPtrs.size());
std::size_t sz = _columnPtrs[curDataSet].size();
if (0 == sz || pos > sz - 1)
throw InvalidAccessException(format("Invalid column number: %u", pos));
return *_columnPtrs[pos];
return *_columnPtrs[curDataSet][pos];
}

View File

@ -94,14 +94,14 @@ void Preparation::freeMemory() const
case DT_CHAR_ARRAY:
{
char* pc = AnyCast<char>(&_values[it->first]);
if (pc) std::free(pc);
std::free(pc);
break;
}
case DT_BOOL_ARRAY:
{
bool* pb = AnyCast<bool>(&_values[it->first]);
if (pb) std::free(pb);
std::free(pb);
break;
}

View File

@ -586,13 +586,14 @@ void ODBCOracleTest::testMultipleResults()
std::string sql = "CREATE OR REPLACE "
"PROCEDURE multiResultsProcedure(paramAge1 IN NUMBER,"
" paramAge2 IN NUMBER,"
" paramAge3 IN NUMBER,"
" ret1 OUT SYS_REFCURSOR, "
" ret2 OUT SYS_REFCURSOR,"
" ret3 OUT SYS_REFCURSOR) IS "
"BEGIN "
" OPEN ret1 FOR SELECT * FROM Person WHERE Age = paramAge1;"
" OPEN ret2 FOR SELECT Age FROM Person WHERE FirstName = 'Bart';"
" OPEN ret3 FOR SELECT * FROM Person WHERE Age = paramAge2;"
" OPEN ret3 FOR SELECT * FROM Person WHERE Age = paramAge2 OR Age = paramAge3 ORDER BY Age;"
"END multiResultsProcedure;";
for (int i = 0; i < 8;)

View File

@ -2950,30 +2950,30 @@ void SQLExecutor::multipleResults(const std::string& sql)
typedef Tuple<std::string, std::string, std::string, Poco::UInt32> Person;
std::vector<Person> people;
people.push_back(Person("Simpson", "Homer", "Springfield", 42));
people.push_back(Person("Simpson", "Bart", "Springfield", 12));
people.push_back(Person("Simpson", "Lisa", "Springfield", 10));
people.push_back(Person("Simpson", "Marge", "Springfield", 38));
people.push_back(Person("Simpson", "Bart", "Springfield", 10));
people.push_back(Person("Simpson", "Lisa", "Springfield", 8));
people.push_back(Person("Simpson", "Maggie", "Springfield", 3));
session() << "INSERT INTO Person VALUES (?, ?, ?, ?)", use(people), now;
Person pHomer, pLisa;
int aHomer = 42, aLisa = 10;
Person pHomer;
int aHomer = 42, aLisa = 8;
Poco::UInt32 aBart = 0;
Poco::UInt32 pos1 = 1;
int pos2 = 2;
try {
session() << sql
, into(pHomer, from(0)), use(aHomer)
std::vector<Person> people2;
Statement stmt(session());
stmt << sql, into(pHomer, from(0)), use(aHomer)
, into(aBart, pos1)
, into(pLisa, pos2), use(aLisa)
, now;
} catch (StatementException& ex)
{
std::cout << ex.toString() << std::endl;
}
, into(people2, from(pos2)), use(aLisa), use(aHomer);
assert (4 == stmt.execute());
assert (Person("Simpson", "Homer", "Springfield", 42) == pHomer);
assert (12 == aBart);
assert (Person("Simpson", "Lisa", "Springfield", 10) == pLisa);
assert (10 == aBart);
assert (2 == people2.size());
assert (Person("Simpson", "Lisa", "Springfield", 8) == people2[0]);
assert (Person("Simpson", "Homer", "Springfield", 42) == people2[1]);
}

View File

@ -499,7 +499,7 @@ public:
void multipleResults(const std::string& sql =
"SELECT * FROM Person WHERE Age = ?; "
"SELECT Age FROM Person WHERE FirstName = 'Bart'; "
"SELECT * FROM Person WHERE Age = ?; ");
"SELECT * FROM Person WHERE Age = ? OR Age = ? ORDER BY Age;");
void sqlChannel(const std::string& connect);
void sqlLogger(const std::string& connect);

View File

@ -285,7 +285,7 @@ private:
}
sqlite3_stmt* _pStmt;
NullIndVec _nulls;
NullIndVec _nulls;
};

View File

@ -95,8 +95,15 @@ protected:
bool canBind() const;
/// Returns true if a valid statement is set and we can bind.
void compileImpl();
/// Compiles the statement, doesn't bind yet
bool compileImpl();
/// Compiles the statement, doesn't bind yet.
/// Returns true if the statement was succesfully compiled.
/// The way SQLite handles batches of statmeents is by compiling
/// one at a time and returning a pointer to the next one.
/// The remainder of the statement is remebered in a string
/// buffer pointed to by _pLeftover member. Non-zero _pLeftover
/// pointing to an empty string means no more statements left
/// to compile.
void bindImpl();
/// Binds parameters
@ -116,15 +123,22 @@ private:
typedef Poco::Data::AbstractBindingVec Bindings;
typedef Poco::Data::AbstractExtractionVec Extractions;
typedef std::vector<Poco::Data::MetaColumn> MetaColumnVec;
typedef std::vector<MetaColumnVec> MetaColumnVecVec;
typedef Poco::SharedPtr<std::string> StrPtr;
typedef Bindings::iterator BindIt;
sqlite3* _pDB;
sqlite3_stmt* _pStmt;
bool _stepCalled;
int _nextResponse;
BinderPtr _pBinder;
ExtractorPtr _pExtractor;
MetaColumnVec _columns;
Poco::UInt32 _affectedRowCount;
sqlite3* _pDB;
sqlite3_stmt* _pStmt;
bool _stepCalled;
int _nextResponse;
BinderPtr _pBinder;
ExtractorPtr _pExtractor;
MetaColumnVecVec _columns;
Poco::UInt32 _affectedRowCount;
StrPtr _pLeftover;
BindIt _bindBegin;
bool _canBind;
bool _isExtracted;
};
@ -143,6 +157,12 @@ inline AbstractBinder& SQLiteStatementImpl::binder()
}
inline bool SQLiteStatementImpl::canBind() const
{
return _canBind;
}
} } } // namespace Poco::Data::SQLite

View File

@ -54,8 +54,11 @@ SQLiteStatementImpl::SQLiteStatementImpl(Poco::Data::SessionImpl& rSession, sqli
_pStmt(0),
_stepCalled(false),
_nextResponse(0),
_affectedRowCount(0)
_affectedRowCount(0),
_canBind(false),
_isExtracted(false)
{
_columns.resize(1);
}
@ -65,29 +68,33 @@ SQLiteStatementImpl::~SQLiteStatementImpl()
}
void SQLiteStatementImpl::compileImpl()
bool SQLiteStatementImpl::compileImpl()
{
if (_pStmt) return;
if (_pLeftover && _pLeftover->empty())
{
_pLeftover = 0;
return false;
}
else if (!_pLeftover)
_bindBegin = bindings().begin();
std::string statement(toString());
if (statement.empty())
sqlite3_stmt* pStmt = 0;
const char* pSql = _pLeftover ? _pLeftover->c_str() : statement.c_str();
if (0 == std::strlen(pSql))
throw InvalidSQLStatementException("Empty statements are illegal");
sqlite3_stmt* pStmt = 0;
const char* pSql = statement.c_str(); // The SQL to be executed
int rc = SQLITE_OK;
const char* pLeftover = 0;
bool queryFound = false;
while (rc == SQLITE_OK && !pStmt && !queryFound)
do
{
rc = sqlite3_prepare_v2(_pDB, pSql, -1, &pStmt, &pLeftover);
if (rc != SQLITE_OK)
{
if (pStmt)
{
sqlite3_finalize(pStmt);
}
if (pStmt) sqlite3_finalize(pStmt);
pStmt = 0;
std::string errMsg = sqlite3_errmsg(_pDB);
Utility::throwException(rc, errMsg);
@ -106,32 +113,45 @@ void SQLiteStatementImpl::compileImpl()
queryFound = true;
}
}
}
} while (rc == SQLITE_OK && !pStmt && !queryFound);
//Finalization call in clear() invalidates the pointer, so the value is remembered here.
//For last statement in a batch (or a single statement), pLeftover == "", so the next call
// to compileImpl() shall return false immediately when there are no more statements left.
std::string leftOver(pLeftover);
clear();
_pStmt = pStmt;
_pLeftover = new std::string(leftOver);
trimInPlace(*_pLeftover);
// prepare binding
_pBinder = new Binder(_pStmt);
_pExtractor = new Extractor(_pStmt);
if (SQLITE_DONE == _nextResponse && _isExtracted)
{
//if this is not the first compile and there has already been extraction
//during previous step, switch to the next set if there is one provided
if (hasMoreDataSets())
{
activateNextDataSet();
_isExtracted = false;
}
}
int colCount = sqlite3_column_count(_pStmt);
for (int i = 0; i < colCount; ++i)
if (colCount)
{
MetaColumn mc(i, sqlite3_column_name(_pStmt, i), Utility::getColumnType(_pStmt, i));
_columns.push_back(mc);
Poco::UInt32 curDataSet = currentDataSet();
if (curDataSet >= _columns.size()) _columns.resize(curDataSet + 1);
for (int i = 0; i < colCount; ++i)
{
MetaColumn mc(i, sqlite3_column_name(_pStmt, i), Utility::getColumnType(_pStmt, i));
_columns[curDataSet].push_back(mc);
}
}
}
bool SQLiteStatementImpl::canBind() const
{
bool ret = false;
if (!bindings().empty() && _pStmt)
ret = (*bindings().begin())->canBind();
return ret;
return true;
}
@ -143,32 +163,61 @@ void SQLiteStatementImpl::bindImpl()
sqlite3_reset(_pStmt);
// bind
Bindings& binds = bindings();
int pc = sqlite3_bind_parameter_count(_pStmt);
if (binds.empty() && 0 == pc) return;
else if (binds.empty() && pc > 0)
throw ParameterCountMismatchException();
else if (!binds.empty() && binds.size() * (*binds.begin())->numOfColumnsHandled() != pc)
throw ParameterCountMismatchException();
std::size_t pos = 1; // sqlite starts with 1 not 0!
Bindings::iterator it = binds.begin();
Bindings::iterator itEnd = binds.end();
if (it != itEnd)
_affectedRowCount = (*it)->numOfRowsHandled();
for (; it != itEnd && (*it)->canBind(); ++it)
int paramCount = sqlite3_bind_parameter_count(_pStmt);
BindIt bindEnd = bindings().end();
if (0 == paramCount || bindEnd == _bindBegin)
{
(*it)->bind(pos);
pos += (*it)->numOfColumnsHandled();
_canBind = false;
return;
}
int availableCount = 0;
Bindings::difference_type bindCount = 0;
Bindings::iterator it = _bindBegin;
for (; it != bindEnd; ++it)
{
availableCount += (*it)->numOfColumnsHandled();
if (availableCount <= paramCount) ++bindCount;
else break;
}
Bindings::difference_type remainingBindCount = bindEnd - _bindBegin;
if (bindCount < remainingBindCount)
{
bindEnd = _bindBegin + bindCount;
_canBind = true;
}
else if (bindCount > remainingBindCount)
throw ParameterCountMismatchException();
if (_bindBegin != bindings().end())
{
_affectedRowCount = (*_bindBegin)->numOfRowsHandled();
Bindings::iterator oldBegin = _bindBegin;
for (std::size_t pos = 1; _bindBegin != bindEnd && (*_bindBegin)->canBind(); ++_bindBegin)
{
if (_affectedRowCount != (*_bindBegin)->numOfRowsHandled())
throw BindingException("Size mismatch in Bindings. All Bindings MUST have the same size");
(*_bindBegin)->bind(pos);
pos += (*_bindBegin)->numOfColumnsHandled();
}
if ((*oldBegin)->canBind())
{
//container binding will come back for more, so we must rewind
_bindBegin = oldBegin;
_canBind = true;
}
else _canBind = false;
}
}
void SQLiteStatementImpl::clear()
{
_columns.clear();
_columns[currentDataSet()].clear();
_affectedRowCount = 0;
if (_pStmt)
@ -176,6 +225,8 @@ void SQLiteStatementImpl::clear()
sqlite3_finalize(_pStmt);
_pStmt=0;
}
_pLeftover = 0;
_canBind = false;
}
@ -194,12 +245,11 @@ bool SQLiteStatementImpl::hasNext()
_stepCalled = true;
_nextResponse = sqlite3_step(_pStmt);
_pExtractor->reset();//to clear the cached null indicators
if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE)
{
Utility::throwException(_nextResponse);
}
_pExtractor->reset();//clear the cached null indicators
return (_nextResponse == SQLITE_ROW);
}
@ -212,13 +262,14 @@ Poco::UInt32 SQLiteStatementImpl::next()
poco_assert (columnsReturned() == sqlite3_column_count(_pStmt));
Extractions& extracts = extractions();
Extractions::iterator it = extracts.begin();
Extractions::iterator it = extracts.begin();
Extractions::iterator itEnd = extracts.end();
std::size_t pos = 0; // sqlite starts with pos 0 for results!
for (; it != itEnd; ++it)
{
(*it)->extract(pos);
pos += (*it)->numOfColumnsHandled();
_isExtracted = true;
}
_stepCalled = false;
}
@ -238,14 +289,15 @@ Poco::UInt32 SQLiteStatementImpl::next()
Poco::UInt32 SQLiteStatementImpl::columnsReturned() const
{
return (Poco::UInt32) _columns.size();
return (Poco::UInt32) _columns[currentDataSet()].size();
}
const MetaColumn& SQLiteStatementImpl::metaColumn(Poco::UInt32 pos) const
{
poco_assert (pos >= 0 && pos <= _columns.size());
return _columns[pos];
Poco::UInt32 curDataSet = currentDataSet();
poco_assert (pos >= 0 && pos <= _columns[curDataSet].size());
return _columns[curDataSet][pos];
}

View File

@ -489,11 +489,12 @@ void SQLiteTest::testInsertSingleBulk()
int x = 0;
Statement stmt((tmp << "INSERT INTO Strings VALUES(:str)", use(x)));
for (x = 0; x < 100; ++x)
for (int i = 0; x < 100; ++x)
{
int i = stmt.execute();
i = stmt.execute();
assert (1 == i);
}
int count = 0;
tmp << "SELECT COUNT(*) FROM Strings", into(count), now;
assert (count == 100);
@ -2140,16 +2141,61 @@ void SQLiteTest::testBindingCount()
int i = 42;
try { tmp << "INSERT INTO Ints VALUES (?)", now; }
catch (ParameterCountMismatchException&) { }
try { tmp << "INSERT INTO Ints VALUES (?)", bind(42), bind(42), now; }
catch (ParameterCountMismatchException&) { }
tmp << "INSERT INTO Ints VALUES (?)", use(i), now;
i = 0;
try { tmp << "SELECT int0 from Ints where int0 = ?", into(i), now; }
catch (ParameterCountMismatchException&) { }
tmp << "SELECT int0 from Ints where int0 = ?", bind(42), into(i), now;
assert (42 == i);
tmp << "DROP TABLE IF EXISTS Ints", now;
tmp << "CREATE TABLE Ints (int0 INTEGER, int1 INTEGER, int2 INTEGER)", now;
try { tmp << "INSERT INTO Ints VALUES (?,?,?)", bind(42), bind(42), now; }
catch (ParameterCountMismatchException&) { }
}
void SQLiteTest::testMultipleResults()
{
Session tmp (SQLite::Connector::KEY, "dummy.db");
tmp << "DROP TABLE IF EXISTS Person", now;
tmp << "CREATE TABLE Person (LastName VARCHAR(30),"
"FirstName VARCHAR(30),"
"Address VARCHAR(30),"
"Age INTEGER)", now;
typedef Tuple<std::string, std::string, std::string, Poco::UInt32> Person;
std::vector<Person> people, people2;
people.push_back(Person("Simpson", "Homer", "Springfield", 42));
people.push_back(Person("Simpson", "Bart", "Springfield", 12));
people.push_back(Person("Simpson", "Lisa", "Springfield", 10));
Person pHomer;
int aHomer = 42, aLisa = 10;
Poco::UInt32 aBart = 0;
Poco::UInt32 pos1 = 1;
int pos2 = 2;
Statement stmt(tmp);
stmt << "INSERT INTO Person VALUES (?, ?, ?, ?);"
"SELECT * FROM Person WHERE Age = ?; "
"SELECT Age FROM Person WHERE FirstName = 'Bart'; "
"SELECT * FROM Person WHERE Age = ? OR Age = ? ORDER BY Age;"
, use(people)
, into(pHomer, from(0)), use(aHomer)
, into(aBart, pos1)
, into(people2, from(pos2)), use(aLisa), use(aHomer);
assert (7 == stmt.execute());
assert (Person("Simpson", "Homer", "Springfield", 42) == pHomer);
assert (12 == aBart);
assert (2 == people2.size());
assert (Person("Simpson", "Lisa", "Springfield", 10) == people2[0]);
assert (Person("Simpson", "Homer", "Springfield", 42) == people2[1]);
}
@ -2236,6 +2282,7 @@ CppUnit::Test* SQLiteTest::suite()
CppUnit_addTest(pSuite, SQLiteTest, testSQLLogger);
CppUnit_addTest(pSuite, SQLiteTest, testExternalBindingAndExtraction);
CppUnit_addTest(pSuite, SQLiteTest, testBindingCount);
CppUnit_addTest(pSuite, SQLiteTest, testMultipleResults);
return pSuite;
}

View File

@ -124,6 +124,7 @@ public:
void testExternalBindingAndExtraction();
void testBindingCount();
void testMultipleResults();
void setUp();
void tearDown();

View File

@ -101,8 +101,13 @@ public:
/// Extracts a value from the param, starting at the given column position.
/// Returns the number of rows extracted.
virtual void reset() = 0;
virtual void reset();
/// Resets the extractor so that it can be re-used.
/// Does nothing in this implementation.
/// Implementations should override it for different behavior.
virtual bool canExtract() const;
/// Returns true. Implementations should override it for different behavior.
virtual AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) = 0;
/// Creates a Prepare object for the extracting object
@ -186,6 +191,17 @@ inline bool AbstractExtraction::isBulk() const
}
inline void AbstractExtraction::reset()
{
}
inline bool AbstractExtraction::canExtract() const
{
return true;
}
} } // namespace Poco::Data

View File

@ -61,8 +61,8 @@ class BulkExtraction: public AbstractExtraction
public:
typedef typename C::value_type T;
BulkExtraction(C& result, Poco::UInt32 limit):
AbstractExtraction(limit, 0, true),
BulkExtraction(C& result, Poco::UInt32 limit, const Position& pos = Position(0)):
AbstractExtraction(limit, pos.value(), true),
_rResult(result),
_default()
{
@ -70,8 +70,8 @@ public:
result.resize(limit);
}
BulkExtraction(C& result, const T& def, Poco::UInt32 limit):
AbstractExtraction(limit, 0, true),
BulkExtraction(C& result, const T& def, Poco::UInt32 limit, const Position& pos = Position(0)):
AbstractExtraction(limit, pos.value(), true),
_rResult(result),
_default(def)
{
@ -161,8 +161,11 @@ class InternalBulkExtraction: public BulkExtraction<C>
public:
typedef typename C::value_type T;
explicit InternalBulkExtraction(C& result, Column<C>* pColumn, Poco::UInt32 limit):
BulkExtraction<C>(result, T(), limit),
explicit InternalBulkExtraction(C& result,
Column<C>* pColumn,
Poco::UInt32 limit,
const Position& pos = Position(0)):
BulkExtraction<C>(result, T(), limit, pos),
_pColumn(pColumn)
/// Creates InternalBulkExtraction.
{
@ -211,62 +214,62 @@ private:
template <typename T>
BulkExtraction<std::vector<T> >* into(std::vector<T>& t, const Bulk& bulk)
BulkExtraction<std::vector<T> >* into(std::vector<T>& t, const Bulk& bulk, const Position& pos = Position(0))
/// Convenience function to allow for a more compact creation of an extraction object
/// with std::vector bulk extraction support.
{
return new BulkExtraction<std::vector<T> >(t, bulk.size());
return new BulkExtraction<std::vector<T> >(t, bulk.size(), pos);
}
template <typename T>
BulkExtraction<std::vector<T> >* into(std::vector<T>& t, BulkFnType)
BulkExtraction<std::vector<T> >* into(std::vector<T>& t, BulkFnType, const Position& pos = Position(0))
/// Convenience function to allow for a more compact creation of an extraction object
/// with std::vector bulk extraction support.
{
Poco::UInt32 size = static_cast<Poco::UInt32>(t.size());
if (0 == size) throw InvalidArgumentException("Zero length not allowed.");
return new BulkExtraction<std::vector<T> >(t, size);
return new BulkExtraction<std::vector<T> >(t, size, pos);
}
template <typename T>
BulkExtraction<std::deque<T> >* into(std::deque<T>& t, const Bulk& bulk)
BulkExtraction<std::deque<T> >* into(std::deque<T>& t, const Bulk& bulk, const Position& pos = Position(0))
/// Convenience function to allow for a more compact creation of an extraction object
/// with std::deque bulk extraction support.
{
return new BulkExtraction<std::deque<T> >(t, bulk.size());
return new BulkExtraction<std::deque<T> >(t, bulk.size(), pos);
}
template <typename T>
BulkExtraction<std::deque<T> >* into(std::deque<T>& t, BulkFnType)
BulkExtraction<std::deque<T> >* into(std::deque<T>& t, BulkFnType, const Position& pos = Position(0))
/// Convenience function to allow for a more compact creation of an extraction object
/// with std::deque bulk extraction support.
{
Poco::UInt32 size = static_cast<Poco::UInt32>(t.size());
if (0 == size) throw InvalidArgumentException("Zero length not allowed.");
return new BulkExtraction<std::deque<T> >(t, size);
return new BulkExtraction<std::deque<T> >(t, size, pos);
}
template <typename T>
BulkExtraction<std::list<T> >* into(std::list<T>& t, const Bulk& bulk)
BulkExtraction<std::list<T> >* into(std::list<T>& t, const Bulk& bulk, const Position& pos = Position(0))
/// Convenience function to allow for a more compact creation of an extraction object
/// with std::list bulk extraction support.
{
return new BulkExtraction<std::list<T> >(t, bulk.size());
return new BulkExtraction<std::list<T> >(t, bulk.size(), pos);
}
template <typename T>
BulkExtraction<std::list<T> >* into(std::list<T>& t, BulkFnType)
BulkExtraction<std::list<T> >* into(std::list<T>& t, BulkFnType, const Position& pos = Position(0))
/// Convenience function to allow for a more compact creation of an extraction object
/// with std::list bulk extraction support.
{
Poco::UInt32 size = static_cast<Poco::UInt32>(t.size());
if (0 == size) throw InvalidArgumentException("Zero length not allowed.");
return new BulkExtraction<std::list<T> >(t, size);
return new BulkExtraction<std::list<T> >(t, size, pos);
}

View File

@ -43,6 +43,7 @@
#include "Poco/Data/Data.h"
#include "Poco/Data/MetaColumn.h"
#include "Poco/SharedPtr.h"
#include "Poco/RefCountedObject.h"
#include <vector>
#include <list>
#include <deque>

View File

@ -124,6 +124,11 @@ public:
_extracted = false;
}
bool canExtract() const
{
return !_extracted;
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<T>(pPrep, pos, _rResult);
@ -198,10 +203,6 @@ public:
return 1u;
}
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<T>(pPrep, pos, _default);
@ -283,10 +284,6 @@ public:
return 1u;
}
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<bool>(pPrep, pos, _default);
@ -366,10 +363,6 @@ public:
return 1u;
}
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<T>(pPrep, pos, _default);
@ -449,10 +442,6 @@ public:
return 1u;
}
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<T>(pPrep, pos, _default);
@ -586,10 +575,6 @@ public:
return 1u;
}
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<T>(pPrep, pos, _default);
@ -649,10 +634,6 @@ public:
return 1u;
}
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<T>(pPrep, pos, _default);
@ -712,16 +693,11 @@ public:
return 1u;
}
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<V>(pPrep, pos, _default);
}
private:
std::map<K, V>& _rResult;
V _default;
@ -776,10 +752,6 @@ public:
return 1u;
}
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{
return new Prepare<V>(pPrep, pos, _default);

View File

@ -51,7 +51,7 @@ class Data_API Limit
/// Limit stores information how many rows a query should return.
{
public:
enum
enum Type
{
LIMIT_UNLIMITED = 0xffffffffu
};

View File

@ -75,6 +75,14 @@ class Data_API RecordSet: private Statement
/// select.execute();
/// RecordSet rs(select);
///
/// The shorter way to do the above is following:
///
/// RecordSet rs(session, "SELECT * FROM Person"[, new SimpleRowFormatter]);
///
/// The third (optional) argument passed to the Recordset constructor is a RowFormatter
/// implementation. The formatter is used in conjunction with << operator for recordset
/// data formating.
///
/// The number of rows in the RecordSet can be limited by specifying
/// a limit for the Statement.
{

View File

@ -285,7 +285,6 @@ public:
Statement& operator , (unsigned long value);
/// Adds the value to the list of values to be supplied to the SQL string formatting function.
#endif
Statement& operator , (Poco::UInt64 value);
/// Adds the value to the list of values to be supplied to the SQL string formatting function.
@ -372,7 +371,20 @@ public:
std::size_t extractionCount() const;
/// Returns the number of extraction storage buffers associated
/// with the statement.
/// with the current data set.
std::size_t dataSetCount() const;
/// Returns the number of data sets associated with the statement.
std::size_t nextDataSet();
/// Returns the index of the next data set.
std::size_t previousDataSet();
/// Returns the index of the previous data set.
bool hasMoreDataSets() const;
/// Returns false if the current data set index points to the last
/// data set. Otherwise, it returns true.
void setRowFormatter(RowFormatter* pRowFormatter);
/// Sets the row formatter for this statement.
@ -683,6 +695,30 @@ inline std::size_t Statement::extractionCount() const
}
inline std::size_t Statement::dataSetCount() const
{
return _pImpl->dataSetCount();
}
inline std::size_t Statement::nextDataSet()
{
return _pImpl->activateNextDataSet();
}
inline std::size_t Statement::previousDataSet()
{
return _pImpl->activatePreviousDataSet();
}
inline bool Statement::hasMoreDataSets() const
{
return _pImpl->hasMoreDataSets();
}
inline Statement::Storage Statement::storage() const
{
return static_cast<Storage>(_pImpl->getStorage());

View File

@ -136,14 +136,16 @@ public:
/// Registers objects used for extracting data with the StatementImpl.
void setExtractionLimit(const Limit& extrLimit);
/// Changes the extractionLimit to extrLimit. Per default no limit (EXTRACT_UNLIMITED) is set.
/// Changes the extractionLimit to extrLimit.
/// Per default no limit (EXTRACT_UNLIMITED) is set.
std::string toString() const;
/// Create a string version of the SQL statement.
Poco::UInt32 execute();
/// Executes a statement. Returns the number of rows extracted for statements
/// returning data or number of rows affected for all other statements (insert, update, delete).
/// Executes a statement. Returns the number of rows
/// extracted for statements returning data or number of rows
/// affected for all other statements (insert, update, delete).
void reset();
/// Resets the statement, so that we can reuse all bindings and re-execute again.
@ -198,8 +200,16 @@ protected:
virtual bool canBind() const = 0;
/// Returns if another bind is possible.
virtual void compileImpl() = 0;
virtual bool compileImpl() = 0;
/// Compiles the statement, doesn't bind yet.
/// Returns true if compilation was succesful.
/// This function will be called at least twice, so
/// for connectors requiring one call only, this function
/// should always return false and internall protect itself
/// against multiple calls.
/// This design is to conform to the connectors (e.g. SQLite)
/// that handle batches of statements as a sequence of independent
/// statements, each with its own prepare/bind/execute operations.
virtual void bindImpl() = 0;
/// Binds parameters.
@ -240,11 +250,11 @@ protected:
/// session is queried for container type setting. If the
/// session container type setting is found, it is used.
/// 3. If neither session nor statement have the internal
/// container type set, std::vector is used.
/// container type set, std::deque is used.
///
/// Supported internal extraction container types are:
/// - std::vector (default)
/// - std::deque
/// - std::deque (default)
/// - std::vector
/// - std::list
SessionImpl& session();
@ -277,7 +287,15 @@ protected:
/// Returns the current data set.
Poco::UInt32 activateNextDataSet();
/// Returns the next data set, or -1 if the last data set was reached.
/// Returns the next data set index, or throws NoDataException if the last
/// data set was reached.
Poco::UInt32 activatePreviousDataSet();
/// Returns the previous data set index, or throws NoDataException if the last
/// data set was reached.
bool hasMoreDataSets() const;
/// Returns true if there are data sets not activated yet.
Poco::UInt32 getExtractionLimit();
/// Returns the extraction limit value.
@ -286,17 +304,22 @@ protected:
/// Returns the extraction limit.
private:
void compile();
bool compile();
/// Compiles the statement, if not yet compiled.
/// Returns true if more compile calls are needed.
void bind();
/// Binds the statement, if not yet bound.
Poco::UInt32 executeWithLimit();
/// Executes with an upper limit set.
/// Executes with an upper limit set. Returns the number of rows
/// extracted for statements returning data or number of rows
/// affected for all other statements (insert, update, delete).
Poco::UInt32 executeWithoutLimit();
/// Executes without an upper limit set.
/// Executes without an upper limit set. Returns the number of rows
/// extracted for statements returning data or number of rows
/// affected for all other statements (insert, update, delete).
void resetExtraction();
/// Resets extraction so it can be reused again.
@ -306,7 +329,7 @@ private:
{
C* pData = new C;
Column<C>* pCol = new Column<C>(mc, pData);
return new InternalExtraction<C>(*pData, pCol);
return new InternalExtraction<C>(*pData, pCol, currentDataSet());
}
template <class C>
@ -314,7 +337,7 @@ private:
{
C* pData = new C;
Column<C>* pCol = new Column<C>(mc, pData);
return new InternalBulkExtraction<C>(*pData, pCol, getExtractionLimit());
return new InternalBulkExtraction<C>(*pData, pCol, getExtractionLimit(), currentDataSet());
}
template <class T>
@ -413,7 +436,7 @@ private:
State _state;
Limit _extrLimit;
Poco::UInt32 _lowerLimit;
int _columnsExtracted;
std::vector<int> _columnsExtracted;
SessionImpl& _rSession;
Storage _storage;
std::ostringstream _ostr;
@ -471,7 +494,8 @@ inline AbstractExtractionVec& StatementImpl::extractions()
inline int StatementImpl::columnsExtracted() const
{
return _columnsExtracted;
poco_assert (_curDataSet < _columnsExtracted.size());
return _columnsExtracted[_curDataSet];
}
@ -600,6 +624,12 @@ inline bool StatementImpl::isBulkSupported() const
}
inline bool StatementImpl::hasMoreDataSets() const
{
return currentDataSet() + 1 < dataSetCount();
}
} } // namespace Poco::Data

View File

@ -131,7 +131,6 @@ void SQLChannel::logAsync(const Message& msg)
void SQLChannel::logSync(const Message& msg)
{
//if (isArchiving()) archive();
if (_pArchiveStrategy) _pArchiveStrategy->archive();
_source = msg.getSource();
@ -289,7 +288,8 @@ void SQLChannel::initLogStatement()
void SQLChannel::registerChannel()
{
Poco::LoggingFactory::defaultFactory().registerChannelClass("SQLChannel", new Poco::Instantiator<SQLChannel, Poco::Channel>);
Poco::LoggingFactory::defaultFactory().registerChannelClass("SQLChannel",
new Poco::Instantiator<SQLChannel, Poco::Channel>);
}

View File

@ -63,16 +63,15 @@ StatementImpl::StatementImpl(SessionImpl& rSession):
_state(ST_INITIALIZED),
_extrLimit(upperLimit((Poco::UInt32) Limit::LIMIT_UNLIMITED, false)),
_lowerLimit(0),
_columnsExtracted(0),
_rSession(rSession),
_storage(STORAGE_UNKNOWN_IMPL),
_ostr(),
_bindings(),
_curDataSet(0),
_bulkBinding(BULK_UNDEFINED),
_bulkExtraction(BULK_UNDEFINED)
{
_extractors.resize(1);
_columnsExtracted.resize(1, 0);
}
@ -88,16 +87,21 @@ Poco::UInt32 StatementImpl::execute()
if (_lowerLimit > _extrLimit.value())
throw LimitException("Illegal Statement state. Upper limit must not be smaller than the lower limit.");
compile();
do
{
if (_extrLimit.value() == Limit::LIMIT_UNLIMITED)
lim += executeWithoutLimit();
else
lim += executeWithLimit();
} while (compile());
if (_extrLimit.value() == Limit::LIMIT_UNLIMITED)
lim = executeWithoutLimit();
else
lim = executeWithLimit();
_state = ST_DONE;
if (lim < _lowerLimit)
throw LimitException("Did not receive enough data.");
if (0 == lim) lim = affectedRowCount();
return lim;
}
@ -105,48 +109,51 @@ Poco::UInt32 StatementImpl::execute()
Poco::UInt32 StatementImpl::executeWithLimit()
{
poco_assert (_state != ST_DONE);
compile();
Poco::UInt32 count = 0;
Poco::UInt32 limit = _extrLimit.value();
do
{
bind();
while (count < limit && hasNext())
count += next();
} while (count < limit && canBind());
if (!canBind() && (!hasNext() || limit == 0))
if (!canBind() && (!hasNext() || limit == 0))
_state = ST_DONE;
else if (hasNext() && limit == count && _extrLimit.isHardLimit())
throw LimitException("HardLimit reached. We got more data than we asked for");
else
throw LimitException("HardLimit reached (retrieved more data than requested).");
else
_state = ST_PAUSED;
return count;
return count ? count : affectedRowCount();
}
Poco::UInt32 StatementImpl::executeWithoutLimit()
{
poco_assert (_state != ST_DONE);
compile();
Poco::UInt32 count = 0;
do
{
bind();
while (hasNext()) count += next();
} while (canBind());
_state = ST_DONE;
return count;
return count ? count : affectedRowCount();
}
void StatementImpl::compile()
bool StatementImpl::compile()
{
if (_state == ST_INITIALIZED)
bool retval = false;
if (_state == ST_INITIALIZED ||
_state == ST_RESET ||
_state == ST_BOUND)
{
compileImpl();
retval = compileImpl();
_state = ST_COMPILED;
if (!extractions().size() && !isStoredProcedure())
@ -158,12 +165,8 @@ void StatementImpl::compile()
fixupExtraction();
fixupBinding();
}
else if (_state == ST_RESET)
{
resetBinding();
resetExtraction();
_state = ST_COMPILED;
}
return retval;
}
@ -187,8 +190,9 @@ void StatementImpl::bind()
void StatementImpl::reset()
{
resetBinding();
resetExtraction();
_state = ST_RESET;
compile();
}
@ -217,12 +221,15 @@ void StatementImpl::fixupExtraction()
Poco::Data::AbstractExtractionVec::iterator it = extractions().begin();
Poco::Data::AbstractExtractionVec::iterator itEnd = extractions().end();
AbstractExtractor& ex = extractor();
_columnsExtracted = 0;
if (_curDataSet >= _columnsExtracted.size())
_columnsExtracted.resize(_curDataSet + 1, 0);
for (; it != itEnd; ++it)
{
(*it)->setExtractor(&ex);
(*it)->setLimit(_extrLimit.value()),
_columnsExtracted += (int)(*it)->numOfColumnsHandled();
_columnsExtracted[_curDataSet] += (int)(*it)->numOfColumnsHandled();
}
}
@ -234,16 +241,8 @@ void StatementImpl::fixupBinding()
AbstractBindingVec::iterator itEnd = bindings().end();
AbstractBinder& bin = binder();
std::size_t numRows = 0;
if (it != itEnd)
numRows = (*it)->numOfRowsHandled();
for (; it != itEnd; ++it)
{
if (numRows != (*it)->numOfRowsHandled())
{
throw BindingException("Size mismatch in Bindings. All Bindings MUST have the same size");
}
(*it)->setBinder(&bin);
}
if (it != itEnd) numRows = (*it)->numOfRowsHandled();
for (; it != itEnd; ++it) (*it)->setBinder(&bin);
}
@ -251,10 +250,7 @@ void StatementImpl::resetBinding()
{
AbstractBindingVec::iterator it = bindings().begin();
AbstractBindingVec::iterator itEnd = bindings().end();
for (; it != itEnd; ++it)
{
(*it)->reset();
}
for (; it != itEnd; ++it) (*it)->reset();
}
@ -262,10 +258,10 @@ void StatementImpl::resetExtraction()
{
Poco::Data::AbstractExtractionVec::iterator it = extractions().begin();
Poco::Data::AbstractExtractionVec::iterator itEnd = extractions().end();
for (; it != itEnd; ++it)
{
(*it)->reset();
}
for (; it != itEnd; ++it) (*it)->reset();
poco_assert (_curDataSet < _columnsExtracted.size());
_columnsExtracted[_curDataSet] = 0;
}
@ -352,6 +348,15 @@ Poco::UInt32 StatementImpl::activateNextDataSet()
}
Poco::UInt32 StatementImpl::activatePreviousDataSet()
{
if (_curDataSet > 0)
return --_curDataSet;
else
throw NoDataException("Beginning of data sets reached.");
}
void StatementImpl::addExtract(AbstractExtraction* pExtraction)
{
poco_check_ptr (pExtraction);

View File

@ -50,12 +50,13 @@ TestStatementImpl::~TestStatementImpl()
}
void TestStatementImpl::compileImpl()
bool TestStatementImpl::compileImpl()
{
// prepare binding
_ptrBinder = new Binder;
_ptrExtractor = new Extractor;
_ptrPrepare = new Preparation;
return false;
}

View File

@ -83,7 +83,7 @@ protected:
bool canBind() const;
/// Returns true if a valid statement is set and we can bind.
void compileImpl();
bool compileImpl();
/// Compiles the statement, doesn't bind yet
void bindImpl();