mirror of
https://github.com/pocoproject/poco.git
synced 2025-01-18 00:15:27 +01:00
SQLite multiple statement handling
This commit is contained in:
parent
1f47c0df2f
commit
5e1605823b
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;)
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -285,7 +285,7 @@ private:
|
||||
}
|
||||
|
||||
sqlite3_stmt* _pStmt;
|
||||
NullIndVec _nulls;
|
||||
NullIndVec _nulls;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ public:
|
||||
|
||||
void testExternalBindingAndExtraction();
|
||||
void testBindingCount();
|
||||
void testMultipleResults();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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.
|
||||
{
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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>);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user