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 /// Implements statement functionality needed for ODBC
{ {
public: public:
typedef std::vector<ODBCMetaColumn*> ColumnPtrVec;
ODBCStatementImpl(SessionImpl& rSession); ODBCStatementImpl(SessionImpl& rSession);
/// Creates the ODBCStatementImpl. /// Creates the ODBCStatementImpl.
@@ -96,8 +94,12 @@ protected:
bool canBind() const; bool canBind() const;
/// Returns true if a valid statement is set and we can bind. /// Returns true if a valid statement is set and we can bind.
void compileImpl(); bool compileImpl();
/// Compiles the statement, doesn't bind yet /// 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(); void bindImpl();
/// Binds all parameters and executes the statement. /// Binds all parameters and executes the statement.
@@ -119,6 +121,8 @@ private:
typedef std::vector<PreparationPtr> PreparationVec; typedef std::vector<PreparationPtr> PreparationVec;
typedef Poco::SharedPtr<Extractor> ExtractorPtr; typedef Poco::SharedPtr<Extractor> ExtractorPtr;
typedef std::vector<ExtractorPtr> ExtractorVec; typedef std::vector<ExtractorPtr> ExtractorVec;
typedef std::vector<ODBCMetaColumn*> ColumnPtrVec;
typedef std::vector<ColumnPtrVec> ColumnPtrVecVec;
static const std::string INVALID_CURSOR_STATE; static const std::string INVALID_CURSOR_STATE;
@@ -167,9 +171,10 @@ private:
ExtractorVec _extractors; ExtractorVec _extractors;
bool _stepCalled; bool _stepCalled;
int _nextResponse; int _nextResponse;
ColumnPtrVec _columnPtrs; ColumnPtrVecVec _columnPtrs;
bool _prepared; bool _prepared;
mutable Poco::UInt32 _affectedRowCount; mutable Poco::UInt32 _affectedRowCount;
bool _compiled;
}; };

View File

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

View File

@@ -65,21 +65,30 @@ ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession):
_stepCalled(false), _stepCalled(false),
_nextResponse(0), _nextResponse(0),
_prepared(false), _prepared(false),
_affectedRowCount(0) _affectedRowCount(0),
_compiled(false)
{ {
} }
ODBCStatementImpl::~ODBCStatementImpl() ODBCStatementImpl::~ODBCStatementImpl()
{ {
ColumnPtrVec::iterator it = _columnPtrs.begin(); ColumnPtrVecVec::iterator it = _columnPtrs.begin();
ColumnPtrVec::iterator itEnd = _columnPtrs.end(); ColumnPtrVecVec::iterator end = _columnPtrs.end();
for(; it != itEnd; ++it) delete *it; 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; _stepCalled = false;
_nextResponse = 0; _nextResponse = 0;
@@ -111,6 +120,9 @@ void ODBCStatementImpl::compileImpl()
makeInternalExtractors(); makeInternalExtractors();
doPrepare(); doPrepare();
_compiled = true;
return false;
} }
@@ -302,9 +314,8 @@ bool ODBCStatementImpl::hasNext()
if (!nextRowReady()) if (!nextRowReady())
{ {
try { activateNextDataSet(); } if (hasMoreDataSets()) activateNextDataSet();
catch (NoDataException&) else return false;
{ return false; }
if (SQL_NO_DATA == SQLMoreResults(_stmt)) if (SQL_NO_DATA == SQLMoreResults(_stmt))
return false; return false;
@@ -418,9 +429,12 @@ void ODBCStatementImpl::checkError(SQLRETURN rc, const std::string& msg)
void ODBCStatementImpl::fillColumns() void ODBCStatementImpl::fillColumns()
{ {
Poco::UInt32 colCount = columnsReturned(); Poco::UInt32 colCount = columnsReturned();
Poco::UInt32 curDataSet = currentDataSet();
if (curDataSet >= _columnPtrs.size())
_columnPtrs.resize(curDataSet + 1);
for (int i = 0; i < colCount; ++i) 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 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) if (0 == sz || pos > sz - 1)
throw InvalidAccessException(format("Invalid column number: %u", pos)); 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: case DT_CHAR_ARRAY:
{ {
char* pc = AnyCast<char>(&_values[it->first]); char* pc = AnyCast<char>(&_values[it->first]);
if (pc) std::free(pc); std::free(pc);
break; break;
} }
case DT_BOOL_ARRAY: case DT_BOOL_ARRAY:
{ {
bool* pb = AnyCast<bool>(&_values[it->first]); bool* pb = AnyCast<bool>(&_values[it->first]);
if (pb) std::free(pb); std::free(pb);
break; break;
} }

View File

@@ -586,13 +586,14 @@ void ODBCOracleTest::testMultipleResults()
std::string sql = "CREATE OR REPLACE " std::string sql = "CREATE OR REPLACE "
"PROCEDURE multiResultsProcedure(paramAge1 IN NUMBER," "PROCEDURE multiResultsProcedure(paramAge1 IN NUMBER,"
" paramAge2 IN NUMBER," " paramAge2 IN NUMBER,"
" paramAge3 IN NUMBER,"
" ret1 OUT SYS_REFCURSOR, " " ret1 OUT SYS_REFCURSOR, "
" ret2 OUT SYS_REFCURSOR," " ret2 OUT SYS_REFCURSOR,"
" ret3 OUT SYS_REFCURSOR) IS " " ret3 OUT SYS_REFCURSOR) IS "
"BEGIN " "BEGIN "
" OPEN ret1 FOR SELECT * FROM Person WHERE Age = paramAge1;" " OPEN ret1 FOR SELECT * FROM Person WHERE Age = paramAge1;"
" OPEN ret2 FOR SELECT Age FROM Person WHERE FirstName = 'Bart';" " 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;"; "END multiResultsProcedure;";
for (int i = 0; i < 8;) 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; typedef Tuple<std::string, std::string, std::string, Poco::UInt32> Person;
std::vector<Person> people; std::vector<Person> people;
people.push_back(Person("Simpson", "Homer", "Springfield", 42)); people.push_back(Person("Simpson", "Homer", "Springfield", 42));
people.push_back(Person("Simpson", "Bart", "Springfield", 12)); people.push_back(Person("Simpson", "Marge", "Springfield", 38));
people.push_back(Person("Simpson", "Lisa", "Springfield", 10)); 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; session() << "INSERT INTO Person VALUES (?, ?, ?, ?)", use(people), now;
Person pHomer, pLisa; Person pHomer;
int aHomer = 42, aLisa = 10; int aHomer = 42, aLisa = 8;
Poco::UInt32 aBart = 0; Poco::UInt32 aBart = 0;
Poco::UInt32 pos1 = 1; Poco::UInt32 pos1 = 1;
int pos2 = 2; int pos2 = 2;
try { std::vector<Person> people2;
session() << sql Statement stmt(session());
, into(pHomer, from(0)), use(aHomer) stmt << sql, into(pHomer, from(0)), use(aHomer)
, into(aBart, pos1) , into(aBart, pos1)
, into(pLisa, pos2), use(aLisa) , into(people2, from(pos2)), use(aLisa), use(aHomer);
, now;
} catch (StatementException& ex)
{
std::cout << ex.toString() << std::endl;
}
assert (4 == stmt.execute());
assert (Person("Simpson", "Homer", "Springfield", 42) == pHomer); assert (Person("Simpson", "Homer", "Springfield", 42) == pHomer);
assert (12 == aBart); assert (10 == aBart);
assert (Person("Simpson", "Lisa", "Springfield", 10) == pLisa); 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 = void multipleResults(const std::string& sql =
"SELECT * FROM Person WHERE Age = ?; " "SELECT * FROM Person WHERE Age = ?; "
"SELECT Age FROM Person WHERE FirstName = 'Bart'; " "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 sqlChannel(const std::string& connect);
void sqlLogger(const std::string& connect); void sqlLogger(const std::string& connect);

View File

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

View File

@@ -95,8 +95,15 @@ protected:
bool canBind() const; bool canBind() const;
/// Returns true if a valid statement is set and we can bind. /// Returns true if a valid statement is set and we can bind.
void compileImpl(); bool compileImpl();
/// Compiles the statement, doesn't bind yet /// 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(); void bindImpl();
/// Binds parameters /// Binds parameters
@@ -116,15 +123,22 @@ private:
typedef Poco::Data::AbstractBindingVec Bindings; typedef Poco::Data::AbstractBindingVec Bindings;
typedef Poco::Data::AbstractExtractionVec Extractions; typedef Poco::Data::AbstractExtractionVec Extractions;
typedef std::vector<Poco::Data::MetaColumn> MetaColumnVec; 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* _pDB;
sqlite3_stmt* _pStmt; sqlite3_stmt* _pStmt;
bool _stepCalled; bool _stepCalled;
int _nextResponse; int _nextResponse;
BinderPtr _pBinder; BinderPtr _pBinder;
ExtractorPtr _pExtractor; ExtractorPtr _pExtractor;
MetaColumnVec _columns; MetaColumnVecVec _columns;
Poco::UInt32 _affectedRowCount; 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 } } } // namespace Poco::Data::SQLite

View File

@@ -54,8 +54,11 @@ SQLiteStatementImpl::SQLiteStatementImpl(Poco::Data::SessionImpl& rSession, sqli
_pStmt(0), _pStmt(0),
_stepCalled(false), _stepCalled(false),
_nextResponse(0), _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()); 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"); 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; int rc = SQLITE_OK;
const char* pLeftover = 0; const char* pLeftover = 0;
bool queryFound = false; bool queryFound = false;
while (rc == SQLITE_OK && !pStmt && !queryFound) do
{ {
rc = sqlite3_prepare_v2(_pDB, pSql, -1, &pStmt, &pLeftover); rc = sqlite3_prepare_v2(_pDB, pSql, -1, &pStmt, &pLeftover);
if (rc != SQLITE_OK) if (rc != SQLITE_OK)
{ {
if (pStmt) if (pStmt) sqlite3_finalize(pStmt);
{
sqlite3_finalize(pStmt);
}
pStmt = 0; pStmt = 0;
std::string errMsg = sqlite3_errmsg(_pDB); std::string errMsg = sqlite3_errmsg(_pDB);
Utility::throwException(rc, errMsg); Utility::throwException(rc, errMsg);
@@ -106,32 +113,45 @@ void SQLiteStatementImpl::compileImpl()
queryFound = true; 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(); clear();
_pStmt = pStmt; _pStmt = pStmt;
_pLeftover = new std::string(leftOver);
trimInPlace(*_pLeftover);
// prepare binding
_pBinder = new Binder(_pStmt); _pBinder = new Binder(_pStmt);
_pExtractor = new Extractor(_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); 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)); Poco::UInt32 curDataSet = currentDataSet();
_columns.push_back(mc); 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);
}
} }
}
return true;
bool SQLiteStatementImpl::canBind() const
{
bool ret = false;
if (!bindings().empty() && _pStmt)
ret = (*bindings().begin())->canBind();
return ret;
} }
@@ -143,32 +163,61 @@ void SQLiteStatementImpl::bindImpl()
sqlite3_reset(_pStmt); sqlite3_reset(_pStmt);
// bind int paramCount = sqlite3_bind_parameter_count(_pStmt);
Bindings& binds = bindings(); BindIt bindEnd = bindings().end();
int pc = sqlite3_bind_parameter_count(_pStmt); if (0 == paramCount || bindEnd == _bindBegin)
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)
{ {
(*it)->bind(pos); _canBind = false;
pos += (*it)->numOfColumnsHandled(); 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() void SQLiteStatementImpl::clear()
{ {
_columns.clear(); _columns[currentDataSet()].clear();
_affectedRowCount = 0; _affectedRowCount = 0;
if (_pStmt) if (_pStmt)
@@ -176,6 +225,8 @@ void SQLiteStatementImpl::clear()
sqlite3_finalize(_pStmt); sqlite3_finalize(_pStmt);
_pStmt=0; _pStmt=0;
} }
_pLeftover = 0;
_canBind = false;
} }
@@ -194,12 +245,11 @@ bool SQLiteStatementImpl::hasNext()
_stepCalled = true; _stepCalled = true;
_nextResponse = sqlite3_step(_pStmt); _nextResponse = sqlite3_step(_pStmt);
_pExtractor->reset();//to clear the cached null indicators
if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE) if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE)
{
Utility::throwException(_nextResponse); Utility::throwException(_nextResponse);
}
_pExtractor->reset();//clear the cached null indicators
return (_nextResponse == SQLITE_ROW); return (_nextResponse == SQLITE_ROW);
} }
@@ -212,13 +262,14 @@ Poco::UInt32 SQLiteStatementImpl::next()
poco_assert (columnsReturned() == sqlite3_column_count(_pStmt)); poco_assert (columnsReturned() == sqlite3_column_count(_pStmt));
Extractions& extracts = extractions(); Extractions& extracts = extractions();
Extractions::iterator it = extracts.begin(); Extractions::iterator it = extracts.begin();
Extractions::iterator itEnd = extracts.end(); Extractions::iterator itEnd = extracts.end();
std::size_t pos = 0; // sqlite starts with pos 0 for results! std::size_t pos = 0; // sqlite starts with pos 0 for results!
for (; it != itEnd; ++it) for (; it != itEnd; ++it)
{ {
(*it)->extract(pos); (*it)->extract(pos);
pos += (*it)->numOfColumnsHandled(); pos += (*it)->numOfColumnsHandled();
_isExtracted = true;
} }
_stepCalled = false; _stepCalled = false;
} }
@@ -238,14 +289,15 @@ Poco::UInt32 SQLiteStatementImpl::next()
Poco::UInt32 SQLiteStatementImpl::columnsReturned() const Poco::UInt32 SQLiteStatementImpl::columnsReturned() const
{ {
return (Poco::UInt32) _columns.size(); return (Poco::UInt32) _columns[currentDataSet()].size();
} }
const MetaColumn& SQLiteStatementImpl::metaColumn(Poco::UInt32 pos) const const MetaColumn& SQLiteStatementImpl::metaColumn(Poco::UInt32 pos) const
{ {
poco_assert (pos >= 0 && pos <= _columns.size()); Poco::UInt32 curDataSet = currentDataSet();
return _columns[pos]; poco_assert (pos >= 0 && pos <= _columns[curDataSet].size());
return _columns[curDataSet][pos];
} }

View File

@@ -489,11 +489,12 @@ void SQLiteTest::testInsertSingleBulk()
int x = 0; int x = 0;
Statement stmt((tmp << "INSERT INTO Strings VALUES(:str)", use(x))); 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); assert (1 == i);
} }
int count = 0; int count = 0;
tmp << "SELECT COUNT(*) FROM Strings", into(count), now; tmp << "SELECT COUNT(*) FROM Strings", into(count), now;
assert (count == 100); assert (count == 100);
@@ -2140,16 +2141,61 @@ void SQLiteTest::testBindingCount()
int i = 42; int i = 42;
try { tmp << "INSERT INTO Ints VALUES (?)", now; } try { tmp << "INSERT INTO Ints VALUES (?)", now; }
catch (ParameterCountMismatchException&) { } catch (ParameterCountMismatchException&) { }
try { tmp << "INSERT INTO Ints VALUES (?)", bind(42), bind(42), now; }
catch (ParameterCountMismatchException&) { }
tmp << "INSERT INTO Ints VALUES (?)", use(i), now; tmp << "INSERT INTO Ints VALUES (?)", use(i), now;
i = 0; i = 0;
try { tmp << "SELECT int0 from Ints where int0 = ?", into(i), now; } try { tmp << "SELECT int0 from Ints where int0 = ?", into(i), now; }
catch (ParameterCountMismatchException&) { } catch (ParameterCountMismatchException&) { }
tmp << "SELECT int0 from Ints where int0 = ?", bind(42), into(i), now; tmp << "SELECT int0 from Ints where int0 = ?", bind(42), into(i), now;
assert (42 == i); 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, testSQLLogger);
CppUnit_addTest(pSuite, SQLiteTest, testExternalBindingAndExtraction); CppUnit_addTest(pSuite, SQLiteTest, testExternalBindingAndExtraction);
CppUnit_addTest(pSuite, SQLiteTest, testBindingCount); CppUnit_addTest(pSuite, SQLiteTest, testBindingCount);
CppUnit_addTest(pSuite, SQLiteTest, testMultipleResults);
return pSuite; return pSuite;
} }

View File

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

View File

@@ -101,8 +101,13 @@ public:
/// Extracts a value from the param, starting at the given column position. /// Extracts a value from the param, starting at the given column position.
/// Returns the number of rows extracted. /// Returns the number of rows extracted.
virtual void reset() = 0; virtual void reset();
/// Resets the extractor so that it can be re-used. /// 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; virtual AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) = 0;
/// Creates a Prepare object for the extracting object /// 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 } } // namespace Poco::Data

View File

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

View File

@@ -124,6 +124,11 @@ public:
_extracted = false; _extracted = false;
} }
bool canExtract() const
{
return !_extracted;
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<T>(pPrep, pos, _rResult); return new Prepare<T>(pPrep, pos, _rResult);
@@ -198,10 +203,6 @@ public:
return 1u; return 1u;
} }
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<T>(pPrep, pos, _default); return new Prepare<T>(pPrep, pos, _default);
@@ -283,10 +284,6 @@ public:
return 1u; return 1u;
} }
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<bool>(pPrep, pos, _default); return new Prepare<bool>(pPrep, pos, _default);
@@ -366,10 +363,6 @@ public:
return 1u; return 1u;
} }
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<T>(pPrep, pos, _default); return new Prepare<T>(pPrep, pos, _default);
@@ -449,10 +442,6 @@ public:
return 1u; return 1u;
} }
virtual void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<T>(pPrep, pos, _default); return new Prepare<T>(pPrep, pos, _default);
@@ -586,10 +575,6 @@ public:
return 1u; return 1u;
} }
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<T>(pPrep, pos, _default); return new Prepare<T>(pPrep, pos, _default);
@@ -649,10 +634,6 @@ public:
return 1u; return 1u;
} }
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<T>(pPrep, pos, _default); return new Prepare<T>(pPrep, pos, _default);
@@ -712,16 +693,11 @@ public:
return 1u; return 1u;
} }
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<V>(pPrep, pos, _default); return new Prepare<V>(pPrep, pos, _default);
} }
private: private:
std::map<K, V>& _rResult; std::map<K, V>& _rResult;
V _default; V _default;
@@ -776,10 +752,6 @@ public:
return 1u; return 1u;
} }
void reset()
{
}
AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos)
{ {
return new Prepare<V>(pPrep, pos, _default); 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. /// Limit stores information how many rows a query should return.
{ {
public: public:
enum enum Type
{ {
LIMIT_UNLIMITED = 0xffffffffu LIMIT_UNLIMITED = 0xffffffffu
}; };

View File

@@ -75,6 +75,14 @@ class Data_API RecordSet: private Statement
/// select.execute(); /// select.execute();
/// RecordSet rs(select); /// 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 /// The number of rows in the RecordSet can be limited by specifying
/// a limit for the Statement. /// a limit for the Statement.
{ {

View File

@@ -285,7 +285,6 @@ public:
Statement& operator , (unsigned long value); Statement& operator , (unsigned long value);
/// Adds the value to the list of values to be supplied to the SQL string formatting function. /// Adds the value to the list of values to be supplied to the SQL string formatting function.
#endif #endif
Statement& operator , (Poco::UInt64 value); Statement& operator , (Poco::UInt64 value);
/// Adds the value to the list of values to be supplied to the SQL string formatting function. /// 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; std::size_t extractionCount() const;
/// Returns the number of extraction storage buffers associated /// 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); void setRowFormatter(RowFormatter* pRowFormatter);
/// Sets the row formatter for this statement. /// 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 inline Statement::Storage Statement::storage() const
{ {
return static_cast<Storage>(_pImpl->getStorage()); return static_cast<Storage>(_pImpl->getStorage());

View File

@@ -136,14 +136,16 @@ public:
/// Registers objects used for extracting data with the StatementImpl. /// Registers objects used for extracting data with the StatementImpl.
void setExtractionLimit(const Limit& extrLimit); 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; std::string toString() const;
/// Create a string version of the SQL statement. /// Create a string version of the SQL statement.
Poco::UInt32 execute(); Poco::UInt32 execute();
/// Executes a statement. Returns the number of rows extracted for statements /// Executes a statement. Returns the number of rows
/// returning data or number of rows affected for all other statements (insert, update, delete). /// extracted for statements returning data or number of rows
/// affected for all other statements (insert, update, delete).
void reset(); void reset();
/// Resets the statement, so that we can reuse all bindings and re-execute again. /// Resets the statement, so that we can reuse all bindings and re-execute again.
@@ -198,8 +200,16 @@ protected:
virtual bool canBind() const = 0; virtual bool canBind() const = 0;
/// Returns if another bind is possible. /// Returns if another bind is possible.
virtual void compileImpl() = 0; virtual bool compileImpl() = 0;
/// Compiles the statement, doesn't bind yet. /// 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; virtual void bindImpl() = 0;
/// Binds parameters. /// Binds parameters.
@@ -240,11 +250,11 @@ protected:
/// session is queried for container type setting. If the /// session is queried for container type setting. If the
/// session container type setting is found, it is used. /// session container type setting is found, it is used.
/// 3. If neither session nor statement have the internal /// 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: /// Supported internal extraction container types are:
/// - std::vector (default) /// - std::deque (default)
/// - std::deque /// - std::vector
/// - std::list /// - std::list
SessionImpl& session(); SessionImpl& session();
@@ -277,7 +287,15 @@ protected:
/// Returns the current data set. /// Returns the current data set.
Poco::UInt32 activateNextDataSet(); 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(); Poco::UInt32 getExtractionLimit();
/// Returns the extraction limit value. /// Returns the extraction limit value.
@@ -286,17 +304,22 @@ protected:
/// Returns the extraction limit. /// Returns the extraction limit.
private: private:
void compile(); bool compile();
/// Compiles the statement, if not yet compiled. /// Compiles the statement, if not yet compiled.
/// Returns true if more compile calls are needed.
void bind(); void bind();
/// Binds the statement, if not yet bound. /// Binds the statement, if not yet bound.
Poco::UInt32 executeWithLimit(); 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(); 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(); void resetExtraction();
/// Resets extraction so it can be reused again. /// Resets extraction so it can be reused again.
@@ -306,7 +329,7 @@ private:
{ {
C* pData = new C; C* pData = new C;
Column<C>* pCol = new Column<C>(mc, pData); Column<C>* pCol = new Column<C>(mc, pData);
return new InternalExtraction<C>(*pData, pCol); return new InternalExtraction<C>(*pData, pCol, currentDataSet());
} }
template <class C> template <class C>
@@ -314,7 +337,7 @@ private:
{ {
C* pData = new C; C* pData = new C;
Column<C>* pCol = new Column<C>(mc, pData); 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> template <class T>
@@ -413,7 +436,7 @@ private:
State _state; State _state;
Limit _extrLimit; Limit _extrLimit;
Poco::UInt32 _lowerLimit; Poco::UInt32 _lowerLimit;
int _columnsExtracted; std::vector<int> _columnsExtracted;
SessionImpl& _rSession; SessionImpl& _rSession;
Storage _storage; Storage _storage;
std::ostringstream _ostr; std::ostringstream _ostr;
@@ -471,7 +494,8 @@ inline AbstractExtractionVec& StatementImpl::extractions()
inline int StatementImpl::columnsExtracted() const 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 } } // namespace Poco::Data

View File

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

View File

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

View File

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