- SQL logging channel and archiving strategy

- row formatting refactored
- affected row count for insert, delete and update returned from Statement::execute()
- internal SQL string formatting capability using Poco::format()
This commit is contained in:
Aleksandar Fabijanic
2008-01-12 18:25:27 +00:00
parent b57f579d16
commit 9e8e627347
63 changed files with 2556 additions and 337 deletions

View File

@@ -0,0 +1,166 @@
//
// ArchiveStrategy.cpp
//
// $Id: //poco/Main/Data/src/ArchiveStrategy.cpp#8 $
//
// Library: Data
// Package: Logging
// Module: ArchiveStrategy
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/ArchiveStrategy.h"
namespace Poco {
namespace Data {
//
// ArchiveStrategy
//
const std::string ArchiveStrategy::DEFAULT_ARCHIVE_DESTINATION = "T_POCO_LOG_ARCHIVE";
ArchiveStrategy::ArchiveStrategy(const std::string& connector,
const std::string& connect,
const std::string& source,
const std::string& destination):
_connector(connector),
_connect(connect),
_source(source),
_destination(destination)
{
open();
}
ArchiveStrategy::~ArchiveStrategy()
{
}
void ArchiveStrategy::open()
{
if (_connector.empty() || _connect.empty())
throw IllegalStateException("Connector and connect string must be non-empty.");
_pSession = new Session(_connector, _connect);
}
//
// ArchiveByAgeStrategy
//
ArchiveByAgeStrategy::ArchiveByAgeStrategy(const std::string& connector,
const std::string& connect,
const std::string& sourceTable,
const std::string& destinationTable):
ArchiveStrategy(connector, connect, sourceTable, destinationTable)
{
initStatements();
}
ArchiveByAgeStrategy::~ArchiveByAgeStrategy()
{
}
void ArchiveByAgeStrategy::archive()
{
if (!session().isConnected()) open();
DateTime now;
_archiveDateTime = now - _maxAge;
getCountStatement().execute();
if (_archiveCount > 0)
{
getCopyStatement().execute();
getDeleteStatement().execute();
}
}
void ArchiveByAgeStrategy::initStatements()
{
std::string src = getSource();
std::string dest = getDestination();
setCountStatement();
_archiveCount = 0;
std::string sql;
Poco::format(sql, "SELECT COUNT(*) FROM %s WHERE DateTime < ?", src);
getCountStatement() << sql, into(_archiveCount), use(_archiveDateTime);
setCopyStatement();
sql.clear();
Poco::format(sql, "INSERT INTO %s SELECT * FROM %s WHERE DateTime < ?", dest, src);
getCopyStatement() << sql, use(_archiveDateTime);
setDeleteStatement();
sql.clear();
Poco::format(sql, "DELETE FROM %s WHERE DateTime < ?", src);
getDeleteStatement() << sql, use(_archiveDateTime);
}
void ArchiveByAgeStrategy::setThreshold(const std::string& age)
{
std::string::const_iterator it = age.begin();
std::string::const_iterator end = age.end();
int n = 0;
while (it != end && std::isspace(*it)) ++it;
while (it != end && std::isdigit(*it)) { n *= 10; n += *it++ - '0'; }
while (it != end && std::isspace(*it)) ++it;
std::string unit;
while (it != end && std::isalpha(*it)) unit += *it++;
Timespan::TimeDiff factor = Timespan::SECONDS;
if (unit == "minutes")
factor = Timespan::MINUTES;
else if (unit == "hours")
factor = Timespan::HOURS;
else if (unit == "days")
factor = Timespan::DAYS;
else if (unit == "weeks")
factor = 7*Timespan::DAYS;
else if (unit == "months")
factor = 30*Timespan::DAYS;
else if (unit != "seconds")
throw InvalidArgumentException("setMaxAge", age);
_maxAge = factor * n;
}
} } // namespace Poco::Data

View File

@@ -64,9 +64,9 @@ RecordSet::RecordSet(Session& rSession,
Statement((rSession << query, now)),
_currentRow(0),
_pBegin(new RowIterator(this)),
_pEnd(new RowIterator(this, true)),
_pRowFormatter(pRowFormatter)
_pEnd(new RowIterator(this, true))
{
if (pRowFormatter) setRowFormatter(pRowFormatter);
}
@@ -74,8 +74,7 @@ RecordSet::RecordSet(const RecordSet& other):
Statement(other.impl().duplicate()),
_currentRow(other._currentRow),
_pBegin(new RowIterator(this)),
_pEnd(new RowIterator(this, true)),
_pRowFormatter(other._pRowFormatter)
_pEnd(new RowIterator(this, true))
{
}
@@ -155,14 +154,14 @@ Row& RecordSet::row(std::size_t pos)
{
if (_rowMap.size())//reuse first row column names to save some memory
{
pRow = new Row(_rowMap.begin()->second->names(), &_pRowFormatter);
pRow = new Row(_rowMap.begin()->second->names(), getRowFormatter());
for (std::size_t col = 0; col < columns; ++col)
pRow->set(col, value(col, pos));
}
else
{
pRow = new Row;
if (_pRowFormatter) pRow->setFormatter(&_pRowFormatter);
pRow->setFormatter(getRowFormatter());
for (std::size_t col = 0; col < columns; ++col)
pRow->append(metaColumn(static_cast<UInt32>(col)).name(), value(col, pos));
}

View File

@@ -35,7 +35,7 @@
#include "Poco/Data/Row.h"
#include "Poco/Data/RowFormatter.h"
#include "Poco/Data/SimpleRowFormatter.h"
#include "Poco/Exception.h"
@@ -52,18 +52,17 @@ std::ostream& operator << (std::ostream &os, const Row& row)
Row::Row():
_pNames(0),
_pFormatter(new RowFormatter)
_pFormatter(new SimpleRowFormatter)
{
}
Row::Row(NameVecPtr pNames, FormatterPtr* pFormatter):
Row::Row(NameVecPtr pNames, const RowFormatterPtr& pFormatter):
_pNames(pNames)
{
if (!_pNames) throw NullPointerException();
if (pFormatter && *pFormatter) _pFormatter = *pFormatter;
else _pFormatter = new RowFormatter;
setFormatter(pFormatter);
_values.resize(_pNames->size());
addSortField(0);
@@ -315,12 +314,12 @@ bool Row::operator < (const Row& other) const
}
void Row::setFormatter(FormatterPtr* pFormatter)
void Row::setFormatter(const RowFormatterPtr& pFormatter)
{
if (pFormatter && *pFormatter)
_pFormatter = *pFormatter;
if (pFormatter)
_pFormatter = pFormatter;
else
_pFormatter = new RowFormatter;
_pFormatter = new SimpleRowFormatter;
}

View File

@@ -43,14 +43,7 @@ namespace Poco {
namespace Data {
RowFormatter::RowFormatter(std::streamsize width):
_width(width)
{
}
RowFormatter::RowFormatter(const std::string& prefix, const std::string& postfix):
_width(DEFAULT_COLUMN_WIDTH),
_prefix(prefix),
_postfix(postfix)
{
@@ -62,44 +55,4 @@ RowFormatter::~RowFormatter()
}
std::string& RowFormatter::formatNames(const NameVecPtr pNames, std::string& formattedNames) const
{
std::ostringstream str;
std::string line(_width * pNames->size(), '-');
NameVec::const_iterator it = pNames->begin();
NameVec::const_iterator end = pNames->end();
for (; it != end; ++it)
{
str << std::left << std::setw(_width) << *it;
}
str << std::endl << line << std::endl;
return formattedNames = str.str();
}
std::string& RowFormatter::formatValues(const ValueVec& vals, std::string& formattedValues) const
{
std::ostringstream str;
ValueVec::const_iterator it = vals.begin();
ValueVec::const_iterator end = vals.end();
for (; it != end; ++it)
{
if (it->isNumeric())
{
str << std::right
<< std::fixed
<< std::setprecision(2);
}
else str << std::left;
str << std::setw(_width) << it->convert<std::string>();
}
str << std::endl;
return formattedValues = str.str();
}
} } // namespace Poco::Data

296
Data/src/SQLChannel.cpp Normal file
View File

@@ -0,0 +1,296 @@
//
// SQLChannel.cpp
//
// $Id: //poco/Main/Data/src/SQLChannel.cpp#3 $
//
// Library: Net
// Package: Logging
// Module: SQLChannel
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/SQLChannel.h"
#include "Poco/Data/SessionFactory.h"
#include "Poco/DateTime.h"
#include "Poco/LoggingFactory.h"
#include "Poco/Instantiator.h"
#include "Poco/NumberParser.h"
#include "Poco/NumberFormatter.h"
#include "Poco/Format.h"
namespace Poco {
namespace Data {
const std::string SQLChannel::PROP_CONNECTOR("connector");
const std::string SQLChannel::PROP_CONNECT("connect");
const std::string SQLChannel::PROP_NAME("name");
const std::string SQLChannel::PROP_TABLE("table");
const std::string SQLChannel::PROP_ARCHIVE_TABLE("archive");
const std::string SQLChannel::PROP_MAX_AGE("keep");
const std::string SQLChannel::PROP_ASYNC("async");
const std::string SQLChannel::PROP_TIMEOUT("timeout");
const std::string SQLChannel::PROP_THROW("throw");
SQLChannel::SQLChannel():
_name("-"),
_table("T_POCO_LOG"),
_timeout(1000),
_throw(true),
_async(true)
{
}
SQLChannel::SQLChannel(const std::string& connector,
const std::string& connect,
const std::string& name):
_connector(connector),
_connect(connect),
_name(name),
_table("T_POCO_LOG"),
_timeout(1000),
_throw(true),
_async(true)
{
open();
}
SQLChannel::~SQLChannel()
{
close();
}
void SQLChannel::open()
{
if (_connector.empty() || _connect.empty())
throw IllegalStateException("Connector and connect string must be non-empty.");
_pSession = new Session(_connector, _connect);
initLogStatement();
}
void SQLChannel::close()
{
wait();
}
void SQLChannel::log(const Message& msg)
{
if (_async) logAsync(msg);
else logSync(msg);
}
void SQLChannel::logAsync(const Message& msg)
{
if (!_pSession || !_pSession->isConnected()) open();
Statement::ResultType ret = wait();
if (0 == ret && !_pLogStatement->done() && !_pLogStatement->initialized())
{
if (_throw)
throw TimeoutException("Timed out waiting for previous statement completion");
else return;
}
logSync(msg);
}
void SQLChannel::logSync(const Message& msg)
{
//if (isArchiving()) archive();
if (_pArchiveStrategy) _pArchiveStrategy->archive();
_source = msg.getSource();
_pid = msg.getPid();
_thread = msg.getThread();
_tid = msg.getTid();
_priority = msg.getPriority();
_text = msg.getText();
_dateTime = msg.getTime();
if (_source.empty()) _source = _name;
_pLogStatement->execute();
}
void SQLChannel::setProperty(const std::string& name, const std::string& value)
{
if (name == PROP_NAME)
{
_name = value;
if (_name.empty()) _name = "-";
}
else if (name == PROP_CONNECTOR)
{
_connector = value;
close(); open();
}
else if (name == PROP_CONNECT)
{
_connect = value;
close(); open();
}
else if (name == PROP_TABLE)
{
_table = value;
initLogStatement();
}
else if (name == PROP_ARCHIVE_TABLE)
{
if (value.empty())
{
_pArchiveStrategy = 0;
}
else if (_pArchiveStrategy)
{
_pArchiveStrategy->setDestination(value);
}
else
{
_pArchiveStrategy = new ArchiveByAgeStrategy(_connector, _connect, _table, value);
}
}
else if (name == PROP_MAX_AGE)
{
if (value.empty() || "forever" == value)
{
_pArchiveStrategy = 0;
}
else if (_pArchiveStrategy)
{
_pArchiveStrategy->setThreshold(value);
}
else
{
ArchiveByAgeStrategy* p = new ArchiveByAgeStrategy(_connector, _connect, _table);
p->setThreshold(value);
_pArchiveStrategy = p;
}
}
else if (name == PROP_ASYNC)
{
_async = isTrue(value);
initLogStatement();
}
else if (name == PROP_TIMEOUT)
{
if (value.empty() || '0' == value[0])
_timeout = Statement::WAIT_FOREVER;
else
_timeout = NumberParser::parse(value);
}
else if (name == PROP_THROW)
{
_throw = isTrue(value);
}
else
{
Channel::setProperty(name, value);
}
}
std::string SQLChannel::getProperty(const std::string& name) const
{
if (name == PROP_NAME)
{
if (_name != "-") return _name;
else return "";
}
else if (name == PROP_CONNECTOR)
{
return _connector;
}
else if (name == PROP_CONNECT)
{
return _connect;
}
else if (name == PROP_TABLE)
{
return _table;
}
else if (name == PROP_ARCHIVE_TABLE)
{
return _pArchiveStrategy ? _pArchiveStrategy->getDestination() : "" ;
}
else if (name == PROP_MAX_AGE)
{
return _pArchiveStrategy ? _pArchiveStrategy->getThreshold() : "forever";
}
else if (name == PROP_TIMEOUT)
{
return NumberFormatter::format(_timeout);
}
else if (name == PROP_THROW)
{
if (_throw) return "true";
else return "false";
}
else
{
return Channel::getProperty(name);
}
}
void SQLChannel::initLogStatement()
{
_pLogStatement = new Statement(*_pSession);
std::string sql;
Poco::format(sql, "INSERT INTO %s VALUES (?,?,?,?,?,?,?,?)", _table);
*_pLogStatement << sql,
use(_source),
use(_name),
use(_pid),
use(_thread),
use(_tid),
use(_priority),
use(_text),
use(_dateTime);
if (_async) _pLogStatement->setAsync();
}
void SQLChannel::registerChannel()
{
Poco::LoggingFactory::defaultFactory().registerChannelClass("SQLChannel", new Poco::Instantiator<SQLChannel, Poco::Channel>);
}
} } // namespace Poco::Data

View File

@@ -58,11 +58,11 @@ SessionFactory& SessionFactory::instance()
}
void SessionFactory::add(const std::string& key, Connector* pIn)
void SessionFactory::add(Connector* pIn)
{
Poco::FastMutex::ScopedLock lock(_mutex);
SessionInfo info(pIn);
std::pair<Connectors::iterator, bool> res = _connectors.insert(std::make_pair(key, info));
std::pair<Connectors::iterator, bool> res = _connectors.insert(std::make_pair(pIn->name(), info));
if (!res.second)
{
res.first->second.cnt++;

View File

@@ -0,0 +1,123 @@
//
// RowFormatter.cpp
//
// $Id: //poco/Main/Data/src/RowFormatter.cpp#1 $
//
// Library: Data
// Package: DataCore
// Module: RowFormatter
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/SimpleRowFormatter.h"
#include "Poco/Exception.h"
#include <iomanip>
namespace Poco {
namespace Data {
SimpleRowFormatter::SimpleRowFormatter(std::streamsize columnWidth):
_colWidth(columnWidth)
{
}
SimpleRowFormatter::SimpleRowFormatter(const SimpleRowFormatter& other):
RowFormatter(other.prefix(), other.postfix()),
_colWidth(other._colWidth)
{
}
SimpleRowFormatter::~SimpleRowFormatter()
{
}
SimpleRowFormatter& SimpleRowFormatter::operator = (const SimpleRowFormatter& row)
{
SimpleRowFormatter tmp(row);
swap(tmp);
return *this;
}
void SimpleRowFormatter::swap(SimpleRowFormatter& other)
{
using std::swap;
setPrefix(other.prefix());
setPostfix(other.postfix());
swap(_colWidth, other._colWidth);
}
std::string& SimpleRowFormatter::formatNames(const NameVecPtr pNames, std::string& formattedNames) const
{
std::ostringstream str;
std::string line(_colWidth * pNames->size(), '-');
NameVec::const_iterator it = pNames->begin();
NameVec::const_iterator end = pNames->end();
for (; it != end; ++it)
{
str << std::left << std::setw(_colWidth) << *it;
}
str << std::endl << line << std::endl;
return formattedNames = str.str();
}
std::string& SimpleRowFormatter::formatValues(const ValueVec& vals, std::string& formattedValues) const
{
std::ostringstream str;
ValueVec::const_iterator it = vals.begin();
ValueVec::const_iterator end = vals.end();
for (; it != end; ++it)
{
if (it->isNumeric())
{
str << std::right
<< std::fixed
<< std::setprecision(2);
}
else str << std::left;
str << std::setw(_colWidth) << it->convert<std::string>();
}
str << std::endl;
return formattedValues = str.str();
}
} } // namespace Poco::Data

View File

@@ -68,7 +68,9 @@ Statement::Statement(const Statement& stmt):
_pImpl(stmt._pImpl),
_async(stmt._async),
_pResult(stmt._pResult),
_pAsyncExec(stmt._pAsyncExec)
_pAsyncExec(stmt._pAsyncExec),
_arguments(stmt._arguments),
_pRowFormatter(stmt._pRowFormatter)
{
}
@@ -94,6 +96,8 @@ void Statement::swap(Statement& other)
swap(_async, other._async);
swap(_pAsyncExec, other._pAsyncExec);
swap(_pResult, other._pResult);
_arguments.swap(other._arguments);
swap(_pRowFormatter, other._pRowFormatter);
}
@@ -111,6 +115,12 @@ Statement::ResultType Statement::execute()
bool isDone = done();
if (initialized() || paused() || isDone)
{
if (_arguments.size())
{
_pImpl->formatSQL(_arguments);
_arguments.clear();
}
if (!isAsync())
{
if (isDone) _pImpl->reset();
@@ -141,9 +151,7 @@ const Statement::Result& Statement::doAsyncExec()
if (done()) _pImpl->reset();
if (!_pAsyncExec)
_pAsyncExec = new AsyncExecMethod(_pImpl, &StatementImpl::execute);
poco_check_ptr (_pAsyncExec);
_pResult = new Result((*_pAsyncExec)());
poco_check_ptr (_pResult);
return *_pResult;
}

View File

@@ -96,6 +96,8 @@ Poco::UInt32 StatementImpl::execute()
if (lim < _lowerLimit)
throw LimitException("Did not receive enough data.");
if (0 == lim) lim = affectedRowCount();
return lim;
}
@@ -361,4 +363,12 @@ void StatementImpl::addExtract(AbstractExtraction* pExtraction)
}
void StatementImpl::formatSQL(std::vector<Any>& arguments)
{
std::string sql;
Poco::format(sql, _ostr.str(), arguments);
_ostr.str(""); _ostr << sql;
}
} } // namespace Poco::Data