// aplQLQueryParse.cpp: implementation of the CaplQLQueryParseBase class.
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ErrMessCd.h"
#include "aplStepQL.h"

#define MY_THROW_QP(_err_code) {throw(SaplErrorDescription( _err_code,_T(__FILE__),_T(__DATE__),__LINE__,_T(""),4,_T("")));}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CaplQLQueryParseBase::CaplQLQueryParseBase(CaplStepData *hStepData):
CaplQLQuery(hStepData)
{
	m_error_string_num=1;
	m_error_pos=1;
	m_res_bush = 0;
	m_is_parse = false;
	m_is_header = true;
	m_pos_read = 0;
	m_recurse_level = 0;
	m_in = _T("");
	m_last_operation_type = aplAND;
	m_TypeSrv = _T("");
	m_CurrUser = 0;
	m_CurrUserGroupe = 0;
	m_CurrUserPerson = 0;
	m_brackets = 0;

}

CaplQLQueryParseBase::~CaplQLQueryParseBase()
{
}



// Основная функция разбора. Ловит исключения всех используемых функций
// и обрабатывает ошибки
bool CaplQLQueryParseBase::parse(CString &query)
{
	m_all_objects.Clear();
	m_extents.Clear();
	m_error_string_num = 1;
	m_error_pos = 1;
	m_is_parse = false;
	m_pos_read = 0;
	int i = 0;
	int id;
	bool state;
	CaplQLExtent *ext = 0;
	CaplEntity *ent = 0;
	CaplInstance* inst;

	CString sTmp4Mid; // Для вызова через отд. переменную т.к. GCC не переваривает по другому

	try
	{
		m_in = query;
 		GetWord(m_cur_word);

		if(m_cur_word == _T("select_load_all"))
			m_load_attr = true;
		else if(m_cur_word != _T("select"))
			MY_THROW_QP(STEP_QL_ERR_WAIT_SELECT);

		// Разгребаем условия окончательной выборки
		m_brackets = 0;
		m_recurse_level = 0;
		m_is_header = true;
        m_res_bush = GetFields(query);
		// Проверяем корректность появления FROM
		if(m_cur_word != _T("from")) MY_THROW_QP(STEP_QL_ERR_WAIT_FROM);
		if(m_res_bush == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
		m_is_header = false;
		
		// Разбираем определения экстентов
		GetWord(m_cur_word);
		while(m_cur_word != _T("end_select"))
		{
			// Это должно быть имя экстента. 
			if(m_cur_word == _T("")) MY_THROW_QP(STEP_QL_ERR_COULD_NOT_READ);
			if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_NAME_EXT);
			
			//Ищем экстент
			ext = FindQLExtent(m_cur_word, true);
			if(m_load_attr)
				ext->with_attr = true;

			// Проверка на переопределение
			if(ext->base_entity != 0){ MY_THROW_QP(STEP_QL_ERR_EXT_DEFINED);}
			// дальше скобка и условие
			GetWord(m_cur_word);
			if(m_cur_word != _T("{")){ MY_THROW_QP(STEP_QL_ERR_WAIT_OPEN_CURLY_BRAKET);}

			// v 1.4 запросов - подзапрос может олпределяться 3-мя способами
			GetWord(m_cur_word);
			
			// имя ентити
			ent = m_data->GetEntityBN(m_cur_word);
			
			if(ent != 0)
			{
				ext->base_entity = ent;
				// Перед вызовом этой рекурсии ей надо подготовить слово
				GetWord(m_cur_word);
				if(m_cur_word == _T("}"))
				{
					ext->conditions = 0;
				}
				else
				{
					ext->conditions = GetConditions(ent);
					// проверяем, все ли верно
					if(ext->conditions == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_EXTENT_DEF);
					if(m_brackets>0) MY_THROW_QP(STEP_QL_ERR_BRAKETS_TOO_MUCH_OPEN);
					if(m_brackets<0) MY_THROW_QP(STEP_QL_ERR_BRAKETS_TOO_MUCH_CLOSE);
					
					// Должна прочитаться закрывающая скобка
					GetWord(m_cur_word);

					if(m_cur_word != _T("}")) MY_THROW_QP(STEP_QL_ERR_WAIT_CLOSE_CURLY_BRAKET);
				}
				ext->is_defined = true;
			}
			else if(m_cur_word[0] == _T('#'))
			{
				ext->base_entity = 0;
				// Читаем список инстансов или экстентов
				ext->res_ext.Clear();
				ext->QLExtents = 0;
				// имя или номер
				sTmp4Mid=m_cur_word.Mid(1);
				state = IsNumber(sTmp4Mid);
				if(state)
				{
					// цикл по занесению инстансов
					do
					{
						if(!state) MY_THROW_QP(STEP_QL_ERR_WAIT_INST_VALUE);
						inst = 0;
						if(m_cur_word[0] != _T('#'))MY_THROW_QP(STEP_QL_ERR_WAIT_INST_VALUE);
						m_cur_word.Delete(0, 1);
						id = _atoi(m_cur_word);
						if(id != 0){ inst = GetExistedInstById(id); }else{ MY_THROW_QP(STEP_QL_ERR_INVALID_INST_NUMBER);}
						if(inst == 0){ MY_THROW_QP(STEP_QL_ERR_INVALID_INST_NUMBER) };// проверка на нулевой атрибут
						ext->res_ext.Add(inst);
						// Запятая или "}"
						GetWord(m_cur_word);
						if(m_cur_word == _T("}")) break;
						if(m_cur_word != _T(",")) MY_THROW_QP(STEP_QL_ERR_WAIT_COMMA);

						// Следующий элемент или закрывающая скобка
						GetWord(m_cur_word);
						sTmp4Mid=m_cur_word.Mid(1);
						state=IsNumber(sTmp4Mid);

					}while(m_cur_word != _T("}"));

					ext->is_defined = true;
				}
				else
				{
					// ветка с кустом групповых операций подзапросов
                    ext->QLExtents = GetFields(query, false);
					ext->is_defined = true;
				}
			}
			else if(m_cur_word[0] == _T('&'))
			{
				CString type_def;
				// язык SQL в подзапросе
				// проверим версию сервера
				if(m_TypeSrv == _T(""))LoadTypeSrv();
				if(m_TypeSrv.CompareNoCase(_T("ORACLE")) != 0 && m_TypeSrv.CompareNoCase(_T("POSTGRES")) != 0)
				{
					MY_THROW_QP(STEP_QL_ERR_FUNCTION_ONLY_DB);
				}
				// определяем тип  стоолбца
				if(m_cur_word.GetLength() == 1)
				{
					GetWord(m_cur_word);
					type_def = m_cur_word;
				}
				else
				{
					type_def = m_cur_word;
					type_def.Delete(0, 1);
				}

				if(type_def.Right(1) == _T("&"))
				{
					type_def.TrimRight(_T("&"));
				}
				else
				{
					GetWord(m_cur_word);
					if(m_cur_word != _T("&"))
					{
						MY_THROW_QP(STEP_QL_ERR_WAIT_TYPE_COLUMN);
					}
				}
				// проверяем на допустимость
				if(type_def.CompareNoCase(_T("aplINSTANCE")) == 0)
				{
				}
				else if(type_def.CompareNoCase(_T("aplSELECT")) == 0)
				{
				}
				else if(type_def.CompareNoCase(_T("aplAGGR")) == 0)
				{
				}
				else
				{
					MY_THROW_QP(STEP_QL_ERR_WAIT_TYPE_COLUMN);
				}

				ext->typeSQL = type_def;

				// читаем SQL и заодно проверяем на SELECT xx FROM
				GetWord(m_cur_word);
				if(m_cur_word.CompareNoCase(_T("SELECT")) != 0)
				{
					MY_THROW_QP(STEP_QL_ERR_MISSED_TYPE_SQL);
				}
				ext->SQL = m_cur_word + _T(" ");
			
				// имя атрибута. Оно может включать в себя точку!
				bool after_point = true;
				do
				{
					if(!GetWord(m_cur_word))
						break;
					if(m_cur_word.CompareNoCase(_T("FROM")) == 0)
					{
						ext->SQL += _T(" ") + m_cur_word;
						break;
					}
					if(m_cur_word == _T(","))
					{
						MY_THROW_QP(STEP_QL_ERR_MISSED_TYPE_SQL);
					}
					if(m_cur_word != _T("."))
					{
						// проверяем, была ли прежде точка (при первом чтении флаг тоже установлен!)
						if(after_point)
						{
							ext->SQL += m_cur_word;
						}
						else
						{
							MY_THROW_QP(STEP_QL_ERR_MISSED_TYPE_SQL);
						}
						after_point = false;
					}
					else
					{
						ext->SQL += m_cur_word;
						after_point = true;
					}
				}while(true);

				// ищем закрывающую скобку
				TCHAR c = 0;
				m_cur_word = _T("");
				do
				{
					if(m_pos_read >= (int)m_in.GetLength()){ break;}
					c = m_in[m_pos_read];

					if(c==_T('\0'))
						MY_THROW_QP(STEP_QL_ERR_COULD_NOT_READ);
					if(c==_T('\n'))
					{
						m_error_string_num++;
						m_error_pos = 1;
					}
					m_pos_read++;
					m_error_pos++;

					if(c != _T('}') ){ m_cur_word += c;}
				
				}while(c != _T('}'));

				ext->SQL += m_cur_word;
				ext->is_defined = true;
			}
			else
			{
				MY_THROW_QP(STEP_QL_ERR_WAIT_ENTITY_NAME);
			}
			// читаем имя следующего екстента или точку
			GetWord(m_cur_word);
			if(m_cur_word == _T("."))
			{
				if(ext->base_entity == 0)
				{
					// Подзапрос задан не ентитей - здесь нельзя раздавать атрибуты!
					MY_THROW_QP(STEP_QL_ERR_ONLY_EXT_BY_ENT);
				}
				// тут имя атрибута
				GetWord(m_cur_word);
				if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);
				ext->attr_sharp = m_data->GetAttrDefinition(ext->base_entity, m_cur_word);
				if(ext->attr_sharp == 0){ MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);}
				if(ext->attr_sharp->type != aplINSTANCE && 
					ext->attr_sharp->type != aplSELECT &&
					ext->attr_sharp->type != aplAGGR)
				{
					MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_INST);
				}
				// указатель на тип получаемой ентити или имя следующего екстента
				GetWord(m_cur_word);
				if(m_cur_word == _T("->"))
				{
					// Имя ентити
					GetWord(m_cur_word);
					if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_ENTITY_NAME);
					// ищем ентити
					ext->res_entity = m_data->GetEntityBN(m_cur_word);
					if(ext->res_entity == 0) MY_THROW_QP(STEP_QL_ERR_INVALID_ENTITY_DEF);
					// тут уж точно имя следующего екстента
					GetWord(m_cur_word);
				}
				else
				{
					//MY_THROW_QP(STEP_QL_ERR_WAIT_ENTITY_POINTER);
				}
			}
		}
		// Проверяем, все ли экстенты определены
		for(i = 0; i<m_extents.GetSize(); i++)
		{
			ext = m_extents.GetAt(i);
			if(!ext->is_defined)
			{
				ext->SetParentErrPosition();
				MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
			}
		}

		// Проверяем корректность (по типам атрибутов) путей в условиях окончательного выбора
		CheckFields(m_res_bush);
		m_is_parse = true;
	}
	catch(SaplErrorDescription error)
	{
		SetErrDef(error.m_err_code);
		CString str;
		str.Format( APL_T("Ошибка %i file %s line %i date %s"),
			error.m_err_code, LPCTSTR(error.m_filename), error.m_err_line, LPCTSTR(error.m_datecompile));

		if(error.m_err_descr != _T(""))
		{
			str += _T("(") + error.m_err_descr + _T(")");
		}
		LogMessage(str, true, true, true, false, 0, 0, 0, _T(__FILE__), _T(__DATE__), __LINE__);

		return false;
	}
#ifdef _MFC_VER
	catch(CMemoryException& mem_exeption)
	{

		TCHAR   szCause[255];  szCause[0]=_T('\0');
		mem_exeption.GetErrorMessage(szCause, 255);
		CString err_mess;
		err_mess.Format(APL_T("При разборе STQL-запроса возникло исключение по памяти: %s"), LPCTSTR(szCause));

		LogMessage(err_mess, true, true, true, false, 0, 0, 0, _T(__FILE__), _T(__DATE__), __LINE__);
		return false;

	}
#endif
	catch(...)
	{
		CString str;
		str = APL_T("При разборе STQL-запроса возникло неизвестное исключение");

		LogMessage(str, true, true, true, false, 0, 0, 0, _T(__FILE__), _T(__DATE__), __LINE__);
		return false;
 	}
	return true;
}//bool CaplQLQueryParseBase::parse(CString &query)


// Поиск экстента (QL-ного) по имени в основном массиве экстентов.
// Если такого имени нет - создание
CaplQLQuery::CaplQLExtent* CaplQLQueryParseBase::FindQLExtent(CString &name, bool auto_create)
{
	CaplQLExtent *ext;
	// Проверка корректности имени
	if(name[0] == _T('#')) MY_THROW_QP(STEP_QL_ERR_INVALID_NAME_EXT);
	if(IsNumber(name[0]))
		MY_THROW_QP(STEP_QL_ERR_INVALID_NAME_EXT);

	for(int i = 0; i<m_extents.Size; i++)
	{ 
		if(m_extents[i]->name == name){ return m_extents[i];}
	};
	if(auto_create)
	{
		ext = new CaplQLExtent(this);
		ext->name = name;
		m_extents.Add(ext);
		return ext;
	}
	return 0;
}//CaplQLExtent* CaplQLQueryParseBase::FindQLExtent(CString &name,bool auto_create)





// Рекурсивно проходит до конца цепочки путей для каждого Field в определении 
// выходного результата на основе экстентов и создает 
// цепочку BranchSelect - PathElement
CaplQLQuery::CaplQLPathElement *CaplQLQueryParseBase::GetAllPath(CString& query)
{
	CaplQLPathElement *cur_step;

	cur_step = new CaplQLPathElement(this);

	// Здесь должно быть имя атрибута
	GetWord(m_cur_word);
	if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_NAME);
	// имя атрибута - для дальнейшего использования
	cur_step->attr_name=m_cur_word;

	// потом стрелочка
	GetWord(m_cur_word);
	if(m_cur_word!=_T("->"))
	{
		// 	MY_THROW_QP(STEP_QL_ERR_WAIT_ENTITY_POINTER);
		// это последний элемент
		cur_step->next_element = 0;
	}
	else
	{
		// Имя ентити
		GetWord(m_cur_word);
		if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_ENTITY_NAME);
		// ищем ентити
		cur_step->res_entity = m_data->GetEntityBN(m_cur_word);
		if(cur_step->res_entity == 0) MY_THROW_QP(STEP_QL_ERR_INVALID_ENTITY_DEF);
		// следующее слово
		GetWord(m_cur_word);
		if(m_cur_word == _T("."))
		{
			// Там дальше продолжается
			cur_step->next_element = (CaplQLBranchSelect*)GetAllPath(query);
		}
		else
		{
			// это последний элемент пути
			cur_step->next_element = 0;
		}
	}
	return cur_step;
}//CaplQLPathElement *CaplQLQueryParseBase::GetAllPath(CString& query)


// Определяет момент выхода из рекурсии при определении выходных 
// полей (Field)
bool CaplQLQueryParseBase::TestEndRecursiv(bool is_ext)
{
	bool flag = false;
	if(m_is_header)
	{
		flag = m_cur_word == _T("from");
	}
	else
	{
		flag = m_cur_word == _T("}");
	}
	if(flag)
	{
		// Мы дошли до конца кустов
		if(m_brackets > 0) MY_THROW_QP(STEP_QL_ERR_BRAKETS_TOO_MUCH_OPEN);
		if(m_brackets < 0) MY_THROW_QP(STEP_QL_ERR_BRAKETS_TOO_MUCH_CLOSE);
		return true;
	}
	if(is_ext && m_cur_word == _T(")"))
	{
		m_brackets--;
		return true;
	}
	return false;
}//bool CaplQLQueryParseBase::TestEndRecursiv(bool is_ext)



// Определение выходной выборки на основе QL-экстентов
// с учетом приоритетов AND OR и скобок
CaplQLQuery::CaplQLBranchSelect *CaplQLQueryParseBase::GetFields(CString& query, bool is_need_read_first)
{
	CaplQLField * field = 0;
	CaplQLBranchSelect *bsel = 0;
	CaplQLGroupOp *gr_op = 0;
	CString sTmp4Mid; // Для вызова через отд. переменную т.к. GCC не переваривает по другому
	bool is_need_read = true;
	if(is_need_read_first)
	{
		// берем первое слово...
		GetWord(m_cur_word);
		// Проверяем на служебное слово NO_CASE
		if(m_cur_word == _T("no_case"))
		{
			m_no_case=true;
			GetWord(m_cur_word);
		}
	}
	// Разгребаем условия окончательной выборки
	if(m_cur_word == _T("("))
	{
		m_brackets++;
        bsel = GetFields(query);
		if(!TestEndRecursiv(false)) GetWord(m_cur_word);
	}
	else
	{
		// проверяем на неправильные слова
		if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
		field = new CaplQLField(this);
		if(m_is_header)
		{
			// В заголовке мы можем задать только имя подзапроса; тело определится позже
			field->extent_name = m_cur_word;
            field->in_result = true;
		}
		else
		{
			// при определении тела подзапроса ипользуемые должны быть определены!
			// ищем екстент
			sTmp4Mid=m_cur_word.Mid(1);
			field->base_extent = FindQLExtent(sTmp4Mid, false);
			if(field->base_extent == 0) MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
			if(!field->base_extent->is_defined) MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
            field->base_extent->is_res_field = m_load_attr;
		}
		bsel = (CaplQLField*)field;
		// точка и путь
		GetWord(m_cur_word);
		if(m_cur_word == _T("."))
		{
			if(m_is_header)
			{
				// Ага, это путь для окончательной выборки
				field->path = GetAllPath(query);
				// aplGetWord(m_cur_word); -- перед выходом GetAllPath читает слово
			}
			else
			{
				// при определении тела подзапроса пути использоваться не могут
				MY_THROW_QP(STEP_QL_ERR_WAIT_LOG_OP);
			}
		}
	}
	while(true)
	{
		is_need_read = true;
		if(TestEndRecursiv(true))
		{
			break;
		}
		// после определениЯ выборки могут быть:
		// групповое выражение (AND, OR)
		if(IsBoolOp(m_cur_word))
		{
			// Ага, это выражение
			if(bsel == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
			gr_op = new CaplQLGroupOp(this);

			gr_op->first_op = bsel;
			if(m_cur_word == _T("and") || m_cur_word == _T("&&"))
			{
				gr_op->operation_type = aplAND;
				GetWord(m_cur_word);
				if(m_cur_word == _T("("))
				{
					m_brackets++;
					gr_op->second_op = GetFields(query);
					if(gr_op->second_op == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
				}
				else
				{
					if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
					// Определяемся с экстентом
					field = new CaplQLField(this);
					if(m_is_header)
					{
						// В заголовке мы можем задать только имя подзапроса; тело определится позже
						field->extent_name = m_cur_word;
                        field->in_result = true;
					}
					else
					{
						// при определении тела подзапроса спользуемые должны быть определены!
						// ищем екстент
						sTmp4Mid=m_cur_word.Mid(1);
						field->base_extent = FindQLExtent(sTmp4Mid, false);
						if(field->base_extent == 0) MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
						if(!field->base_extent->is_defined) MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
                        field->base_extent->is_res_field = m_load_attr;
					}
						// точка и путь
					GetWord(m_cur_word);
					if(m_cur_word == _T("."))
					{
						// Ага, это путь для окончательной выборки
						field->path = GetAllPath(query);
					}
					gr_op->second_op = (CaplQLBranchSelect*)field;
					is_need_read = false;
				}
			}
			else if(m_cur_word == _T("or") || m_cur_word == _T("||"))
			{
				gr_op->operation_type = aplOR;
				// Дальше должен быть следующая ветвть логических операторов
				gr_op->second_op = GetFields(query);
				if(gr_op->second_op == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
			}
			else if(m_cur_word == _T("not") )
			{
				GetWord(m_cur_word);
				if(m_cur_word == _T("in") )
				{
					gr_op->operation_type = aplGNOTIN;
					// Дальше должен быть следующая ветвть логических операторов
					gr_op->second_op = GetFields(query);
					if(gr_op->second_op == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_OUTPUT_DESCR);
				}
				else
				{
					MY_THROW_QP(STEP_QL_ERR_UNDEFAINED_WORD);
				}
			}
			else
			{
				MY_THROW_QP(STEP_QL_ERR_UNDEFAINED_WORD);
			}
			bsel = (CaplQLBranchSelect*)gr_op;

			if(TestEndRecursiv(false)){ break;}
			// Читаем следующее слово
			if(is_need_read){ GetWord(m_cur_word);}
		}
		else
		{
			MY_THROW_QP(STEP_QL_ERR_WAIT_1234);
		}
	}
	return bsel;
}//CaplQLBranchSelect *CaplQLQueryParseBase::GetFields(CString& query)

// Эта функция должна быть перегружена на клиенте и на сервере
CaplInstance* CaplQLQueryParseBase::GetExistedInstById(apl_id /*id*/){return 0;}

bool CaplQLQueryParseBase::ReadTypeConvert(aplQLLogAttrConvert &attr_convert)
{
	CString sval;
	if(m_cur_word[0] == _T('@'))
	{
		// это временно! Пока оракловый сервер не умеет так преобразовывать
		//MY_THROW_QP(STEP_QL_ERR_UNDEFAINED_WORD);

		// это функция преобразования
		m_cur_word.MakeUpper();
		if(m_cur_word == _T("@TO_INT"))
		{
			attr_convert = aplValToInt;
		}
		else if(m_cur_word == _T("@TO_REAL"))
		{
			attr_convert = aplValToReal;
		}
		else if(m_cur_word == _T("@TO_STRING"))
		{
			attr_convert = aplValToString;
		}
		else
		{
			MY_THROW_QP(STEP_QL_ERR_UNDEFAINED_WORD);
		}
		// читаем скобку
		GetWord(sval);
		if(sval != _T("(")) MY_THROW_QP(STEP_QL_ERR_NEED_OPEN_BRAKETS);
		// читаем имя атрибута
		GetWord(m_cur_word);
		// читаем скобку
		GetWord(sval);
		if(sval != _T(")")) MY_THROW_QP(STEP_QL_ERR_BRAKETS_TOO_MUCH_OPEN);
	}
	return true;
}

// Рекурсивная функция разбирает определения QL-экстентов на основе условий
// объединенных AND OR и скобками
// с проверкой совместимости и существования атрибутов у ентитей 
CaplQLQuery::CaplQLBranchSelect *CaplQLQueryParseBase::GetConditions(CaplEntity *ent_in)
{
	int i = 0;
	int ival = 0; 
	apl_id inst_id;
	double fval = 0; 
	CString sval;
	CaplQLExtent *ext = 0;
	CaplQLPathElement *pel = 0;
	CaplEntity *ent = 0;
	CaplInstance* inst = 0;
	CaplValue* val = 0;
	CaplQLBranchSelect *bsel = 0;
	CaplQLLogOp* log_op = 0;
	CaplQLGroupOp* gr_op = 0;
	bool is_break_or = true;


	if(m_cur_word == _T("}")) return bsel;
	if(m_cur_word == _T(")"))
	{
		m_brackets--;
		return bsel;
	}
	m_recurse_level++;

	if(m_cur_word == _T("("))
	{
		m_brackets++;
		GetWord(m_cur_word);
		bsel=GetConditions(ent_in);
		while(true)
		{
			GetWord(m_cur_word);
			
			if(m_cur_word == _T("}")) break;
			
			if(m_cur_word == _T(")"))
			{
				m_brackets--;
				break;
			}
			gr_op = new CaplQLGroupOp(this);
			gr_op->first_op = bsel;

			bsel = (CaplQLBranchSelect*)gr_op;

			if(m_cur_word == _T("or") || m_cur_word == _T("||"))
			{
				if(m_last_operation_type != aplOR)
				{
					m_cur_word = _T("(");
					m_brackets--;
					is_break_or = true;
				}
				else
				{
					GetWord(m_cur_word);
					is_break_or = false;
				}
				gr_op->operation_type = aplOR;
				m_last_operation_type = aplOR;
				gr_op->second_op = GetConditions(ent_in);
				if(is_break_or)break;
			}
			else if(m_cur_word == _T("and") || m_cur_word == _T("&&"))
			{
				GetWord(m_cur_word);
				gr_op->operation_type = aplAND;
				m_last_operation_type = aplAND;
				gr_op->second_op = GetConditions(ent_in);
			}
			else
			{
				MY_THROW_QP(STEP_QL_ERR_WAIT_123);
			}
		}
	}
	else if(m_cur_word == _T("."))
	{
		// Это продолжение пути. Bsel - типа path
		pel = new CaplQLPathElement(this);
		bsel = (CaplQLBranchSelect*)pel;
		// имя атрибута
		GetWord(m_cur_word);
		if(m_cur_word.Left(1) == _T("#") )
		{
			// псевдоатриюуты
			pel->attr = 0;
			pel->res_entity = ent_in;
			//m_need_lastest_buffer = true; - костыль устарел, будет использован при следующем изменении формата буфера
			if(m_cur_word == _T("#"))
			{
				// это указание на id инстансы
				pel->pseudo_attr = aplPAInstId;
				//m_need_lastest_buffer = false;
			}
			else if(m_cur_word == _T("#id"))
			{
				// это указание на id инстансы - по новому
				pel->pseudo_attr = aplPAInstId;
			}
			else if(m_cur_word == _T("#user_create"))
			{
				// это указание на пользователя создавшего
				pel->pseudo_attr = aplPAUserCreate;
			}
			else if(m_cur_word == _T("#date_create"))
			{
				// это указание на дату создания
				pel->pseudo_attr = aplPADateCreate;
			}
			else if(m_cur_word == _T("#user_update"))
			{
				// это указание на пользователя изменившего
				pel->pseudo_attr = aplPAUserUpdate;
			}
			else if(m_cur_word == _T("#date_update"))
			{
				// это указание на дату изменения
				pel->pseudo_attr = aplPADateUpdate;
			}
			else
			{
				MY_THROW_QP(STEP_QL_ERR_WAIT_PSEUDO_ATTR);
			}
			if(m_TypeSrv == _T(""))LoadTypeSrv();
			if(pel->pseudo_attr != aplPAInstId && m_TypeSrv != _T("ORACLE") && m_TypeSrv != _T("POSTGRES"))
			{
				MY_THROW_QP(STEP_QL_ERR_FUNCTION_ONLY_DB);
			}
		}
		
		ReadTypeConvert(pel->attr_convert);

		if(m_cur_word.Left(1) != _T("#") && m_cur_word != _T("@"))
		{
			pel->attr = m_data->GetAttrDefinition(ent_in, m_cur_word);
			if(pel->attr == 0) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);
		}
		pel->attr_name = m_cur_word;
		GetWord(m_cur_word);
		if(m_cur_word == _T("->"))
		{
			if(pel->attr == 0)
			{
				MY_THROW_QP(STEP_QL_ERR_WAIT_LOG_OP);
			}
			// Это имя получаемой энтити
			GetWord(m_cur_word);
			ent = m_data->GetEntityBN(m_cur_word);
			if(ent == 0) MY_THROW_QP(STEP_QL_ERR_INVALID_ENTITY_DEF);
			if(pel->attr->inst_type != 0)
			{
				bool flag = true;
				if(pel->attr->inst_type != ent)
				{
					for(i = 0; i<ent->all_supertypes.GetSize(); i++)
					{
						if(ent->all_supertypes.GetAt(i) == pel->attr->inst_type)
						{
							flag = false;
						}
					}
					if(flag) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_ENTITY);
				}
			}
			pel->res_entity = ent;
			GetWord(m_cur_word);
			//проверка что  условия нет, ветка условий нужна только для ограничения по типу входящих в нее instаnce 
			if(m_cur_word == _T("and") || m_cur_word == _T("&&"))
			{
				m_pos_read -= 3;
			}
			else if(m_cur_word == _T("or") || m_cur_word == _T("||"))
			{
				m_pos_read -= 2;
			}
			else if(m_cur_word == _T(")") || m_cur_word == _T("}"))
			{
				m_pos_read -= 1;
			}
			else
			{
				pel->next_element=GetConditions(ent);
			}
		}
		else
		{
			// А это арифметическая операция на атрибутах-коэффициентах (если она задана) и условие
			pel->arithmetic = GetArithmEl(ent_in, pel->attr, pel->attr_convert);

			if(pel->arithmetic != 0 && pel->attr == 0)
			{
				MY_THROW_QP(STEP_QL_ERR_PSEUDO_ATTR_NO_ARITHMETIC);
			}

			log_op = new CaplQLLogOp(this);
			pel->operation = log_op;
			if(m_cur_word == _T("<"))
			{
				log_op->operation_type = aplSMALL;
			}
			else if(m_cur_word == _T(">"))
			{
				log_op->operation_type = aplBIG;
			}
			else if(m_cur_word == _T("<="))
			{
				log_op->operation_type = aplSMALLEQUAL;
			}
			else if(m_cur_word == _T(">="))
			{
				log_op->operation_type = aplBIGEQUAL;
			}
			else if(m_cur_word == _T("="))
			{
				log_op->operation_type = aplEQUAL;
			}
			else if(m_cur_word == _T("!="))
			{
				log_op->operation_type = aplNOTEQUAL;
			}
			else if(m_cur_word == _T("in"))
			{
				log_op->operation_type = aplIN;
			}
			else if(m_cur_word == _T("not") || m_cur_word == _T("!"))
			{
				GetWord(m_cur_word);
				if(m_cur_word == _T("in"))
				{
					log_op->operation_type = aplNOTIN;
				}
				else if(m_cur_word == _T("="))
				{
					log_op->operation_type = aplNOTEQUAL;
				}
				else if(m_cur_word == _T("like"))
				{
					log_op->operation_type = aplNOTLIKE;
				}
				else if(m_cur_word == _T("exist"))
				{
					log_op->operation_type = aplNOTEXIST;
				}
				else if(m_cur_word == _T("like_left"))
				{
					log_op->operation_type = aplNOTLIKELEFT;
				}
				else if(m_cur_word == _T("like_right"))
				{
					log_op->operation_type = aplNOTLIKERIGHT;
				}
				else if(m_cur_word == _T("aggr_empty"))
				{
					log_op->operation_type = aplNOTEMPTY;
				}
			}
			else if(m_cur_word == _T("not_in"))
			{
				log_op->operation_type = aplNOTIN;
			}
			else if(m_cur_word == _T("like"))
			{
				log_op->operation_type = aplLIKE;
			}
			else if(m_cur_word == _T("like_left"))
			{
				log_op->operation_type = aplLIKELEFT;
			}
			else if(m_cur_word == _T("like_right"))
			{
				log_op->operation_type = aplLIKERIGHT;
			}
			else if(m_cur_word == _T("aggr_empty"))
			{
				log_op->operation_type = aplEMPTY;
			}
			else if(m_cur_word == _T("is"))
			{
				bool is_not = false;
				GetWord(m_cur_word);
				if(m_cur_word == _T("not"))
				{
					is_not = true;
					GetWord(m_cur_word);
				}
				if(m_cur_word != _T("null"))
					MY_THROW_QP(STEP_QL_ERR_WAIT_IS_NULL);

				if(pel->attr == 0 || pel->attr_convert != aplValNoConvert)
					MY_THROW_QP(STEP_QL_ERR_IS_NULL_NEED_ATTR);

				if(is_not)
					log_op->operation_type = aplISNOTNULL;
				else
					log_op->operation_type = aplISNULL;
			}
			else
			{
				MY_THROW_QP(STEP_QL_ERR_WAIT_LOG_OPERATION);
			}
			// В зависимости от типа атрибута определяем допустимый тип значения
			aplValueType type = aplNOTYPE;
			if(pel->attr == 0)
			{
				if(pel->pseudo_attr == aplPADateCreate || pel->pseudo_attr == aplPADateUpdate)
				{
					type = aplSTRING;
				}
				else
				{
					type = aplINSTANCE;
				}
			}
			else
			{
				type = pel->attr->type;
				if(pel->attr_convert == aplValToInt)
				{
					type = aplINTEGER;
				}
				else if(pel->attr_convert == aplValToReal)
				{
					type = aplREAL;
				}
				else if(pel->attr_convert == aplValToString)
				{
					type = aplSTRING;
				} 
			}
			// А это значение
			// при некоторых условиях читать его надо!
			if(log_op->operation_type == aplISNULL || log_op->operation_type == aplISNOTNULL)
			{
				// значение проверять не нужно
				log_op->value_val = 0;
				log_op->selector = aplValNoValue;
			}
			else if(log_op->operation_type != aplEMPTY && log_op->operation_type != aplNOTEMPTY)
			{
				GetWord(m_cur_word);
				if(m_cur_word == _T("."))
				{
					bool f(true);
					int pos_read_prev = m_pos_read;
					CString tmp = m_cur_word;
					GetWord(m_cur_word);
					if(m_cur_word==_T("t") || m_cur_word==_T("f"))
					{
						tmp+=m_cur_word;
						GetWord(m_cur_word);
						if(m_cur_word == _T("."))
						{
							tmp+=m_cur_word;
							m_cur_word = tmp;
							f=false;
						}
					}
					if(f){m_pos_read = pos_read_prev; m_cur_word = _T(".");}
				}
				if(m_cur_word == _T("."))
				{
					// ба, да это атрибут! читаем-ка его имя
					GetWord(m_cur_word);
					// пытаемся его найти
					log_op->value_attr = m_data->GetAttrDefinition(ent_in, m_cur_word);
					if(log_op->value_attr == 0) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);
					log_op->selector = aplValAttr;
				}
				else
				{
					val = new CaplValue();
					log_op->value_val = val;
					log_op->selector = aplValValue;
				}
			}
			else
			{
				if(type != aplAGGR){ MY_THROW_QP(STEP_QL_ERR_OP_ONLY_FOR_AGGR);}
				log_op->value_val = 0;
				log_op->selector = aplValNoValue;
			}

			if(log_op->operation_type == aplISNULL || log_op->operation_type == aplISNOTNULL)
			{
				// типы проверять не нужно
			}
			else if(type == aplINSTANCE || type == aplSELECT)
			{
				if(log_op->operation_type != aplEQUAL  && log_op->operation_type != aplNOTEQUAL  && 
					log_op->operation_type != aplIN  && log_op->operation_type != aplNOTIN   && 
					log_op->operation_type != aplSMALL  && log_op->operation_type != aplSMALLEQUAL && 
					log_op->operation_type != aplBIG  && log_op->operation_type != aplBIGEQUAL )
				{
					MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
				}
				if(log_op->selector == aplValAttr)
				{
					if(log_op->value_attr->type != aplINSTANCE && log_op->value_attr->type != aplSELECT){ MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_INST);}
				}
				else
				{
					if(IsServiceSymbol(m_cur_word)){ MY_THROW_QP(STEP_QL_ERR_WAIT_VALUE);}
					if(m_cur_word[0] != _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INST_EXT_VALUE);}
					m_cur_word.Delete(0, 1);
					inst_id = (apl_id) _atoi(m_cur_word);
					if(inst_id== 0 && m_cur_word != _T("0"))
					{
						CaplInstance* inst = 0;
						if(m_cur_word == _T("current_user"))
						{
							if(m_CurrUser==0)LoadCurrUser();
							inst = m_CurrUser;
						}
						else if(m_cur_word == _T("current_user_groupe"))
						{
							if(m_CurrUserGroupe==0)LoadCurrUser();
							inst = m_CurrUserGroupe;
						}
						else if(m_cur_word == _T("current_person"))
						{
							if(m_CurrUserPerson==0)LoadCurrUser();
							inst = m_CurrUserPerson;
						}
						if(0 != inst) inst_id = inst->GetId();
					}
					if(inst_id > 0 || (m_cur_word[0] == _T('0') && m_cur_word.GetLength() == 1))
					{
						// Это номер инстансы
						// проверяем на соответсвие отношений
						if(log_op->operation_type == aplEQUAL || log_op->operation_type == aplNOTEQUAL || 
							log_op->operation_type == aplSMALL  || log_op->operation_type == aplSMALLEQUAL || 
							log_op->operation_type == aplBIG  || log_op->operation_type == aplBIGEQUAL)
						{
							inst = 0;
							if(inst_id != 0) inst = GetExistedInstById(inst_id);
							//проверка на нулевой атрибут
							if(inst== 0 && inst_id != 0)
							{
								if(log_op->operation_type == aplEQUAL || log_op->operation_type == aplNOTEQUAL)
								{
									MY_THROW_QP(STEP_QL_ERR_INVALID_INST_NUMBER)
								}
								else
								{
									inst = new CaplInstance(m_data, 0, inst_id, false);
									inst->SetTemporary(true);
									m_data->instances.Unique = false;
									m_data->instances.Add(inst);
									m_data->instances.Unique = true;

								}
							}
							val->Set(inst);
						}
						else
						{
							MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
	//						log_op->SetParentErrPosition();
						}
					}
					else
					{
						// это имя екстента
						// проверяем на соответсвие отношений
						if(log_op->operation_type != aplIN && log_op->operation_type != aplNOTIN)
						{
							log_op->SetParentErrPosition();
							MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
						}
						delete(log_op->value_val); 
						log_op->value_val = 0;
						val = 0;
						ext = FindQLExtent(m_cur_word, false);
						if(ext == 0 ){MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);}
						log_op->selector = aplValExtent;
						log_op->value_ext = ext; 
					}
				}
			}
			else if(type == aplINTEGER)
			{
				if(log_op->selector == aplValAttr)
				{
					if(log_op->value_attr->type != aplINTEGER ){ MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_INTEGER);}
				}
				else
				{
					if(IsServiceSymbol(m_cur_word))
					{
						if(m_cur_word != _T("-"))
						{
							MY_THROW_QP(STEP_QL_ERR_WAIT_VALUE);
						}
						else
						{
							// число отрицательное
							GetWord( m_cur_word);
							m_cur_word = _T("-") + m_cur_word;
						}
					}
					if(m_cur_word[0] == _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE)};
					if(m_cur_word[0] == _T('\"') || m_cur_word[0] == _T('\'')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE)}
					if(m_cur_word.Find(_T('.')) != -1){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE);}
					if(log_op->operation_type == aplIN || log_op->operation_type == aplLIKE || 
						log_op->operation_type == aplLIKELEFT || log_op->operation_type == aplLIKERIGHT)
					{
						log_op->SetParentErrPosition();
						MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
					}
					ival = _atoi(m_cur_word);
					val->Set((int)ival);
				}
			}
			else if(type == aplREAL)
			{
				if(log_op->selector == aplValAttr)
				{
					if(log_op->value_attr->type != aplREAL ){ MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_REAL);}
				}
				else
				{
					if(IsServiceSymbol(m_cur_word))
					{
						if(m_cur_word != _T("-"))
						{
							MY_THROW_QP(STEP_QL_ERR_WAIT_VALUE);
						}
						else
						{
							// число отрицательное
							GetWord(m_cur_word);
							m_cur_word = _T("-") + m_cur_word;
						}
					}
					if(m_cur_word[0]==_T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_REAL_VALUE)};
					if(m_cur_word[0]==_T('\"') || m_cur_word[0] == _T('\'')){ MY_THROW_QP(STEP_QL_ERR_WAIT_REAL_VALUE)}
					if(log_op->operation_type == aplIN || log_op->operation_type == aplLIKE || 
						log_op->operation_type == aplLIKELEFT || log_op->operation_type == aplLIKERIGHT)
					{
						log_op->SetParentErrPosition();
						MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
					}
					fval = __atof(m_cur_word);
					val->Set((double)fval);
				}
			}
			else if(type == aplSTRING || type == aplENUMERATION)
			{
				if(log_op->selector == aplValAttr)
				{
					if(log_op->value_attr->type != aplSTRING && log_op->value_attr->type != aplENUMERATION){ MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_STRING);}
				}
				else
				{
					if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_VALUE);
					if(m_cur_word[0] == _T('#'))
					{
						MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE);
					}
					if(m_cur_word[0] == _T('\"'))
					{
						// Это строка
						m_cur_word.Remove(_T('\"'));
						val->Set((CString)m_cur_word);
					}
					else if(m_cur_word[0] == _T('\''))
					{
						// Это строка
						m_cur_word.Remove(_T('\''));
						val->Set((CString)m_cur_word);
					}
					else if(m_cur_word[0]==_T('@'))
					{
						if(pel->pseudo_attr == aplPADateCreate || pel->pseudo_attr == aplPADateUpdate )
						{
							MY_THROW_QP(STEP_QL_ERR_PSEUDO_ATTR_DATE_MISSED_VAL);
						}
						CaplAggr aggr;
						int count = 0;
						// Это описание запроса для БЛОБа
						log_op->selector = aplValInBlobString;
						if(log_op->operation_type != aplEQUAL && log_op->operation_type != aplLIKE)
						{
							log_op->SetParentErrPosition();
							MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
						}					
						if(m_cur_word.CompareNoCase(_T("@BLOB_CONSIST")) != 0) MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE);
						GetWord(m_cur_word);
						if(m_cur_word[0] != _T('(')) MY_THROW_QP(STEP_QL_ERR_WAIT_OPEN_CURLY_BRAKET);
						do{
							if(count>0)
							{
								aggr.Add(*val);
							}
							GetWord(m_cur_word);
							if(m_cur_word[0] == _T('\"'))
							{
								// Это строка
								m_cur_word.Remove(_T('\"'));
								if(m_cur_word.GetLength() == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE);
								val->Set((CString)m_cur_word);
							}
							else if(m_cur_word[0] == _T('\''))
							{
								// Это строка
								m_cur_word.Remove(_T('\''));
								if(m_cur_word.GetLength() == 0) MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE);
								val->Set((CString)m_cur_word);
							}
							else
							{
								MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE);
							}
							GetWord(m_cur_word);
							count++;
						}while(m_cur_word[0] == _T(','));

						if(count > 1)
						{
							aggr.Add(*val);
							val->Set(aggr);
						}
						if(m_cur_word[0] != _T(')')) MY_THROW_QP(STEP_QL_ERR_WAIT_CLOSE_CURLY_BRAKET);
					}
					else
					{
						MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE);
					}
					if(pel->pseudo_attr == aplPADateCreate || pel->pseudo_attr == aplPADateUpdate )
					{
						// проверяем формат строки
						if(m_cur_word.GetLength() != 14)
						{
							MY_THROW_QP(STEP_QL_ERR_PSEUDO_ATTR_DATE_MISSED_VAL);
						}
						else 
						{
							for(int i1 = 0; i1<(int)m_cur_word.GetLength(); i1++)
							{
								if(!IsNumber(m_cur_word[i1]))
									MY_THROW_QP(STEP_QL_ERR_PSEUDO_ATTR_DATE_MISSED_VAL);
							}
						}
					}
				}
			}
			else if(type == aplBOOL)
			{
				if(log_op->selector == aplValAttr)
				{
					if(log_op->value_attr->type != aplBOOL ){ MY_THROW_QP(STEP_QL_ERR_WAIT_ATTR_BOOL);}
				}
				else
				{
					if(m_cur_word == _T("true")){ val->Set((bool)true);}
					else if(m_cur_word == _T(".t.")){ val->Set((bool)true);}
					else if(m_cur_word == _T("false")){ val->Set((bool)false);}
					else if(m_cur_word == _T(".f.")){ val->Set((bool)false);}
					else if(IsServiceSymbol(m_cur_word)){MY_THROW_QP(STEP_QL_ERR_WAIT_VALUE);}
					else if(m_cur_word[0] == _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_BOOL_VALUE);}
					else{ MY_THROW_QP(STEP_QL_ERR_WAIT_BOOL_VALUE)};
					// провряем на соответсвие отношений
					if(log_op->operation_type != aplEQUAL && 
						log_op->operation_type != aplNOTEQUAL)
					{
						log_op->SetParentErrPosition();
						MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
					}
				}
			}
			else if(type == aplAGGR)
			{
				if(log_op->selector == aplValAttr)
				{
					MY_THROW_QP(STEP_QL_ERR_OP_AGGR_NOT_AVAIL);
				}
				else if(log_op->selector == aplValNoValue)
				{
				}
				else
				{
					if(IsServiceSymbol(m_cur_word)) MY_THROW_QP(STEP_QL_ERR_WAIT_VALUE);
					///				if(m_cur_word[0]!=_T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INST_VALUE)};
					if(log_op->operation_type != aplLIKE && pel->attr->add_types[0] != aplSTRING)
					{
						log_op->SetParentErrPosition();
						MY_THROW_QP(STEP_QL_ERR_INVALID_LOG_OP);
					}
					switch(pel->attr->add_types[0])
					{
					case aplINSTANCE:
					case aplSELECT:
						if(m_cur_word[0] != _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INST_VALUE);}
						m_cur_word.Delete(0, 1);
						inst_id = (apl_id)_atoi(m_cur_word);
						if(inst_id > 0)
						{
							// Это номер инстансы
							// ищем в кеше
							inst = GetExistedInstById(inst_id);
							if(inst == 0){ MY_THROW_QP(STEP_QL_ERR_INVALID_INST_NUMBER)}
							val->type = aplINSTANCE;
							val->instval = inst;
							log_op->value_val = val;
							log_op->selector = aplValValue;
						}
						else
						{
							// это имя екстента. С аггром не сочетается
							MY_THROW_QP(STEP_QL_ERR_WAIT_INST_VALUE);
						}
						break;
					case aplINTEGER:
						if(m_cur_word[0] == _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE)};
						if(m_cur_word[0] == _T('\"') || m_cur_word[0] == _T('\'')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE)}
						if(m_cur_word.Find(_T('.')) != -1){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE);}
						ival = _atoi(m_cur_word);
						val->Set((int)ival);
						break;
					case aplREAL:
						if(m_cur_word[0] == _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE)};
						if(m_cur_word[0] == _T('\"') || m_cur_word[0] == _T('\'')){ MY_THROW_QP(STEP_QL_ERR_WAIT_INT_VALUE)}
						fval = __atof(m_cur_word);
						val->Set((double)fval);
						break;
					case aplSTRING:
						if(m_cur_word[0] == _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE)};
						if(m_cur_word[0] != _T('\"') && m_cur_word[0] != _T('\'')){ MY_THROW_QP(STEP_QL_ERR_WAIT_STRING_VALUE)}
						if(m_cur_word[0] == _T('\"'))
						{
							// Это строка
							m_cur_word.Remove(_T('\"'));
							val->Set((CString)m_cur_word);
						}
						else if(m_cur_word[0] == _T('\''))
						{
							// Это строка
							m_cur_word.Remove(_T('\''));
							val->Set((CString)m_cur_word);
						}
						break;
					case aplBOOL:
						if(m_cur_word[0] == _T('#')){ MY_THROW_QP(STEP_QL_ERR_WAIT_BOOL_VALUE);}
						// провряем на соответсвие отношений
						if(m_cur_word == _T("true")){ val->Set((bool)true);}
						else if(m_cur_word == _T("false")){ val->Set((bool)false);}
						else{ MY_THROW_QP(STEP_QL_ERR_WAIT_BOOL_VALUE)};
						break;
					default:
					// aplNOTYPE, aplLOGICAL,aplBINARY,aplBLOB,aplENUMERATION
						MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_TYPE);
					}			
				}
			}
			else
			{
			// aplNOTYPE, aplLOGICAL,aplBINARY,aplBLOB,aplENUMERATION
				MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_TYPE);
			}
		}
	}
	else
	{
		MY_THROW_QP(STEP_QL_ERR_WAIT_CONDITION_DEF);
	}

	m_recurse_level--;
	return bsel;
}//CaplQLBranchSelect *CaplQLQueryParseBase::GetConditions(CString& query,CaplEntity *ent_in)


CaplQLQueryParseBase::CaplQLArithmeticOpPath* CaplQLQueryParseBase::GetArithmEl(CaplEntity *ent_in, CaplAttr* base_attr, aplQLLogAttrConvert base_attr_convert)
{
	CaplQLArithmeticOpPath* arithmetic = 0;

	if(m_cur_word == _T("+") || m_cur_word == _T("-") || m_cur_word == _T("*") || m_cur_word == _T("/"))
	{
		if(base_attr != 0)
		{
			if(base_attr->type != aplINTEGER && base_attr->type != aplREAL && base_attr_convert != aplValToInt && base_attr_convert != aplValToReal)
			{
				MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_TYPE);
			}
		}

		arithmetic = new CaplQLArithmeticOpPath(this);
		if(m_cur_word == _T("+"))
		{
			arithmetic->arithm_op = aplAOAdd;
		}
		else if(m_cur_word == _T("-"))
		{
			arithmetic->arithm_op = aplAOSub;
		}
		else if(m_cur_word == _T("*"))
		{
			arithmetic->arithm_op = aplAOMul;
		}
		else if(m_cur_word == _T("/"))
		{
			arithmetic->arithm_op = aplAODiv;
		}

		// над чем производятся операции
		GetWord(m_cur_word);
		if(m_cur_word == _T("."))
		{
			// ба, да это атрибут! читаем-ка его имя
			GetWord(m_cur_word);
			// проверяенм на преобразование типов
			ReadTypeConvert(arithmetic->attr_convert);
			// пытаемся его найти
			arithmetic->attr2 = m_data->GetAttrDefinition(ent_in, m_cur_word);
			if(arithmetic->attr2 == 0 ) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);
			if(arithmetic->attr2->type != aplINTEGER && arithmetic->attr2->type != aplREAL  && 
				arithmetic->attr_convert != aplValToInt && arithmetic->attr_convert != aplValToReal)
			{
				MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_TYPE);
			}
		}
		else
		{
			arithmetic->attr2 = 0;
			arithmetic->num2 = __atof(m_cur_word);
		}
		GetWord(m_cur_word);
		arithmetic->next_element = GetArithmEl(ent_in, 0, aplValNoConvert);
	}

	return arithmetic;
}



// проверяет корректность определения выходной выборки с точки
// зрения существованиея указанных экстентов, совместимости и 
// существования атрибутов у ентитей 
bool CaplQLQueryParseBase::CheckFields(CaplQLBranchSelect *bsel, CaplEntity* upper_ent)
{
	CaplQLField* field = 0;
	CaplQLPathElement* p_el = 0;
	CaplEntity * ent = 0;
	CaplAttr *attr = 0;
	bool flag = true;
	int i = 0;
	m_error_string_num = -1;
	if(bsel == 0 ){ MY_THROW_QP(STEP_QL_ERR_MISSED_POINTER);}

	if(bsel->type == aplTOGroupOp)
	{
		return (CheckFields(((CaplQLGroupOp*)bsel)->first_op) && 
					CheckFields(((CaplQLGroupOp*)bsel)->second_op));
	}
	else if(bsel->type == aplTOField)
	{
		field = (CaplQLField*)bsel;
		field->base_extent = FindQLExtent(field->extent_name, false);
		if(field->base_extent == 0)
		{
			field->SetParentErrPosition();
			MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
		}
		field->base_extent->in_result = field->in_result;
		if(field->base_extent->base_entity == 0)
		{
			if(field->base_extent->is_defined)
			{
				if(field->base_extent->typeSQL != _T("") && field->base_extent->SQL != _T(""))
				{
                    field->base_extent->is_res_field = m_load_attr;// эти результаты тоже надо получить с сервера ORACLE
				}
                // а еще подзапрос может быть определен как группа инстансов или екстентов.
				// В любом случае проверить корректность не могем
				return true;
			}
			field->base_extent->SetParentErrPosition();
			MY_THROW_QP(STEP_QL_ERR_EXT_NOT_DEFINED);
		}
        if(field->path != 0)
		{
			ent = field->base_extent->res_entity;
			if(ent == 0) ent = field->base_extent->base_entity;
			attr = m_data->GetAttrDefinition(ent, field->path->attr_name);
			if(attr == 0)
			{
				field->path->SetParentErrPosition();
				MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);
			}
			field->path->attr = attr;
			if(attr->inst_type != 0 && field->path->res_entity != 0)
			{
				flag = true;
				if(attr->inst_type != field->path->res_entity)
				{
					for(i = 0; i<field->path->res_entity->all_supertypes.GetSize() ;i++)
					{
						if(field->path->res_entity->all_supertypes.GetAt(i) == attr->inst_type)
						{
							flag = false;
						}
					}
					field->path->SetParentErrPosition();
					if(flag) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_ENTITY);
				}
			}
			if(field->path->next_element != 0)
			{
				// далее что-то идет
				return CheckFields(field->path->next_element, field->path->res_entity);
			}
		}
	}
	else if(bsel->type == aplTOPathElement)
	{
		if(upper_ent == 0){MY_THROW_QP(STEP_QL_ERR_BUSH_STRUCTURE_MISSED)};
		ent = upper_ent;
		p_el = (CaplQLPathElement*)bsel;
		attr = m_data->GetAttrDefinition(ent, p_el->attr_name);
		if(attr == 0)
		{
			p_el->SetParentErrPosition();
			MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_DEF);
		}
		p_el->attr = attr;
		// теперь мы работаем с этой енитией
		ent = p_el->res_entity;
		if(attr->inst_type != 0 && ent != 0)
		{
			flag = true;
			if(attr->inst_type != ent)
			{
				for(i = 0; i<ent->all_supertypes.GetSize(); i++)
				{
					if(ent->all_supertypes.GetAt(i) == attr->inst_type)
					{
						flag = false;
					}
				}
				p_el->SetParentErrPosition();
				if(flag) MY_THROW_QP(STEP_QL_ERR_INVALID_ATTR_ENTITY);
			}
		}
		if(p_el->next_element != 0)
		{
			// далее что-то идет
			return CheckFields(p_el->next_element,ent);
		}
	}
	else
	{
		MY_THROW_QP(STEP_QL_ERR_BUSH_STRUCTURE_MISSED)
	}
	return true;
}//bool CaplQLQueryParseBase::CheckFields(CaplQLBranchSelect *bsel,CaplEntity* upper_ent)



void CaplQLQueryParseBase::SetErrDef(int err_def)
{
	switch(err_def)
	{
#define _DEF_ER_FUNCT
#include "ErrMessCd.h"
	}
}//void CaplQLQueryParseBase::SetErrDef(int err_def)

CString CaplQLQueryParseBase::GetLastError(int &string_num, int &pos_in_string, int *global_pos, int *length)
{
	string_num = m_error_string_num;
	pos_in_string = m_error_pos;
	if(global_pos != 0)
		*global_pos = m_pos_read;
	if(length != 0)
		*length = m_cur_word.GetLength();
	return m_error_definition;
}

bool CaplQLQueryParseBase::IsServSym(CString &str)
{
	if(str ==_T("select"))return true;
	if(str ==_T("from"))return true;
	if(str ==_T("end_select"))return true;
	return false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
bool CaplQLQueryParseBase::IsRezSym(TCHAR c)
{
	switch(c)
	{
	//case _T('.'):
	case _T(';'):
	case _T(','):
	case _T('\r'):
	case _T('\n'):
	case _T('\t'):
    case _T(' '):
	case _T('('):
	case _T(')'):
	case _T('{'):
	case _T('}'):
	case _T('='):
	case _T('>'):
	case _T('<'):
	case _T(':'):
	case _T('!'):
		//	case _T('+'):
	case _T('/'):
	case _T('*'):
	case _T('-'):
	case _T('^'):
	case _T('&'):
		return true;
		break;
	case _T('.'):
		if(m_pos_read == 0) return true;
		if(IsNumber(m_in[m_pos_read-1]) && IsNumber(m_in[m_pos_read+1]))
		{
			return false;
		}
		return true;
		break;
	default:
		return false;
		break;
	}
}
//////////////////////////////////////////////////////////
bool CaplQLQueryParseBase::IsLogOp(CString &str)
{
	if(str == _T("=")) return true;
	if(str == _T("<")) return true;
	if(str == _T(">")) return true;
	if(str == _T(">=")) return true;
	if(str == _T("<=")) return true;
	if(str == _T("!=")) return true;
	if(str == _T("like")) return true;
	if(str == _T("in")) return true;
	if(str == _T("not_in")) return true;
//	if(str == _T("+")) return true;
//	if(str == _T("*")) return true;
//	if(str == _T("-")) return true;
//	if(str == _T("^")) return true;
	return false;
}
//////////////////////////////////////////////////////////
bool CaplQLQueryParseBase::IsBoolOp(CString &str)
{
	if(str == _T("and")) return true;
	if(str == _T("&&")) return true;
	if(str == _T("or")) return true;
	if(str == _T("||")) return true;
//	if(str == _T("xor")) return true;
	if(str == _T("not")) return true;
	return false;
}
/*
//////////////////////////////////////////////////////////
bool CaplQLQueryParseBase::IsSetOp(CString &str)
{
	if(str == _T("+")) return true;
	if(str == _T("-")) return true;
	if(str == _T("*")) return true;
	if(str == _T("^")) return true;
	return false;
}
*/
//////////////////////////////////////////////////////////
bool CaplQLQueryParseBase::IsServiceSymbol(CString &str)
{
	bool retval;
	retval = (IsBoolOp(str) || IsLogOp(str) || IsServSym(str));
	if(str != _T("") && !retval){ retval = IsRezSym(str[0]);}
	return retval;
}

bool CaplQLQueryParseBase::GetWord(CString &word)
{// query,m_pos,m_cur_word
	word = _T("");
	if(m_in == _T("")) MY_THROW_QP(STEP_QL_ERR_COULD_NOT_READ);
    TCHAR c=_T('\0');
    bool flag = true;

begin:
    // пропуск разделителей
    flag = true;
    while(flag)
    {
		if(m_pos_read >= (int)m_in.GetLength())
			MY_THROW_QP(STEP_QL_ERR_COULD_NOT_READ);

        c = m_in[m_pos_read];
        switch(c)
        {
        case _T('\0'):
            MY_THROW_QP(STEP_QL_ERR_COULD_NOT_READ);
        case _T(' '):
        case _T('\r'):
        case _T('\t'):
            m_pos_read++;
			m_error_pos++;
			break;
        case _T('\n'):
			m_pos_read++; 
			m_error_string_num++;
			m_error_pos=1; 
			break;
        default:
            flag = false;
            break;
        }
    }

     // пропуск комментариев
    if(c == _T('/'))         
	{
		if(m_in[m_pos_read+1] == _T('/'))
		{
			m_pos_read+=2;
			m_error_pos+=2;
			while((m_pos_read < (int)m_in.GetLength()) && (m_in[m_pos_read] != _T('\0')))
			{
				if(m_in[m_pos_read] == _T('\r')) break; 
				if(m_in[m_pos_read] == _T('\n'))
				{
					m_error_string_num++; 
					m_error_pos=1; 
					break;
				}
				m_pos_read++;
			}
            m_pos_read++;
            goto begin;
		}
		else if(m_in[m_pos_read+1] == _T('*'))
		{
			m_pos_read += 3;
			m_error_pos += 3;
			while((m_pos_read < (int)m_in.GetLength()) && m_in[m_pos_read] != _T('\0'))
			{
				 if((m_in[m_pos_read] == _T('/')) &&(m_in[m_pos_read-1] == _T('*'))) break; 
				 if(m_in[m_pos_read] == _T('\n')){m_error_string_num++; m_error_pos = 1;} 
				 m_pos_read++;
				 m_error_pos++;
			 }
             m_pos_read++;
			 m_error_pos++;
             goto begin;
		}
	}

    // чтение служебного символа
	if(IsRezSym(c))
	{
		word = c; m_pos_read++ ;m_error_pos++;
		if((c == _T('<')) || (c == _T('>')) || (c == _T('!')))
			{c = m_in[m_pos_read]; if(c == _T('=')) {word+=c; m_pos_read++; m_error_pos++;}}
		if(c == _T('!'))
			if(m_in[m_pos_read] == _T('=')){ word += _T('='); m_pos_read++; m_error_pos++;}
		if(c == _T('-'))
			if(m_in[m_pos_read] == _T('>')){word += _T('>'); m_pos_read++; m_error_pos++;}
		return true;
	}
    // чтение строки
	if(c == _T('"'))      
	{
		word = _T("\"");
		m_pos_read++; m_error_pos++;
		while ((m_pos_read < (int)m_in.GetLength()) && (m_in[m_pos_read] != _T('\0')) )
		{
			c = m_in[m_pos_read];
			if(c == _T('"'))
			{
				if(m_in[m_pos_read-1] != _T('\\'))
				{ 
					m_pos_read++;
					m_error_pos++; 
					word += _T("\"");
					break;
				}
			}
			word += c;
			m_pos_read++;
			m_error_pos++;
		}
		return true;
	}
	if(c == _T('\''))      
	{
		word = _T("\'");
		m_pos_read++;
		m_error_pos++;
		while ((m_pos_read < (int)m_in.GetLength()) && (m_in[m_pos_read] != _T('\0')))
		{
			c = m_in[m_pos_read];
			if(c == _T('\''))
			{
				//if(m_in[m_pos_read-1]!='\\')
				//{ 
					m_pos_read++;
					m_error_pos++; 
					word += _T("\'"); 
					break;
				//}
			}
			word += c;
			m_pos_read++;
			m_error_pos++;
		}
		return true;
	}
	// чтение слова
//	bool is_all_number = true;
	while(true) 
	{
		if(m_pos_read >= (int)m_in.GetLength())
			break;
		c = m_in.GetAt(m_pos_read);
        if(c == _T('\0')) break;
		else if(c == _T(' ')) break;

		/*else if(m_in[i]==_T('.'))
		{
			//if(word == _T("")) break;
			is_all_number = true;
			for(j = 0;j<word.GetLength();j++)
			{
				if((word[j]<'0')||(word[j]>'9')||(word[j]=='-')||(word[j]=='+'))
					{is_all_number = false; break;}
			}
			if(!is_all_number){break;}

		}*/
        else if(IsRezSym(c)) break;
		word += c;
		m_pos_read++;
		m_error_pos++;
	}
    word.MakeLower();
	return true;
}

