#include "dbqop.h"

namespace databases{

namespace odbc_database_operations{

	//STRUCTS DEFINITIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//DBConnection DEFINITIONS
	DBConnection::DBConnection():
		environment_handle(nullptr),database_connection_handle(nullptr),
		environment_handle_allocated(false),connection_handle_allocated(false),
		sql_command_prepared(false),asynch_support(false),is_connected(false),
		is_open(false),flag(0),connection_string(""),output_connection_string(""),
		server_string(""),user_string(""),password_string(""),last_message("")
	{

	}

	DBConnection::~DBConnection()
	{
		// Connection
		if(database_connection_handle!=SQL_NULL_HDBC)
		{
			SQLDisconnect(database_connection_handle);
			SQLFreeHandle(SQL_HANDLE_DBC,database_connection_handle);
			database_connection_handle=SQL_NULL_HDBC;
		}

		// Environment
		if(environment_handle!=SQL_NULL_HENV)
		{
			SQLFreeHandle(SQL_HANDLE_ENV,environment_handle);
			environment_handle=SQL_NULL_HENV;
		}
	}

	DBConnection& DBConnection::operator=(const DBConnection& rhs)
	{
		if(this != &rhs)// handle self assignment
		{
			this->environment_handle_allocated=rhs.environment_handle_allocated;
			this->connection_handle_allocated=rhs.connection_handle_allocated;
			this->sql_command_prepared=rhs.sql_command_prepared;
			this->asynch_support=rhs.asynch_support;
			this->is_connected=rhs.is_connected;
			this->is_open=rhs.is_open;
			this->connection_string=rhs.connection_string;
			this->output_connection_string=rhs.output_connection_string;
			this->server_string=rhs.server_string;
			this->user_string=rhs.user_string;
			this->password_string=rhs.password_string;
			this->last_message=rhs.last_message;
			this->flag=rhs.flag;
			this->environment_handle=rhs.environment_handle;//Environment handle
			this->database_connection_handle=rhs.database_connection_handle;//Database connection handle
		}
		return *this;
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//DBColumn DEFINITIONS
	DBColumn::DBColumn():
		table_owner(0),column_type(0),catalog_name_length(0),schema_name_length(0),
		table_name_length(0),column_name_length(0),column_type_name_length(0),
		data_length_indicator(0),column_buffer_length(0),column_buffer(nullptr)
	{

	}

	DBColumn::~DBColumn()
	{
		delete[] column_buffer;
	}

	DBColumn::DBColumn(const short& new_column_type,const long& buffer_length):
		table_owner(0),column_type(new_column_type),catalog_name_length(0),
		schema_name_length(0),table_name_length(0),column_name_length(0),
		column_type_name_length(0),data_length_indicator(0),
		column_buffer_length(buffer_length),column_buffer(nullptr)
	{
		column_buffer=new SQLCHAR[buffer_length];
	}

	DBColumn::DBColumn(const DBColumn& other):
		table_owner(0),column_type(0),catalog_name_length(0),schema_name_length(0),
		table_name_length(0),column_name_length(0),column_type_name_length(0),
		data_length_indicator(0),column_buffer_length(0),column_buffer(nullptr)
	{
		table_owner=other.table_owner;
		column_type=other.column_type;
        catalog_name_length=other.catalog_name_length;
        schema_name_length=other.schema_name_length;
        table_name_length=other.table_name_length;
        column_name_length=other.column_name_length;
        column_type_name_length=other.column_type_name_length;
        data_length_indicator=other.data_length_indicator;
        column_buffer_length=other.column_buffer_length;//Max byte length of the column
        memcpy(catalog_name,other.catalog_name,SQL_MAX_CATALOG_NAME_LEN);
        memcpy(schema_name,other.schema_name,SQL_MAX_SCHEMA_NAME_LEN);
        memcpy(table_name,other.table_name,SQL_MAX_TABLE_NAME_LEN);
        memcpy(column_name,other.column_name,SQL_MAX_COLUMN_NAME_LEN);
        memcpy(column_type_name,other.column_type_name,MAX_COL_TYPE_NAME_LENGTH);
        column_buffer=new SQLCHAR[other.column_buffer_length];
        memcpy(column_buffer,other.column_buffer,other.column_buffer_length);
	}

	DBColumn& DBColumn::operator=(const DBColumn& rhs)
	{
		if(this != &rhs)
		{
			this->table_owner=rhs.table_owner;
			this->column_type=rhs.column_type;
			this->catalog_name_length=rhs.catalog_name_length;
			this->schema_name_length=rhs.schema_name_length;
			this->table_name_length=rhs.table_name_length;
			this->column_name_length=rhs.column_name_length;
			this->column_type_name_length=rhs.column_type_name_length;
			this->data_length_indicator=rhs.data_length_indicator;
			this->column_buffer_length=rhs.column_buffer_length;//Max byte length of the column
			memcpy(this->catalog_name,rhs.catalog_name,SQL_MAX_CATALOG_NAME_LEN);
			memcpy(this->schema_name,rhs.schema_name,SQL_MAX_SCHEMA_NAME_LEN);
			memcpy(this->table_name,rhs.table_name,SQL_MAX_TABLE_NAME_LEN);
			memcpy(this->column_name,rhs.column_name,SQL_MAX_COLUMN_NAME_LEN);
			memcpy(this->column_type_name,rhs.column_type_name,MAX_COL_TYPE_NAME_LENGTH);
			this->column_buffer=new SQLCHAR[rhs.column_buffer_length];
			memcpy(this->column_buffer,rhs.column_buffer,rhs.column_buffer_length);
		}
		return *this;
	}

	brw::Archive& DBColumn::operator&(brw::Archive& ar)
	{
		return ar;
	}
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//DBRow DEFINITIONS
	DBRow::DBRow():
		table_owner(0),row_index(0),row_data()
	{
	}

	DBRow::DBRow(const DBRow& other):
		table_owner(other.table_owner),row_index(other.row_index),row_data(other.row_data)
	{
	}

	DBRow::DBRow(const uintptr_t& row_table_owner,const UINT& new_row_index):
		table_owner(0),row_index(new_row_index),row_data()
	{
		DBTable table=*reinterpret_cast<DBTable*>(row_table_owner);
		table_owner=row_table_owner;
		//row_index=new_row_index;
		row_data=RowData(table.total_columns);
		for(short i=0;i<table.total_columns;i++)
		{
			CellData value;
			value.buffer_size=table.table_columns[i].column_buffer_length;
			value.buffer=new SQLCHAR[table.table_columns[i].column_buffer_length];
			row_data[i]=value;
		}
	}

	DBRow& DBRow::operator=(const DBRow& rhs)
	{
		if(this != &rhs)
		{
			this->table_owner=rhs.table_owner;
			this->row_index=rhs.row_index;
			this->row_data=rhs.row_data;
		}
		return *this;
	}

	DBRow::~DBRow()
	{

	}

	void* DBRow::getValue(const short& column_index) const
	{
		DBTable table=*reinterpret_cast<DBTable*>(table_owner);
		if(table.total_columns==0)
		{
			return nullptr;
		}
		CellData value=row_data[column_index];
		if(!value.buffer)
		{
			return nullptr;
		}
		long len=value.buffer_size;
		switch(table.table_columns[column_index].column_type)
		{
			case SQL_BIT:
			{

				break;
			}
			case SQL_CHAR:
			{
				char data1[len+1];
				for(long i=0;i<len;i++)
				{
					data1[i]=(char)value.buffer[i];
				}
				data1[len]='\0';
				static std::string value1;
				value1=std::string(data1);
				return &value1;
			}
			case SQL_VARCHAR:
			{
				char* data2=new char[len+1];
				for(long i=0;i<len;i++)
				{
					data2[i]=(char)value.buffer[i];
				}
				data2[len]='\0';
				static std::string value2;
				value2=std::string(data2);
				return &value2;
			}
			case SQL_LONGVARCHAR:
			{

				break;
			}
			case SQL_WCHAR:
			{

				break;
			}
			case SQL_WVARCHAR:
			{

				break;
			}
			case SQL_WLONGVARCHAR:
			{

				break;
			}
			case SQL_TINYINT:
			{
				PSQLCHAR data7=reinterpret_cast<PSQLCHAR>(value.buffer);
                static unsigned char value7;
				value7=(unsigned char)(*data7);
				return &value7;
			}
			case SQL_SMALLINT:
			{
				short* data8=reinterpret_cast<short*>(value.buffer);
                static short value8;
				value8=(short)(*data8);
				return &value8;
			}
			case SQL_INTEGER:
			{
				int* data9=reinterpret_cast<int*>(value.buffer);
                static int value9;
				value9=(int)(*data9);
				return &value9;
				break;
			}
			case SQL_BIGINT:
			{
				long* data10=reinterpret_cast<long*>(value.buffer);
				static long value10;
				value10=long(*data10);
				return &value10;
			}
			case SQL_DECIMAL:
			{

				break;
			}
			case SQL_NUMERIC:
			{

				break;
			}
			case SQL_REAL:
			{

				break;
			}
			case SQL_FLOAT:
			{
				float* data14=reinterpret_cast<float*>(value.buffer);
				static float value14;
				value14=float(*data14);
				return &value14;
			}
			case SQL_DOUBLE:
			{
				double* data15=reinterpret_cast<double*>(value.buffer);
				static double value15;
				value15=double(*data15);
				return &value15;
			}
			case SQL_DATE:
			{

				break;
			}
			case SQL_TIME:
			{

				break;
			}
			case SQL_TYPE_TIMESTAMP:
			{
				PTIMESTAMP buffer18=reinterpret_cast<PTIMESTAMP>(value.buffer);
				tm data18;

				data18.tm_year=buffer18->year;
				data18.tm_mon=buffer18->month;
				data18.tm_mday=buffer18->day;
				data18.tm_hour=buffer18->hour;
				data18.tm_min=buffer18->minute;
				data18.tm_sec=buffer18->second;

				tm timeinfo = {};
				timeinfo.tm_year = data18.tm_year - 1900;
				timeinfo.tm_mon = data18.tm_mon - 1;
				timeinfo.tm_mday = data18.tm_mday;
				mktime(&timeinfo);
				data18.tm_yday = timeinfo.tm_yday;
				data18.tm_wday=timeinfo.tm_wday;

				static tm value18;
				value18=data18;
				return &value18;
			}
			case SQL_BINARY:
			{
				break;
			}
			case SQL_VARBINARY:
			{
				break;
			}
			case SQL_LONGVARBINARY:
			{
				break;
			}
			default:
			{
				break;
			}
		}
		return nullptr;
	}

	void DBRow::getValue(const short& column_index,CellData& value) const
	{
		DBTable table=*reinterpret_cast<DBTable*>(table_owner);
		if(table.total_columns==0)
		{
			value.buffer=nullptr;
			value.buffer_size=0;
		}
		else
		{
			CellData value_data;
			CellData cell=row_data[column_index];
			value_data.buffer_size=cell.buffer_size;
			value_data.buffer=new SQLCHAR[value_data.buffer_size];
			memcpy(
					value_data.buffer,
					cell.buffer,
					cell.buffer_size
					);
			value=value_data;
		}
	}

	void DBRow::setValue(const short& column_index,void* value) const
	{
	}

	void DBRow::setValue(const short& column_index,const CellData& value)
	{
		row_data[column_index]=value;
	}




	DBRow DBRow::cloneRow() const
	{
		DBRow clone;
		clone.table_owner=table_owner;
		clone.row_index=row_index;
		clone.row_data=row_data;
		return clone;
	}
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//DBTable DEFINITIONS
	DBTable::DBTable():
		sql_statement(""),database_name(""),catalog_name(""),schema_name(""),table_name(""),
		total_columns(0),total_primary_key_columns(0),total_foreign_key_columns(0),
		primary_key_columns(nullptr),foreign_key_columns(nullptr),total_rows(0),
		table_columns(nullptr),statement_handle(nullptr),table_data(),
		is_primary_keys_set(false),is_foreign_keys_set(false)
	{
	}

	DBTable::~DBTable()
	{
		if(statement_handle!=SQL_NULL_HSTMT)
		{
			SQLFreeHandle(SQL_HANDLE_STMT,statement_handle);
			statement_handle=SQL_NULL_HSTMT;
		}
		delete[] table_columns;
		delete[] primary_key_columns;
	}

	DBTable::DBTable(const short& new_total_columns,DBColumn* new_table_columns):
		sql_statement(""),database_name(""),catalog_name(""),schema_name(""),table_name(""),
		total_columns(0),total_primary_key_columns(0),total_foreign_key_columns(0),
		primary_key_columns(nullptr),foreign_key_columns(nullptr),total_rows(0),
		table_columns(nullptr),statement_handle(nullptr),table_data(),
		is_primary_keys_set(false),is_foreign_keys_set(false)
	{
		total_columns=new_total_columns;
		table_columns=new DBColumn[new_total_columns];
		primary_key_columns=new short[new_total_columns];
		foreign_key_columns=new short[new_total_columns];
		for(short i=0;i<new_total_columns;i++)
		{
			table_columns[i]=DBColumn(new_table_columns[i]);
		}
		table_data=TableData();
	}

	DBTable::DBTable(const DBTable& other):
		sql_statement(other.sql_statement),database_name(other.database_name),
		catalog_name(other.catalog_name),schema_name(other.schema_name),
		table_name(other.table_name),total_columns(0),total_primary_key_columns(0),
		total_foreign_key_columns(0),primary_key_columns(nullptr),
		foreign_key_columns(nullptr),total_rows(other.total_rows),table_columns(nullptr),
		statement_handle(nullptr),table_data(),is_primary_keys_set(false),
		is_foreign_keys_set(false)
	{
		total_columns=other.total_columns;
		total_primary_key_columns=other.total_primary_key_columns;
		total_foreign_key_columns=other.total_foreign_key_columns;
		table_columns=new DBColumn[other.total_columns];
		primary_key_columns=new short[other.total_primary_key_columns];
		foreign_key_columns=new short[other.total_foreign_key_columns];
		for(short i=0;i<other.total_columns;i++)
		{
			table_columns[i]=other.table_columns[i];
		}
		for(short i=0;i<other.total_primary_key_columns;i++)
		{
			primary_key_columns[i]=other.primary_key_columns[i];
		}
		for(short i=0;i<other.total_foreign_key_columns;i++)
		{
			foreign_key_columns[i]=other.foreign_key_columns[i];
		}
		statement_handle=other.statement_handle;
		table_data=other.table_data;
	}

	DBTable& DBTable::operator=(const DBTable& rhs)
	{
		if(this != &rhs)
		{
			this->sql_statement=rhs.sql_statement;
			this->database_name=rhs.database_name;
			this->catalog_name=rhs.catalog_name;
			this->schema_name=rhs.schema_name;
			this->table_name=rhs.table_name;
			this->total_columns=rhs.total_columns;
			this->total_primary_key_columns=rhs.total_primary_key_columns;
			this->total_foreign_key_columns=rhs.total_foreign_key_columns;
			this->total_rows=rhs.total_rows;
			this->table_columns=new DBColumn[rhs.total_columns];
			this->primary_key_columns=new short[rhs.total_primary_key_columns];
			this->foreign_key_columns=new short[rhs.total_foreign_key_columns];
			for(short i=0;i<rhs.total_columns;i++)
			{
				this->table_columns[i]=rhs.table_columns[i];
			}
			for(short i=0;i<rhs.total_primary_key_columns;i++)
			{
				this->primary_key_columns[i]=rhs.primary_key_columns[i];
			}
			for(short i=0;i<rhs.total_foreign_key_columns;i++)
			{
				this->foreign_key_columns[i]=rhs.foreign_key_columns[i];
			}
			this->statement_handle=rhs.statement_handle;
			this->table_data=rhs.table_data;
			this->is_primary_keys_set=rhs.is_primary_keys_set;
			this->is_foreign_keys_set=rhs.is_foreign_keys_set;
		}
		return *this;
	}

	brw::Archive& DBTable::operator&(brw::Archive& ar)
	{
		switch(ar.getFileVersion())
        {
		case FILE_VERSION:
			{
				break;

			}
		default:
			{
				break;
			}
        }
        return ar;
	}



	DBRow DBTable::newRow() const
	{
		DBRow new_row=DBRow(reinterpret_cast<uintptr_t>(this),0);
		return new_row;
	}

	DBRow DBTable::getRow(const UINT& row_index) const
	{
		DBRow row=DBRow(reinterpret_cast<uintptr_t>(this),row_index);
		for(short i=0;i<total_columns;i++)
		{
			CellData value;
			getValue(row_index,i,value);
			row.setValue(i,value);
		}
		return row;
	}

	void DBTable::addRow(UINT& new_row_index)
	{
		RowData row_data=RowData(total_columns);
		for(short i=0;i<total_columns;i++)
		{
			CellData value;
			value.buffer_size=table_columns[i].column_buffer_length;
			value.buffer=new SQLCHAR[table_columns[i].column_buffer_length];
			row_data[i]=value;
		}
		table_data.push_back(row_data);
		new_row_index=total_rows;
		total_rows++;
	}

	void DBTable::addRow(const DBRow& new_row)
	{
		RowData row_data=RowData(total_columns);
		for(short i=0;i<total_columns;i++)
		{
			CellData value;
			new_row.getValue(i,value);
			row_data[i]=value;
		}
		table_data.push_back(row_data);
		total_rows++;
	}

	void* DBTable::getValue(const UINT& row_index,const short& column_index) const
	{
		if((total_columns==0) || (total_rows==0))
		{
			return nullptr;
		}
		CellData value=table_data[row_index][column_index];
		if(!value.buffer)
		{
			return nullptr;
		}
		long len=value.buffer_size;
		switch(table_columns[column_index].column_type)
		{
			case SQL_BIT:
			{

				break;
			}
			case SQL_CHAR:
			{
				char data1[len+1];
				for(long i=0;i<len;i++)
				{
					data1[i]=(char)value.buffer[i];
				}
				data1[len]='\0';
				static std::string value1;
				value1=std::string(data1);
				return &value1;
			}
			case SQL_VARCHAR:
			{
				char* data2=new char[len+1];
				for(long i=0;i<len;i++)
				{
					data2[i]=(char)value.buffer[i];
				}
				data2[len]='\0';
				static std::string value2;
				value2=std::string(data2);
				return &value2;
			}
			case SQL_LONGVARCHAR:
			{

				break;
			}
			case SQL_WCHAR:
			{

				break;
			}
			case SQL_WVARCHAR:
			{

				break;
			}
			case SQL_WLONGVARCHAR:
			{

				break;
			}
			case SQL_TINYINT:
			{
				PSQLCHAR data7=reinterpret_cast<PSQLCHAR>(value.buffer);
                static unsigned char value7;
				value7=(unsigned char)(*data7);
				return &value7;
			}
			case SQL_SMALLINT:
			{
				short* data8=reinterpret_cast<short*>(value.buffer);
                static short value8;
				value8=(short)(*data8);
				return &value8;
			}
			case SQL_INTEGER:
			{
				int* data9=reinterpret_cast<int*>(value.buffer);
                static int value9;
				value9=(int)(*data9);
				return &value9;
				break;
			}
			case SQL_BIGINT:
			{
				long* data10=reinterpret_cast<long*>(value.buffer);
				static long value10;
				value10=long(*data10);
				return &value10;
			}
			case SQL_DECIMAL:
			{

				break;
			}
			case SQL_NUMERIC:
			{

				break;
			}
			case SQL_REAL:
			{

				break;
			}
			case SQL_FLOAT:
			{
				float* data14=reinterpret_cast<float*>(value.buffer);
				static float value14;
				value14=float(*data14);
				return &value14;
			}
			case SQL_DOUBLE:
			{
				double* data15=reinterpret_cast<double*>(value.buffer);
				static double value15;
				value15=double(*data15);
				return &value15;
			}
			case SQL_DATE:
			{

				break;
			}
			case SQL_TIME:
			{

				break;
			}
			case SQL_TYPE_TIMESTAMP:
			{
				PTIMESTAMP buffer18=reinterpret_cast<PTIMESTAMP>(value.buffer);
				tm data18;

				data18.tm_year=buffer18->year;
				data18.tm_mon=buffer18->month;
				data18.tm_mday=buffer18->day;
				data18.tm_hour=buffer18->hour;
				data18.tm_min=buffer18->minute;
				data18.tm_sec=buffer18->second;

				tm timeinfo = {};
				timeinfo.tm_year = data18.tm_year - 1900;
				timeinfo.tm_mon = data18.tm_mon - 1;
				timeinfo.tm_mday = data18.tm_mday;
				mktime(&timeinfo);
				data18.tm_yday = timeinfo.tm_yday;
				data18.tm_wday=timeinfo.tm_wday;

				static tm value18;
				value18=data18;
				return &value18;
			}
			case SQL_BINARY:
			{

				break;
			}
			case SQL_VARBINARY:
			{

				break;
			}
			case SQL_LONGVARBINARY:
			{

				break;
			}
			default:
			{

			}
		}
		return nullptr;
	}

	void DBTable::getValue
		(
			const UINT& row_index,
			const short& column_index,
			CellData& value
		) const
	{
		CellData cell=table_data[row_index][column_index];
		if(!cell.buffer)
		{
			value.buffer=nullptr;
			value.buffer_size=0;
		}
		else
		{
			value.buffer_size=cell.buffer_size;
			value.buffer=new SQLCHAR[cell.buffer_size];
			memcpy(value.buffer,cell.buffer,cell.buffer_size);
		}
	}

	void DBTable::setValue
		(
			const UINT& row_index,
			const short& column_index,
			void* value,
			const long& value_length
		)
	{
		CellData cell_value;
		if(!value)
		{
			cell_value.buffer=nullptr;
			cell_value.buffer_size=0;
		}
		else
		{
			long len;
			switch(table_columns[column_index].column_type)
			{
				case SQL_BIT:
				{
					break;
				}
				case SQL_CHAR:
				{
					char* buffer1=reinterpret_cast<char*>(value);
					len=sizeof(char);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer1,len);
					break;
				}
				case SQL_VARCHAR:
				{
					char* buffer2=reinterpret_cast<char*>(value);
					len=value_length;
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer2,len);
					break;
				}
				case SQL_LONGVARCHAR:
				{
					break;
				}
				case SQL_WCHAR:
				{
					break;
				}
				case SQL_WVARCHAR:
				{
					break;
				}
				case SQL_WLONGVARCHAR:
				{
					break;
				}
				case SQL_TINYINT:
				{
					unsigned char* buffer7=reinterpret_cast<unsigned char*>(value);
					len=sizeof(unsigned char);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer7,len);
					break;
				}
				case SQL_SMALLINT:
				{
					short* buffer8=reinterpret_cast<short*>(value);
					len=sizeof(short);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer8,len);
					break;
				}
				case SQL_INTEGER:
				{
					int* buffer9=reinterpret_cast<int*>(value);
					len=sizeof(int);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer9,len);
					break;
				}
				case SQL_BIGINT:
				{
					long* buffer10=reinterpret_cast<long*>(value);
					len=sizeof(long);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer10,len);
					break;
				}
				case SQL_DECIMAL:
				{
					break;
				}
				case SQL_NUMERIC:
				{
					break;
				}
				case SQL_REAL:
				{
					break;
				}
				case SQL_FLOAT:
				{
					float* buffer14=reinterpret_cast<float*>(value);
					len=sizeof(float);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer14,len);
					break;
				}
				case SQL_DOUBLE:
				{
					double* buffer15=reinterpret_cast<double*>(value);
					len=sizeof(double);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer15,len);
					break;
				}
				case SQL_DATE:
				{
					break;
				}
				case SQL_TIME:
				{
					break;
				}
				case SQL_TYPE_TIMESTAMP:
				{
					PTIMESTAMP buffer18=reinterpret_cast<PTIMESTAMP>(value);
					len=sizeof(SQL_TIMESTAMP_STRUCT);
					cell_value.buffer=new SQLCHAR[len];
					cell_value.buffer_size=len;
					memcpy(cell_value.buffer,buffer18,len);
					break;
				}
				case SQL_BINARY:
				{
					break;
				}
				case SQL_VARBINARY:
				{
					break;
				}
				case SQL_LONGVARBINARY:
				{
					break;
				}
				default:
				{
					break;
				}
			}
		}
		table_data[row_index][column_index]=cell_value;
	}

	void DBTable::setValue
		(
			const UINT& row_index,
			const short& column_index,
			const CellData& value
		)
	{
		CellData cell;
		if(!value.buffer)
		{
			cell.buffer=nullptr;
			cell.buffer_size=0;
		}
		else
		{
			cell.buffer_size=value.buffer_size;
			cell.buffer=new SQLCHAR[value.buffer_size];
			memcpy(cell.buffer,value.buffer,value.buffer_size);
		}
		table_data[row_index][column_index]=cell;
	}

	bool DBTable::findRow
		(
			const short& key_column_index,
			const CellData& value,
			const UINT& starting_row_index,
			DBRow& row
		)
	{
		if(total_columns==0 || total_rows==0)
		{
			return false;
		}
		for(UINT i=starting_row_index;i<total_rows;i++)
		{
			if(memcmp
				(
					table_data[i][key_column_index].buffer,
					value.buffer,
					value.buffer_size
				)==0)
			{
				row=getRow(i);
				return true;
			}
		}
		return false;
	}

	bool DBTable::findRowPattern
		(
			const short& key_column_index,
			const std::string& value,
			const UINT& starting_row_index,
			DBRow& row
		) const
	{
		if(total_columns==0 || total_rows==0)
		{
			return false;
		}
		const char*       pattern;
		CellData          cell;
		std::string::size_type pos=value.find_first_of('%');

		if(pos==std::string::npos)//When search is an exact search
		{
			pattern=new char[value.size()];
			memcpy((char*)pattern,value.c_str(),value.size());
			for(UINT i=starting_row_index;i<total_rows;i++)
			{
				cell=table_data[i][key_column_index];
				if(cell.buffer)
				{
					if(std::strcmp(reinterpret_cast<char*>(cell.buffer),pattern)==0)
					{
						row=getRow(i);
						return true;
					}
				}
			}
		}
		else if(pos==0 && value.find_last_of('%')==(value.size()-1))//When search pattern is of the form %pattern%
		{
			std::string::size_type len=value.size()-2;
			pattern=new char[len];
			memcpy((char*)pattern,value.substr(1,len).c_str(),len);
			for(UINT i=starting_row_index;i<total_rows;i++)
			{
				cell=table_data[i][key_column_index];
				if(cell.buffer)
				{
					if(std::strstr(reinterpret_cast<char*>(cell.buffer),pattern))
					{
						row=getRow(i);
						return true;
					}
				}
			}
		}
		else if(pos==0)//When search pattern is of the form %pattern
		{
			std::string::size_type len=value.size()-1;
			pattern=new char[len];
			memcpy((char*)pattern,value.substr(1,len).c_str(),len);
			char* match;
			for(UINT i=starting_row_index;i<total_rows;i++)
			{
				cell=table_data[i][key_column_index];
				if(cell.buffer)
				{
					if(cell.buffer_size>=(long)len)
					{
						match=std::strstr(reinterpret_cast<char*>(cell.buffer),pattern);
						if(match)
						{
							if((match-reinterpret_cast<char*>(cell.buffer))==
									(long)(cell.buffer_size-len))
							{
								row=getRow(i);
								return true;
							}
						}
					}
				}
			}
		}
		else//When search pattern is of the form pattern%
		{
			std::string::size_type len=value.size()-1;
			pattern=new char[len];
			memcpy((char*)pattern,value.substr(0,len).c_str(),len);
			char* match;
			for(UINT i=starting_row_index;i<total_rows;i++)
			{
				cell=table_data[i][key_column_index];
				if(cell.buffer)
				{
					if(cell.buffer_size>=(long)len)
					{
						match=std::strstr(reinterpret_cast<char*>(cell.buffer),pattern);
						if(match)
						{
							if((match-reinterpret_cast<char*>(cell.buffer))==0)
							{
								row=getRow(i);
								return true;
							}
						}
					}
				}
			}
		}
		return false;
	}

	bool DBTable::getPrimaryKeys
		(
			short*& primary_keys,
			short& total_primary_keys,
			std::string& message
		)
	{
		if(!is_primary_keys_set)
		{
			DBKeyColumn* table_primary_keys;
			if(!DBGetPrimaryKeys
					(
						*this,
						table_primary_keys,
						total_primary_key_columns,
						message
					))
			{
				return false;
			}
			primary_key_columns=new short[total_primary_key_columns];
			for(short i=0;i<total_primary_key_columns;i++)
			{
				primary_key_columns[i]=table_primary_keys[i].key_ordinal;
			}
		}
		total_primary_keys=total_primary_key_columns;
		primary_keys=new short[total_primary_key_columns];
		for(short i=0;i<total_primary_key_columns;i++)
		{
			primary_keys[i]=primary_key_columns[i];
		}
		is_primary_keys_set=true;
		return true;
	}

	bool DBTable::getForeignKeys
		(
			short*& foreign_keys,
			short& total_foreign_keys,
			std::string& message
		)
	{
		if(!is_foreign_keys_set)
		{
			DBKeyColumn* table_foreign_keys;
			if(!DBGetPrimaryKeys
					(
						*this,
						table_foreign_keys,
						total_foreign_key_columns,
						message
					))
			{
				return false;
			}
			foreign_key_columns=new short[total_foreign_key_columns];
			for(short i=0;i<total_primary_key_columns;i++)
			{
				foreign_key_columns[i]=table_foreign_keys[i].key_ordinal;
			}
		}
		total_foreign_keys=total_foreign_key_columns;
		foreign_keys=new short[total_foreign_key_columns];
		for(short i=0;i<total_foreign_key_columns;i++)
		{
			foreign_keys[i]=foreign_key_columns[i];
		}
		is_foreign_keys_set=true;
		return true;
	}

	DBTable DBTable::cloneTable()
	{
	    DBTable table;
	    table.sql_statement=sql_statement;
	    table.database_name=database_name;
	    table.catalog_name=catalog_name;
	    table.schema_name=schema_name;
	    table.table_name=table_name;
	    table.total_columns=total_columns;
	    table.total_primary_key_columns=total_primary_key_columns;
	    table.total_rows=total_rows;
	    table.table_columns=new DBColumn[total_columns];
	    table.primary_key_columns=new short[total_columns];
	    for(short i=0;i<total_columns;i++)
		{
			table.table_columns[i]=DBColumn(table_columns[i]);
			table.primary_key_columns[i]=primary_key_columns[i];
		}
	    table.table_columns=table_columns;
		table.statement_handle=nullptr;
		table.table_data=TableData(table_data);
	    return table;
	}

	DBTable DBTable::cloneSchema()
	{
		DBTable table;
	    table.sql_statement=sql_statement;
	    table.database_name=database_name;
	    table.catalog_name=catalog_name;
	    table.schema_name=schema_name;
	    table.table_name=table_name;
	    table.total_columns=total_columns;
	    table.total_primary_key_columns=total_primary_key_columns;
	    table.total_rows=total_rows;
	    table.table_columns=new DBColumn[total_columns];
	    table.primary_key_columns=new short[total_columns];
	    for(short i=0;i<total_columns;i++)
		{
			table.table_columns[i]=DBColumn(table_columns[i]);
			table.primary_key_columns[i]=primary_key_columns[i];
		}
		table.statement_handle=nullptr;
		table.table_data=TableData();
	    return table;
	}

	DBTable DBTable::getSubTable
		(
			const short columns_indices[],
			const short& total_sub_columns
		)
	{
		DBColumn* columns;
		DBTable   table;
		UINT      n;
		CellData  value;

		columns=new DBColumn[total_sub_columns];
		for(short i=0;i<total_sub_columns;i++)
		{
			columns[i]=table_columns[columns_indices[i]];
		}

		table=DBTable(total_sub_columns,columns);

		for(UINT i=0;i<total_rows;i++)
		{
			table.addRow(n);
			for(short j=0;j<total_sub_columns;j++)
			{
				getValue(i,columns_indices[j],value);
				table.setValue(n,j,value);
			}
		}

		return table;
	}

	void DBTable::splitPKFKDC
		(
			DBTable& primary_keys,
			DBTable& foreign_keys,
			DBTable& data_columns,
			bool& has_primary_keys,
			bool& has_foreign_keys
		)
	{
		if(!is_primary_keys_set)
		{
			short* pk_indices;
			short  total_pk;
			std::string message;
			getPrimaryKeys(pk_indices,total_pk,message);
		}
		if(!is_foreign_keys_set)
		{
			short* fk_indices;
			short  total_fk;
			std::string message;
			getForeignKeys(fk_indices,total_fk,message);
		}

		UINT      n;
		CellData  value;
		short*    column_iterator;
		short*    pk_end=primary_key_columns+total_primary_key_columns;
		short*    fk_end=foreign_key_columns+total_foreign_key_columns;
		short     pk_count=0;
		short     fk_count=0;
		short     dc_count=0;
		short     total_data_columns=total_columns-total_primary_key_columns
										-total_foreign_key_columns;
		DBColumn* pk_columns=new DBColumn[total_primary_key_columns];
		DBColumn* fk_columns=new DBColumn[total_foreign_key_columns];
		DBColumn* dc_columns=new DBColumn[total_data_columns];
		TABLE_COLUMN_TYPES column_types[total_columns];
		for(short i=0;i<total_columns;i++)
		{
			column_iterator=std::find
				(
					primary_key_columns,
					pk_end,
					i
				);
			if(column_iterator!=pk_end)
			{
				column_types[i]=TABLE_COLUMN_TYPES::PRIMARY_KEY;
				pk_columns[pk_count]=table_columns[i];
				pk_count++;
			}
			else
			{
				column_iterator=std::find
					(
						foreign_key_columns,
						fk_end,
						i
					);
				if(column_iterator!=fk_end)
				{
					column_types[i]=TABLE_COLUMN_TYPES::FOREIGN_KEY;
					fk_columns[fk_count]=table_columns[i];
					fk_count++;
				}
				else
				{
					column_types[i]=TABLE_COLUMN_TYPES::DATA_COLUMN;
					dc_columns[dc_count]=table_columns[i];
					dc_count++;
				}
			}
		}

		if(pk_count>0)
		{
			primary_keys=DBTable(pk_count,pk_columns);
			has_primary_keys=true;
		}
		else
		{
			has_primary_keys=false;
		}
		if(fk_count>0)
		{
			foreign_keys=DBTable(fk_count,fk_columns);
			has_foreign_keys=true;
		}
		else
		{
			has_foreign_keys=false;
		}
		if(dc_count>0)
		{
			data_columns=DBTable(dc_count,dc_columns);
		}
		pk_count=0;
		fk_count=0;
		dc_count=0;
		for(short j=0;j<total_columns;j++)
		{
			switch(column_types[j])
			{
				case TABLE_COLUMN_TYPES::PRIMARY_KEY:
				{
					for(UINT i=0;i<total_rows;i++)
					{
						getValue(i,j,value);
						primary_keys.addRow(n);
						primary_keys.setValue(n,pk_count,value);
					}
					pk_count++;
					break;
				}
				case TABLE_COLUMN_TYPES::FOREIGN_KEY:
				{
					for(UINT i=0;i<total_rows;i++)
					{
						getValue(i,j,value);
						foreign_keys.addRow(n);
						foreign_keys.setValue(n,fk_count,value);
					}
					fk_count++;
					break;
				}
				case TABLE_COLUMN_TYPES::DATA_COLUMN:
				{
					for(UINT i=0;i<total_rows;i++)
					{
						getValue(i,j,value);
						data_columns.addRow(n);
						data_columns.setValue(n,dc_count,value);
					}
					dc_count++;
					break;
				}
			}
		}

	}

	SQLHSTMT& getStatementHandle(DBTable& table)
	{
		return table.statement_handle;
	}

	void setStatementHandle(DBTable& table,const SQLHSTMT handle)
	{
		table.statement_handle=handle;
	}
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//DBDataSet DEFINITIONS

	DBDataSet::DBDataSet():
		data_connection(),data_tables(),total_data_tables(0)
	{
		total_data_tables=0;
	}

	DBDataSet::DBDataSet
		(
			const std::string& sql_statement,
			const std::string& driver,
			const std::string& server,
			const std::string& user,
			const std::string& password
		):
		data_connection(),data_tables(),total_data_tables(0)
	{
		DBConnect(data_connection,driver,server,user,password);
	}

	DBDataSet::~DBDataSet()
	{}

	std::vector<DBTable>::iterator DBDataSet::begin()
	{
		return data_tables.begin();
	}

	std::vector<DBTable>::iterator DBDataSet::end()
	{
		return data_tables.end();
	}
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//PROCEDURES DEFINITIONS
	bool DBConnect
		(
			DBConnection& connection,
			const std::string& driver,
			const std::string& server,
			const std::string& user,
			const std::string& password
		)
	{
		SQLHWND   app_handle;
		SQLRETURN rcode;
		int       nstr;
		char*     msg;

	#ifdef __linux__
		app_handle=nullptr;
	#endif // __linux__

	#ifdef __windows__
		app_handle=GetDesktopWindow();
	#endif // __windows__

		//Allocate environment handle
		rcode=SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&connection.environment_handle);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.environment_handle_allocated=false;
			connection.last_message="\n[DBConnect] Error allocating handle in \
			SQLAllocHandle!";
			return false;
		}
		connection.environment_handle_allocated=true;

		//Set ODBC version environmet attribute
		rcode=SQLSetEnvAttr
			(
				connection.environment_handle,
				SQL_ATTR_ODBC_VERSION,
				(SQLPOINTER)SQL_OV_ODBC3,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBConnect] Error setting environment attribute in \
				SQLSetEnvAttr with SQL_ATTR_ODBC_VERSION attribute!";
			return false;
		}

		//Allocate database connection handle
		rcode=SQLAllocHandle
			(
				SQL_HANDLE_DBC,
				connection.environment_handle,
				&connection.database_connection_handle
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.connection_handle_allocated=false;
			connection.last_message="\n[DBConnect] Error allocating connection handle in \
				SQLAllocHandle!";
			return false;
		}

		PSQLCHAR    input_connection_string;
		SQLSMALLINT input_connection_string_length;//(SQLSMALLINT)(sizeof(inConStr)/sizeof(SQLCHAR));
		SQLSMALLINT output_connection_string_buffer_length=1024;//Applications should allocate at least 1,024 characters for this buffer
		SQLCHAR     output_connection_string[output_connection_string_buffer_length];
		SQLSMALLINT output_connection_string_length;
		//===========================================================================================================================================================================
		//VERY SENSITIVE PART OF THE APPLICATION
		//AN EXAMPLE OF CONNECTION STRING THAT WORKS IS "DRIVER={Oracle73 Ver 2.5};UID=DAVALILLOCM;ASY=ON;DBQ=OCCP05;PWD=DAVALILLOCM&PDV;SAVEFILE=CONSTR.dsn;Trusted_Connection=YES;"
		//AN EXAMPLE OF FILEDSN THAT WORKS IS "FILEDSN=d:\\Documents and Settings\\davalillocm\\Escritorio\\CONNECTION.dsn;";
		nstr=0;
		connection.connection_string="DRIVER={DRIVERPAR};UID=UIDPAR;ASY=ON;DBQ=SERVERPAR;\
		PWD=PWDPAR;SAVEFILE=CONSTR.dsn;Trusted_Connection=YES;";

		//Replace driver parameter
		connection.connection_string=
		connection.connection_string.replace
			(
				connection.connection_string.find("DRIVERPAR",nstr),
				9,
				driver
			);
		nstr+=driver.length();

		//Replace user id parameter
		connection.connection_string=
		connection.connection_string.replace
			(
				connection.connection_string.find("UIDPAR",nstr),
				6,
				user
			);
		nstr+=user.length();

		//Replace server parameter
		connection.connection_string=
		connection.connection_string.replace
			(
				connection.connection_string.find("SERVERPAR",nstr),
				9,
				server
			);
		nstr+=server.length();

		//Replace password parameter
		connection.connection_string=
		connection.connection_string.replace
			(
				connection.connection_string.find("PWDPAR",nstr),
				6,
				password
			);
		nstr+=password.length();
		//===========================================================================================================================================================================

		input_connection_string=(PSQLCHAR)connection.connection_string.c_str();
		input_connection_string_length=(SQLSMALLINT)connection.connection_string.length();
		//(SQLSMALLINT)(sizeof(inConStr)/sizeof(SQLCHAR));

		rcode=SQLDriverConnect
			(
				connection.database_connection_handle,
				app_handle,
				input_connection_string,
				input_connection_string_length,
				output_connection_string,
				output_connection_string_buffer_length,
				&output_connection_string_length,
				SQL_DRIVER_NOPROMPT
			);//SQL_DRIVER_NOPROMPT
		if(rcode != SQL_SUCCESS && rcode != SQL_SUCCESS_WITH_INFO)
		{
			msg=new char[output_connection_string_length+1];
			memcpy
				(
					msg,
					(char*)output_connection_string,
					output_connection_string_buffer_length
				);
			connection.last_message="\n[DBConnect] ";
			connection.last_message+=msg;
			connection.last_message+=DBGetErrorMsg
				(
					connection.database_connection_handle,
					SQL_HANDLE_DBC
				);
			connection.flag=(int)input_connection_string_length;//(int)rcode;
			delete[] msg;
			return false;
		}

		msg=new char[output_connection_string_length+1];
		memcpy(msg,(char*)output_connection_string,output_connection_string_length);
		connection.last_message=std::string(msg);

		connection.is_connected=true;
		connection.is_open=true;
		connection.connection_string="";
		connection.server_string=server;
		connection.user_string=user;
		connection.password_string=password;
		connection.flag=0;

		return true;
	}

	bool DBDisconnect(DBConnection& connection)
	{
		if(SQLDisconnect(connection.database_connection_handle)==SQL_SUCCESS)
		{
			return true;
		}
		return false;
	}

	VECSTR DBGetDrivers(DBConnection& connection)
	{
		SQLRETURN   rcode;
		SQLCHAR     driverName[SQL_MAX_DSN_LENGTH];
		SQLSMALLINT driverNameLength;
		SQLCHAR     driverAttr[1024];
		SQLSMALLINT driverAttrLength;

		bool odbcsucceed;
		int n;
		int m;
		VECSTR drivers=VECSTR();

		odbcsucceed=true;
		while(odbcsucceed)
		{
			rcode=SQLDrivers
				(
					connection.environment_handle,
					SQL_FETCH_NEXT,
					driverName,
					SQL_MAX_DSN_LENGTH,
					&driverNameLength,
					driverAttr,
					1024,
					&driverAttrLength
				);
			if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
			{
				odbcsucceed=false;
			}
			else
			{
				n=(int)driverNameLength+1;
				m=(int)driverAttrLength+1;
				char DN[n];
				char DNAttrs[m];
				memcpy(DN,(char*)driverName,n);
				memcpy(DNAttrs,(char*)driverAttr,m);
				drivers.push_back(std::string(DN));
				drivers.push_back(std::string(DNAttrs));
			}
		}
		return drivers;
	}

	VECSTR DBGetDataSources(DBConnection& connection)
	{
		SQLRETURN   rcode;
		SQLSMALLINT bufferLengthSN=SQL_MAX_DSN_LENGTH;
		SQLCHAR     sourceName[bufferLengthSN];
		SQLSMALLINT sourceNameLength;
		SQLCHAR     sourceNameDesc[1024];
		SQLSMALLINT sourceNameDescLength;

		bool odbcsucceed;
		int n;
		int m;
		VECSTR dataSources=VECSTR();

		rcode=SQLDataSources
			(
				connection.environment_handle,
				SQL_FETCH_FIRST,
				sourceName,
				bufferLengthSN,
				&sourceNameLength,
				sourceNameDesc,
				1024,
				&sourceNameDescLength
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			return dataSources;
		}
		n=(int)sourceNameLength+1;
		m=(int)sourceNameDescLength+1;
		char SN[n];
		char SNDesc[m];
		memcpy(SN,(char*)sourceName,n);
		memcpy(SNDesc,(char*)sourceNameDesc,m);
		dataSources.push_back(std::string(SN));
		dataSources.push_back(std::string(SNDesc));
		odbcsucceed=true;
		while(odbcsucceed)
		{
			rcode=SQLDataSources
				(
					connection.environment_handle,
					SQL_FETCH_NEXT,
					sourceName,
					bufferLengthSN,
					&sourceNameLength,
					sourceNameDesc,
					1024,
					&sourceNameDescLength
				);
			if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
			{
				odbcsucceed=false;
			}
			else
			{
				n=(int)sourceNameLength+1;
				m=(int)sourceNameDescLength+1;
				char SNI[n];
				char SNIDesc[m];
				memcpy(SNI,(char*)sourceName,n);
				memcpy(SNIDesc,(char*)sourceNameDesc,m);
				dataSources.push_back(std::string(SNI));
				dataSources.push_back(std::string(SNIDesc));
			}
		}
		return dataSources;
	}

	bool DBSelect(DBConnection& connection,const std::string& sqlcmd,DBTable& select_table)
	{
		//Prevents go any further if the connection is closed
		if(!connection.is_open)
        {
            connection.last_message="\n[DBSelect] Connection not established or closed!";
			return false;
        }

		SQLHSTMT statement_handle;

        //Prepare the SQL statement
		if(!DBPrepareSelect(connection,statement_handle,sqlcmd))
		{
			return false;
		}

		SQLRETURN   rcode;
		SQLSMALLINT stmt_columns=0;
		DBColumn*   sql_columns=nullptr;
		SQLSMALLINT i;
		SQLCHAR     sql_command_text[MAX_COMMAND_LENGTH];
		SQLINTEGER  sql_command_length;

//==================================================================
		//EXECUTE STATEMENT
		rcode=SQLExecDirect
			(
				statement_handle,
				(PSQLCHAR)sqlcmd.c_str(),
				(SQLINTEGER)sqlcmd.length()
			);
		while(rcode==SQL_STILL_EXECUTING)
		{
			rcode=SQLExecDirect
				(
					statement_handle,
					(PSQLCHAR)sqlcmd.c_str(),
					(SQLINTEGER)sqlcmd.length()
				);
		}
		if(rcode==SQL_ERROR && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBSelect] SQLExecDirect error!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		else if(rcode==SQL_NEED_DATA)
		{
			connection.last_message="\n[DBSelect] SQLExecDirect execution requires \
			data!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		//cout << "SQLExecDirect execution succeed!" << endl;
		//==================================================================
		//==================================================================
		//SHOW EXECUTED SQL COMMAND
		rcode=SQLNativeSql
			(
				connection.database_connection_handle,
				(PSQLCHAR)sqlcmd.c_str(),
				(SQLINTEGER)sqlcmd.length(),
				sql_command_text,
				MAX_COMMAND_LENGTH,
				&sql_command_length
			);
		if(rcode==SQL_SUCCESS || rcode==SQL_SUCCESS_WITH_INFO)
		{
			std::string sended_command;
			sended_command=std::string((size_t)sql_command_length,'\0');
			std::copy
				(
					sql_command_text,
					sql_command_text+sql_command_length,
					sended_command.begin()
				);
			//cout << "\n \n  Command executed \n \n " << endl;
			//cout << sended_command << " \n \n " << endl;
		}
		else
		{
			connection.last_message="\n[DBSelect] SQLNativeSql error requesting sent \
			statement!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		//==================================================================
//==================================================================
		//GET NUMBER OF COLUMNS
		//After be prepared the total number of columns must be retrieved
		//but before the statement execution is performed
		rcode=SQLNumResultCols(statement_handle,&stmt_columns);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBSelect] SQLNumResultCols error getting the \
			total number of columns!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		//==================================================================
//==================================================================
		//CREATE COLUMNS AND BIND THEM
		if(!DBGetColumnsAndBind(connection,statement_handle,stmt_columns,sql_columns))
		{
			return false;
		}
		select_table=DBTable(stmt_columns,sql_columns);
		//==================================================================
//==================================================================
		//FETCH VALUES
		select_table.total_rows=0;
		UINT row_index;

		//Call SQLFetch function
		rcode=SQL_SUCCESS;
		while(rcode==SQL_SUCCESS)
		{
		DBSELECT_FETCH:
			rcode=SQLFetch(statement_handle);
			if(rcode==SQL_STILL_EXECUTING)
			{
				goto DBSELECT_FETCH;
			}
			if(rcode==SQL_SUCCESS || rcode==SQL_SUCCESS_WITH_INFO)
			{
				select_table.addRow(row_index);//Add an empty row

				for(i=0;i<stmt_columns;i++)
				{
					CellData value;
					if(sql_columns[i].data_length_indicator==SQL_NULL_DATA)
					{
						value.buffer=nullptr;
						value.buffer_size=0;
						//cout << "row field is NULL " << endl;
					}
					else
					{
						value.buffer=new SQLCHAR[sql_columns[i].data_length_indicator];
						value.buffer_size=sql_columns[i].data_length_indicator;
						memcpy
							(
								value.buffer,
								sql_columns[i].column_buffer,
								sql_columns[i].data_length_indicator
							);
					}
					select_table.setValue(row_index,i,value);
				}

				if(rcode==SQL_SUCCESS_WITH_INFO)
				{
					//Show state
					connection.last_message+="\n Row state ";
					connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
					//cout << connection.last_message << endl;
				}
			}
		}

		switch(rcode)
		{
			case SQL_SUCCESS_WITH_INFO:
			{
				//Show state
				connection.last_message+="\n";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				break;
			}
			case SQL_NO_DATA:
			{
				//Show state
				connection.last_message+="\n[DBSelect] All rows fetched!";
				//cout << connection.last_message << endl;
				break;
			}
			case SQL_ERROR:
			{
				//Show error
				connection.last_message+="\n[DBSelect] Error in SQLFetch ";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << "Error setting cursor's position!" << endl;
				return false;
			}
			case SQL_INVALID_HANDLE:
			{
				connection.last_message+="\n[DBSelect] Invalid statement handle!";
				//cout << "Invalid statement handle!" << endl;
				return false;
			}
			default:
			{
				break;
			}
		}
		//==================================================================

		//cout << "Total rows " << select_table.total_rows << endl;
		setStatementHandle(select_table,statement_handle);
		//==================================================================
		//TO SET TABLE NAME, CATALOG NAME AND SCHEMA NAME
		select_table.catalog_name=std::string
			(
				(char*)select_table.table_columns[0].catalog_name
			);
		select_table.schema_name=std::string
			(
				(char*)select_table.table_columns[0].schema_name
			);
		select_table.table_name=std::string
			(
				(char*)select_table.table_columns[0].table_name
			);
		//==================================================================

		//Close the cursor
		rcode=SQLCloseCursor(statement_handle);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBSelect] SQLCloseCursor error closing cursor!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		rcode=SQLFreeStmt(statement_handle,SQL_RESET_PARAMS);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBSelect] SQLFreeStmt error resetting parameters!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		return true;
	}

	bool DBInsert
		(
			DBConnection& connection,
			const std::string& sqlcmd,
			const DBTable& rows_to_insert,
			DBTable& rows_rejected
		)
	{
		//Prevents go any further if the connection is closed
		if(!connection.is_open)
        {
            connection.last_message="\n[DBInsert] Connection not established or closed!";
			return false;
        }

		SQLRETURN rcode;
		SQLHSTMT  statement_handle;
		DBColumn* sql_columns=new DBColumn[rows_to_insert.total_columns];

		for(short i=0;i<rows_to_insert.total_columns;i++)
		{
			sql_columns[i]=rows_to_insert.table_columns[i];
		}

        //Prepare the SQL statement
		if(!DBPrepareInsert(connection,statement_handle,sqlcmd))
		{
			return false;
		}

		if(!DBBindInsertParameters
				(
					connection,
					statement_handle,
					rows_to_insert.total_columns,
					sql_columns
				))
		{
			return false;
		}
		//cout << "SQL parameters bounded!" << endl;

		//Prepare SQl command
		rcode=SQLPrepare
			(
				statement_handle,
				(PSQLCHAR)sqlcmd.c_str(),
				(SQLINTEGER)strlen(sqlcmd.c_str())
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.sql_command_prepared=false;
			connection.last_message="\n[DBInsert] Can't prepare SQL statement!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		connection.sql_command_prepared=true;
		//cout << "SQL Insert prepared!" << endl;

		rows_rejected=DBTable(rows_to_insert.total_columns,rows_to_insert.table_columns);

		for(UINT i=0;i<rows_to_insert.total_rows;i++)
		{
			//Set data to bounded buffer
			for(short j=0;j<rows_to_insert.total_columns;j++)
			{
				CellData value;
				rows_to_insert.getValue(i,j,value);
				if(!value.buffer)
				{
					sql_columns[j].column_buffer_length=0;
					sql_columns[j].data_length_indicator=SQL_NULL_DATA;
				}
				else
				{
					memcpy(sql_columns[j].column_buffer,value.buffer,value.buffer_size);
					sql_columns[j].data_length_indicator=value.buffer_size;
				}
			}

			rcode=SQLExecute(statement_handle);

			if(rcode != SQL_SUCCESS && rcode != SQL_SUCCESS_WITH_INFO)
			{
				DBRow rejected;
				rejected=rows_to_insert.getRow(i);
				rows_rejected.addRow(rejected);
				//cout << "Row " << i << " rejected" << endl;
				//cout << DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT) << endl;
			}
		}

		rcode=SQLFreeStmt(statement_handle,SQL_RESET_PARAMS);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBInsert] SQLFreeStmt error resetting parameters!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

	    return true;
	}

    bool DBUpdate
		(
			DBConnection& connection,
			const std::string& sqlcmd,
			const DBTable& rows_to_update,
			const DBTable& primary_keys_values,
			DBTable& rows_rejected
		)
    {
    	//Prevents go any further if the connection is closed
		if(!connection.is_open)
        {
            connection.last_message="\n[DBUpdate] Connection not established or closed!";
			return false;
        }

    	SQLRETURN rcode;
    	SQLHSTMT  statement_handle;
		DBColumn* sql_columns=new DBColumn[rows_to_update.total_columns];
		DBColumn* pk_columns=new DBColumn[primary_keys_values.total_columns];
		CellData  value;

		for(short i=0;i<rows_to_update.total_columns;i++)
		{
			sql_columns[i]=rows_to_update.table_columns[i];
		}

		for(short i=0;i<primary_keys_values.total_columns;i++)
		{
			pk_columns[i]=primary_keys_values.table_columns[i];
		}

        //Prepare the SQL statement
		if(!DBPrepareUpdate(connection,statement_handle,sqlcmd))
		{
			return false;
		}

		if(!DBBindUpdateParameters
				(
					connection,
					statement_handle,
					rows_to_update.total_columns,
					primary_keys_values.total_columns,
					sql_columns,
					pk_columns
				))
		{
			return false;
		}
		//cout << "SQL parameters bounded!" << endl;

		//Prepare SQl command
		rcode=SQLPrepare
			(
				statement_handle,
				(PSQLCHAR)sqlcmd.c_str(),
				(SQLINTEGER)strlen(sqlcmd.c_str())
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.sql_command_prepared=false;
			connection.last_message="\n[DBUpdate] Can't prepare SQL statement!";
			connection.last_message+="";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		connection.sql_command_prepared=true;
		//cout << "SQL Update prepared!" << endl;

		rows_rejected=DBTable(rows_to_update.total_columns,rows_to_update.table_columns);

		for(UINT i=0;i<rows_to_update.total_rows;i++)
		{
			//Set data to bounded buffer
			for(short j=0;j<rows_to_update.total_columns;j++)
			{
				rows_to_update.getValue(i,j,value);
				if(!value.buffer)
				{
					sql_columns[j].column_buffer_length=0;
					sql_columns[j].data_length_indicator=SQL_NULL_DATA;
				}
				else
				{
					sql_columns[j].column_buffer_length=value.buffer_size;
					sql_columns[j].data_length_indicator=value.buffer_size;
					memcpy(sql_columns[j].column_buffer,value.buffer,value.buffer_size);
				}
			}

			for(short j=0;j<primary_keys_values.total_columns;j++)
			{
				primary_keys_values.getValue(i,j,value);
				pk_columns[j].column_buffer_length=value.buffer_size;
				pk_columns[j].data_length_indicator=value.buffer_size;
				memcpy(pk_columns[j].column_buffer,value.buffer,value.buffer_size);
			}

			rcode=SQLExecute(statement_handle);

			if(rcode != SQL_SUCCESS && rcode != SQL_SUCCESS_WITH_INFO)
			{
				DBRow rejected;
				rejected=rows_to_update.getRow(i);
				rows_rejected.addRow(rejected);
				//cout << "Row " << i << " rejected" << endl;
				//cout << DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT) << endl;
			}

		}

        return true;
    }

    bool DBDelete
		(
			DBConnection& connection,
			const std::string& sqlcmd,
			const DBTable& primary_keys_values,
			DBTable& rows_rejected
		)
    {
    	//Prevents go any further if the connection is closed
		if(!connection.is_open)
        {
            connection.last_message="\n[DBDelete] Connection not established or closed!";
			return false;
        }

    	SQLRETURN rcode;
    	SQLHSTMT  statement_handle;
		DBColumn* pk_columns=new DBColumn[primary_keys_values.total_columns];
		CellData  value;

		for(short i=0;i<primary_keys_values.total_columns;i++)
		{
			pk_columns[i]=primary_keys_values.table_columns[i];
		}

        //Prepare the SQL statement
		if(!DBPrepareDelete(connection,statement_handle,sqlcmd))
		{
			return false;
		}

		if(!DBBindDeleteParameters
				(
					connection,
					statement_handle,
					primary_keys_values.total_columns,
					pk_columns
				))
		{
			return false;
		}
		//cout << "SQL parameters bounded!" << endl;

		//Prepare SQl command
		rcode=SQLPrepare
			(
				statement_handle,
				(PSQLCHAR)sqlcmd.c_str(),
				(SQLINTEGER)strlen(sqlcmd.c_str())
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.sql_command_prepared=false;
			connection.last_message="\n[DBDelete] Can't prepare SQL statement!";
			connection.last_message+="";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		connection.sql_command_prepared=true;
		//cout << "SQL Delete prepared!" << endl;

		rows_rejected=DBTable
			(
				primary_keys_values.total_columns,
				primary_keys_values.table_columns
			);

		for(UINT i=0;i<primary_keys_values.total_rows;i++)
		{
			for(short j=0;j<primary_keys_values.total_columns;j++)
			{
				primary_keys_values.getValue(i,j,value);
				pk_columns[j].column_buffer_length=value.buffer_size;
				pk_columns[j].data_length_indicator=value.buffer_size;
				memcpy(pk_columns[j].column_buffer,value.buffer,value.buffer_size);
			}

			rcode=SQLExecute(statement_handle);

			if(rcode != SQL_SUCCESS && rcode != SQL_SUCCESS_WITH_INFO)
			{
				DBRow rejected;
				rejected=primary_keys_values.getRow(i);
				rows_rejected.addRow(rejected);
				//cout << "Row " << i << " rejected" << endl;
				//cout << DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT) << endl;
			}

		}


    	return true;
    }

    bool DBGetPrimaryKeys
		(
			DBTable& table,
			DBKeyColumn*& primary_keys,
			short& total_primary_keys,
			std::string& message
		)
    {
    	SQLRETURN   rcode;
    	HSTMT       handle=getStatementHandle(table);
    	PSQLCHAR    table_catalog_name=(PSQLCHAR)table.catalog_name.c_str();
    	PSQLCHAR    table_schema_name=(PSQLCHAR)table.schema_name.c_str();
    	PSQLCHAR    table_name=(PSQLCHAR)table.table_name.c_str();
    	SQLSMALLINT table_catalog_name_length=(SQLSMALLINT)table.catalog_name.size();
    	SQLSMALLINT table_schema_name_length=(SQLSMALLINT)table.schema_name.size();
    	SQLSMALLINT table_name_length=(SQLSMALLINT)table.table_name.size();
    	SQLSMALLINT key_ordinal;
    	SQLSMALLINT k;
    	std::vector<DBKeyColumn> table_primary_keys;

    	SQLBindCol(handle,6,SQL_C_SSHORT,(SQLPOINTER)&key_ordinal,sizeof(SQLSMALLINT),NULL);

    	rcode=SQLPrimaryKeys
			(
				handle,
				table_catalog_name,
				table_catalog_name_length,
				table_schema_name,
				table_schema_name_length,
				table_name,
				table_name_length
			);
		while(rcode==SQL_STILL_EXECUTING)
		{
			rcode=SQLPrimaryKeys
				(
					handle,
					table_catalog_name,
					table_catalog_name_length,
					table_schema_name,
					table_schema_name_length,
					table_name,
					table_name_length
				);
		}
		if(rcode==SQL_ERROR)
		{
			message="\n[DBGetPrimaryKeys] Error executing SQLPrimaryKeys!\n";
			message+=DBGetErrorMsg(handle,SQL_HANDLE_STMT);
			return false;
		}
		while(rcode==SQL_SUCCESS || rcode==SQL_SUCCESS_WITH_INFO)
		{
			rcode=SQLFetch(handle);
			while(rcode==SQL_STILL_EXECUTING)
			{
				rcode=SQLFetch(handle);
			}

			if(rcode==SQL_SUCCESS || rcode==SQL_SUCCESS_WITH_INFO)
			{
				DBKeyColumn key;
				memcpy
					(
						key.key_catalog_name,
						table_catalog_name,
						table_catalog_name_length
					);
				memcpy
					(
						key.key_schema_name,
						table_schema_name,
						table_schema_name_length
					);
				memcpy
					(
						key.key_table_name,
						table_name,
						table_name_length
					);
				k=key_ordinal;
				memcpy
					(
						key.key_column_name,
						table.table_columns[k].column_name,
						table.table_columns[k].column_name_length
					);
				key.key_ordinal=key_ordinal;
				key.key_catalog_name_length=table_catalog_name_length;
				key.key_schema_name_length=table_schema_name_length;
				key.key_table_name_length=table_name_length;
				key.key_column_name_length=table.table_columns[k].column_name_length;
				key.key_name_length=0;
				table_primary_keys.push_back(key);
			}
		}

		primary_keys=new DBKeyColumn[table_primary_keys.size()];
		for(size_t i=0;i<table_primary_keys.size();i++)
		{
			primary_keys[i]=table_primary_keys[i];
		}
		total_primary_keys=(short)table_primary_keys.size();

		rcode=SQLFreeStmt(handle,SQL_RESET_PARAMS);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			message="\n[DBGetPrimaryKeys] SQLFreeStmt error resetting parameters!";
			message+=DBGetErrorMsg(handle,SQL_HANDLE_STMT);
			return false;
		}

    	return true;
    }

    bool DBGetForeignKeys
		(
			DBTable& table,
			DBKeyColumn*& foreign_keys,
			short& total_foreign_keys,
			std::string& message
		)
	{
		SQLRETURN   rcode;
    	HSTMT       handle=getStatementHandle(table);
    	PSQLCHAR    table_catalog_name=(PSQLCHAR)table.catalog_name.c_str();
    	PSQLCHAR    table_schema_name=(PSQLCHAR)table.schema_name.c_str();
    	PSQLCHAR    table_name=(PSQLCHAR)table.table_name.c_str();
    	SQLSMALLINT table_catalog_name_length=(SQLSMALLINT)table.catalog_name.size();
    	SQLSMALLINT table_schema_name_length=(SQLSMALLINT)table.schema_name.size();
    	SQLSMALLINT table_name_length=(SQLSMALLINT)table.table_name.size();
    	SQLSMALLINT key_ordinal;
    	SQLSMALLINT k;
    	std::vector<DBKeyColumn> table_foreign_keys;

    	SQLBindCol(handle,6,SQL_C_SSHORT,(SQLPOINTER)&key_ordinal,sizeof(SQLSMALLINT),NULL);

    	rcode=SQLForeignKeys
			(
				handle,
				NULL,
				0,
				NULL,
				0,
				NULL,
				0,
				table_catalog_name,
				table_catalog_name_length,
				table_schema_name,
				table_schema_name_length,
				table_name,
				table_name_length
			);
		while(rcode==SQL_STILL_EXECUTING)
		{
			rcode=SQLForeignKeys
				(
					handle,
					NULL,
					0,
					NULL,
					0,
					NULL,
					0,
					table_catalog_name,
					table_catalog_name_length,
					table_schema_name,
					table_schema_name_length,
					table_name,
					table_name_length
				);
		}
		if(rcode==SQL_ERROR)
		{
			message="\n[DBGetPrimaryKeys] Error executing SQLPrimaryKeys!\n";
			message+=DBGetErrorMsg(handle,SQL_HANDLE_STMT);
			return false;
		}
		while(rcode==SQL_SUCCESS || rcode==SQL_SUCCESS_WITH_INFO)
		{
			rcode=SQLFetch(handle);
			while(rcode==SQL_STILL_EXECUTING)
			{
				rcode=SQLFetch(handle);
			}

			if(rcode==SQL_SUCCESS || rcode==SQL_SUCCESS_WITH_INFO)
			{
				DBKeyColumn key;
				memcpy
					(
						key.key_catalog_name,
						table_catalog_name,
						table_catalog_name_length
					);
				memcpy
					(
						key.key_schema_name,
						table_schema_name,
						table_schema_name_length
					);
				memcpy
					(
						key.key_table_name,
						table_name,
						table_name_length
					);
				k=key_ordinal;
				memcpy
					(
						key.key_column_name,
						table.table_columns[k].column_name,
						table.table_columns[k].column_name_length
					);
				key.key_ordinal=key_ordinal;
				key.key_catalog_name_length=table_catalog_name_length;
				key.key_schema_name_length=table_schema_name_length;
				key.key_table_name_length=table_name_length;
				key.key_column_name_length=table.table_columns[k].column_name_length;
				key.key_name_length=0;
				table_foreign_keys.push_back(key);
			}
		}

		foreign_keys=new DBKeyColumn[table_foreign_keys.size()];
		for(size_t i=0;i<table_foreign_keys.size();i++)
		{
			foreign_keys[i]=table_foreign_keys[i];
		}
		total_foreign_keys=(short)table_foreign_keys.size();

		rcode=SQLFreeStmt(handle,SQL_RESET_PARAMS);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			message="\n[DBGetForeignKeys] SQLFreeStmt error resetting parameters!";
			message+=DBGetErrorMsg(handle,SQL_HANDLE_STMT);
			return false;
		}

		return true;
	}

	short DBGetNumberOfColumns(SQLHSTMT& statement_handle)
	{
		SQLRETURN rcode;
		SQLSMALLINT total_columns;
		rcode=SQLNumResultCols(statement_handle,&total_columns);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			return -1;
		}
		return (short)total_columns;
	}

	UINT DBGetNumberOfRows(SQLHSTMT& statement_handle)
	{
		SQLRETURN rcode;
		SQLLEN total_rows;
		rcode=SQLRowCount(statement_handle,&total_rows);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			return -1;
		}
		return (UINT)total_rows;
	}

	std::string DBGetErrorMsg(SQLHANDLE handle,SQLSMALLINT handle_type)
	{
		std::string msg="";
		std::string error_message;
		std::string error_message_code;
		SQLSMALLINT total_messages=1;
		SQLRETURN   rcode;
		SQLCHAR     sql_state[6];
		SQLCHAR     sql_error_message[SQL_MAX_MESSAGE_LENGTH];
		SQLINTEGER  sql_native_error;
		SQLSMALLINT sql_error_message_length;
		SQLSMALLINT handle_type_enum;

		switch(handle_type)
		{
			case SQL_HANDLE_DBC:
			{
				handle_type_enum=SQL_HANDLE_DBC;
				break;
			}
			case SQL_HANDLE_DESC:
			{
				handle_type_enum=SQL_HANDLE_DESC;
				break;
			}
			case SQL_HANDLE_ENV:
			{
				handle_type_enum=SQL_HANDLE_ENV;
				break;
			}
			case SQL_HANDLE_STMT:
			{
				handle_type_enum=SQL_HANDLE_STMT;
				break;
			}
			default:
				break;
		}
		while(true)
		{
			rcode = SQLGetDiagRec
				(
					handle_type_enum,
					handle,
					total_messages,
					sql_state,
					&sql_native_error,
					sql_error_message,
					SQL_MAX_MESSAGE_LENGTH,
					&sql_error_message_length
				);
			if(rcode==SQL_SUCCESS)
			{
				error_message=std::string((size_t)sql_error_message_length,'\0');
				std::copy
					(
						sql_error_message,
						sql_error_message+sql_error_message_length,
						error_message.begin()
					);
				error_message_code="\n["+std::string((char*)sql_state)+"]";
				msg+=error_message_code+error_message+"";
				total_messages++;
				if(total_messages>MAX_ERROR_MESSAGES)
				{
					break;
				}
			}
			else
			{
				break;
			}
		}
		if(!(msg.compare("")==0))
		{
			msg+="";
		}
		return msg;
	}

	std::string GetSQLTypeName(const short sql_type_id,bool& is_type_variable)
	{
		std::string type_name;
		is_type_variable=false;
		switch(sql_type_id)
		{
			case SQL_BIT:
			{
				type_name="SQL_BIT";
				break;
			}
			case SQL_CHAR:
			{
				type_name="SQL_CHAR";
				break;
			}
			case SQL_VARCHAR:
			{
				type_name="SQL_VARCHAR";
				break;
			}
			case SQL_LONGVARCHAR:
			{
				type_name="SQL_LONGVARCHAR";
				break;
			}
			case SQL_WCHAR:
			{
				type_name="SQL_WCHAR";
				is_type_variable=true;
				break;
			}
			case SQL_WVARCHAR:
			{
				type_name="SQL_WVARCHAR";
				is_type_variable=true;
				break;
			}
			case SQL_WLONGVARCHAR:
			{
				type_name="SQL_WLONGVARCHAR";
				is_type_variable=true;
				break;
			}
			case SQL_TINYINT:
			{
				type_name="SQL_TINYINT";
				break;
			}
			case SQL_SMALLINT:
			{
				type_name="SQL_SMALLINT";
				break;
			}
			case SQL_INTEGER:
			{
				type_name="SQL_INTEGER";
				break;
			}
			case SQL_BIGINT:
			{
				type_name="SQL_BIGINT";
				break;
			}
			case SQL_DECIMAL:
			{
				type_name="SQL_DECIMAL";
				break;
			}
			case SQL_NUMERIC:
			{
				type_name="SQL_NUMERIC";
				break;
			}
			case SQL_REAL:
			{
				type_name="SQL_REAL";
				break;
			}
			case SQL_FLOAT:
			{
				type_name="SQL_FLOAT";
				break;
			}
			case SQL_DOUBLE:
			{
				type_name="SQL_DOUBLE";
				break;
			}
			case SQL_DATE:
			{
				type_name="SQL_DATE";
				break;
			}
			case SQL_TIME:
			{
				type_name="SQL_TIME";
				break;
			}
			case SQL_TIMESTAMP:
			{
				type_name="SQL_TIMESTAMP";
				break;
			}
			case SQL_BINARY:
			{
				type_name="SQL_BINARY";
				is_type_variable=true;
				break;
			}
			case SQL_VARBINARY:
			{
				type_name="SQL_VARBINARY";
				is_type_variable=true;
				break;
			}
			case SQL_LONGVARBINARY:
			{
				type_name="SQL_LONGVARBINARY";
				is_type_variable=true;
				break;
			}
			default:
			{
				type_name="SQL_INTEGER";
				break;
			}
		}

		return type_name;
	}

//==========================================================================================
//METHODS FOR INTEROPERABILITY WITH EXCEL LIBREOFFICE MACRO

	bool DBGetColumnsAndBind
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const short& total_columns,
			DBColumn*& columns
		)
	{
		SQLRETURN   rcode;
		SQLSMALLINT type_enum;
		short       i,j;
		long        column_type;
		long        column_length;
		columns=new DBColumn[total_columns];

		for(i=0;i<total_columns;i++)
		{
			j=i+1;
			//some drivers may only write the lower 32-bit or 16-bit of a buffer and leave
			//the higher-order bit unchanged. Therefore, applications should initialize the
			//value to 0 before calling SQLColAttribute
			column_type=0;
			column_length=0;

			//Get the column's type
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_DESC_CONCISE_TYPE,
					NULL,
					0,
					NULL,
					&column_type
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}
			columns[i].column_type=(short)(column_type);

			//Get the column's byte length
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_COLUMN_LENGTH,
					NULL,
					0,
					NULL,
					&column_length
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}
			columns[i].column_buffer_length=column_length;

			//Get column's name
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_DESC_NAME,
					columns[i].column_name,
					SQL_MAX_COLUMN_NAME_LEN,
					&columns[i].column_name_length,
					NULL
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

			//Get table name that contains the column
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_COLUMN_TABLE_NAME,
					columns[i].table_name,
					SQL_MAX_TABLE_NAME_LEN,
					&columns[i].table_name_length,
					NULL
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

			//Get column's type name
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_DESC_TYPE_NAME,
					columns[i].column_type_name,
					MAX_COL_TYPE_NAME_LENGTH,
					&columns[i].column_type_name_length,
					NULL
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

			//Get column's catalog name
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_DESC_CATALOG_NAME,
					columns[i].catalog_name,
					SQL_MAX_CATALOG_NAME_LEN,
					&columns[i].catalog_name_length,
					NULL
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

			//Get column's schema name
			rcode=SQLColAttribute
				(
					statement_handle,
					j,
					SQL_DESC_SCHEMA_NAME,
					columns[i].schema_name,
					SQL_MAX_SCHEMA_NAME_LEN,
					&columns[i].schema_name_length,
					NULL
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLColAttribute error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

			columns[i].column_buffer=new SQLCHAR[column_length];

			switch(columns[i].column_type)
			{
				case SQL_BIT:
				{
					break;
				}
				case SQL_CHAR:
				{
					type_enum=SQL_C_CHAR;
					break;
				}
				case SQL_VARCHAR:
				{
					type_enum=SQL_C_CHAR;
					break;
				}
				case SQL_LONGVARCHAR:
				{

					break;
				}
				case SQL_WCHAR:
				{
					break;
				}
				case SQL_WVARCHAR:
				{

					break;
				}
				case SQL_WLONGVARCHAR:
				{

					break;
				}
				case SQL_TINYINT:
				{
					type_enum=SQL_C_TINYINT;
					break;
				}
				case SQL_SMALLINT:
				{
					type_enum=SQL_C_SHORT;
					break;
				}
				case SQL_INTEGER:
				{
					type_enum=SQL_C_LONG;
					break;
				}
				case SQL_BIGINT:
				{
					type_enum=SQL_C_SBIGINT;
					break;
				}
				case SQL_DECIMAL:
				{
					break;
				}
				case SQL_NUMERIC:
				{
					break;
				}
				case SQL_REAL:
				{
					break;
				}
				case SQL_FLOAT:
				{
					type_enum=SQL_C_FLOAT;
					break;
				}
				case SQL_DOUBLE:
				{
					type_enum=SQL_C_DOUBLE;
					break;
				}
				case SQL_DATE:
				{
					break;
				}
				case SQL_TIME:
				{
					break;
				}
				case SQL_TYPE_TIMESTAMP:
				{
					type_enum=SQL_C_TYPE_TIMESTAMP;
					break;
				}
				case SQL_BINARY:
				{
					break;
				}
				case SQL_VARBINARY:
				{

					break;
				}
				case SQL_LONGVARBINARY:
				{

					break;
				}
				default:
				{
					break;
				}
			}

			rcode=SQLBindCol
				(
					statement_handle,
					j,
					type_enum,
					columns[i].column_buffer,
					columns[i].column_buffer_length,
					&columns[i].data_length_indicator
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBGetColumnsAndBind] SQLBindCol error!";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				return false;
			}

		}

		return true;
	}

	bool DBBindInsertParameters
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const short& total_columns,
			DBColumn*& columns
		)
	{

		SQLRETURN   rcode;
		SQLSMALLINT type_enum;
		SQLSMALLINT format_size=0;
		short       i,j;

		for(i=0;i<total_columns;i++)
		{
			j=i+1;

			switch(columns[i].column_type)
			{
				case SQL_BIT:
				{
					break;
				}
				case SQL_CHAR:
				{
					type_enum=SQL_C_CHAR;
					columns[i].data_length_indicator=sizeof(char);
					break;
				}
				case SQL_VARCHAR:
				{
					type_enum=SQL_C_CHAR;
					columns[i].data_length_indicator=SQL_NTS;
					break;
				}
				case SQL_LONGVARCHAR:
				{

					break;
				}
				case SQL_WCHAR:
				{
					break;
				}
				case SQL_WVARCHAR:
				{

					break;
				}
				case SQL_WLONGVARCHAR:
				{

					break;
				}
				case SQL_TINYINT:
				{
					type_enum=SQL_C_TINYINT;
					columns[i].data_length_indicator=sizeof(unsigned char);
					break;
				}
				case SQL_SMALLINT:
				{
					type_enum=SQL_C_SHORT;
					columns[i].data_length_indicator=sizeof(short);
					break;
				}
				case SQL_INTEGER:
				{
					type_enum=SQL_C_LONG;
					columns[i].data_length_indicator=sizeof(int);
					break;
				}
				case SQL_BIGINT:
				{
					type_enum=SQL_C_SBIGINT;
					columns[i].data_length_indicator=sizeof(long);
					break;
				}
				case SQL_DECIMAL:
				{
					break;
				}
				case SQL_NUMERIC:
				{
					break;
				}
				case SQL_REAL:
				{
					break;
				}
				case SQL_FLOAT:
				{
					type_enum=SQL_C_FLOAT;
					format_size=9;
					columns[i].data_length_indicator=sizeof(float);
					break;
				}
				case SQL_DOUBLE:
				{
					type_enum=SQL_C_DOUBLE;
					columns[i].data_length_indicator=sizeof(double);
					format_size=16;
					break;
				}
				case SQL_DATE:
				{
					break;
				}
				case SQL_TIME:
				{
					break;
				}
				case SQL_TYPE_TIMESTAMP:
				{
					type_enum=SQL_C_TYPE_TIMESTAMP;
					format_size=4;
					columns[i].data_length_indicator=sizeof(SQL_TIMESTAMP_STRUCT);
					break;
				}
				case SQL_BINARY:
				{
					break;
				}
				case SQL_VARBINARY:
				{

					break;
				}
				case SQL_LONGVARBINARY:
				{

					break;
				}
				default:
				{
					break;
				}
			}

			rcode = SQLBindParameter
				(
					statement_handle,
					j,
					SQL_PARAM_INPUT,
					type_enum,
					columns[i].column_type,
					columns[i].column_buffer_length,
					format_size,
					columns[i].column_buffer,
					columns[i].column_buffer_length,
					&columns[i].data_length_indicator
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBBindInsertParameters] ";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

		}

		return true;
	}

	bool DBBindUpdateParameters
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const short& total_data_columns,
			const short& total_primary_keys,
			DBColumn*& data_columns,
			DBColumn*& primary_keys
		)
	{

		SQLRETURN   rcode;
		SQLSMALLINT type_enum;
		SQLSMALLINT format_size=0;
		short       i;
		short       parameter_count=0;

		for(i=0;i<total_data_columns;i++)
		{
			parameter_count++;

			switch(data_columns[i].column_type)
			{
				case SQL_BIT:
				{
					break;
				}
				case SQL_CHAR:
				{
					type_enum=SQL_C_CHAR;
					data_columns[i].data_length_indicator=sizeof(char);
					break;
				}
				case SQL_VARCHAR:
				{
					type_enum=SQL_C_CHAR;
					data_columns[i].data_length_indicator=SQL_NTS;
					break;
				}
				case SQL_LONGVARCHAR:
				{

					break;
				}
				case SQL_WCHAR:
				{
					break;
				}
				case SQL_WVARCHAR:
				{

					break;
				}
				case SQL_WLONGVARCHAR:
				{

					break;
				}
				case SQL_TINYINT:
				{
					type_enum=SQL_C_TINYINT;
					data_columns[i].data_length_indicator=sizeof(unsigned char);
					break;
				}
				case SQL_SMALLINT:
				{
					type_enum=SQL_C_SHORT;
					data_columns[i].data_length_indicator=sizeof(short);
					break;
				}
				case SQL_INTEGER:
				{
					type_enum=SQL_C_LONG;
					data_columns[i].data_length_indicator=sizeof(int);
					break;
				}
				case SQL_BIGINT:
				{
					type_enum=SQL_C_SBIGINT;
					data_columns[i].data_length_indicator=sizeof(long);
					break;
				}
				case SQL_DECIMAL:
				{
					break;
				}
				case SQL_NUMERIC:
				{
					break;
				}
				case SQL_REAL:
				{
					break;
				}
				case SQL_FLOAT:
				{
					type_enum=SQL_C_FLOAT;
					format_size=9;
					data_columns[i].data_length_indicator=sizeof(float);
					break;
				}
				case SQL_DOUBLE:
				{
					type_enum=SQL_C_DOUBLE;
					data_columns[i].data_length_indicator=sizeof(double);
					format_size=16;
					break;
				}
				case SQL_DATE:
				{
					break;
				}
				case SQL_TIME:
				{
					break;
				}
				case SQL_TYPE_TIMESTAMP:
				{
					type_enum=SQL_C_TYPE_TIMESTAMP;
					format_size=4;
					data_columns[i].data_length_indicator=sizeof(SQL_TIMESTAMP_STRUCT);
					break;
				}
				case SQL_BINARY:
				{
					break;
				}
				case SQL_VARBINARY:
				{

					break;
				}
				case SQL_LONGVARBINARY:
				{

					break;
				}
				default:
				{
					break;
				}
			}

			rcode = SQLBindParameter
				(
					statement_handle,
					parameter_count,
					SQL_PARAM_INPUT,
					type_enum,
					data_columns[i].column_type,
					data_columns[i].data_length_indicator,
					format_size,
					data_columns[i].column_buffer,
					data_columns[i].column_buffer_length,
					&data_columns[i].data_length_indicator
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBBindUpdateParameters] error executing \
				SQLBindParameter at columns!\n";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

		}

		for(i=0;i<total_primary_keys;i++)
		{
			parameter_count++;

			switch(primary_keys[i].column_type)
			{
				case SQL_BIT:
				{
					break;
				}
				case SQL_CHAR:
				{
					type_enum=SQL_C_CHAR;
					primary_keys[i].data_length_indicator=sizeof(char);
					break;
				}
				case SQL_VARCHAR:
				{
					type_enum=SQL_C_CHAR;
					primary_keys[i].data_length_indicator=SQL_NTS;
					break;
				}
				case SQL_LONGVARCHAR:
				{

					break;
				}
				case SQL_WCHAR:
				{
					break;
				}
				case SQL_WVARCHAR:
				{

					break;
				}
				case SQL_WLONGVARCHAR:
				{

					break;
				}
				case SQL_TINYINT:
				{
					type_enum=SQL_C_TINYINT;
					primary_keys[i].data_length_indicator=sizeof(unsigned char);
					break;
				}
				case SQL_SMALLINT:
				{
					type_enum=SQL_C_SHORT;
					primary_keys[i].data_length_indicator=sizeof(short);
					break;
				}
				case SQL_INTEGER:
				{
					type_enum=SQL_C_LONG;
					primary_keys[i].data_length_indicator=sizeof(int);
					break;
				}
				case SQL_BIGINT:
				{
					type_enum=SQL_C_SBIGINT;
					primary_keys[i].data_length_indicator=sizeof(long);
					break;
				}
				case SQL_DECIMAL:
				{
					break;
				}
				case SQL_NUMERIC:
				{
					break;
				}
				case SQL_REAL:
				{
					break;
				}
				case SQL_FLOAT:
				{
					type_enum=SQL_C_FLOAT;
					format_size=9;
					primary_keys[i].data_length_indicator=sizeof(float);
					break;
				}
				case SQL_DOUBLE:
				{
					type_enum=SQL_C_DOUBLE;
					primary_keys[i].data_length_indicator=sizeof(double);
					format_size=16;
					break;
				}
				case SQL_DATE:
				{
					break;
				}
				case SQL_TIME:
				{
					break;
				}
				case SQL_TYPE_TIMESTAMP:
				{
					type_enum=SQL_C_TYPE_TIMESTAMP;
					format_size=4;
					primary_keys[i].data_length_indicator=sizeof(SQL_TIMESTAMP_STRUCT);
					break;
				}
				case SQL_BINARY:
				{
					break;
				}
				case SQL_VARBINARY:
				{

					break;
				}
				case SQL_LONGVARBINARY:
				{

					break;
				}
				default:
				{
					break;
				}
			}

			rcode = SQLBindParameter
				(
					statement_handle,
					parameter_count,
					SQL_PARAM_INPUT,
					type_enum,
					primary_keys[i].column_type,
					primary_keys[i].data_length_indicator,
					format_size,
					primary_keys[i].column_buffer,
					primary_keys[i].column_buffer_length,
					&primary_keys[i].data_length_indicator
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBBindUpdateParameters] error executing \
				SQLBindParameter at primary keys!\n";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

		}

		return true;
	}

	bool DBBindDeleteParameters
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const short& total_primary_keys,
			DBColumn*& primary_keys
		)
	{
		SQLRETURN   rcode;
		SQLSMALLINT type_enum;
		SQLSMALLINT format_size=0;
		short       i;
		short       parameter_count=0;

		for(i=0;i<total_primary_keys;i++)
		{
			parameter_count++;

			switch(primary_keys[i].column_type)
			{
				case SQL_BIT:
				{
					break;
				}
				case SQL_CHAR:
				{
					type_enum=SQL_C_CHAR;
					primary_keys[i].data_length_indicator=sizeof(char);
					break;
				}
				case SQL_VARCHAR:
				{
					type_enum=SQL_C_CHAR;
					primary_keys[i].data_length_indicator=SQL_NTS;
					break;
				}
				case SQL_LONGVARCHAR:
				{

					break;
				}
				case SQL_WCHAR:
				{
					break;
				}
				case SQL_WVARCHAR:
				{

					break;
				}
				case SQL_WLONGVARCHAR:
				{

					break;
				}
				case SQL_TINYINT:
				{
					type_enum=SQL_C_TINYINT;
					primary_keys[i].data_length_indicator=sizeof(unsigned char);
					break;
				}
				case SQL_SMALLINT:
				{
					type_enum=SQL_C_SHORT;
					primary_keys[i].data_length_indicator=sizeof(short);
					break;
				}
				case SQL_INTEGER:
				{
					type_enum=SQL_C_LONG;
					primary_keys[i].data_length_indicator=sizeof(int);
					break;
				}
				case SQL_BIGINT:
				{
					type_enum=SQL_C_SBIGINT;
					primary_keys[i].data_length_indicator=sizeof(long);
					break;
				}
				case SQL_DECIMAL:
				{
					break;
				}
				case SQL_NUMERIC:
				{
					break;
				}
				case SQL_REAL:
				{
					break;
				}
				case SQL_FLOAT:
				{
					type_enum=SQL_C_FLOAT;
					format_size=9;
					primary_keys[i].data_length_indicator=sizeof(float);
					break;
				}
				case SQL_DOUBLE:
				{
					type_enum=SQL_C_DOUBLE;
					primary_keys[i].data_length_indicator=sizeof(double);
					format_size=16;
					break;
				}
				case SQL_DATE:
				{
					break;
				}
				case SQL_TIME:
				{
					break;
				}
				case SQL_TYPE_TIMESTAMP:
				{
					type_enum=SQL_C_TYPE_TIMESTAMP;
					format_size=4;
					primary_keys[i].data_length_indicator=sizeof(SQL_TIMESTAMP_STRUCT);
					break;
				}
				case SQL_BINARY:
				{
					break;
				}
				case SQL_VARBINARY:
				{

					break;
				}
				case SQL_LONGVARBINARY:
				{

					break;
				}
				default:
				{
					break;
				}
			}

			rcode = SQLBindParameter
				(
					statement_handle,
					parameter_count,
					SQL_PARAM_INPUT,
					type_enum,
					primary_keys[i].column_type,
					primary_keys[i].data_length_indicator,
					format_size,
					primary_keys[i].column_buffer,
					primary_keys[i].column_buffer_length,
					&primary_keys[i].data_length_indicator
				);
			if(rcode!=SQL_SUCCESS)
			{
				//Show state
				connection.last_message+="\n[DBBindDeleteParameters] error executing \
				SQLBindParameter at primary keys!\n";
				connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
				//cout << connection.last_message << endl;
				return false;
			}

		}

		return true;
	}

	bool DBPrepareSelect
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const std::string& sqlcmd
		)
	{
		SQLRETURN rcode;

		//Allocate statement handle
		rcode=SQLAllocHandle
			(
				SQL_HANDLE_STMT,
				connection.database_connection_handle,
				&statement_handle
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareSelect] Can't allocate statement handle!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CURSOR_TYPE,
				(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareSelect] Can't set cursor attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor row size statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_ROW_ARRAY_SIZE,
				(SQLPOINTER)1,
				SQL_IS_UINTEGER
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareSelect] Can't set cursor size attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set concurrency statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CONCURRENCY,
				(SQLPOINTER)SQL_CONCUR_READ_ONLY,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareSelect] Can't set concurrency attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Prepare SQl command
		rcode=SQLPrepare
			(
				statement_handle,
				(PSQLCHAR)sqlcmd.c_str(),
				(SQLINTEGER)strlen(sqlcmd.c_str())
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.sql_command_prepared=false;
			connection.last_message="\n[DBPrepareSelect] Can't prepare SQL statement!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		connection.sql_command_prepared=true;

		//THE ASYNCHRONOUS SUPPORT FOR NOW IS NOT USED GIVEN THAT DM (DRIVER MANAGER)
		//IS A TOO OLD VERSION THAT NOT SUPPORT ASKING FOR ASYNCHRONOUS TASKS TO FINISH
		//FOR NOW THIS PROBLEM CANNOT BE HANDLED
		//Set asynchronous support statement attribute
		//rcode=SQLSetStmtAttr(connection.statement_handle,SQL_ATTR_ASYNC_ENABLE,(SQLPOINTER)SQL_ASYNC_ENABLE_ON,0);
		//if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		//{
		//	connection.last_message="\n[DBPrepareStatement]Can't set asynchronous support attribute!";
		//	connection.last_message+="";
		//	connection.last_message+=DBGetErrorMsg(connection,SQL_HANDLE_STMT);
		//	return false;
		//}

		return true;
	}

	bool DBPrepareInsert
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const std::string& sqlcmd
		)
	{
	    SQLRETURN rcode;

		//Allocate statement handle
		rcode=SQLAllocHandle
			(
				SQL_HANDLE_STMT,
				connection.database_connection_handle,
				&statement_handle
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't allocate statement handle!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CURSOR_TYPE,
				(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't set cursor attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor row size statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_ROW_ARRAY_SIZE,
				(SQLPOINTER)1,
				SQL_IS_UINTEGER
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't set cursor size attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set concurrency statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CONCURRENCY,
				(SQLPOINTER)SQL_CONCUR_DEFAULT,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't set concurrency attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//To indicate column binding
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_PARAM_BIND_TYPE,
				SQL_PARAM_BIND_BY_COLUMN,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't set parameter binding type \
			 attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//To indicate the number of parameters to bind
		SQLUSMALLINT total_parameters;
		size_t n=std::count(sqlcmd.begin(), sqlcmd.end(),'?');
		if(n>0)
		{
			total_parameters=(SQLUSMALLINT)n;
		}
		else
		{
			connection.last_message="\n[DBPrepareInsert] Bad SQL command!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_PARAMSET_SIZE,
				(SQLPOINTER)(&total_parameters),
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't set the number of parameters \
			binding attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//To indicate the rows per parameter set
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_PARAMSET_SIZE,
				(SQLPOINTER)1,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareInsert] Can't set parameter binding type \
			 attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

//		//Prepare SQl command
//		rcode=SQLPrepare(connection.statement_handle,(PSQLCHAR)sqlcmd.c_str(),
//						(SQLINTEGER)strlen(sqlcmd.c_str()));
//		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
//		{
//			connection.sql_command_prepared=false;
//			connection.last_message="\n[DBPrepareInsert] Can't prepare SQL statement!";
//			connection.last_message+="";
//			connection.last_message+=DBGetErrorMsg(connection,SQL_HANDLE_STMT);
//			return false;
//		}
//		connection.sql_command_prepared=true;

	    return true;
	}

	bool DBPrepareUpdate
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const std::string& sqlcmd
		)
	{
	    SQLRETURN rcode;

		//Allocate statement handle
		rcode=SQLAllocHandle
			(
				SQL_HANDLE_STMT,
				connection.database_connection_handle,
				&statement_handle
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareUpdate] Can't allocate statement handle!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CURSOR_TYPE,
				(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareUpdate] Can't set cursor attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor row size statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_ROW_ARRAY_SIZE,
				(SQLPOINTER)1,
				SQL_IS_UINTEGER
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareUpdate] Can't set cursor size attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set concurrency statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CONCURRENCY,
				(SQLPOINTER)SQL_CONCUR_DEFAULT,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareUpdate] Can't set concurrency attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//To indicate column binding
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_PARAM_BIND_TYPE,
				SQL_PARAM_BIND_BY_COLUMN,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareUpdate] Can't set parameter binding type \
			 attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

	    return true;
	}

	bool DBPrepareDelete
		(
			DBConnection& connection,
			SQLHSTMT& statement_handle,
			const std::string& sqlcmd
		)
	{
		SQLRETURN rcode;

		//Allocate statement handle
		rcode=SQLAllocHandle
			(
				SQL_HANDLE_STMT,
				connection.database_connection_handle,
				&statement_handle
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareDelete] Can't allocate statement handle!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CURSOR_TYPE,
				(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareDelete] Can't set cursor attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set cursor row size statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_ROW_ARRAY_SIZE,
				(SQLPOINTER)1,
				SQL_IS_UINTEGER
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareDelete] Can't set cursor size attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//Set concurrency statement attribute
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_CONCURRENCY,
				(SQLPOINTER)SQL_CONCUR_DEFAULT,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareDelete] Can't set concurrency attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

		//To indicate column binding
		rcode=SQLSetStmtAttr
			(
				statement_handle,
				SQL_ATTR_PARAM_BIND_TYPE,
				SQL_PARAM_BIND_BY_COLUMN,
				0
			);
		if(rcode!=SQL_SUCCESS && rcode!=SQL_SUCCESS_WITH_INFO)
		{
			connection.last_message="\n[DBPrepareDelete] Can't set parameter binding type \
			 attribute!";
			connection.last_message+=DBGetErrorMsg(statement_handle,SQL_HANDLE_STMT);
			return false;
		}

	    return true;
	}

}//odbc_database_operations namespace

}//databases namespace