// Мирошниченко Кирилл aka spock
// #163860836
// e-mail: spock@km.ru
//
#include "stdafx.h"
#include "sp_cooledb.h"
#include "DBPropSet.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//_____________________________________________________________________________
//
COLEDBFldsInf::COLEDBFldsInf() : 
	m_nType1C(CType(0)), 
	m_Index(0), 
	m_strName(""), 
	m_bIsType1C(false), 
	m_eExtType1C(eToOther), 
	m_bIsLong(false) {}

extern CDataBase7* pDataBase7;
extern CMetaDataCont* pMetaDataCont;
extern CBkEndUI* pBkEndUI;

extern char szMNPErrorPrefix[];

BEGIN_BL_METH_MAP(COLEDBCommand)
    BL_METH_FUNC_DEF_PARAM("ExecuteStatement",	 "ВыполнитьИнструкцию", 3, funcExecuteStatement, defsExecuteStatement)
	BL_METH_FUNC_DEF_PARAM("Execute",			 "Выполнить", 1, funcExecute, defsExecute)
	
	BL_METH_PROC_DEF_PARAM("PutObjectList",		 "УложитьСписокОбъектов", 3, procPutObjectList, defsPutObjectList)

	BL_METH_PROC("Close",			 "Закрыть", 0, procClose)
	BL_METH_PROC("Debug",			 "Отладка", 1, procDebug)
	BL_METH_PROC("SetTextParam",	 "УстановитьТекстовыйПараметр", 2, procSetTextParam)
	BL_METH_PROC("SetTempTablesDir", "УстановитьКаталогВремТаблиц", 1, procSetTempTablesDir)
	BL_METH_PROC("SetExecTimeOut",	 "УстановитьТаймаутВыполнения", 1, procSetExecTimeOut)
	BL_METH_PROC("AddParam",		 "ДобПараметр", 5, procAddParam)
	BL_METH_PROC("SetParam",		 "УстановитьПараметр", 2, procSetParam)
	BL_METH_PROC("DeleteParam",		 "УдалитьПараметр", 1, procDeleteParam)
	BL_METH_PROC("DeleteParams",	 "УдалитьПараметры", 0, procDeleteParams)
	BL_METH_PROC("Prepare",			 "Подготовить", 1, procPrepare)
	BL_METH_PROC("Unprepare",		 "ОтменитьПодготовку", 0, procUnprepare)
	
	BL_METH_FUNC("ParamCnt",		 "КолвоПараметров", 0, funcParamCnt)
END_BL_METH_MAP()

BEGIN_BL_PROP_MAP(COLEDBCommand)
END_BL_PROP_MAP()

IMPLEMENT_MY_NONCREATE_CONTEXT(COLEDBCommand, "OLEDBCommand", "OLEDBCommand");

//_____________________________________________________________________________
//
static ULONG AddOffset(ULONG nCurrent, ULONG nAdd)
{
	struct foobar
	{
		char foo;
		long bar;
	};
	
	ULONG nAlign = offsetof(foobar, bar);
	
	return nCurrent + nAdd + (nAdd % nAlign);
}

//_____________________________________________________________________________
//
COLEDBCommand::COLEDBCommand(CComPtr<IDBCreateCommand> pIDBCreateCommand) : CMyContextBase(), m_IsDebugMode(false)
{
	HRESULT hr;
	CString strErr;

	m_bCreated	 = true;
	::UuidCreateNil(&m_GUID);
	m_szTempTablesDir.Empty();

	m_ExecTimeout = -1;
	m_bPrepared = false;

	hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown**)&m_pICommandText);
	if(FAILED(hr))
	{
		strErr = "FAILED! IDBCreateCommand::CreateCommand()";
		GetErrorDescription(strErr);
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return;
	}

	m_TmpTblsList.RemoveAll();
}

//_____________________________________________________________________________
//
COLEDBCommand::~COLEDBCommand()
{
	RemoveAllTmpTables();

	if(m_pICommandText)
	{
		m_pICommandText.Release();
	}

	m_bCreated = false;
	m_bPrepared = false;

	DeleteParams();

	m_ParamList.RemoveAll();
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::defsExecute(int nParam, CValue* param) const
{
	if (0 == nParam){
		*param = "";
		return TRUE;
	}
	return FALSE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::defsExecuteStatement(int nParam, CValue* param) const
{
	switch(nParam)
	{
	case 0: *param = "";
		break;
	case 1: param->Reset();
		break;
	case 2: *param = CNumeric(1);
		break;
	default:
		return FALSE;
	};
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::defsPutObjectList(int nParam, CValue* param) const
{
	if(nParam == 2){
		*param = "";	
		return TRUE;
	}
	return FALSE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::funcExecuteStatement(CValue& rValue, CValue** ppValue)
{
	USES_CONVERSION;
	
	CString strErr;
	HRESULT hr;

	CValueTable* pVT	 = NULL;
	CPtrArray* pVL		 = NULL;
	CVTExtended* pVTExt	 = NULL;

	// второй параметр не передан - создаем ТЗ
	//
	if(UNDEFINE_TYPE_1C == ppValue[1]->GetTypeCode())
	{
		rValue.CreateObject("ТаблицаЗначений");
		pVT = CValue2VT(rValue);
	}
	else
	{
		if(!(pVT = CValue2VT(*ppValue[1])))
		{
			//if(!(pVL = CValue2VL(*ppValue[1])))
				//pVTExt = CValue2VTExt(*ppValue[1]);
		}
		rValue = *ppValue[1];
	}

	if(!pVT && !pVL && !pVTExt)
	{
		strErr = "Не удалось получить возвращаемый объект!";
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}

	if(!m_bCreated)
	{
		strErr = "Использование метода без создания команды!";
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}

	HACCESSOR hPrmAccessor = NULL;
	DBPARAMS Params, *pParams = NULL;
	CString strSQL;

	DBPARAMBINDINFO_ pPrmBindInfo;
	DBBINDING_ pPrmDBBinding;
	ULONG_ pPrmOrdinals;
	BYTE_ pBuffer;

	if(!m_bPrepared)
	{
		strSQL = ppValue[0]->GetString();
		
		hr = COLEDBCommand::ParseQuery(strSQL);
		if(!hr)
			return FALSE;
	}
	else
	{
		int nParamCnt = m_ParamList.GetCount();
		
		if(nParamCnt > 0)
		{
			pPrmBindInfo.Alloc(nParamCnt);
			if(pPrmBindInfo == NULL)
			{
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBPARAMBINDINFO", mmRedErr);
				return FALSE;
			}

			pPrmDBBinding.Alloc(nParamCnt);
			if(pPrmDBBinding == NULL)
			{
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBBINDING", mmRedErr);
				return FALSE;
			}
			
			pPrmOrdinals.Alloc(nParamCnt);
			if(pPrmOrdinals == NULL)
			{
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов", mmRedErr);
				return FALSE;
			}

			ICommandWithParameters_ pICmdWithParams;
			
			strErr.Empty();
			hr = BindWithParams(strErr, nParamCnt, pICmdWithParams, pPrmBindInfo, pPrmDBBinding, pPrmOrdinals);
			if(!hr)
			{
				CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
				return FALSE;
			}

			IAccessor_ pIAccessor;
			
			hr = COLEDBCommand::CreateAccessor(/*IN*/DBACCESSOR_PARAMETERDATA, /*IN*/0, /*OUT*/pIAccessor, /*IN*/pICmdWithParams, /*OUT*/&hPrmAccessor, /*IN*/(ULONG&)nParamCnt, /*IN*/pPrmDBBinding);
			if(!hr)
				return FALSE;
			
			hr = GetParamsDataBuf(pBuffer);
			if(!hr)
				return FALSE;

			Params.pData		 = pBuffer;
			Params.cParamSets	 = 1;
			Params.hAccessor	 = hPrmAccessor;
			pParams				 = &Params;
		}
	}

	long nRowsAffected = 0;
	CComPtr<IRowset> pIRowset;

	hr = COLEDBCommand::Execute(strSQL, pParams, &nRowsAffected, (IUnknown**)&pIRowset, false, m_bPrepared);
	if(!hr)
		return FALSE;

	// Если после исполнения запроса получаем пустой rowset, значит дальше нет смысла продолжать
	// возвращаем пустую ТЗ. А вообще это не ошибка. Например, такое может быть, если выполнить 
	// этим методом drop table XXX - результатом будет только RowsAffected
	//
	if(pIRowset == NULL)
	{
		// В случае с SQLOLEDB после запроса select * from XXX через переменную
		// nRowsAffected возвращается значение -1.
		// Если же использовать провайдер VFPOLEDB, то после такого же запроса 
		// получаем количество выбранных строк. 
		//
		return TRUE;
	}
	
	ULONG cCols					 = 0;
	ULONG cbMaxRowSize			 = 0;
	bool HasKindField			 = false; // флаг - если в выборке есть хоть одно поле вида "ХХХ_вид" или "XXX_kind", то выставляем в значение true

	DBBINDING_ pDBBindings;
	COLEDBFldsInf_ pColumnsInfo;

	hr = COLEDBCommand::Binding(pIRowset, &cCols, pDBBindings, pColumnsInfo, &cbMaxRowSize, HasKindField);
	if(!hr)
		return FALSE;
	
	HACCESSOR hAccessor = DB_NULL_HACCESSOR;
	IAccessor_ pIAccessor;

	hr = COLEDBCommand::CreateAccessor(DBACCESSOR_ROWDATA, 0, pIAccessor, pIRowset, &hAccessor, cCols, pDBBindings);
	if(!hr)
		return FALSE;

	if(pVT)
	{
		// Нужно очищать ТЗ
		//
		if(ppValue[2]->GetNumeric().operator long())
		{
			pVT->Clear(TRUE);

			// Инфо по колонкам
			//
			CString strFormat;
			for(ULONG nCol = 0; nCol < cCols; nCol++)
			{
				CString szName(pColumnsInfo[nCol].m_strName);
				CType ColType(pColumnsInfo[nCol].m_nType1C);

				// Пока выяснил, что если типизируем поле как Перечисление (без указания вида),
				// то стоит создавать колонку нетипизированную (так делает сама 1с).
				// Про другие типы ничего пока не знаю
				//
				if((ENUM_TYPE_1C == ColType.GetTypeCode()) && (0 == ColType.GetTypeID()) 
					|| (CALCULATIONKIND_TYPE_1C == ColType.GetTypeCode()))
					pVT->AddColumn(szName, CType(0), szName, 0, strFormat, 0);
				else
					pVT->AddColumn(szName, ColType, szName, 0, strFormat, 0);
			}
		}
		else
		{
			// Выбираем минимальное кол-во колонок в ТЗ - либо из переданной, либо из созданной
			//
			cCols = min(cCols, pVT->GetColumnCount());
		}
	}

	BYTE_ pRowValues;
	
	pRowValues.Alloc(cbMaxRowSize);
	if(pRowValues == NULL)
	{
		CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов", mmRedErr);
		return FALSE;
	}

	ULONG cRowsObtained = 0;
	HROW m_hRow[NUMROWS_CHUNK];
	HROW* phRow = &m_hRow[0];

	ULONG cRowNum = 0;
	CValue v;
	CMetaDataWork::TypeStringValue eType;

	wchar_t* wstrVal;
	CString strTemp;
	DBDATE* dbdateVal;
	DBTIMESTAMP* dbdtVal;
	CNumeric strNum;
	ULONG dwStatus;
	
	ISequentialStream* pISequentialStream = NULL;

	BYTE_ pRBuffer;
	
	pRBuffer.Alloc(BLOCK_SIZE);
	if(pRBuffer == NULL)
	{
		CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов", mmRedErr);
		return FALSE;
	}
	
	ULONG cRead = 0;
	
	for(;;)
	{
		hr = pIRowset->GetNextRows(NULL, 0, NUMROWS_CHUNK, &cRowsObtained, &phRow);
		if(FAILED(hr))
		{
			strErr = "FAILED! IAccessor::GetNextRows()";
			GetErrorDescription(strErr);
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			return FALSE;
		}
		
		if(cRowsObtained == 0)
			break;
		
		for(ULONG iRow = 0; iRow < cRowsObtained; iRow++)
		{
			hr = pIRowset->GetData(m_hRow[iRow], hAccessor, pRowValues);
			if(FAILED(hr))
			{
				strErr = "FAILED! IRowset::GetData()";
				GetErrorDescription(strErr);
				CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
				return FALSE;
			}
			
			pVT->NewRow(cRowNum);
			
			for(ULONG nCol = 0; nCol < cCols; nCol++)
			{
				v.Reset();
				
				dwStatus = (ULONG)pRowValues[pDBBindings[nCol].obStatus];
				
				if(pColumnsInfo[nCol].m_bIsLong)
				{
					if(DBSTATUS_S_ISNULL == dwStatus)
					{
						v.SetType(pColumnsInfo[nCol].m_nType1C);
						v.Reset();
						continue;
					}
					
					pISequentialStream = *((ISequentialStream**)(pRowValues + pDBBindings[nCol].obValue));
					
					strTemp.Empty();
					
					do
					{
						// очищаем память
						//
						memset(pRBuffer, 0, BLOCK_SIZE);
						
						pISequentialStream->Read(pRBuffer, BLOCK_SIZE, &cRead);
						
						if(cRead > 0)
							strTemp += (const char *)((BYTE*)pRBuffer);
					} while(cRead >= BLOCK_SIZE);
					
					pISequentialStream->Release();
					
					v.SetType(pColumnsInfo[nCol].m_nType1C);
					v = strTemp;
				}
				else
				{
					CType rt = pColumnsInfo[nCol].m_nType1C;
					
					switch(pColumnsInfo[nCol].m_wType)
					{
					case DBTYPE_NUMERIC:
					case DBTYPE_I2:
					case DBTYPE_I4:
					case DBTYPE_R4:
					case DBTYPE_R8:
					case DBTYPE_CY:
					case DBTYPE_DECIMAL:
					case DBTYPE_UI1:
					case DBTYPE_I1:
					case DBTYPE_UI2:
					case DBTYPE_UI4:
					case DBTYPE_I8:
					case DBTYPE_UI8:
						{
							// spock: 2005-12-15
							// Про обработку NULL-значений я и забыл :(
							// и про числовые забыл вообще
							// БАГ: если есть значения NULL. то провайдер возвращает предыдущее значение
							// баг с числовыми нашел DmitrO
							// А я растяпа забыл про числовые, когда правил 2005-11-13
							//
							if(DBSTATUS_S_ISNULL == dwStatus)
							{
								v.SetType(pColumnsInfo[nCol].m_nType1C);
								v = 0L;
								break;
							}
							
							v.SetType(pColumnsInfo[nCol].m_nType1C);
							v = strNum.FromString((const char*)&pRowValues[pDBBindings[nCol].obValue], NULL);
						}
						break;
					
					case DBTYPE_BOOL:
						{
							char* strVal = (char*)&pRowValues[pDBBindings[nCol].obValue];

							// spock: 2005-11-13
							// Про обработку NULL-значений я и забыл :(
							// Обрабатываем просто: есть NULL, значит просто прописываем
							// ноль в ТЗ
							//
							if(DBSTATUS_S_ISNULL == dwStatus)
							{
								v.SetType(pColumnsInfo[nCol].m_nType1C);
								v = 0L;
								break;
							}
							
							strTemp.Format("%s", strVal);
							v.SetType(pColumnsInfo[nCol].m_nType1C);
							
							if(strTemp.CompareNoCase("False") == 0)
								v = 0L;
							else
								v = 1L;
						}
						break;
					
					case DBTYPE_DBDATE:
						{
							dbdateVal = (DBDATE*)&pRowValues[pDBBindings[nCol].obValue];

							v.SetType(pColumnsInfo[nCol].m_nType1C);
							
							// spock: 2005-11-13
							// Про обработку NULL-значений я и забыл :(
							// Обрабатываем просто: есть NULL, значит просто прописываем
							// пустую дату в ТЗ
							//
							if(DBSTATUS_S_ISNULL == dwStatus)
							{
								CDate dateVal(0, 0, 0);
								dateVal.m_DateNum = 0;
								
								v = dateVal;
								break;
							}

							if(dbdateVal->year == 1753 && 
								dbdateVal->month == 1 && 
								dbdateVal->day == 1)
							{
								// скваль 2к
								//
								CDate dateVal(0, 0, 0);
								dateVal.m_DateNum = 0;
								
								v = dateVal;
							}
							else if(dbdateVal->year == 1899 && 
								dbdateVal->month == 12 && 
								dbdateVal->day == 30)
							{
								// фоксовый драйвер/провайдер
								//
								CDate dateVal(0, 0, 0);
								dateVal.m_DateNum = 0;

								v = dateVal;
							}
							else
							{
								CDate dateVal(dbdateVal->year, dbdateVal->month, dbdateVal->day);
								v = dateVal;
							}
						}
						break;
					
					case DBTYPE_DBTIMESTAMP:
						{
							dbdtVal = (DBTIMESTAMP*)&pRowValues[pDBBindings[nCol].obValue];

							v.SetType(pColumnsInfo[nCol].m_nType1C);

							// spock: 2005-11-13
							// Про обработку NULL-значений я и забыл :(
							// Обрабатываем просто: есть NULL, значит просто прописываем
							// пустую дату в ТЗ
							//
							if(DBSTATUS_S_ISNULL == dwStatus)
							{
								CDate dateVal(0, 0, 0);
								dateVal.m_DateNum = 0;

								v = dateVal;
								break;
							}

							if(dbdtVal->year == 1753 && 
								dbdtVal->month == 1 && 
								dbdtVal->day == 1)
							{
								// скваль 2к
								//
								CDate dateVal(0, 0, 0);
								dateVal.m_DateNum = 0;
								
								v = dateVal;
							}
							else if(dbdtVal->year == 1899 && 
								dbdtVal->month == 12 && 
								dbdtVal->day == 30)
							{
								// фоксовый драйвер
								//
								CDate dateVal(0, 0, 0);
								dateVal.m_DateNum = 0;
								
								v = dateVal;
							}
							else
							{
								CDate dateVal(dbdtVal->year, dbdtVal->month, dbdtVal->day);
								v = dateVal;
							}
						}
						break;
					
					case DBTYPE_WSTR:
						{
							wstrVal = (wchar_t*)&pRowValues[pDBBindings[nCol].obValue];

							// spock: 2005-11-13
							// Про обработку NULL-значений я и забыл :(
							// Обрабатываем просто: есть NULL, значит просто прописываем
							// пустое значение в ТЗ
							//
							if(DBSTATUS_S_ISNULL == dwStatus)
							{
								v.SetType(pColumnsInfo[nCol].m_nType1C);
								v = "";
								break;
							}
							
							v.SetType(pColumnsInfo[nCol].m_nType1C);
							v = OLE2T(wstrVal);
						}
						break;
					
					default:
						{
							// spock: 2005-11-13
							// Про обработку NULL-значений я и забыл :(
							// Обрабатываем просто: есть NULL, значит просто прописываем
							// пустое значение в ТЗ
							//
							if(DBSTATUS_S_ISNULL == dwStatus)
							{
								v.Reset();
								break;
							}
							
							if(true == pColumnsInfo[nCol].m_bIsType1C)
							{
								CString strVal;
								CType LocalType(0);

								if(0 == rt.GetTypeID())
								{
									int nTypeCode = rt.GetTypeCode();
									
									switch(nTypeCode)
									{
									case ENUM_TYPE_1C:
									case REFERENCE_TYPE_1C:
									case CALENDAR_TYPE_1C:
									case ACCOUNT_TYPE_1C:
										{
											strVal	 = (char*)&pRowValues[pDBBindings[nCol].obValue];
											eType	 = CMetaDataWork::LongString;
											LocalType = rt;
										}
										break;
									
									case CALCULATIONKIND_TYPE_1C:
										{
											strVal	 = (char*)&pRowValues[pDBBindings[nCol].obValue];

											int nLen = strVal.GetLength();
											if(13 == nLen)
											{
												CString sLocVal(strVal.Mid(4, 6));
												sLocVal.TrimLeft();
												sLocVal.TrimRight();
												strVal = sLocVal;
											}

											eType = CMetaDataWork::ShortString;

											LocalType = rt;

											v.SetType(rt);
											v.SetObjID(CObjID(strtol(strVal, NULL, 36), CDBSign("   ")));
										}
										break;
									
									case DOCUMENT_TYPE_1C:
									case UNDEFINE_TYPE_1C:
										{
											ULONG nKindCol = 0;

											ExtTp1C eExtType1C = pColumnsInfo[nCol].m_eExtType1C;
											
											if(HasKindField)
												nKindCol = FindKindCol(pColumnsInfo, cCols, pColumnsInfo[nCol].m_strName);

											if(!nKindCol)
											{
												strVal	 = (char*)&pRowValues[pDBBindings[nCol].obValue];
												if(UNDEFINE_TYPE_1C == nTypeCode)
													eType= CMetaDataWork::VeryLongString;
												else
													eType= CMetaDataWork::LongString;

												LocalType= rt;
											}
											else
											{
												CString strValLoc	 = (char*)&pRowValues[pDBBindings[nCol].obValue];
												CString strKindVal	 = (char*)&pRowValues[pDBBindings[nKindCol - 1].obValue];

												if(eToSb == eExtType1C)
												{
													CBuhDef* pBuhDef = pMetaDataCont->GetBuhDef();
													if(pBuhDef)
													{
														CString strKindID(strKindVal);
														strKindID.TrimLeft();
														strKindID.TrimRight();
														
														long nKindID = strtol(strKindID, NULL, 36);
														
														CSbKindDef* pSbKindDef = pBuhDef->GetSbKindDefs()->GetItem(nKindID);
														if(pSbKindDef)
														{
															LocalType= CType(pSbKindDef->m_TypeCode, pSbKindDef->m_Kind);
															strVal	 = strValLoc;
															eType	 = pSbKindDef->m_Kind ? CMetaDataWork::ShortString : CMetaDataWork::LongString;
														}
													}
												}
												else // eToDoc
												{
													strVal	 = strKindVal + strValLoc;
													eType	 = CMetaDataWork::LongString;
													LocalType= rt;
												}
											}
										}
										break;
									
									default:
										{
											strVal	 = (char*)&pRowValues[pDBBindings[nCol].obValue];
											eType	 = CMetaDataWork::ShortString;

											LocalType = rt;
										}
										break;
									}
								}
								else
								{
									strVal	 = (char*)&pRowValues[pDBBindings[nCol].obValue];
									eType	 = CMetaDataWork::ShortString;

									LocalType = rt;
								}
								
								if(CALCULATIONKIND_TYPE_1C != rt.GetTypeCode())
									CMetaDataWork::Make1C_ValueFromString(v, LocalType, strVal, eType);
							}
							else
							{
								char* strVal = (char*)&pRowValues[pDBBindings[nCol].obValue];
								
								v.SetType(pColumnsInfo[nCol].m_nType1C);
								v = strVal;
							}
							break;
						}
					};
				}
				pVT->SetValue(v, nCol, cRowNum);
			}
			++cRowNum;
		}
		
		hr = pIRowset->ReleaseRows(cRowsObtained, m_hRow, NULL, NULL, NULL);
		if(FAILED(hr))
		{
			strErr = "FAILED! IAccessor::GetData()";
			GetErrorDescription(strErr);
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			return FALSE;
		}
	}
	
	if(pIAccessor)
		pIAccessor->ReleaseAccessor(hAccessor, NULL);
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::funcExecute(CValue& rValue, CValue** ppValue)
{
	CString strErr;
	HRESULT hr;

	if(!m_bCreated)
	{
		strErr = "Использование метода без создания команды!";
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}
	
	HACCESSOR hPrmAccessor = NULL;
	DBPARAMS Params, *pParams = NULL;
	CString strSQL;
	
	DBPARAMBINDINFO_ pPrmBindInfo;
	DBBINDING_ pPrmDBBinding;
	ULONG_ pPrmOrdinals;
	BYTE_ pBuffer;

	if(!m_bPrepared)
	{
		strSQL = ppValue[0]->GetString();
		
		hr = COLEDBCommand::ParseQuery(strSQL);
		if(!hr)
		{
			rValue = 0L;
			return FALSE;
		}
	}
	else
	{
		int nParamCnt = m_ParamList.GetCount();

		if(nParamCnt > 0)
		{
			pPrmBindInfo.Alloc(nParamCnt);
			if(pPrmBindInfo == NULL)
			{
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBPARAMBINDINFO", mmRedErr);
				return FALSE;
			}
			
			pPrmDBBinding.Alloc(nParamCnt);
			if(pPrmDBBinding == NULL)
			{
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBBINDING", mmRedErr);
				return FALSE;
			}
			
			pPrmOrdinals.Alloc(nParamCnt);
			if(pPrmOrdinals == NULL)
			{
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов", mmRedErr);
				return FALSE;
			}

			ICommandWithParameters_ pICmdWithParams;

			strErr.Empty();
			
			hr = BindWithParams(strErr, nParamCnt, pICmdWithParams, pPrmBindInfo, pPrmDBBinding, pPrmOrdinals);
			if(!hr)
			{
				CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
				return FALSE;
			}

			IAccessor_ pIAccessor;

			hr = COLEDBCommand::CreateAccessor(DBACCESSOR_PARAMETERDATA, 0, pIAccessor, pICmdWithParams, &hPrmAccessor, (ULONG&)nParamCnt, pPrmDBBinding);
			if(!hr)
				return FALSE;
			
			hr = GetParamsDataBuf(pBuffer);
			if(!hr)
				return FALSE;
			
			Params.pData		 = pBuffer;
			Params.cParamSets	 = 1;
			Params.hAccessor	 = hPrmAccessor;
			pParams				 = &Params;
		}
	}

	long nRowsAffected = 0;

	hr = Execute(strSQL, pParams, &nRowsAffected, NULL, false, m_bPrepared);
	if(!hr)
	{
		rValue = 0L;
		return FALSE;
	}
	
	rValue	 = nRowsAffected;
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procPutObjectList(CValue** ppValue)
{
	HRESULT hr = PutObjectsList(ppValue);
	if(!hr)
		return FALSE;
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procClose(CValue** ppValue)
{
	RemoveAllTmpTables();

	if(m_pICommandText)
		m_pICommandText.Release();

	m_bCreated = false;
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procDebug(CValue** ppValue)
{
	m_IsDebugMode = (0 == ppValue[0]->GetNumeric()) ? false : true;
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procSetTextParam(CValue** ppValue)
{
	m_MetaNameParser.SetParameter(ppValue[0]->GetString(), *ppValue[1]);
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procSetTempTablesDir(CValue** ppValue)
{
	CString strErr;

	m_szTempTablesDir.Empty();
	m_szTempTablesDir = ppValue[0]->GetString();

	if(m_szTempTablesDir.IsEmpty())
	{
		strErr.Format("Введена пустая строка!");
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}

	if(m_szTempTablesDir.Right(1) != "\\")
		m_szTempTablesDir += "\\";

	if(m_szTempTablesDir.Find(' ') > 0)
	{
		strErr.Format("Введена строка с пробелом!");
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procSetExecTimeOut(CValue** ppValue)
{
	m_ExecTimeout = ppValue[0]->GetNumeric();
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procAddParam(CValue** ppValue)
{
	CString strErr;

	int nSize = m_ParamList.GetCount();
	
	// эта память удаляется
	//
	COLEDBParam* oParam = new COLEDBParam;
	
	oParam->m_Ordinal = nSize + 1;

	DWORD dwParamIO;
	switch(ppValue[0]->GetNumeric())
	{
	case 1: dwParamIO = DBPARAMIO_INPUT;
		break;
	case 2: dwParamIO = DBPARAMIO_OUTPUT;
		break;
	case 3: dwParamIO = DBPARAMIO_INPUT|DBPARAMIO_OUTPUT;
		break;
	default:
		{
			strErr.Format("Неверный параметр (1)!");
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			return FALSE;
		}
	}

	WORD nType = ppValue[1]->GetNumeric();
	if(0 == GetTypeSize(nType))
	{
		strErr.Format("Неверный тип парамета (2)");
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
	}
	
	oParam->m_ParamIO	 = dwParamIO;
	oParam->m_Type		 = nType;
	oParam->m_Length	 = ppValue[2]->GetNumeric();
	oParam->m_Precision	 = ppValue[3]->GetNumeric();
	
	m_ParamList.AddTail(oParam);
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procSetParam(CValue** ppValue)
{
	CString strErr;

	int nOrdinal = ppValue[0]->GetNumeric();
	
	int nSize = m_ParamList.GetCount();
	if(!nSize)
	{
		strErr.Format("Список параметров пуст!");
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}
	
	if((nOrdinal > nSize) || (0 == nOrdinal))
	{
		strErr.Format("Недопустимый индекс параметра!");
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}

	COLEDBParam* oParam = m_ParamList.GetAt(m_ParamList.FindIndex(nOrdinal - 1));
	oParam->m_Value = *ppValue[1];

	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procDeleteParam(CValue** ppValue)
{
	int nParam = ppValue[0]->GetNumeric();
	int nCnt = m_ParamList.GetCount();
	
	if((nCnt > 0) && (nCnt >= nParam))
	{
		POSITION pos = m_ParamList.FindIndex(nParam - 1);
		COLEDBParam* oParam = m_ParamList.GetAt(pos);

		if(oParam)
			delete oParam;
		
		m_ParamList.RemoveAt(pos);
	}
	else
	{
		CBLModule::RaiseExtRuntimeError("Не верный номер параметра", mmRedErr);
		return FALSE;
	}

	return TRUE;
}

void COLEDBCommand::DeleteParams()
{
	POSITION pos = m_ParamList.GetHeadPosition();
	
	for(int nItem = 0, nCnt = m_ParamList.GetCount(); nItem < nCnt; nItem++)
	{
		COLEDBParam* oParam = m_ParamList.GetNext(pos);
		if(oParam)
			delete oParam;
	}
	
	m_ParamList.RemoveAll();
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procDeleteParams(CValue** ppValue)
{
	DeleteParams();
	
	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procPrepare(CValue** ppValue)
{
	CString strErr;
	HRESULT hr;
	
	CString strSQL(ppValue[0]->GetString());

	hr = COLEDBCommand::ParseQuery(strSQL);
	if(!hr)
		return FALSE;
	
	strErr.Empty();
	hr = Prepare(strSQL, strErr);
	if(!hr)
	{
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}
	
	m_bPrepared = true;

	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::procUnprepare(CValue** ppValue)
{
	CString strErr;
	HRESULT hr;
	
	hr = Unprepare(strErr);
	if(!hr)
	{
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return FALSE;
	}

	m_bPrepared = false;

	return TRUE;
}

//_____________________________________________________________________________
//
BOOL COLEDBCommand::funcParamCnt(CValue& rValue, CValue** ppValue)
{
	rValue = m_ParamList.GetCount();
	return TRUE;
}

//_____________________________________________________________________________
//
void COLEDBCommand::GetErrorDescription(CString& strError) const
{
	HRESULT hr;

	IErrorInfo* pIErrorInfo;

	try
	{
		hr = ::GetErrorInfo(0, &pIErrorInfo);
		if(SUCCEEDED(hr))
		{
			BSTR errDescr;
			pIErrorInfo->GetDescription(&errDescr);

			strError += ": ";
			strError += errDescr;

			::SysFreeString(errDescr);
		}
		pIErrorInfo->Release();
	}
	catch(...)
	{
		strError += ": Undefined critical error!";
	}
}

//_____________________________________________________________________________
//
bool COLEDBCommand::Binding(IRowset_& pIRowset, ULONG* pnCols, DBBINDING_& pDBBindings, COLEDBFldsInf_& pColInfo1C, ULONG* pcMaxRowSize, bool& HasKindField) const
{
	USES_CONVERSION;

	HRESULT hr;
	CString strErr;

	CComPtr<IColumnsInfo> spIColumnsInfo;
	
	CComPtr<IMalloc> spMalloc;
	if(FAILED(CoGetMalloc(MEMCTX_TASK, &spMalloc)))
	{
		CBLModule::RaiseExtRuntimeError("FAILED! IMalloc::CoGetMalloc(): Не удалось выделить память под объект!", mmRedErr);
		return false;
	}

	hr = pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&spIColumnsInfo);
	if(FAILED(hr))
	{
		strErr = "FAILED! IRowset::QueryInterface()";
		GetErrorDescription(strErr);
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return false;
	}

	ULONG nCols = 0;
	BSTR pColumnStrings;
	DBCOLUMNINFO* pColumnsInfo;

	hr = spIColumnsInfo->GetColumnInfo(&nCols, &pColumnsInfo, &pColumnStrings);
	if(FAILED(hr))
	{
		spMalloc->Free(pColumnsInfo);
		spMalloc->Free(pColumnStrings);

		strErr = "FAILED! IColumnsInfo::GetColumnInfo()";
		GetErrorDescription(strErr);
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return false;
	}

	ULONG nDataOffset = 0;
	ULONG nLengthOffset;
	ULONG nStatusOffset;

	pDBBindings.Alloc(nCols);
	if(pDBBindings == NULL)
	{
		spMalloc->Free(pColumnsInfo);
		spMalloc->Free(pColumnStrings);

		CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBBINDING", mmRedErr);
		return false;
	}

	pColInfo1C.Alloc(nCols);
	if(pColInfo1C == NULL)
	{
		spMalloc->Free(pColumnsInfo);
		spMalloc->Free(pColumnStrings);

		CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов COLEDBFldsInf", mmRedErr);
		return false;
	}

	for(ULONG nCol = 0; nCol < nCols; nCol++)
	{
		// заполняем мою структуру
		//
		CString strName = OLE2T(pColumnsInfo[nCol].pwszName);

		pColInfo1C[nCol].m_Index	 = nCol;
		pColInfo1C[nCol].m_strName	 = strName;
		pColInfo1C[nCol].m_wType	 = pColumnsInfo[nCol].wType;
		pColInfo1C[nCol].m_nType1C	 = GetType1C(&pColInfo1C[nCol]);

		// Для небольшой оптимизации: чтобы не искать при любом виде запроса колонку с названием "XXX_вид"/"XXX_kind"
		//
		if((pColInfo1C[nCol].m_strName.Find("_kind") >= 0) || (pColInfo1C[nCol].m_strName.Find("_вид") >= 0))
			HasKindField = true;

		//
		// нужно различать BLOB колонки от обычных.
		// MSDN говорит, что "text" and "ntext" (its for mssql) - 
		// это DBCOLUMNFLAGS_ISLONG = true or Maximum column size > 4,000 characters
		//
		
		if(pColumnsInfo[nCol].dwFlags & DBCOLUMNFLAGS_ISLONG)
		{
			pColInfo1C[nCol].m_bIsLong	 = true;

			pDBBindings[nCol].pObject	 = new DBOBJECT;
			DBOBJECT* pLocDBObject		 = pDBBindings[nCol].pObject;

			if(pLocDBObject == NULL)
			{
				spMalloc->Free(pColumnsInfo);
				spMalloc->Free(pColumnStrings);
				
				CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBOBJECT", mmRedErr);
				return false;
			}

			pLocDBObject->dwFlags		 = STGM_READ;
			pLocDBObject->iid			 = IID_ISequentialStream;

			pDBBindings[nCol].wType		 = DBTYPE_IUNKNOWN;
			pDBBindings[nCol].cbMaxLen	 = sizeof(ISequentialStream*);
		}
		else
		{
			pColInfo1C[nCol].m_bIsLong	 = false;
			
			pDBBindings[nCol].pObject	 = NULL;

			switch(pColumnsInfo[nCol].wType)
			{
				case DBTYPE_STR:
				case DBTYPE_WSTR:
					{
						pDBBindings[nCol].wType		 = pColumnsInfo[nCol].wType;
						pDBBindings[nCol].cbMaxLen	 = pColumnsInfo[nCol].ulColumnSize + sizeof(char);
					}
					break;
				
				case DBTYPE_DBDATE:
				case DBTYPE_DBTIMESTAMP:
					{
						pDBBindings[nCol].wType		 = pColumnsInfo[nCol].wType;
						pDBBindings[nCol].cbMaxLen	 = pColumnsInfo[nCol].ulColumnSize;
					}
					break;
				
				default:
					{
						pDBBindings[nCol].wType		 = DBTYPE_STR;
						pDBBindings[nCol].cbMaxLen	 = 100;
					}
					break;
			};
		}

		nLengthOffset	 = AddOffset(nDataOffset, pDBBindings[nCol].cbMaxLen);
		nStatusOffset	 = AddOffset(nLengthOffset, sizeof(ULONG));

		pDBBindings[nCol].iOrdinal	 = pColumnsInfo[nCol].iOrdinal;
		pDBBindings[nCol].obValue	 = nDataOffset;
		pDBBindings[nCol].obLength	 = nLengthOffset;
		pDBBindings[nCol].obStatus	 = nStatusOffset;
		pDBBindings[nCol].pTypeInfo	 = NULL;
		pDBBindings[nCol].pBindExt	 = NULL;
		pDBBindings[nCol].dwPart	 = DBPART_VALUE | DBPART_STATUS;
		pDBBindings[nCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
		pDBBindings[nCol].eParamIO	 = DBPARAMIO_NOTPARAM;
		pDBBindings[nCol].dwFlags	 = 0;
		pDBBindings[nCol].bPrecision = pColumnsInfo[nCol].bPrecision;
		pDBBindings[nCol].bScale	 = pColumnsInfo[nCol].bScale;

		nDataOffset = AddOffset(nStatusOffset, sizeof(DBSTATUS));

		int nTypeCode = pColInfo1C[nCol].m_nType1C.GetTypeCode();
		
		switch(nTypeCode)
		{
			case ENUM_TYPE_1C:
			case REFERENCE_TYPE_1C:
			case CALENDAR_TYPE_1C:
			case CALCULATIONKIND_TYPE_1C:
			case ACCOUNT_TYPE_1C:
			case SUBCONTOKIND_TYPE_1C:
			case CHART_OF_ACC_TYPE_1C:
			case UNDEFINE_TYPE_1C:
			case DOCUMENT_TYPE_1C:
				pColInfo1C[nCol].m_bIsType1C = true;
				break;
			
			default:
				pColInfo1C[nCol].m_bIsType1C = false;
				break;
		}
	}
	
	*pnCols			 = nCols;
	*pcMaxRowSize	 = nDataOffset;

	spMalloc->Free(pColumnsInfo);
	spMalloc->Free(pColumnStrings);
	
	return true;
}

//_____________________________________________________________________________
//
CType COLEDBCommand::GetType1C(COLEDBFldsInf* fInfo) const
{
	if(pDataBase7->GetDataSourceType() == DATA_SOURCE_TYPE_DBF)
	{
		int nPos = -1;
		CType rt(0);
		CString strName = fInfo->m_strName;
		
		nPos = strName.Find(TypeNamePrefix);
		if(nPos >= 0)
		{
			CString Type1C(strName.operator LPCTSTR() + nPos + (sizeof(TypeNamePrefix) - 1));
			Type1C.Replace('_', '.');

			CMetaDataWork::SetTypeAndKind(rt, Type1C);

			strName				 = strName.Left(nPos);
			fInfo->m_strName	 = strName;

			if(!Type1C.CollateNoCase("Субконто") || !Type1C.CollateNoCase("Subconto"))
				fInfo->m_eExtType1C = eToSb;
			
			else if(!Type1C.CollateNoCase("Документ") || !Type1C.CollateNoCase("Document"))
				fInfo->m_eExtType1C = eToDoc;

			else
				fInfo->m_eExtType1C = eToOther;
			
			return rt;
		}
	}

	switch(fInfo->m_wType)
	{
		case DBTYPE_STR:
		case DBTYPE_WSTR:
		case DBTYPE_DBTIME:
			return CType(2);
            break;

		case DBTYPE_NUMERIC:
		case DBTYPE_I2:
		case DBTYPE_I4:
		case DBTYPE_R4:
		case DBTYPE_R8:
		case DBTYPE_CY:
		case DBTYPE_DECIMAL:
		case DBTYPE_UI1:
		case DBTYPE_BOOL:
		case DBTYPE_I1:
		case DBTYPE_UI2:
		case DBTYPE_UI4:
		case DBTYPE_I8:
		case DBTYPE_UI8:
			return CType(1); // DmitrO говорит, что тип Число с Длина(0).Точность(0) - нормально
            break;

		case DBTYPE_DBDATE:
		case DBTYPE_DBTIMESTAMP:
			return CType(3);
			break;
		
		default:
			return CType(0);
			break;
	};
}

//_____________________________________________________________________________
//
bool COLEDBCommand::ParseQuery(CString& strQuery)
{
	// OUT: [S_OK, S_FALSE]
	//
	CString strErr;

	strQuery.TrimLeft();
	strQuery.TrimRight();

	if(strQuery.IsEmpty())
	{
		strErr = "A SQL statement is empty!";
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		
		return false;
	}

	m_MetaNameParser.SetQueryText(strQuery);
	
	try
	{
		m_MetaNameParser.Parse();
	}
	catch(CMNPException* MNPException)
	{
		strErr = szMNPErrorPrefix + MNPException->GetErrorDescr();
		MNPException->Delete();

		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);

		return false;
	}
	catch(...)
	{
		CBLModule::RaiseExtRuntimeError("MetaNameParser critical error!", mmRedErr);

		return false;
	};

	strQuery = m_MetaNameParser.GetQueryText();

	if(m_IsDebugMode)
		pBkEndUI->DoMessageLine(strQuery, mmNone);
	
	return true;
}

//_____________________________________________________________________________
//
bool COLEDBCommand::Execute(const CString& strSQL, DBPARAMS* pParams, long* pnRowsAffected, IUnknown** pIRowset, bool bQuiet, bool bPrepared) const
{
	USES_CONVERSION;
	
	HRESULT hr;
	CString strErr;

	if(!bPrepared)
	{
		hr = m_pICommandText->SetCommandText(DBGUID_DEFAULT, T2OLE(strSQL));
		if(FAILED(hr))
		{
			if(bQuiet)
				return false;
			
			strErr = "FAILED! ICommandText::SetCommandText()";
			GetErrorDescription(strErr);
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			return false;
		}
	}

	if(m_ExecTimeout >= 0)
	{
		CComPtr<ICommandProperties> spCommProperties;

		hr = m_pICommandText->QueryInterface(&spCommProperties);
		if(FAILED(hr))
		{
			strErr = "FAILED! ICommandProperties::QueryInterface()";
			GetErrorDescription(strErr);
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			return false;
		}

		CDBPropSet PropSet(DBPROPSET_ROWSET);
		PropSet.AddProperty(DBPROP_COMMANDTIMEOUT, m_ExecTimeout);
		
		hr = spCommProperties->SetProperties(1, &PropSet);
		if(FAILED(hr))
		{
			strErr = "FAILED! ICommandProperties::SetProperties(): Не удалось установить property";
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			
			return false;
		}
	}

	if(!pIRowset)
		hr = m_pICommandText->Execute(NULL, IID_NULL, pParams, pnRowsAffected, NULL);
	else
		hr = m_pICommandText->Execute(NULL, IID_IRowset, pParams, pnRowsAffected, pIRowset);

	if(FAILED(hr))
	{
		if(bQuiet)
			return false;

		strErr = "FAILED! ICommandText::Execute()";
		GetErrorDescription(strErr);
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return false;
	}

	return true;
}

//_____________________________________________________________________________
//
bool COLEDBCommand::CreateAccessor(DBACCESSORFLAGS eDBACCFLAGS, ULONG cbRowSize, IAccessor_& pIAccessor, IUnknown* pIUnknown, HACCESSOR* phAccessor, ULONG& cCntItems, DBBINDING_& pDBBindings) const
{
	// OUT: [S_FALSE, S_OK]
	//
	HRESULT hr;
	CString strErr;

	hr = pIUnknown->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
	if(FAILED(hr))
	{
		strErr = "FAILED! IUnknown::QueryInterface()";
		GetErrorDescription(strErr);
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return false;
	}

	DBBINDSTATUS_ pDBBindStatus;

	pDBBindStatus.Alloc(cCntItems);
	if(pDBBindStatus == NULL)
	{
		CBLModule::RaiseExtRuntimeError("Не удалось выделить память под массив объектов DBBINDSTATUS", mmRedErr);
		return false;
	}

	hr = pIAccessor->CreateAccessor(eDBACCFLAGS, cCntItems, pDBBindings, cbRowSize, phAccessor, pDBBindStatus);
	if(FAILED(hr))
	{
		strErr = "FAILED! IAccessor::CreateAccessor()";

		switch(hr)
		{
		case DB_E_BADACCESSORFLAGS:
			strErr += " [One or more accessor flags were invalid]";
			break;

		case DB_E_BYREFACCESSORNOTSUPPORTED:
			strErr += " [Reference accessors are not supported by this provider]";
			break;

		case DB_E_ERRORSOCCURRED:
			strErr += " [Multiple-step OLE DB operation generated errors]";
			break;

		case DB_E_NOTREENTRANT:
			strErr += " [Consumer's event handler called a non-reentrant method in the provider]";
			break;

		case DB_E_NULLACCESSORNOTSUPPORTED:
			strErr += " [Null accessors are not supported by this provider]";
			break;
		
		default:
			break;
		}

		GetErrorDescription(strErr);
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		
		return false;
	}
	
	return true;
}

//_____________________________________________________________________________
//
bool COLEDBCommand::PutObjectsList(CValue** ppValue)
{
	CString strErr;
	HRESULT hr;

	// Сразу позаботимся, чтобы параметр был пустым
	//
	CValue* pTableName = ppValue[1];
	pTableName->Reset();

	// Сгенерируем новый GUID
	//
	::UuidCreate(&m_GUID);
	
	unsigned char* strGUID;
	::UuidToString(&m_GUID, &strGUID);
	
	// Получим текстовое значение GUID
	//
	CString szTableName = strGUID;
	szTableName = m_szTempTablesDir + szTableName;
	::RpcStringFree(&strGUID);

	CString strSQL;
	
	// Создаем временную таблицу
	//
	strSQL.Format("CREATE TABLE %s (VAL C(9) NOT NULL, ISFOLDER N(1,0) NOT NULL)", szTableName);

	long nRowsAffected = 0;
	
	hr = COLEDBCommand::Execute(strSQL, NULL, &nRowsAffected, NULL);
	if(!hr)
	{
		strErr.Format("Не удалось создать временную таблицу!");
		CBLModule::RaiseExtRuntimeError(strErr, 0);

		return false;
	}

	// Заполняем список: временные таблицы
	//
	m_TmpTblsList.AddTail(szTableName);

	CString RefName = ppValue[2]->GetString();

	bool ToDeleteFldr = false;

	if(ppValue[0]->GetTypeCode() == AGREGATE_TYPE_1C)
	{
		// Если передан список значений
		//
		CBLContext* pValCont = ppValue[0]->GetContext();

		if(pValCont && !strcmp(pValCont->GetRuntimeClass()->m_lpszClassName, "CValueListContext"))
		{
			CPtrArray* pVL = *(CPtrArray**) (((char*)pValCont) + 0x30);
			
			int nlistSize = pVL->GetSize();
			for(int i = 0; i < nlistSize; i++)
			{
				CValue* pValItem = (CValue*)pVL->GetAt(i);

				hr = PutAggregateObjects(pValItem, &szTableName, &RefName);
				if(!hr)
					return false;
			}

			if(nlistSize > 0)
				ToDeleteFldr = true;
		}
		else
		{
			strErr.Format("Недопустимое значение первого аргумента метода!");
			CBLModule::RaiseExtRuntimeError(strErr, 0);
			return false;
		}
	}
	else if(ppValue[0]->GetTypeCode() > DATE_TYPE_1C)
	{
		// Если передан агрегатный объект
		//
		CValue* pAggregate	 = ppValue[0];

		hr = PutAggregateObjects(pAggregate, &szTableName, &RefName);
		if(!hr)
			return false;

		ToDeleteFldr = true;
	}

	if(ToDeleteFldr)
	{
		// Теперь нужно из временной таблицы удалить строки - группы (isfolder=1)
		//
		strSQL = "DELETE FROM %tmptblnm WHERE ISFOLDER = 1";
		strSQL.Replace("%tmptblnm", szTableName);

		nRowsAffected = 0;
		
		hr = COLEDBCommand::Execute(strSQL, NULL, &nRowsAffected, NULL);
		if(!hr)
		{
			strErr.Format("Не удалось удалить группы из временной таблицы!");
			CBLModule::RaiseExtRuntimeError(strErr, 0);
			
			return false;
		}

		strSQL = "USE";

		nRowsAffected = 0;

		hr = COLEDBCommand::Execute(strSQL, NULL, &nRowsAffected, NULL);
		if(!hr)
		{
			strErr.Format("Не удалось сменить контекст базы данных!");
			CBLModule::RaiseExtRuntimeError(strErr, 0);
			
			return false;
		}
	}

	// Все, теперь можно вернуть имя временной таблицы
	//
	*pTableName	 = szTableName.operator LPCTSTR();
	
	return true;
	}

//_____________________________________________________________________________
//
bool COLEDBCommand::PutAggregateObjects(CValue* pValue, const CString* pszTableName, const CString* RefName)
{
	CString strErr;
	CString strSQL;
	HRESULT hr;

	CSbCntTypeDef* pSbCntTypeDef = NULL;

	if(!RefName->IsEmpty())
	{
		pSbCntTypeDef = pMetaDataCont->GetSTypeDef(*RefName);
		
		if(!pSbCntTypeDef)
		{
			strErr.Format("Недопустимое значение третьего пераметра метода!");
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			
			return false;
		}
	}

	int nLevelsCount = 1;
	int nIsFldr		 = 2;
	
	if(!pValue->IsEmpty() && !RefName->IsEmpty())
	{
		CValue arValue;
		arValue.Reset();

		pValue->LinkContext(FALSE);
		CBLContext* pValCont = pValue->GetContext();
		pValCont->CallAsFunc(pValCont->FindMethod("IsGroup"), arValue, NULL);
		nIsFldr = 0 == arValue.GetNumeric() ? 2 : 1;

		nLevelsCount = pSbCntTypeDef->GetLevelsLimit();
	}

	CString ObjStr = "'";
	ObjStr += CMetaDataWork::GetObjDBString(*pValue, CMetaDataWork::ShortString);
	ObjStr += "'";

	strSQL.Empty();
	strSQL.Format("INSERT INTO %s (VAL, ISFOLDER) VALUES (%s, %d)", (*pszTableName), ObjStr, nIsFldr);

	long nRowsAffected = 0;
	
	hr = COLEDBCommand::Execute(strSQL, NULL, &nRowsAffected, NULL);
	if(!hr)
	{
		strErr.Format("Не удалось добавить корневую строку!");
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);

		return false;
	}

	// Если не передали вид справочника, то дальше не выполняем
	//
	if(RefName->IsEmpty() || nLevelsCount < 2)
		return true;

	strSQL.Empty();

	strSQL = "INSERT INTO %tmptblnm (VAL, ISFOLDER)\r\n\
			SELECT SC.ID as VAL, SC.ISFOLDER as ISFOLDER\r\n\
			FROM %sctbl as SC\r\n\
			WHERE\r\n\
			(SC.PARENTID IN (SELECT T.VAL FROM %tmptblnm as T))\r\n\
			AND (SC.ID NOT IN (SELECT TT.VAL FROM %tmptblnm as TT))";

	strSQL.Replace("%tmptblnm", (*pszTableName));
	
	CString RefTable;
	RefTable.Format("sc%d", pSbCntTypeDef->GetID());
	strSQL.Replace("%sctbl", RefTable);

	strErr.Empty();
	hr = Prepare(strSQL, strErr);
	if(!hr)
	{
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return false;
	}

	// Нужно заполнять временную таблицу итеративно, проход - спуск ниже уровнем
	//
	do
	{
		nRowsAffected = 0;

		hr = COLEDBCommand::Execute(strSQL, NULL, &nRowsAffected, NULL, false, true);
		if(!hr)
		{
			strErr.Format("Не удалось добавить строки!");
			CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
			
			return false;
		}
	//}while (nRowsAffected > 0 && !FAILED(hr));
	}while(nRowsAffected > 0);

	strErr.Empty();
	hr = Unprepare(strErr);
	if(!hr)
	{
		CBLModule::RaiseExtRuntimeError(strErr, mmRedErr);
		return false;
	}

	return true;
}

//_____________________________________________________________________________
//
void COLEDBCommand::RemoveTmpTable(CString& strTmpTblName)
{
	// Значит уже закрыли команду, вообще такого не должно быть
	// но лучше перебздеть, чем недобздеть
	//
	if(!m_pICommandText)
		return;

	CString strErr;
	HRESULT hr;

	CString strSQL("DROP TABLE ");
	strSQL += strTmpTblName;

	long nRowsAffected = 0;

	hr = COLEDBCommand::Execute(strSQL, NULL, &nRowsAffected, NULL, true);
}

//_____________________________________________________________________________
//
void COLEDBCommand::RemoveAllTmpTables()
{
	int nCountTmps = m_TmpTblsList.GetCount();
	if(nCountTmps > 0)
	{
		for(int i = 0; i < nCountTmps; ++i)
			RemoveTmpTable(m_TmpTblsList.GetAt(m_TmpTblsList.FindIndex(i)));

		m_TmpTblsList.RemoveAll();
	}
}

//_____________________________________________________________________________
//
ULONG COLEDBCommand::FindKindCol(const COLEDBFldsInf* pColumnsInfo, const ULONG& cMaxCols, const CString& strColName) const
{
	CString tmpNameRus(strColName);
	tmpNameRus += "_вид";
	
	CString tmpNameEng(strColName);
	tmpNameEng += "_kind";

	for(ULONG cCol = 0; cCol < cMaxCols; cCol++)
	{
		int nRus = tmpNameRus.CompareNoCase(pColumnsInfo[cCol].m_strName);
		int nEng = tmpNameEng.CompareNoCase(pColumnsInfo[cCol].m_strName);
		
		if((0 == nRus) || (0 == nEng))
			return cCol + 1;
	}

	return 0;
}

//_____________________________________________________________________________
//
LPOLESTR COLEDBCommand::wGetTypeByStr(const WORD DBType) const
{
	switch(DBType)
	{
		case DBTYPE_I1:			 return L"DBTYPE_I1";
		case DBTYPE_I2:			 return L"DBTYPE_I2";
		case DBTYPE_I4:			 return L"DBTYPE_I4";
		case DBTYPE_I8:			 return L"DBTYPE_I8";
		case DBTYPE_UI1:		 return L"DBTYPE_UI1";
		case DBTYPE_UI2:		 return L"DBTYPE_UI2";
		case DBTYPE_UI4:		 return L"DBTYPE_UI4";
		case DBTYPE_UI8:		 return L"DBTYPE_UI8";
		case DBTYPE_R4:			 return L"DBTYPE_R4";
		case DBTYPE_R8:			 return L"DBTYPE_R8";
		case DBTYPE_CY:			 return L"DBTYPE_CY";
		case DBTYPE_DECIMAL:	 return L"DBTYPE_DECIMAL";
		case DBTYPE_NUMERIC:	 return L"DBTYPE_NUMERIC";
		case DBTYPE_BOOL:		 return L"DBTYPE_BOOL";
		case DBTYPE_ERROR:		 return L"DBTYPE_ERROR";
		case DBTYPE_UDT:		 return L"DBTYPE_UDT";
		case DBTYPE_VARIANT:	 return L"DBTYPE_VARIANT";
		case DBTYPE_IDISPATCH:	 return L"DBTYPE_IDISPATCH";
		case DBTYPE_IUNKNOWN:	 return L"DBTYPE_IUNKNOWN";
		case DBTYPE_GUID:		 return L"DBTYPE_GUID";
		case DBTYPE_DATE:		 return L"DBTYPE_DATE";
		case DBTYPE_DBDATE:		 return L"DBTYPE_DBDATE";
		case DBTYPE_DBTIME:		 return L"DBTYPE_DBTIME";
		case DBTYPE_DBTIMESTAMP: return L"DBTYPE_DBTIMESTAMP";
		case DBTYPE_BSTR:		 return L"DBTYPE_BSTR";
		case DBTYPE_WSTR:		 return L"DBTYPE_WCHAR";
		case DBTYPE_BYTES:		 return L"DBTYPE_BINARY";
		case DBTYPE_FILETIME:	 return L"DBTYPE_FILETIME";
		case DBTYPE_VARNUMERIC:	 return L"DBTYPE_VARNUMERIC";
		case DBTYPE_PROPVARIANT: return L"DBTYPE_PROPVARIANT";
		default:				 return L"DBTYPE_CHAR";
	}
}

//_____________________________________________________________________________
//
bool COLEDBCommand::BindWithParams(CString& strErr, const int& nParamCnt, ICommandWithParameters_& pICmdParams, DBPARAMBINDINFO_& pParamBindInfo, DBBINDING_& pDBBinding, ULONG_& pParamOrdinals)
{
	HRESULT hr;

	ULONG nDataOffset = 0;
	ULONG nLengthOffset = 0;
	ULONG nStatusOffset = 0;

	for(int nItem = 0; nItem < nParamCnt; nItem++)
	{
		ULONG nSize = 0;
		//COLEDBParam& oParam = m_ParamList.ElementAt(nItem);
		COLEDBParam* oParam = m_ParamList.GetAt(m_ParamList.FindIndex(nItem));

		switch(oParam->m_Type)
		{
			case DBTYPE_STR:
			case DBTYPE_WSTR:
				nSize = oParam->m_Length * GetTypeSize(oParam->m_Type);
				nSize += GetTypeSize(oParam->m_Type); // под конец строки
				break;
			
			default:
				nSize = GetTypeSize(oParam->m_Type);
				break;
		};

		pParamBindInfo[nItem].pwszDataSourceType	 = wGetTypeByStr(oParam->m_Type);
		pParamBindInfo[nItem].pwszName				 = NULL;
		pParamBindInfo[nItem].dwFlags				 = oParam->m_ParamIO;
		pParamBindInfo[nItem].ulParamSize			 = nSize;
		pParamBindInfo[nItem].bPrecision			 = oParam->m_Precision;
		pParamBindInfo[nItem].bScale				 = 0;

		pDBBinding[nItem].wType		 = oParam->m_Type;
		pDBBinding[nItem].cbMaxLen	 = nSize;

		nLengthOffset = nDataOffset + nSize;

		pDBBinding[nItem].iOrdinal		 = oParam->m_Ordinal;
		pDBBinding[nItem].obValue		 = nDataOffset;
		pDBBinding[nItem].obLength		 = nLengthOffset;
		pDBBinding[nItem].obStatus		 = 0;//nStatusOffset;
		pDBBinding[nItem].pTypeInfo		 = NULL;
		pDBBinding[nItem].pObject		 = NULL;
		pDBBinding[nItem].pBindExt		 = NULL;
		pDBBinding[nItem].dwPart		 = DBPART_VALUE | DBPART_LENGTH;
		pDBBinding[nItem].dwMemOwner	 = DBMEMOWNER_CLIENTOWNED;
		pDBBinding[nItem].dwFlags		 = 0;
		pDBBinding[nItem].eParamIO		 = oParam->m_ParamIO;
		pDBBinding[nItem].bPrecision	 = oParam->m_Precision;
		pDBBinding[nItem].bScale		 = 0;

		nDataOffset = nLengthOffset + sizeof(long);

		pParamOrdinals[nItem] = oParam->m_Ordinal;
	}

	hr = m_pICommandText->QueryInterface(IID_ICommandWithParameters, (void**)&pICmdParams);
	if(FAILED(hr))
	{
		strErr = "FAILED! ICommandText::QueryInterface(ICommandWithParameters)";
		GetErrorDescription(strErr);
		return false;
	}

	hr = pICmdParams->SetParameterInfo(nParamCnt, pParamOrdinals, pParamBindInfo);
	if(FAILED(hr))
	{
		strErr = "FAILED! ICommandWithParameters::SetParameterInfo()";
		
		switch(hr)
		{
			case E_INVALIDARG: strErr += " (INVALIDARG)";
				break;

			case DB_E_BADORDINAL: strErr += " (BADORDINAL)";
				break;

			case DB_E_BADPARAMETERNAME: strErr += " (BADPARAMETERNAME)";
				break;

			case DB_E_BADTYPENAME: strErr += " (BADTYPENAME)";
				break;

			case DB_E_OBJECTOPEN: strErr += " (OBJECTOPEN)";
				break;

			default:
				break;
		}

		GetErrorDescription(strErr);
		return false;
	}
	
	return true;
}

//_____________________________________________________________________________
//
bool COLEDBCommand::Unprepare(CString& strErr) const
{
	HRESULT hr;

	CComPtr<ICommandPrepare> pICommandPrepare;

	hr = m_pICommandText->QueryInterface(IID_ICommandPrepare, (void**)&pICommandPrepare);
	if(FAILED(hr))
	{
		strErr = "FAILED! ICommandText::QueryInterface(ICommandPrepare)";
		GetErrorDescription(strErr);
		return false;
	}

	if(FAILED(pICommandPrepare->Unprepare()))
	{
		strErr = "FAILED! ICommandPrepare::Unprepare(0)";
		GetErrorDescription(strErr);
		return false;
	}

	return true;
}

//_____________________________________________________________________________
//
bool COLEDBCommand::Prepare(const CString& strSQL, CString& strErr) const
{
	USES_CONVERSION;

	HRESULT hr;

	hr = m_pICommandText->SetCommandText(DBGUID_DBSQL, T2OLE(strSQL));
	if(FAILED(hr))
	{
		strErr = "FAILED! ICommandText::SetCommandText()";
		GetErrorDescription(strErr);
		return false;
	}

	CComPtr<ICommandPrepare> pICommandPrepare;

	hr = m_pICommandText->QueryInterface(IID_ICommandPrepare, (void**)&pICommandPrepare);
	if(FAILED(hr))
	{
		strErr = "FAILED! ICommandText::QueryInterface(ICommandPrepare)";
		GetErrorDescription(strErr);
		return false;
	}
	
	if(FAILED(pICommandPrepare->Prepare(0)))
	{
		strErr = "FAILED! ICommandPrepare::Prepare(0)";
		GetErrorDescription(strErr);
		return false;
	}

	return true;
}

//_____________________________________________________________________________
//
bool COLEDBCommand::GetParamsDataBuf(BYTE_& pBuffer)
{
	int nParamCnt = m_ParamList.GetCount();

	long nSize = 0;
	int nItem;

	for(nItem = 0; nItem < nParamCnt; nItem++)
	{
		COLEDBParam* oParam = m_ParamList.GetAt(m_ParamList.FindIndex(nItem));

		WORD nType = oParam->m_Type;

		switch(nType)
		{
			case DBTYPE_STR:
			case DBTYPE_WSTR:
				{
					nSize = nSize + oParam->m_Length * GetTypeSize(nType);
					nSize = nSize + GetTypeSize(nType); // под конец строки
				}
				break;
			
			default:
				{
					if(GetTypeSize(nType))
						nSize = nSize + GetTypeSize(nType);
					else
						return false;
				}
				break;
		};

		nSize = nSize + sizeof(long); // под размер параметра (sizeof(long) = 4)
	}

	pBuffer.Alloc(nSize);
	if(pBuffer == NULL)
		return false;

	memset(pBuffer, 0, sizeof(BYTE)*nSize);

	long offset = 0;
	for(nItem = 0; nItem < nParamCnt; nItem++)
	{
		COLEDBParam* oParam = m_ParamList.GetAt(m_ParamList.FindIndex(nItem));

		CValue& Val = oParam->m_Value;
		long nParamLen = 0;
		int nCode = Val.GetTypeCode();

		CValueToDBTYPE ValTo(&Val, nCode);
		
		switch(oParam->m_Type)
		{
			case DBTYPE_STR:
				{
					nParamLen = oParam->m_Length;
					CString strVal = ValTo.AsSTR(nParamLen);

					LPCTSTR szVal = strVal.operator LPCTSTR();
					memcpy(pBuffer + offset, szVal, nParamLen * GetTypeSize(oParam->m_Type));

					pBuffer[offset + nParamLen] = '\0';
					offset += (nParamLen + 1); // + \0

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_DECIMAL:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					DECIMAL Dec = ValTo.AsDECIMAL();

					memcpy(pBuffer + offset, &Dec, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;
			
			case DBTYPE_DBDATE:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					DBDATE dbDate = ValTo.AsDBDATE();

					memcpy(pBuffer + offset, &dbDate, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_I2:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					SHORT nVal = ValTo.AsI2();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_I4:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					LONG nVal = ValTo.AsI4();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_R4:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					FLOAT nVal = ValTo.AsR4();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_R8:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					DOUBLE nVal = ValTo.AsR8();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_CY:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					LARGE_INTEGER nVal = ValTo.AsCY();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;
			
			case DBTYPE_DATE:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;
					
					DATE nVal = ValTo.AsDATE();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;
					
					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_BOOL:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					VARIANT_BOOL nVal = ValTo.AsBOOL();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_UI1:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					BYTE nVal = ValTo.AsUI1();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_I1:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					signed char nVal = ValTo.AsI1();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_UI2:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					USHORT nVal = ValTo.AsUI2();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_UI4:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					ULONG nVal = ValTo.AsUI4();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_I8:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					LARGE_INTEGER nVal = ValTo.AsI8();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_UI8:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					ULARGE_INTEGER nVal = ValTo.AsUI8();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_GUID:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					GUID nVal = ValTo.AsGUID();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			/*case DBTYPE_WSTR:
				{
				}
				break;*/

			case DBTYPE_NUMERIC:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					DB_NUMERIC nVal = ValTo.AsNUMERIC();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_DBTIMESTAMP:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					DBTIMESTAMP nVal = ValTo.AsDBTIMESTAMP();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;

			case DBTYPE_DBTIME:
				{
					nParamLen = GetTypeSize(oParam->m_Type);
					if(0 == nParamLen)
						return false;

					DBTIME nVal = ValTo.AsDBTIME();

					memcpy(pBuffer + offset, &nVal, nParamLen);
					offset += nParamLen;

					memcpy(pBuffer + offset, &nParamLen, sizeof(long));
					offset += sizeof(long);
				}
				break;
			
			default:
				return false;
		}
	}

	return true;
}

//_____________________________________________________________________________
//
long COLEDBCommand::GetTypeSize(WORD nType)
{
	long nSize;
	
	switch(nType)
	{
		case DBTYPE_I2: nSize = sizeof(SHORT);
			break;
		
		case DBTYPE_I4: nSize = sizeof(LONG);
			break;
		
		case DBTYPE_R4: nSize = sizeof(FLOAT);
			break;
		
		case DBTYPE_R8: nSize = sizeof(DOUBLE);
			break;
		
		case DBTYPE_CY: nSize = sizeof(LARGE_INTEGER);
			break;
		
		case DBTYPE_DATE: nSize = sizeof(DATE);
			break;
		
		case DBTYPE_BOOL: nSize = sizeof(VARIANT_BOOL);
			break;
		
		case DBTYPE_DECIMAL: nSize = sizeof(DECIMAL);
			break;
		
		case DBTYPE_UI1: nSize = sizeof(BYTE);
			break;
		
		case DBTYPE_I1: nSize = sizeof(signed char);
			break;
		
		case DBTYPE_UI2: nSize = sizeof(USHORT);
			break;
		
		case DBTYPE_UI4: nSize = sizeof(ULONG);
			break;
		
		case DBTYPE_I8: nSize = sizeof(LARGE_INTEGER);
			break;
		
		case DBTYPE_UI8: nSize = sizeof(ULARGE_INTEGER);
			break;
		
		case DBTYPE_GUID: nSize = sizeof(GUID);
			break;
		
		case DBTYPE_STR: nSize = sizeof(char);
			break;

		case DBTYPE_DBTIME: nSize = sizeof(DBTIME);
			break;
			
		/*case DBTYPE_WSTR: nSize = sizeof(wchar_t);
			break;*/
		
		case DBTYPE_NUMERIC: nSize = sizeof(DB_NUMERIC);
			break;

		case DBTYPE_DBTIMESTAMP: nSize = sizeof(DBTIMESTAMP);
			break;

		case DBTYPE_DBDATE: nSize = sizeof(DBDATE);
			break;
		
		default: nSize = 0;
			break;
	}

	return nSize;
}