#define _WIN32_DCOM
#include "mod_mssql.h"
#include <stdio.h>

static size_t string_apply_maxlen (const char * const string, const size_t len, const size_t maxlen)
{
	static const uint8_t numbytes_utf8[256] = {
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
		3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
	};

	size_t i = 0;
	size_t chars = 0;
	do {
		uint8_t	numbytes = numbytes_utf8[(uint8_t)string[i]];

		i += numbytes;

		if (i > len)
			return 0;

		if (++chars == maxlen)
			return i;
	} while (i != len);

	return i;
}

static void check_provider_version (providers_t *providers)
{
	for (int i = 0; providers[i].subkey != NULL; i++) {
		HKEY hkey;
		LONG ret = RegOpenKeyEx (providers[i].key, providers[i].subkey, 0, KEY_QUERY_VALUE, &hkey);

		if (ret == ERROR_SUCCESS) {
			providers[i].installed = 1;
			RegCloseKey (hkey);
			break;
		}
	}
}

int mod_init ()
{
	if(FAILED(CoInitialize(NULL)))
		return -1;

	return 1;
}

int mod_connect (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if(_handle == NULL ) {
		return -1;
	}
	if(_handle->lasterrormessage != NULL ) free (_handle->lasterrormessage);
	_handle->lasterrormessage = NULL;
	_handle->lasterrno = 0;

	if (_handle->cfg->connectionstring != NULL) {
		_handle->connectionstring = _handle->cfg->connectionstring;
	} else {
		int pos = 0;

		free (_handle->connectionstring);
		_handle->connectionstring = (char *) malloc (1024);

		if (strstr (_handle->cfg->host, "Provider=") == NULL) {
			providers_t providers[] = {
				{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\MSOLEDBSQL\\CurrentVersion", "MSOLEDBSQL", 0 },
				{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\SQLNCLI13\\CurrentVersion", "SQLNCLI13", 0 },
				{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\SQLNCLI12\\CurrentVersion", "SQLNCLI12", 0 },
				{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\SQLNCLI11\\CurrentVersion", "SQLNCLI11", 0 },
				{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\SQLNCLI10\\CurrentVersion", "SQLNCLI10", 0 },
				{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Native Client\\CurrentVersion\\CurrentVersion", "SQLNCLI", 0 },
				{ 0, NULL, "SQLOLEDB", 1 },
			};

			check_provider_version (providers);

			int i;
			for (i = 0;; i++) {
				if (providers[i].installed == 1) {
					pos = sprintf (_handle->connectionstring, "Provider=%s;", providers[i].provider);
					break;
				}
			}
		}

		if (strstr (_handle->cfg->host, "Data Source=") == NULL)
			pos += sprintf (_handle->connectionstring + pos, "Data Source=%s", _handle->cfg->host);
		else
			pos += sprintf (_handle->connectionstring + pos, "%s", _handle->cfg->host);

		if (_handle->cfg->host[strlen (_handle->cfg->host) - 1] != ';')
			pos += sprintf (_handle->connectionstring + pos, ";");

		if (strstr (_handle->cfg->host, "Initial Catalog=") == NULL)
			pos += sprintf (_handle->connectionstring + pos, "Initial Catalog=%s;", _handle->cfg->database);

		if (_handle->cfg->username != NULL && _handle->cfg->password != NULL)
			pos += sprintf (_handle->connectionstring + pos, "uid=%s;pwd=%s;", _handle->cfg->username, _handle->cfg->password);
		else
			pos += sprintf (_handle->connectionstring + pos, "Trusted_Connection=Yes;");
	}

	try {
        _handle->pConnection.CreateInstance("ADODB.Connection");
		_handle->pConnection->ConnectionString = _handle->connectionstring;
		_handle->pConnection->Open("", "", "", adConnectUnspecified);
		_handle->status = MOD_STATUS_OK;
	}
	catch (_com_error &e) {
		if(((char*)((_bstr_t)e.Description())) != NULL) {
			_handle->lasterrormessage = strdup(((char*)((_bstr_t)e.Description())));
		}
		else
			_handle->lasterrormessage = strdup("Unknown connection error");
		if(e.WCode() == NULL)
			_handle->lasterrno = -1;
		else
			_handle->lasterrno = e.WCode();
		_handle->status = MOD_STATUS_DISCONNECTED;
		return -1;
	}
	_handle->pConnection->CommandTimeout = 0; /* set the query timeout to infinite */

	if (mod_executesql(handle, "SET NOCOUNT OFF") != 0)
		printf("SET NOCOUNT OFF failed: %s\n", _handle->lasterrormessage);

	if (mod_executesql(handle, "SET XACT_ABORT OFF") != 0)
		printf("SET XACT_ABORT OFF failed: %s\n", _handle->lasterrormessage);

	return 1;
}

void *mod_create (mod_config *cfg)
{
	mod_t *_handle = new (mod_t);

	if (_handle == NULL)
		return NULL;

	_handle->lasterrormessage = NULL;
	_handle->string = NULL;
	_handle->query = NULL;
	_handle->mod_result_head = NULL;
	_handle->mod_result_tail = NULL;
	_handle->cfg = cfg;
	_handle->stringlen = 0;
	_handle->querylen = 0;
	_handle->lasterrno = 0;
	_handle->connectionstring = NULL;
	_handle->status = 0;
	_handle->pConnection = NULL;

	_handle->string  = (char*)malloc (1024);
	_handle->stringlen = 1024;

	if (_handle->string == NULL) {
		delete _handle;
		return NULL;
	}

	return _handle;
}

int mod_destroy (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return -1;

	free (_handle->lasterrormessage);
	free (_handle->string);

	if (_handle->cfg->connectionstring == NULL)
		free (_handle->connectionstring);

	delete _handle;
	return 1;
}

int mod_disconnect (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return -1;

	if (_handle->pConnection != NULL && _handle->pConnection->State == adStateOpen) 
		_handle->pConnection->Close(); 

	return 1;
}

int mod_getlasterror (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return -1;

	return _handle->lasterrno;
}

char *mod_geterrorstring (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return NULL;

	return (char*)_handle->lasterrormessage;
}

void mod_reconnect (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return;

	if(_handle->status != MOD_STATUS_SHUTDOWN )
		_handle->status = MOD_STATUS_DISCONNECTED;
	while(_handle->status == MOD_STATUS_DISCONNECTED) {
		try {
			if(_handle->pConnection->State == adStateOpen) _handle->pConnection->Close();
			_handle->pConnection->ConnectionString = _handle->connectionstring;
			_handle->pConnection->Open( "", "", "", adConnectUnspecified);
			_handle->pConnection->CommandTimeout = 0; /* set the query timeout to infinite */
			if(_handle->status != MOD_STATUS_SHUTDOWN )
				_handle->status = MOD_STATUS_OK;
			mod_executesql(handle,"SET NOCOUNT OFF");
			return;
		}
		catch (_com_error &e) {
			if(_handle->lasterrormessage != NULL ) free (_handle->lasterrormessage);
			if(((char*)((_bstr_t)e.Description())) != NULL) {
				_handle->lasterrormessage = strdup(((char*)((_bstr_t)e.Description())));
			}
			else {
				_handle->lasterrormessage = strdup("Unknown reconnect error");
			}
			if(e.WCode() == NULL) {
				_handle->lasterrno = -1;
			}
			else {
				_handle->lasterrno = e.WCode();
			}
			Sleep(1000);
		}
		Sleep(1000);
	}
	return;
}

void mod_freeresult (void *handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return;

	while (_handle->mod_result_head != NULL) {
		mod_result *next = _handle->mod_result_head->next;

		free (_handle->mod_result_head->columnvalue);
		free (_handle->mod_result_head);

		_handle->mod_result_head = next;
	}

	_handle->mod_result_tail = NULL;
}

int convert_to_multibyte (void *handle, char *query)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return 0;

	if(query == NULL) {
		if(_handle->lasterrormessage != NULL ) free (_handle->lasterrormessage);
		_handle->lasterrormessage = strdup ("Error converting UTF-8 NULL query");
		_handle->lasterrno = -1;
		return 0;
	}
	const int len = MultiByteToWideChar (CP_UTF8, 0, query, -1, NULL, 0);
	if (len == 0) {
		_handle->lasterrormessage = strdup ("Error converting UTF-8 to widechar pass 1");
		_handle->lasterrno = GetLastError();
		return 0;
	}
	if (len > _handle->querylen) {
		_handle->querylen = len;
		_handle->query = (wchar_t *) realloc (_handle->query, _handle->querylen * sizeof (wchar_t));
	}

	if (MultiByteToWideChar (CP_UTF8, 0, query, -1, _handle->query, len) == 0) {
		if(_handle->lasterrormessage != NULL ) free (_handle->lasterrormessage);
		_handle->lasterrormessage = strdup ("Error converting UTF-8 to widechar pass 2");
		_handle->lasterrno = GetLastError();
		return 0;
	}
	return 1;
}

void *mod_querysql(void *handle, char *query, int *returncode)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return NULL;

	_RecordsetPtr set("ADODB.Recordset");

	mod_freeresult(_handle);
	*returncode = 0;
	if(0 == convert_to_multibyte(handle,query))
		return NULL;

	try {
		set->Open(_handle->query,_handle->pConnection.GetInterfacePtr(), adOpenForwardOnly,adLockReadOnly, adCmdText);

		if(set->GetState() == adStateClosed)
			return NULL;

		if(set->RecordCount != 0 ) {
			while(set->EndOfFile == FALSE) {
				const long _fcount = set->Fields->GetCount();
		
				for (long i = 0; i < _fcount; i++) {
					_variant_t vtFieldValue = set->Fields->Item[i]->Value;

					if (_handle->mod_result_tail == NULL ) {
						_handle->mod_result_tail = (mod_result*)calloc(1,sizeof(mod_result));
						_handle->mod_result_head = _handle->mod_result_tail;
					} else {
						_handle->mod_result_tail->next = (mod_result*)calloc(1,sizeof(mod_result));
						_handle->mod_result_tail = _handle->mod_result_tail->next;
					}

					switch(vtFieldValue.vt) {
						case VT_NULL :
							break;

						case VT_CY:
						case VT_R4:
						case VT_R8:
						case VT_DECIMAL:
							_handle->mod_result_tail->columnvalue = (char*)malloc(256);
							sprintf(_handle->mod_result_tail->columnvalue,"%f",(double) vtFieldValue);	// should never be null since it would be a VT_NULL
							break;

						default:
							_handle->mod_result_tail->columnvalue = strdup((char*)(_bstr_t) vtFieldValue);	// should never be null since it would be a VT_NULL
							break;
					}
				}
				set->MoveNext();
			}
		}
		set->Close();
	}

	catch (_com_error& e) {
		int rc = -4;

		if (_handle->pConnection->Errors->Count > 0) {
			for (long i = 0; i < _handle->pConnection->Errors->Count; i++) {
				ErrorPtr pErr = _handle->pConnection->Errors->Item[i];

				if (pErr->NativeError == 2601 || pErr->NativeError == 2627)
					rc = -1;
				else if (pErr->NativeError == 1205 || pErr->NativeError == 1222)
					rc = -3;
				else if (pErr->NativeError == 3930)
					rc = -5;
				else if (pErr->NativeError == 6005 || pErr->NativeError == 6006)
					rc = -2;
				else if (pErr->NativeError == 0 || pErr->NativeError == 20 || pErr->NativeError == 233 || pErr->NativeError == 109 || pErr->NativeError == 10054 || pErr->NativeError == 3449 || pErr->NativeError == 9001)
					rc = -2;

				free(_handle->lasterrormessage);
				_handle->lasterrormessage = strdup((LPCSTR)pErr->Description);
				_handle->lasterrno = pErr->NativeError;
			}
		} else {
			free(_handle->lasterrormessage);

			if (((char*)((_bstr_t)e.Description())) != NULL) {
				_handle->lasterrormessage = strdup(((char*)((_bstr_t)e.Description())));
			} else {
				_handle->lasterrormessage = strdup("Unknown query error");
			}

			if (e.Error() == NULL)
				_handle->lasterrno = -4;
			else
				_handle->lasterrno = e.Error();
		}

		*returncode = rc;

		return NULL;
	}

	return _handle->mod_result_head;
}

int mod_executesql (void *handle, char *query)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return -1;

	if (_handle->lasterrormessage != NULL) {
		free (_handle->lasterrormessage);
		_handle->lasterrormessage = NULL;
	}
	_handle->lasterrno = 0;

	if(0 == convert_to_multibyte(handle,query))
		return -1;

	_variant_t	affected (0L);
	try {
		_handle->pConnection->Execute(_handle->query,&affected,adExecuteNoRecords);
	}
	catch (_com_error &e) {
		int rc = -4;

		if (_handle->pConnection->Errors->Count > 0) {
			for (long i = 0; i < _handle->pConnection->Errors->Count; i++) {
				ErrorPtr pErr = _handle->pConnection->Errors->Item[i];

				if (pErr->NativeError == 2601 || pErr->NativeError == 2627)
					rc = -1;
				else if (pErr->NativeError == 1205 || pErr->NativeError == 1222)
					rc = -3;
				else if (pErr->NativeError == 3930)
					rc = -5;
				else if (pErr->NativeError == 6005 || pErr->NativeError == 6006)
					rc = -2;
				else if (pErr->NativeError == 0 || pErr->NativeError == 20 || pErr->NativeError == 233 || pErr->NativeError == 109 || pErr->NativeError == 10054 || pErr->NativeError == 3449 || pErr->NativeError == 9001)
					rc = -2;

				free(_handle->lasterrormessage);
				_handle->lasterrormessage = strdup ((LPCSTR)pErr->Description);
				_handle->lasterrno = pErr->NativeError;
			}
		} else {
			free(_handle->lasterrormessage);

			if (((char*)((_bstr_t)e.Description())) != NULL) {
				_handle->lasterrormessage = strdup(((char*)((_bstr_t)e.Description())));
			} else {
				_handle->lasterrormessage = strdup("Unknown error in execute");
			}

			if (e.Error() == NULL)
				_handle->lasterrno = -1;
			else
				_handle->lasterrno = e.Error();
		}

		return rc;
	}

	long a = affected.operator long();
	if (a <= 0) {
		a = 0;
		_handle->lasterrormessage = strdup("Affected rows:0");
		_handle->lasterrno = 0;
	}

	return a;
}

int mod_exit()
{
	CoUninitialize();
	return 1;
}

char *mod_add_value (void *handle,char *_value, size_t maxlen)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL || _value == NULL)
		return NULL;

	size_t _len = strlen (_value);

	if (maxlen != 0 && _len > maxlen)
		_len = string_apply_maxlen (_value, _len, maxlen);

	const size_t newlen = _len * 2;

	if (newlen + 4 > _handle->stringlen ) {
		_handle->stringlen = newlen + 4;
		_handle->stringlen += 1024 - (_handle->stringlen % 1024);
		_handle->string = (char* )realloc (_handle->string,_handle->stringlen);
	}

	char	*np = _handle->string;
	*np++ = 'N';
	*np++ = '\'';

	size_t i;
	for (i = 0; i < _len; i++) {
		if (_value[i] == '\'')
			*np++ = '\'';

		*np++ = _value[i];
	}

	*np++ = '\'';
	*np = '\0';

	return _handle->string;
}

uint8_t mod_get_status(void	*handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return 0;

	return	_handle->status;
}

void mod_set_status (void *handle, uint8_t status)
{
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (_handle == NULL)
		return;

	_handle->status = status;
	return;
}

uint8_t mod_add_column (void *handle,char *tablename,char *columnname,char *type,char *len,uint8_t nullable,int _unsigned,int command)
{
	mod_t *_handle = static_cast<mod_t*>(handle);
	int pos = 0;

	if (_handle == NULL)
		return -1;

	if (command == 0)
		pos = sprintf(_handle->string ,"ALTER TABLE %s ADD %s ",tablename,columnname);
	else if (command == 1)
		pos = sprintf(_handle->string ,"ALTER TABLE %s ALTER COLUMN %s ",tablename,columnname);

	if (0 == strcmp(type,"double")) {
		pos += sprintf(_handle->string + pos, "float");
	} else if(0 == strcmp(type,"date")) {
		if (_handle->cfg->version >= 10)	// supports date
			pos += sprintf(_handle->string + pos, "date");
		else
			pos += sprintf(_handle->string + pos, "char(10)");
	} else if (0 == strcmp(type,"time")) {
		if (_handle->cfg->version >= 10)	// supports time
			pos += sprintf(_handle->string + pos, "time");
		else
			pos += sprintf(_handle->string + pos, "char(8)");
	} else if (0 == strcmp(type,"longtext")) {
		pos += sprintf(_handle->string + pos, "varchar(max)");
		len = NULL;
	} else if (0 == strcmp(type,"mediumtext")) {
		pos += sprintf(_handle->string + pos, "varchar(max)");
		len = NULL;
	} else if (0 == strcmp(type,"text")) {
		pos += sprintf(_handle->string + pos, "varchar(max)");
		len = NULL;
	} else if(0 == strcmp(type,"bigint")) {
		pos += sprintf(_handle->string + pos, "numeric");

		if (len == NULL) {
			pos += sprintf(_handle->string + pos, "(20,0)");
		} else if (strchr (len, ',') == NULL) {
			pos += sprintf(_handle->string + pos, "(%s,0)", len);
			len = NULL;
		}
	} else if (0 == strcmp(type,"decimal") || 0 == strcmp(type,"numeric")) {
		if (len != NULL) {
			char *endp;
			int precision = strtol (len, &endp, 10);
			int scale = 0;

			if (endp != NULL && *endp == ',')
				scale = strtol (endp + 1, NULL, 10);

			/* numeric/decimal on MSSQL have a max size of 38,38 */
			if (precision > 38)
				precision = 38;

			if (scale > 38)
				scale = 38;

			pos += sprintf(_handle->string + pos,"numeric(%d,%d)", precision, scale);
			len = NULL;
		} else {
			pos += sprintf(_handle->string + pos,"numeric");
		}
	} else {
		pos += sprintf(_handle->string + pos,"%s",type);
	}

	if (len != NULL && 0 != strtoul(len,NULL,10))
		pos += sprintf(_handle->string + pos,"(%s)",len);

	pos += sprintf(_handle->string + pos," %sNULL",nullable == 0 ? "NOT " : "");

	if (command == 2) {
		if (tablename != NULL) {
			strcpy (tablename, _handle->string);
			return 1;
		}
		return -1;
	}

	if(0 == convert_to_multibyte(handle,_handle->string))
		return -1;

	_variant_t	affected (0L);
	try {
		_handle->pConnection->Execute(_handle->query,&affected,adCmdText);
	}
	catch (_com_error &e) {
		if (_handle->lasterrormessage != NULL)
			free (_handle->lasterrormessage);

		if(((char*)((_bstr_t)e.Description())) != NULL) {
			_handle->lasterrormessage = strdup(((char*)((_bstr_t)e.Description())));
		} else
			_handle->lasterrormessage = strdup("Unknown alter table error");

		if(e.Error() == NULL) 
			_handle->lasterrno = -1;
		else
			_handle->lasterrno = e.Error();

		return -1;
	}
	return 1;
}

int mod_get_info (void *handle, mod_info *modinfo, int modinfolen)
{
	int error_code;
	mod_result *head;

	if (handle == NULL || modinfolen < sizeof (struct mod_info_s))
		return 0;

	head = (mod_result *) mod_querysql(handle,"SELECT SERVERPROPERTY('productversion')", &error_code);
	mod_t *_handle = static_cast<mod_t*>(handle);

	if (head != NULL && head->columnvalue != NULL) {
		modinfo->version = strdup (head->columnvalue);
		_handle->cfg->version = strtol (modinfo->version, NULL, 10);
	} else {
		modinfo->version = NULL;
		_handle->cfg->version = 0;
	}

	modinfo->transaction = strdup ("BEGIN TRANSACTION");
	modinfo->upsert = 0;

	mod_freeresult (handle);
	return 1;
}

Databaseinfo *mod_get_databaseinfo(void	*handle)
{
	mod_t *_handle = static_cast<mod_t*>(handle);
	Databaseinfo *_newdi = NULL;
	Databaseinfo *_head = NULL;
	Databaseinfo *_tail = NULL;

	if (_handle == NULL)
		return NULL;

	_RecordsetPtr pRst("ADODB.Recordset");

	try {
		pRst->Open("INFORMATION_SCHEMA.columns", _variant_t((IDispatch *) _handle->pConnection, true), adOpenStatic, adLockReadOnly, adCmdTable);
		while(!pRst->EndOfFile) {
			_newdi = (Databaseinfo*) calloc(1,sizeof(Databaseinfo));
			_newdi->tablename = strdup ((char*)((_bstr_t) pRst->GetFields()->GetItem("TABLE_NAME")->GetValue()));
			_newdi->fieldname = strdup ((char*)((_bstr_t) pRst->Fields->Item["COLUMN_NAME"]->Value));

			variant_t type = pRst->GetFields()->GetItem("DATA_TYPE")->GetValue();
			variant_t charlen = pRst->GetFields()->GetItem("CHARACTER_MAXIMUM_LENGTH")->GetValue();
			variant_t precision = pRst->GetFields()->GetItem("NUMERIC_PRECISION")->GetValue();
			variant_t scale = pRst->GetFields()->GetItem("NUMERIC_SCALE")->GetValue();
			variant_t timeprecision = pRst->GetFields()->GetItem("DATETIME_PRECISION")->GetValue();

			const int charlen_value = charlen.vt == VT_NULL ? 0 : (int)charlen;
			const int precision_value = precision.vt == VT_NULL ? 0 : (int)precision;
			const int scale_value = scale.vt == VT_NULL ? 0 : (int)scale;

			if (strcmp ((char *)((_bstr_t)type), "float") == 0)
				_newdi->type = strdup ("double");
			else if (strcmp ((char *)((_bstr_t)type), "text") == 0)
				_newdi->type = strdup ("mediumtext");
			else if (strcmp ((char *)((_bstr_t)type), "ntext") == 0)
				_newdi->type = strdup ("mediumtext");
			else if (strcmp ((char *)((_bstr_t)type), "nchar") == 0)
				_newdi->type = strdup ("char");
			else if ((strcmp ((char *)((_bstr_t)type), "nvarchar") == 0 || strcmp ((char *)((_bstr_t)type), "varchar") == 0) && charlen_value == -1)
				_newdi->type = strdup ("mediumtext");
			else if (strcmp ((char *)((_bstr_t)type), "nvarchar") == 0)
				_newdi->type = strdup ("varchar");
			else if (strcmp ((char *)((_bstr_t)type), "numeric") == 0 && precision_value >= 10 && precision_value <= 20 && scale_value == 0)
				_newdi->type = strdup ("bigint");
			else if (strcmp ((char *)((_bstr_t)type), "numeric") == 0)
				_newdi->type = strdup ("decimal");
			else
				_newdi->type = strdup ((char *)((_bstr_t)type));

			char len[256];

			if (precision.vt != VT_NULL) {
				if (scale.vt != VT_NULL)
					sprintf (len, "%d,%d", precision_value, scale_value);
				else
					sprintf (len, "%d", precision_value);

				_newdi->len = strdup (len);
			} else if (charlen.vt != VT_NULL && charlen_value != -1) {
				sprintf (len, "%d", charlen_value);
				_newdi->len = strdup (len);
			} else if (timeprecision.vt != VT_NULL) {
				sprintf (len, "%d", (int)timeprecision);
				_newdi->len = strdup (len);
			}

			if (strcmp ((char *)((_bstr_t)pRst->Fields->Item["IS_NULLABLE"]->Value), "YES") == 0)
				_newdi->nullable = 1;

			if(_head == NULL) {
				_head = _newdi;
				_tail = _newdi;
			}
			else {
				_tail->next = _newdi;
				_newdi->previous = _tail;
				_tail = _newdi;
			}
			pRst->MoveNext();
		}
		pRst->Close();
	}
	catch (_com_error &e) {
		printf ("error: %s\n", (char *)e.Description());
		if(((char*)((_bstr_t)e.Description())) != NULL) {
			_handle->lasterrormessage = strdup(((char*)((_bstr_t)e.Description())));
		}
		else
			_handle->lasterrormessage = strdup("Unknown error in check database");
			
		if(e.Error() == NULL) 
			_handle->lasterrno = -1;
		else
			_handle->lasterrno = e.Error();
		return NULL;
	}
	return _head;
}

void mod_free_databaseinfo (Databaseinfo *dbinfo)
{
	if (dbinfo == NULL)
		return;

	while(dbinfo != NULL) {
		Databaseinfo *next = dbinfo->next;

		free(dbinfo->tablename);
		free(dbinfo->fieldname);
		free(dbinfo->type);
		free(dbinfo->len);
		free(dbinfo);

		dbinfo = next;
	}
}
