//
// SQLChannel.h
//
// Library: Data
// Package: Logging
// Module:  SQLChannel
//
// Definition of the SQLChannel class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#ifndef Data_SQLChannel_INCLUDED
#define Data_SQLChannel_INCLUDED


#include "Poco/Data/Data.h"
#include "Poco/Data/Connector.h"
#include "Poco/Data/Session.h"
#include "Poco/Data/Statement.h"
#include "Poco/Data/ArchiveStrategy.h"
#include "Poco/Channel.h"
#include "Poco/Message.h"
#include "Poco/AutoPtr.h"
#include "Poco/String.h"


namespace Poco {
namespace Data {


class Data_API SQLChannel: public Poco::Channel
	/// This Channel implements logging to a SQL database.
	/// The channel is dependent on the schema. The DDL for
	/// table creation (subject to target DDL dialect dependent
	/// modifications) is:
	///
	/// "CREATE TABLE T_POCO_LOG (Source VARCHAR,
	///		Name VARCHAR,
	///		ProcessId INTEGER,
	///		Thread VARCHAR,
	///		ThreadId INTEGER,
	///		Priority INTEGER,
	///		Text VARCHAR,
	///		DateTime DATE)"
	///
	/// The table name is configurable through "table" property.
	/// Other than DateTime filed name used for optional time-based archiving purposes, currently the
	/// field names are not mandated. However, it is recomended to use names as specified above.
	///
	/// To provide as non-intrusive operation as possbile, the log entries are cached and
	/// inserted into the target database asynchronously by default. The blocking, however, will occur
	/// before the next entry insertion with default timeout of 1 second. The default settings can be
	/// overridden (see async, timeout and throw properties for details).
	/// If throw property is false, insertion timeouts are ignored, otherwise a TimeoutException is thrown.
	/// To force insertion of every entry, set timeout to 0. This setting, however, introduces
	/// a risk of long blocking periods in case of remote server communication delays.
{
public:
	using Ptr = Poco::AutoPtr<SQLChannel>;

	SQLChannel();
		/// Creates SQLChannel.

	SQLChannel(const std::string& connector,
		const std::string& connect,
		const std::string& name = "-");
		/// Creates a SQLChannel with the given connector, connect string, timeout, table and name.
		/// The connector must be already registered.

	void open();
		/// Opens the SQLChannel.

	void close();
		/// Closes the SQLChannel.

	void log(const Message& msg);
		/// Writes the log message to the database.

	void setProperty(const std::string& name, const std::string& value);
		/// Sets the property with the given value.
		///
		/// The following properties are supported:
		///     * name:      The name used to identify the source of log messages.
		///                  Defaults to "-".
		///
		///     * connector: The target data storage connector name.
		///
		///     * connect:   The target data storage connection string.
		///
		///     * table:     Destination log table name. Defaults to "T_POCO_LOG".
		///                  Table must exist in the target database.
		///
		///     * keep:      Max row age for the log table. To disable archiving,
		///                  set this property to empty string or "forever".
		///
		///     * archive:   Archive table name. Defaults to "T_POCO_LOG_ARCHIVE".
		///                  Table must exist in the target database. To disable archiving,
		///                  set this property to empty string.
		///
		///     * async:     Indicates asynchronous execution. When excuting asynchronously,
		///                  messages are sent to the target using asyncronous execution.
		///                  However, prior to the next message being processed and sent to
		///                  the target, the previous operation must have been either completed
		///                  or timed out (see timeout and throw properties for details on
		///                  how abnormal conditos are handled).
		///
		///     * timeout:   Timeout (ms) to wait for previous log operation completion.
		///                  Values "0" and "" mean no timeout. Only valid when logging
		///                  is asynchronous, otherwise ignored.
		///
		///     * throw:     Boolean value indicating whether to throw in case of timeout.
		///                  Setting this property to false may result in log entries being lost.
		///                  True values are (case insensitive) "true", "t", "yes", "y".
		///                  Anything else yields false.

	std::string getProperty(const std::string& name) const;
		/// Returns the value of the property with the given name.

	std::size_t wait();
		/// Waits for the completion of the previous operation and returns
		/// the result. If chanel is in synchronous mode, returns 0 immediately.

	static void registerChannel();
		/// Registers the channel with the global LoggingFactory.

	static const std::string PROP_CONNECT;
	static const std::string PROP_CONNECTOR;
	static const std::string PROP_NAME;
	static const std::string PROP_TABLE;
	static const std::string PROP_ARCHIVE_TABLE;
	static const std::string PROP_MAX_AGE;
	static const std::string PROP_ASYNC;
	static const std::string PROP_TIMEOUT;
	static const std::string PROP_THROW;

protected:
	~SQLChannel();

private:
	using SessionPtr = Poco::SharedPtr<Session>;
	using StatementPtr = Poco::SharedPtr<Statement>;
	using Priority = Poco::Message::Priority;
	using StrategyPtr = Poco::SharedPtr<ArchiveStrategy>;

	void initLogStatement();
		/// Initiallizes the log statement.

	void initArchiveStatements();
	/// Initiallizes the archive statement.

	void logAsync(const Message& msg);
		/// Waits for previous operation completion and
		/// calls logSync(). If the previous operation times out,
		/// and _throw is true, TimeoutException is thrown, oterwise
		/// the timeout is ignored and log entry is lost.

	void logSync(const Message& msg);
		/// Inserts the message in the target database.

	bool isTrue(const std::string& value) const;
		/// Returns true is value is "true", "t", "yes" or "y".
		/// Case insensitive.

	std::string  _connector;
	std::string  _connect;
	SessionPtr   _pSession;
	StatementPtr _pLogStatement;
	std::string  _name;
	std::string  _table;
	int          _timeout;
	bool         _throw;
	bool         _async;

	// members for log entry cache (needed for async mode)
	std::string _source;
	long        _pid;
	std::string _thread;
	long        _tid;
	int         _priority;
	std::string _text;
	DateTime    _dateTime;

	StrategyPtr _pArchiveStrategy;
};


//
// inlines
//

inline std::size_t SQLChannel::wait()
{
	if (_async && _pLogStatement)
		return _pLogStatement->wait(_timeout);

	return 0;
}


inline bool SQLChannel::isTrue(const std::string& value) const
{
	return ((0 == icompare(value, "true")) ||
			(0 == icompare(value, "t")) ||
			(0 == icompare(value, "yes")) ||
			(0 == icompare(value, "y")));
}


} } // namespace Poco::Data


#endif // Data_SQLChannel_INCLUDED
