//+=============================================================================
//
// file :         DataBaseUtils.cpp
//
// description :  Various utility for the DataBase.
//
// project :      TANGO Device Servers
//
// author(s) :    A.Gotz, P.Verdier, JL Pons
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010,2011,2012,2013
//						European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Tango.  If not, see <http://www.gnu.org/licenses/>.
//
//=============================================================================

#include <tango/tango.h>
#include "DataBase.h"
#include "DataBaseClass.h"
#include "DatabaseConnectionHandle.h"
#include "DatabaseConnectionPool.h"
#include "Logging.h"

#include <errmsg.h>

#include <stdio.h>

namespace DataBase_ns {

//+----------------------------------------------------------------------------
//
// method : 		DataBase::replace_wildcard(char *wildcard_c_str)
//
// description : 	utility method to replace all occurrences of
//			wildcards (*) with SQL wildcards % and to escape
//			all occurrences	of '%' and '_' with '\'
//
// in :			string **wildcard_c_str - wildcard C string
//
// out :		void - nothing
//
//-----------------------------------------------------------------------------
std::string DataBase::replace_wildcard(const char *wildcard_c_str)
{
	std::string wildcard(wildcard_c_str);
	std::string::size_type index;

	DEBUG_STREAM << "DataBase::replace_wildcard() wildcard in " << wildcard << std::endl;
// escape %

	index = 0;
	while ((index = wildcard.find('%',index)) != std::string::npos)
	{
		wildcard.insert(index, 1, '\\');
		index = index+2;
	}

// escape _

	index = 0;
	while ((index = wildcard.find('_',index)) != std::string::npos)
	{
		wildcard.insert(index, 1, '\\');
		index = index+2;
	}


// escape "

	index = 0;
	while ((index = wildcard.find('"',index)) != std::string::npos)
	{
		wildcard.insert(index, 1, '\\');
		index = index+2;
	}


// escape '

	index = 0;
	while ((index = wildcard.find('\'',index)) != std::string::npos)
	{
		wildcard.insert(index, 1, '\\');
		index = index+2;
	}

// replace wildcard * with %

	while ((index = wildcard.find('*')) != std::string::npos)
	{
		wildcard.replace(index, 1, 1, '%');
	}

	DEBUG_STREAM << "DataBase::replace_wildcard() wildcard out " << wildcard << std::endl;
	return wildcard;
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::escape_string(char *string_c_str)
//
// description : 	utility method to escape all occurrences
//			of ' and " with '\'
//
// in :			const char *string_c_str -  C string to be modified.
//
// out :		string - The result string .
//
//-----------------------------------------------------------------------------
std::string DataBase::escape_string(const char *string_c_str)
{
	std::string escaped_string(string_c_str);
	std::string::size_type index;

	DEBUG_STREAM << "DataBase::escape_string() string in : " << escaped_string << std::endl;

	//	escape bakckslash
	index = 0;
	while ((index = escaped_string.find('\\',index)) != std::string::npos)
	{
		//	Check if double backslash already treated by client
		std::string	s = escaped_string.substr(index+1);

			//	Check if another escape sequence treated by client
		if (s.find('\"')==0 || s.find('\'')==0)
			index++;
		else
		{
			escaped_string.insert(index, 1, '\\');
			index += 2;
		}
	}

	//	escape "
	index = 0;
	while ((index = escaped_string.find('"',index)) != std::string::npos)
	{
		if (index==0)	//	Cannot have '\' at -1 !
		{
			escaped_string.insert(index, 1, '\\');
			index += 2;
		}
		else
		{
			//	Check if double quotes already treated by client
			std::string	s = escaped_string.substr(index-1);
			if (s.find('\\')==0)
				index++;
			else
			{
				escaped_string.insert(index, 1, '\\');
				index += 2;
			}
		}
	}


	//	escape '
	index = 0;
	while ((index = escaped_string.find('\'',index)) != std::string::npos)
	{
		if (index==0)	//	Cannot have '\' at -1 !
		{
			escaped_string.insert(index, 1, '\\');
			index += 2;
		}
		else
		{
			//	Check if simple quotes already treated by client
			std::string	s = escaped_string.substr(index-1);
			if (s.find('\\')==0)
				index++;
			else
			{
				escaped_string.insert(index, 1, '\\');
				index += 2;
			}
		}
	}

	DEBUG_STREAM << "DataBase::escaped_string() wildcard out : " << escaped_string << std::endl;
	return escaped_string;
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::device_name_to_dfm(std::string &device_name,
//			          char **domain, char **family, char **member)
//
// description : 	utility function to return domain, family and member
//			from device name. Assumes device name has (optional)
//			protocol and instance stripped off i.e. conforms
//			to domain/family/member
//
// in :			char *devname - device name
//
// out :		bool - true or false
//
//-----------------------------------------------------------------------------
bool DataBase::device_name_to_dfm(std::string &devname, char domain[], char family[], char member[])
{
	std::string::size_type index, index2;

	DEBUG_STREAM << "DataBase::device_name_to_dfm() device name in " << devname << std::endl;

	index = devname.find('/');
	index2 = devname.find('/', index+1);

	(devname.substr(0,index)).copy(domain,index);
	domain[index] = '\0';

	(devname.substr(index+1,index2-index)).copy(family,index2-index);
	family[index2-index-1] = '\0';

	(devname.substr(index2+1)).copy(member,devname.length()-index2);
	member[devname.length()-index2-1] = '\0';

	DEBUG_STREAM << "DataBase::device_name_to_dfm() domain/family/member out " << domain << "/" << family << "/" << member << std::endl;

	return true;
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::check_device_name(std::string *device_name)
//
// description : 	utility function to check whether device name conforms
//			to the TANGO naming convention of
//
//			[tango!taco:][//instance/]domain/family/member
//
// in :			string *device_name - device name
//
// out :		bool - true or false
//
//-----------------------------------------------------------------------------
bool DataBase::check_device_name(std::string &device_name_str)
{
	std::string devname(device_name_str);
	std::string::size_type index, index2;

	DEBUG_STREAM << "DataBase::check_device_name(): device_name in " << devname << std::endl;

// check there are no special characters which could be interpreted
// as wildcards or which are otherwise excluded

	if (devname.find('*') != std::string::npos) return false;

// check protocol - "tango:" | "taco:"

	if (devname.substr(0,6) == "tango:")
	{
		devname.erase(0,6);
	}
	else
	{
		if (devname.substr(0,5) == "taco:")
		{
			devname.erase(0,5);
		}
	}

// check instance - "//instance/"

	if (devname.substr(0,2) == "//")
	{
		index = devname.find('/',(std::string::size_type)2);
		if (index == 0 || index == std::string::npos)
		{
			return false;

		}
		devname.erase(0,index+1);
	}

// check name conforms to "D/F/M"

	index = devname.find('/');
	index2 = devname.find('/',index+1);

	if (index == 0 || index == std::string::npos || index2 == std::string::npos ||
	    index2-index <= 0 || devname.length() - index2 <= 0)
	{
		return false;
	}

	device_name_str = devname;

	DEBUG_STREAM << "DataBase::check_device_name(): device_name out " << device_name_str << std::endl;

	return true;
}

//+------------------------------------------------------------------
/**
 *	method:	DataBase::init_timing_stats
 *
 *	description:	method to initialise the timing statistics variables
 *	Returns void
 *
 * @param	none
 * @return	void
 *
 */
//+------------------------------------------------------------------
void DataBase::init_timing_stats()
{
	TimingStatsStruct new_timing_stats = {0.0, 0.0, 0.0, 0.0, 0.0};

	timing_stats_mutex.lock();

	timing_stats_map["DbImportDevice"] = new TimingStatsStruct;
	timing_stats_map["DbExportDevice"] = new TimingStatsStruct;
	timing_stats_map["DbGetHostServerList"] = new TimingStatsStruct;
	timing_stats_map["DbGetHostList"] = new TimingStatsStruct;
	timing_stats_map["DbGetServerList"] = new TimingStatsStruct;
	timing_stats_map["DbGetDevicePropertyList"] = new TimingStatsStruct;
	timing_stats_map["DbGetClassPropertyList"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceMemberList"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceFamilyList"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceDomainList"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceProperty"] = new TimingStatsStruct;
	timing_stats_map["DbPutDeviceProperty"] = new TimingStatsStruct;
	timing_stats_map["DbDeleteDeviceProperty"] = new TimingStatsStruct;
	timing_stats_map["DbInfo"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceClassList"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceAttributeProperty"] = new TimingStatsStruct;
	timing_stats_map["DbPutDeviceAttributeProperty"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceAttributeProperty2"] = new TimingStatsStruct;
	timing_stats_map["DbPutDeviceAttributeProperty2"] = new TimingStatsStruct;
	timing_stats_map["DbUnExportServer"] = new TimingStatsStruct;
	timing_stats_map["DbGetDeviceExportedList"] = new TimingStatsStruct;
	timing_stats_map["DbExportEvent"] = new TimingStatsStruct;
	timing_stats_map["DbImportEvent"] = new TimingStatsStruct;
	timing_stats_map["DbGetDataForServerCache"] = new TimingStatsStruct;
	timing_stats_map["DbPutClassProperty"] = new TimingStatsStruct;
	timing_stats_map["DbMySqlSelect"] = new TimingStatsStruct;
	timing_stats_map["DbGetDevicePipeProperty"] = new TimingStatsStruct;
	timing_stats_map["DbPutDevicePipeProperty"] = new TimingStatsStruct;

	timing_stats_size = timing_stats_map.size();
	timing_stats_average = new double[timing_stats_size];
	timing_stats_minimum = new double[timing_stats_size];
	timing_stats_maximum = new double[timing_stats_size];
	timing_stats_calls = new double[timing_stats_size];
	timing_stats_index = new Tango::DevString[timing_stats_size];

	//	now loop over map and initialise remaining variables
	std::map<std::string,TimingStatsStruct*>::iterator iter;
	int i=0;

	for (iter = timing_stats_map.begin(); iter != timing_stats_map.end(); iter++)
	{
		*(iter->second) = new_timing_stats;
		timing_stats_index[i] = (char*)malloc(strlen(iter->first.c_str())+1);
		strcpy(timing_stats_index[i],iter->first.c_str());
		i++;
	}

	timing_stats_mutex.unlock();
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::check_history_tables()
//
// description : 	Return history id
//
//-----------------------------------------------------------------------------
void DataBase::check_history_tables()
{
	TangoSys_MemStream	sql_query_stream;
	MYSQL_RES *result;

	INFO_STREAM << "DataBase::check_history_tables(): entering" << std::endl;

	sql_query_stream.str("");
	sql_query_stream << "SELECT count(*) FROM property_device_hist";
	DEBUG_STREAM << "DataBase::check_history_tables(): sql_query " << sql_query_stream.str() << std::endl;
	result = query(sql_query_stream.str(),"check_history_tables()");
	mysql_free_result(result);
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::get_id()
//
// description : 	Return history id
//					In this method, we don't use the classical query()
//					method in order to be sure that the UPDATE and the following
//					mysql_insert_id() are done using the same MySQL connection
//
//-----------------------------------------------------------------------------

Tango::DevULong64 DataBase::get_id(const char *name, DatabaseConnectionHandle &dch)
{
	TangoSys_MemStream sql_query;

	sql_query.str("");
	sql_query << "UPDATE " << name << "_history_id SET id=LAST_INSERT_ID(id+1)";
	std::string tmp_str = sql_query.str();

	if (mysql_real_query(dch.db(), tmp_str.c_str(),tmp_str.length()) != 0)
	{
		TangoSys_OMemStream o;

		WARN_STREAM << "DataBase::get_id() failed to query TANGO database:" << std::endl;
		WARN_STREAM << "  query = " << tmp_str << std::endl;
		WARN_STREAM << " (SQL error=" << mysql_error(dch.db()) << ")" << std::endl;

		o << "Failed to query TANGO database (error=" << mysql_error(dch.db()) << ")" << std::ends;

		Tango::Except::throw_exception(DB_SQLError,o.str(),"DataBase::get_id()");
	}

	my_ulonglong val = mysql_insert_id(dch.db());
	if (val == 0)
	{
		TangoSys_OMemStream o;

		o << "Failed to get history id : " << name;

		Tango::Except::throw_exception(DB_SQLError,o.str(),"DataBase::get_id()");
	}

	return (Tango::DevULong64)val;
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::simple_query()
//
// description : 	Execute a SQL query , ignore the result.
//
//-----------------------------------------------------------------------------
void DataBase::simple_query(std::string sql_query,const char *method, DatabaseConnectionHandle &dch)
{
//
// Call MySQL
//

	if (mysql_real_query(dch.db(), sql_query.c_str(),sql_query.length()) != 0)
	{
		TangoSys_OMemStream o;
		TangoSys_OMemStream o2;

		WARN_STREAM << "DataBase::" << method << " failed to query TANGO database:" << std::endl;
		WARN_STREAM << "  query = " << sql_query << std::endl;
		WARN_STREAM << " (SQL error=" << mysql_error(dch.db()) << ")" << std::endl;

		o << "Failed to query TANGO database (error=" << mysql_error(dch.db()) << ")";
		o << "\n.The query was: " << sql_query << std::ends;
		o2 << "DataBase::" << method << std::ends;

		Tango::Except::throw_exception(DB_SQLError,o.str(),o2.str());
	}
}

void DataBase::simple_query(std::string sql_query,const char *method)
{
	DatabaseConnectionHandle dch(dcp);

	simple_query(sql_query, method, dch);
}

//+----------------------------------------------------------------------------
//
// method : 		DataBase::query()
//
// description : 	Execute a SQL query and return the result.
//
//-----------------------------------------------------------------------------
MYSQL_RES *DataBase::query(std::string sql_query,const char *method, DatabaseConnectionHandle& dch)
{
	MYSQL_RES *result;

//
// Call MySQL
//

	if (mysql_real_query(dch.db(), sql_query.c_str(),sql_query.length()) != 0)
	{
		TangoSys_OMemStream o;
		TangoSys_OMemStream o2;

		WARN_STREAM << "DataBase::" << method << " failed to query TANGO database:" << std::endl;
		WARN_STREAM << "  query = " << sql_query << std::endl;
		WARN_STREAM << " (SQL error=" << mysql_error(dch.db()) << ")" << std::endl;

		o << "Failed to query TANGO database (error=" << mysql_error(dch.db()) << ")";
		o << "\nThe query was: " << sql_query << std::ends;
		o2 << "DataBase::" << method << std::ends;

		Tango::Except::throw_exception(DB_SQLError,o.str(),o2.str());
	}

	if ((result = mysql_store_result(dch.db())) == NULL)
	{
		TangoSys_OMemStream o;
		TangoSys_OMemStream o2;

		WARN_STREAM << "DataBase:: " << method << " : mysql_store_result() failed  (error=" << mysql_error(dch.db()) << ")" << std::endl;

		o << "mysql_store_result() failed (error=" << mysql_error(dch.db()) << ")";
		o2 << "DataBase::" << method;

		Tango::Except::throw_exception(DB_SQLError,o.str(),o2.str());
	}

	return result;

}

MYSQL_RES *DataBase::query(std::string sql_query,const char *method)
{
	DatabaseConnectionHandle dch(dcp);

	return query(sql_query, method, dch);
}

//+------------------------------------------------------------------
/**
 *	method:	purge_property()
 *
 *	description:	purge a property history table.
 *
 */
//+------------------------------------------------------------------
void DataBase::purge_property(const char *table,const char *field,const char *object,const char *name, DatabaseConnectionHandle &dch) {

  TangoSys_MemStream sql_query;
  MYSQL_RES *result;
  MYSQL_ROW row2;

  sql_query.str("");
  sql_query << "SELECT DISTINCT id,date FROM " << table
            << " WHERE " << field << "=\"" << object << "\" AND name=\"" << name
            << "\" ORDER by date";

  result = query(sql_query.str(),"purge_property()", dch);
  int nb_item = mysql_num_rows(result);

  if(nb_item>historyDepth) {
    // Purge
    int toDelete = nb_item-historyDepth;
    for(int j=0;j<toDelete;j++) {
        row2 = mysql_fetch_row(result);
        sql_query.str("");
        sql_query << "DELETE FROM " << table << " WHERE id='" << row2[0] << "'";
		simple_query(sql_query.str(),"purge_property()", dch);
    }
  }

  mysql_free_result(result);
}

//+------------------------------------------------------------------
/**
 *	method:	purge_attribute_property()
 *
 *	description:	purge an attribute property history table.
 *
 */
//+------------------------------------------------------------------
void DataBase::purge_att_property(const char *table,const char *field,const char *object,const char *attribute,const char *name, DatabaseConnectionHandle& dch) {

  TangoSys_MemStream sql_query;
  MYSQL_RES *result;
  MYSQL_ROW row2;

  //TANGO_LOG << "purge_att_property(" << object << "," << attribute << "," << name << ")" << std::endl;

  sql_query.str("");
  sql_query << "SELECT DISTINCT id,date FROM " << table
            << " WHERE " << field << "=\"" << object << "\" AND name=\"" << name
            << "\" AND attribute=\"" << attribute << "\" ORDER by date";

  result = query(sql_query.str(),"purge_att_property()", dch);
  int nb_item = mysql_num_rows(result);

  if(nb_item>historyDepth) {
    // Purge
    int toDelete = nb_item-historyDepth;
    for(int j=0;j<toDelete;j++) {
		row2 = mysql_fetch_row(result);
        sql_query.str("");
        sql_query << "DELETE FROM " << table << " WHERE id='" << row2[0] << "'";
		simple_query(sql_query.str(),"purge_att_property()", dch);
    }
  }
  mysql_free_result(result);
}

//+------------------------------------------------------------------
/**
 *	method:	purge_pipe_property()
 *
 *	description:	purge an pipe property history table.
 *
 */
//+------------------------------------------------------------------
void DataBase::purge_pipe_property(const char *table,const char *field,const char *object,const char *pipe,const char *name, DatabaseConnectionHandle &dch) {

  TangoSys_MemStream sql_query;
  MYSQL_RES *result;
  MYSQL_ROW row2;

  //TANGO_LOG << "purge_pipe_property(" << object << "," << pipe << "," << name << ")" << std::endl;

  sql_query.str("");
  sql_query << "SELECT DISTINCT id,date FROM " << table
            << " WHERE " << field << "=\"" << object << "\" AND name=\"" << name
            << "\" AND pipe=\"" << pipe << "\" ORDER by date";

  result = query(sql_query.str(),"purge_pipe_property()", dch);
  int nb_item = mysql_num_rows(result);

  if(nb_item>historyDepth) {
    // Purge
    int toDelete = nb_item-historyDepth;
    for(int j=0;j<toDelete;j++) {
		row2 = mysql_fetch_row(result);
        sql_query.str("");
        sql_query << "DELETE FROM " << table << " WHERE id='" << row2[0] << "'";
		simple_query(sql_query.str(),"purge_pipe_property()", dch);
    }
  }
  mysql_free_result(result);
}

//+------------------------------------------------------------------
/**
 *	method:	host_port_from_ior()
 *
 *	description:	Get host and port from a device IOR
 *
 */
//+------------------------------------------------------------------

bool DataBase::host_port_from_ior(const char *iorstr,std::string &h_p)
{
	size_t s = (iorstr ? strlen(iorstr) : 0);

  	if (s < 4)
    	return false;

  	const char *p = iorstr;
  	if (p[0] != 'I' || p[1] != 'O' || p[2] != 'R' || p[3] != ':')
    	return false;

  	s = (s - 4) / 2;  // how many octets are there in the string
  	p += 4;

  	cdrMemoryStream buf((CORBA::ULong)s,0);

  	for (int i=0; i<(int)s; i++)
	{
    	int j = i*2;
    	CORBA::Octet v;

    	if (p[j] >= '0' && p[j] <= '9')
		{
      		v = ((p[j] - '0') << 4);
    	}
    	else if (p[j] >= 'a' && p[j] <= 'f')
		{
      		v = ((p[j] - 'a' + 10) << 4);
    	}
    	else if (p[j] >= 'A' && p[j] <= 'F')
		{
      		v = ((p[j] - 'A' + 10) << 4);
    	}
    	else
      		return false;

    	if (p[j+1] >= '0' && p[j+1] <= '9')
		{
      		v += (p[j+1] - '0');
    	}
    	else if (p[j+1] >= 'a' && p[j+1] <= 'f')
		{
      		v += (p[j+1] - 'a' + 10);
    	}
    	else if (p[j+1] >= 'A' && p[j+1] <= 'F')
		{
      		v += (p[j+1] - 'A' + 10);
    	}
    	else
      		return false;

    	buf.marshalOctet(v);
  	}

  	buf.rewindInputPtr();
  	CORBA::Boolean b = buf.unmarshalBoolean();
  	buf.setByteSwapFlag(b);

  	IOP::IOR ior;

  	ior.type_id = IOP::IOR::unmarshaltype_id(buf);
  	ior.profiles <<= buf;


    if (ior.profiles.length() == 0 && strlen(ior.type_id) == 0)
	{
      	return false;
    }
    else
	{
      	for (unsigned long count=0; count < ior.profiles.length(); count++)
		{
			if (ior.profiles[count].tag == IOP::TAG_INTERNET_IOP)
			{
	  			IIOP::ProfileBody pBody;
	  			IIOP::unmarshalProfile(ior.profiles[count],pBody);

//
// Three possible cases for host name:
// 1 - The host is stored in IOR as IP numbers
// 2 - The host name is stored in IOR as the canonical host name
// 3 - The FQDN is stored in IOR
// We allways try to get the host name as the FQDN
//

				ho = pBody.address.host.in();
				bool host_is_name = false;

				std::string::size_type pos = ho.find('.');
				if (pos == std::string::npos)
					host_is_name = true;
				else
				{
					for (unsigned int loop =0;loop < pos;++loop)
					{
						if (isdigit((int)ho[loop]) == 0)
						{
							host_is_name = true;
							break;
						}
					}
				}

				if (host_is_name == false)
				{
					struct sockaddr_in s;
					char service[20];

					s.sin_family = AF_INET;
					int res;
#ifdef _TG_WINDOWS_
					s.sin_addr.s_addr = inet_addr(ho.c_str());
					if (s.sin_addr.s_addr != INADDR_NONE)
#else
					res = inet_pton(AF_INET,ho.c_str(),&(s.sin_addr.s_addr));
					if (res == 1)
#endif
					{
						res = getnameinfo((const struct sockaddr *)&s,sizeof(s),ho_name,sizeof(ho_name),service,sizeof(service),0);
						if (res == 0)
						{
							h_p = ho_name;
							h_p = h_p + ':';
						}
						else
							h_p = ho + ':';
					}
					else
						h_p = ho + ':';
				}
				else
				{
					if (pos == std::string::npos)
					{
						Tango::DeviceProxy::get_fqdn(ho);
					}

					h_p = ho + ':';
				}

//
// Add port number
//

				std::stringstream ss;
				ss << pBody.address.port;
				h_p = h_p + ss.str();

				break;
			}
		}
	}

	return true;
}


//+------------------------------------------------------------------
/**
 *	method:	AutoLock class ctor and dtor
 *
 *	description:	AutoLock is a small helper class which get a MySQL
 *	              connection from the pool and manages the DB transaction.
 */
//+------------------------------------------------------------------

AutoLock::AutoLock(DataBase *db)
  :the_db(db),
  m_dch(the_db->dcp)
{
	const std::string sql_query{"SET autocommit=0;"}; // or START TRANSACTION
	the_db->simple_query(sql_query,"AutoLock",m_dch);
}

AutoLock::~AutoLock()
{
	const std::string sql_query{"SET autocommit=1;"}; // or COMMIT
	the_db->simple_query(sql_query,"~AutoLock",m_dch);
}

/// Parse a string to return an integer
/// Current implementation does not complain about trailing garbage.
template<>
int DataBase::ParseString<int>(const char * c_str)
{
  int val;
  const int ret = sscanf(c_str, "%d", &val);

  if(ret != 1 || ret == EOF)
  {
    WARN_STREAM << "Could not parse string: " << std::string(c_str) << std::endl;
    Tango::Except::throw_exception(DB_ParseStringError, "Could not parse string to number", "DataBase::ParseString");
  }

  return val;
}

}
