// aplQLQuery.cpp: implementation of the CaplQLQuery class.
//
//////////////////////////////////////////////////////////////////////

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

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

#undef APL_THROW_LEVEL
#define APL_THROW_LEVEL SaplErrorDescriptionLevel_CaplData

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

CaplQLQuery::CaplQLObject::CaplQLObject(CaplQLQuery* parent)
{
    if(parent==0)
    {
        m_def_pos=-1;
        m_def_str=-1;
        m_parent=0;
    }
    else
    {
        m_def_pos=parent->m_error_pos;
        m_def_str=parent->m_error_string_num;
        m_parent=parent;
        m_parent->m_all_objects.Add(this);
    }
    type=aplTOObject;
}
CaplQLQuery::CaplQLObject::~CaplQLObject()
{
    m_def_pos=-1;
    type=aplTOObject;
}

void CaplQLQuery::CaplQLObject::SetParentErrPosition()
{
    if(m_parent!=0)
    {
        m_parent->m_error_pos=m_def_pos;
        m_parent->m_error_string_num=m_def_str;
    }
}


CaplQLQuery::CaplQLExtent::CaplQLExtent(CaplQLQuery* parent):CaplQLObject(parent)
{
    base_entity=0;res_entity=0;conditions=0;name=_T("");
    type=aplTOExtent;attr_sharp=0;is_res_field=false;QLExtents=0;is_defined=false;
    in_result=false;with_attr=false;

}

CaplQLQuery::CaplQLExtent::~CaplQLExtent()
{
}

CaplQLQuery::CaplQLGroupOp::CaplQLGroupOp(CaplQLQuery* parent):CaplQLBranchSelect(parent)
{
    first_op=0;
    second_op=0;
    type=aplTOGroupOp;
    operation_type=aplNOGRPREL;
}
CaplQLQuery::CaplQLGroupOp::~CaplQLGroupOp()
{
}

CaplQLQuery::CaplQLLogOp::CaplQLLogOp(CaplQLQuery* parent):CaplQLObject(parent)
{
    value_val=0;value_ext=0;
    operation_type=aplNOLOGREL;selector=aplValNoValue;
    type=aplTOLogOp;
}

CaplQLQuery::CaplQLLogOp::~CaplQLLogOp()
{
    if(value_val!=0 && (selector==aplValValue || selector==aplValInBlobString))
    {
        delete value_val;
    }
};


CaplQLQuery::CaplQLArithmeticOpPath::CaplQLArithmeticOpPath(CaplQLQuery* parent):CaplQLObject(parent)
{
    type=aplTOArithmeticOpPath;
    arithm_op=aplAONoOp;
    attr2=0;
    attr_convert=aplValNoConvert;
    num2=0;
    next_element = 0;
}

int CaplQLQuery::CaplQLArithmeticOpPath::GetAOOrder()
{
    switch(arithm_op)
    {
    case aplAONoOp:
        return 0;
        break;
    case aplAOAdd:
    case aplAOSub:
        return 1;
        break;
    case aplAOMul:
    case aplAODiv:
        return 2;
        break;
    }
    return 0;
}

CaplQLQuery::CaplQLPathElement::CaplQLPathElement(CaplQLQuery* parent):CaplQLBranchSelect(parent)
{
    type=aplTOPathElement;
    next_element=0;
    res_entity=0;
    operation=0;
    attr_name=_T("");
    attr=0;
    pseudo_attr=aplPAInstId;
    attr_convert=aplValNoConvert;
    arithmetic = 0;
}

CaplQLQuery::CaplQLPathElement::~CaplQLPathElement()
{
}

CaplQLQuery::CaplQLField::CaplQLField(CaplQLQuery* parent):CaplQLBranchSelect(parent)
{
    type=aplTOField;
    base_extent=0;
    path=0;extent_name=_T("");
    in_result=false;
}

CaplQLQuery::CaplQLField::~CaplQLField()
{
}


CaplQLQuery::CaplQLBranchSelect::CaplQLBranchSelect(CaplQLQuery* parent):CaplQLObject(parent)
{
}

CaplQLQuery::CaplQLBranchSelect::~CaplQLBranchSelect()
{
}


CaplQLQuery::CaplQLQuery(CaplStepData *hStepData)
{
    m_LastErrDescr=_T("No err");
    m_error_string_num=1;
    m_error_pos=1;
    m_res_bush=0;
    m_data=hStepData;
    m_no_case=false;//учитывать различия регистра
    m_percent_on_el=0;
    m_counter_percent=0;
    m_class_info=_T("");
    m_load_attr=false;
    m_need_lastest_buffer = false;
    m_stack_level = 0;
    m_ignore_nonextsts_inst=false;
}

CaplQLQuery::~CaplQLQuery()
{
    m_all_objects.Clear();
}

void CaplQLQuery::SetFromOlder(CaplQLQuery *older)
{
    m_LastErrDescr=_T("No err");
    int i;
    if(older==0)return;
    m_need_lastest_buffer=older->m_need_lastest_buffer;

    for(i=0;i<older->m_all_objects.GetSize();i++)
    {
        this->m_all_objects.Add(older->m_all_objects.GetAt(i));
    }

    for(i=0;i<older->m_extents.GetSize();i++)
    {
        this->m_extents.Add(older->m_extents.GetAt(i));
    }
    older->m_extents.Clear();
    this->m_data=older->m_data;
    this->m_res_bush=older->m_res_bush;older->m_res_bush=0;
    older->m_all_objects.AutoKillReference=false;
    this->m_no_case=older->m_no_case;
}

bool CaplQLQuery::SaveToDataBuf(CaplDataBuf &dbuf, bool load_extent, CaplPagin* pagin)
{
    m_LastErrDescr=_T("No err");
    int i,j;
    INT32 k, type_int;
    double type_real;
    BYTE type_BYTE;
    CaplQLObject *obj;
    CaplQLPathElement* p_el;
    CaplQLArithmeticOpPath* a_el;
    CaplQLExtent* ext;
    CaplQLGroupOp* gr_op;
    CaplQLLogOp* log_op;
    CaplQLField* field;


    CString str;
    aplExtent all_inst;
    CaplInstance* inst;
    CaplValue val;
    CaplMap inst_map;

    m_all_objects.Sort();
    dbuf.Clear();
    dbuf.m_ansi_string = m_data->m_global_ansi_string;

    // костыль на первое время - клиент будет посылать новый формат буфера только когда это действительно надо
    if(pagin != 0)
    {
        dbuf.Add(APL_STEP_QL_FORMAT_ID, sizeof(APL_STEP_QL_FORMAT_ID));
        // записываем размеры разбиения на порции
        pagin->Add2Buf(dbuf);
    }
    else
        dbuf.Add(APL_STEP_QL_FORMAT_ID_2_2, sizeof(APL_STEP_QL_FORMAT_ID_2_2));

    // записываем флаг возвращать весь экстент (1) или только размер экстента (0)
    UINT32 d_load_extent = load_extent?1:0;
    dbuf.Add(&d_load_extent,sizeof(d_load_extent));

    // Список объектов
    k=m_all_objects.Size;
    dbuf.Add(&k,sizeof(k));

    for(i=0;i<m_all_objects.Size;i++)
    {
        obj=m_all_objects.GetAt(i);
        if(obj==0) return false;

        type_int=(INT32)obj->type;
        dbuf.Add(&type_int,sizeof(type_int));

        if(obj->type==aplTOLogOp)
        {
            if(((CaplQLLogOp*)obj)->selector==aplValValue || ((CaplQLLogOp*)obj)->selector==aplValInBlobString)
            {
                if(((CaplQLLogOp*)obj)->value_val==0)return false;
                if(((CaplQLLogOp*)obj)->value_val->type==aplINSTANCE)
                {
                    ((CaplQLLogOp*)obj)->value_val->Get(inst);
                    all_inst.Add(inst);
                }
            }
        }
        if(obj->type==aplTOExtent)
        {
            for(j=0;j<((CaplQLExtent*)obj)->res_ext.Size;j++)
            {
                inst=((CaplQLExtent*)obj)->res_ext.GetAt(j);
                all_inst.Add(inst);
            }
        }
    }
    // Карта инстансов
    //instmap=new pCaplInstance[all_inst.GetSize()];
    k=all_inst.GetSize();

    dbuf.Add(&k,sizeof(k));
    try
    {
        for(i=0;i<all_inst.GetSize();i++)
        {
            inst=all_inst[i];
            if(inst==0)APL_THROW(APLAPIERR_BADDATA);

            inst_map.Add(inst, i);
            type_int=inst->GetId();
            dbuf.Add(&type_int,sizeof(type_int));

            if(inst->GetType()==0)
            {
                if(!inst->GetTemporary())APL_THROW(APLAPIERR_BADDATA);
                else type_int=0;
            }
            else
            {
                type_int=inst->GetType()->id;
            }
            dbuf.Add(&type_int,sizeof(type_int));
        }

        // Атрибуты
        for(i=0;i<m_all_objects.Size;i++)
        {
            obj=m_all_objects.GetAt(i);
            if(obj==0)APL_THROW(APLAPIERR_BADDATA);

            switch(obj->type)
            {
            case aplTOObject:
                break;
            case aplTOBranchSelect:
                break;
            case aplTOQuery:
                break;
            case aplTOExtent:
                ext=(CaplQLExtent*)obj;
                // имя экстента
                str=ext->name;
                val.Set(str);
                aplAppendBufValue(dbuf, inst_map, &val);
                // базовый тип
                if(ext->base_entity==0){
                    type_int=0;
                }else{
                    type_int=ext->base_entity->id;
                }
                dbuf.Add(&type_int,sizeof(type_int));
                // тип результата
                if(ext->res_entity==0){
                    type_int=0;// только что определили (res_entity м.б. нулевым)
                }else{
                    type_int=ext->res_entity->id;
                }
                dbuf.Add(&type_int,sizeof(type_int));
                // кореневой объект условий
                type_int=m_all_objects.Find(ext->conditions);
                dbuf.Add(&type_int,sizeof(type_int));
                // уточняющий атрибут после скобок запроса
                if(ext->attr_sharp==0){type_int=0;}else{type_int=ext->attr_sharp->id;}
                dbuf.Add(&type_int,sizeof(type_int));
                // флаг - читать результат экстента на промежуточный сервер
                type_int=ext->is_res_field?1:0;
                dbuf.Add(&type_int,sizeof(type_int));
                // v 1.4
                // ветка с кустом групповых операций подзапросов
                type_int=m_all_objects.Find(ext->QLExtents);
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=ext->res_ext.Size;
                dbuf.Add(&type_int,sizeof(type_int));
                for(j=0;j<ext->res_ext.Size;j++)
                {
                    inst=ext->res_ext.GetAt(j);
                    val.Set(inst);
                    aplAppendBufValue(dbuf, inst_map, &val);
                }
                // v 1.5
                str=ext->SQL;
                val.Set(str);
                aplAppendBufValue(dbuf, inst_map, &val);
                // v 1.6
                str=ext->typeSQL;
                val.Set(str);
                aplAppendBufValue(dbuf, inst_map, &val);
                // v 1.8
                type_BYTE=(((CaplQLExtent*)obj)->in_result?1:0);
                dbuf.Add(&type_BYTE,sizeof(type_BYTE));
                type_BYTE=(((CaplQLExtent*)obj)->with_attr?1:0);
                dbuf.Add(&type_BYTE,sizeof(type_BYTE));
                break;

            case aplTOGroupOp:
                gr_op=(CaplQLGroupOp*)obj;
                type_int=(int)gr_op->operation_type;
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=m_all_objects.Find(gr_op->first_op);
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=m_all_objects.Find(gr_op->second_op);
                dbuf.Add(&type_int,sizeof(type_int));
                break;

            case aplTOLogOp:
                log_op=(CaplQLLogOp*)obj;
                type_int=(int)log_op->operation_type;
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=(int)log_op->selector;
                dbuf.Add(&type_int,sizeof(type_int));
                if(log_op->selector!=aplValNoValue && log_op->value_val==0)APL_THROW(APLAPIERR_BADDATA);
                if(log_op->selector==aplValValue || log_op->selector==aplValInBlobString){
                    aplAppendBufValue(dbuf, inst_map, log_op->value_val);
                }else if(log_op->selector==aplValAttr){
                    type_int=log_op->value_attr->id;
                    dbuf.Add(&type_int,sizeof(type_int));
                }else if(log_op->selector==aplValExtent){
                    type_int=m_all_objects.Find(log_op->value_ext);
                    dbuf.Add(&type_int,sizeof(type_int));
                }
                break;

            case aplTOPathElement:
                p_el=(CaplQLPathElement*)obj;
                if(p_el->attr==0){
                    //APL_THROW(APLAPIERR_BADDATA);
                    type_int=0;
                }else{
                    type_int=(int)p_el->attr->id;
                }
                dbuf.Add(&type_int,sizeof(type_int));
                if(p_el->res_entity==0){
                    type_int=0;
                }else{
                    type_int=(int)p_el->res_entity->id;
                }
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=m_all_objects.Find(p_el->next_element);
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=m_all_objects.Find(p_el->operation);
                dbuf.Add(&type_int,sizeof(type_int));
                //if(ver_1_7){break;}
                type_int=(int)p_el->attr_convert;
                dbuf.Add(&type_int,sizeof(type_int));
                // ver 2_0
                type_int=m_all_objects.Find(p_el->arithmetic);
                dbuf.Add(&type_int,sizeof(type_int));
                // ver 2_1
                type_int=(int)p_el->pseudo_attr;
                dbuf.Add(&type_int,sizeof(type_int));

                break;

            case aplTOArithmeticOpPath:
                a_el=(CaplQLArithmeticOpPath*)obj;

                type_int=m_all_objects.Find(a_el->next_element);
                dbuf.Add(&type_int,sizeof(type_int));

                type_int=(int)a_el->arithm_op;
                dbuf.Add(&type_int,sizeof(type_int));

                if(a_el->attr2==0){
                    //APL_THROW(APLAPIERR_BADDATA);
                    type_int=0;
                }else{
                    type_int=(int)a_el->attr2->id;
                }
                dbuf.Add(&type_int,sizeof(type_int));

                // ver 1_9
                type_int = a_el->attr_convert;
                dbuf.Add(&type_int,sizeof(type_int));

                type_real=a_el->num2;
                dbuf.Add(&type_real,sizeof(type_real));

                break;

            case aplTOField:
                field=(CaplQLField*)obj;
                type_int=m_all_objects.Find(field->base_extent);
                dbuf.Add(&type_int,sizeof(type_int));
                type_int=m_all_objects.Find(field->path);
                dbuf.Add(&type_int,sizeof(type_int));
                break;
            }
        }
        // параметры Query
        k=m_extents.GetSize();
        dbuf.Add(&k,sizeof(k));
        for(i=0;i<m_extents.GetSize();i++)
        {
            type_int=m_all_objects.Find((CaplQLObject*)m_extents.GetAt(i));
            dbuf.Add(&type_int,sizeof(type_int));
        }
        type_int=m_all_objects.Find((CaplQLObject*)m_res_bush);
        dbuf.Add(&type_int,sizeof(type_int));
        if(m_no_case){type_int=1;}else{type_int=0;}
        dbuf.Add(&type_int,sizeof(type_int));
    }
    catch(SaplErrorDescription error)
    {
        //delete[] instmap;
        m_data->SetLastErrorEx(error);
        return false;
    }

    //delete[] instmap;
    return true;
}//bool CaplQLQuery::SaveToDataBuf(CaplDataBuf &dbuf){

void CaplQLQuery::ProcessHeaderError(const char* version)
{
    if(CStringA(version).Find(APL_STEP_QL_FORMAT_ID_BASE)==0)
    {
        APL_THROW(APLAPIERR_INCOMPATIBLDATAFORMAT);
    }
    else
    {
        APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
    }
}

CaplInstance* CaplQLQuery::GetInst4Query(apl_id inst_id, apl_id ent_id)
{
    CaplInstance* inst=0;
    if(m_data==0)return 0;
    if(ent_id==0)
    {
        inst = new CaplInstance(m_data,0,inst_id,false);
        inst->SetTemporary(true);
        inst->SetId(inst_id);
        m_data->instances.Unique=false;
        m_data->instances.Add(inst);
        m_data->instances.Unique=true;
    }
    else
    {
        aplQFindInstIdInExtent(*((aplExtent*)(&(m_data->instances))),inst_id,-1,true,&inst);

        //inst=m_data->instances.GetAt(j);
        if(inst==0)
        {
            if(m_ignore_nonextsts_inst)
            {
                inst = new CaplInstance(m_data,0,inst_id,false);
                return inst;
            }
            else
            {
                APL_THROW(APLAPIERR_BADDATA);
            }
        }
        if(inst->GetType()==0)
        {
            CString str;
            str.Format(_T(" inst is %i, type client %i"),inst_id,ent_id);
            APL_THROW_EX(APLAPIERR_BADDATA,str);
        }
        if(inst_id!=inst->GetId() || ent_id!=inst->GetType()->id)
        {
            inst=0;
        }
        if(inst==0)APL_THROW(APLAPIERR_BADDATA);
    }
    return inst;
}

void CaplQLQuery::ProcessCatchError(SaplErrorDescription &error)
{
    if(m_data!=0)m_data->SetLastErrorEx(error);
}

bool CaplQLQuery::LoadFromDataBuf(CaplDataBuf &dbuf, UINT32 *p_load_extent)
{

    CaplStackLogger stack_logger(_T("CaplQLQuery::LoadFromDataBuf"));

    char version[20];
    m_LastErrDescr=_T("No err");
    int i,j;
    INT32 k=0,type_int,num_inst;
    double type_real;
    BYTE type_BYTE;
    CaplQLObject *obj=0;
    CString str;
    CaplInstance* inst;
    pCaplInstance *instmap=0;
    CaplValue val;
//	bool load_from_ver_1_3=false;
//	bool load_from_ver_1_4=false;
//	bool load_from_ver_1_5=false;
//	bool load_from_ver_1_6=false;
//	bool load_from_ver_1_7=false;
    bool load_from_ver_1_8=false;
    bool load_from_ver_1_9=false;
    bool load_from_ver_2_0=false;
    bool load_from_ver_2_1=false;
    bool load_from_ver_2_2=false;
    bool load_from_ver_2_3=false;

    if(m_all_objects.GetSize()>0)
    {
        m_all_objects.Clear();
    };
    m_data->SetLastError(APLAPIERR_NOERROR);

    try{
        if(NULL != p_load_extent) *p_load_extent = 1;

        dbuf.Read(version,sizeof(APL_STEP_QL_FORMAT_ID));

        if(CStringA(APL_STEP_QL_FORMAT_ID)==version)
            load_from_ver_2_3 = true;
        else
        {
//	ПРи обработке буфера версии 1_7 обнаружились ошибки.
//	не тратим время на их поиск, а объявляем буферы ранее 1_8 устаревшими (это версии PSS до 3.5.0.0 включительно)
            if(CStringA(APL_STEP_QL_FORMAT_ID_1_8)==version)
                load_from_ver_1_8=true;
            else if(CStringA(APL_STEP_QL_FORMAT_ID_1_9)==version)
                load_from_ver_1_9=true;
            else if(CStringA(APL_STEP_QL_FORMAT_ID_2_0)==version)
                load_from_ver_2_0=true;
            else if(CStringA(APL_STEP_QL_FORMAT_ID_2_1)==version)
                load_from_ver_2_1=true;
            else if(CStringA(APL_STEP_QL_FORMAT_ID_2_2)==version)
                load_from_ver_2_2=true;
            else
            {
                ProcessHeaderError(version);
                return false;
            }
        }

        if(load_from_ver_2_3)
        {
            // читаем размеры разбиения на порции
            if(!m_pagin.ReadFromBuf(dbuf, m_data))
                APL_THROW_EX(APLAPIERR_INVALIDDATAFORMAT, APL_T("Ошибка чтения данных пангинации."));
        }
        else m_pagin.setEmpty();

        if(load_from_ver_2_2 || load_from_ver_2_3)
        {
            UINT32 d_load_extent;
            dbuf.Read(&d_load_extent,sizeof(d_load_extent));
            if(NULL != p_load_extent) *p_load_extent = d_load_extent;
        }

        // Список объектов
        dbuf.Read(&k,sizeof(k));
        if(k > MAX_OBJECTS_IN_QUERY)
        {
            m_LastErrDescr.Format(APL_T("Слишком много объектов в запросе: %i"),k);
            return false;
        }
        for(i=0;i<(int)k;i++)
        {
            dbuf.Read(&type_int,sizeof(type_int));
            switch((aplQLTypeObject)type_int)
            {
            case aplTOObject:
                obj=new CaplQLObject(this);break;
            case aplTOBranchSelect:
                obj=new CaplQLBranchSelect(this);break;
            case aplTOQuery:
                break;
            case aplTOExtent:
                obj=new CaplQLExtent(this);break;
            case aplTOGroupOp:
                obj=new CaplQLGroupOp(this);break;
            case aplTOLogOp:
                obj=new CaplQLLogOp(this);break;
            case aplTOPathElement:
                obj=new CaplQLPathElement(this);break;
            case aplTOField:
                obj=new CaplQLField(this);break;
            case aplTOArithmeticOpPath:
                obj=new CaplQLArithmeticOpPath(this);break;
            }
            if(obj==0)APL_THROW_EX(APLAPIERR_OUT_OF_MEMORY,APL_T("Ошибка выделения памяти под объект"));
        }
        // Карта инстансов
        dbuf.Read(&k,sizeof(k));
        if(k > MAX_OBJECTS_IN_QUERY)
        {
            m_LastErrDescr.Format(APL_T("Слишком много инстансов в запросе: %i"),k);
            return false;
        }

        instmap=new pCaplInstance[k];
        inst=0;
        for(i=0;i<(int)k;i++)
        {
            dbuf.Read(&num_inst,sizeof(num_inst));
            dbuf.Read(&type_int,sizeof(type_int));
            inst=GetInst4Query(num_inst,type_int);
            instmap[i]=inst;
        }

        // Атрибуты объектов
        for(i=0;i<m_all_objects.Size;i++)
        {
            obj=m_all_objects.GetAt(i);
            if(obj==0)APL_THROW_EX(APLAPIERR_BADDATA,APL_T("Пустой объект в массиве"));

            switch(obj->type)
            {
            case aplTOObject:
                break;
            case aplTOBranchSelect:
                break;
            case aplTOQuery:
                break;

            case aplTOExtent:
                // имя экстента
                aplReadValueFromBuf(dbuf,instmap,&val);
                val.Get(str);
                ((CaplQLExtent*)obj)->name=str;
                // базовый тип
                dbuf.Read(&type_int,sizeof(type_int));
                if(type_int==0)
                {
                    ((CaplQLExtent*)obj)->base_entity=0;
                }
                else
                {
                    ((CaplQLExtent*)obj)->base_entity=m_data->GetEntityById(type_int);
                }
                // тип результата
                dbuf.Read(&type_int,sizeof(type_int));
                if(type_int==0)
                {
                    ((CaplQLExtent*)obj)->res_entity=0;
                }
                else if(((CaplQLExtent*)obj)->base_entity!=0 &&
                         (apl_id)type_int==((CaplQLExtent*)obj)->base_entity->id)
                {
                    ((CaplQLExtent*)obj)->res_entity=((CaplQLExtent*)obj)->base_entity;
                }
                else
                {
                    ((CaplQLExtent*)obj)->res_entity=m_data->GetEntityById(type_int);
                }
                // кореневой объект условий
                dbuf.Read(&type_int,sizeof(type_int));

                if(type_int==-1)
                {
                    ((CaplQLExtent*)obj)->conditions=0;
                }
                else
                {
                    ((CaplQLExtent*)obj)->conditions=(CaplQLBranchSelect*)m_all_objects.GetAt(type_int);
                }
                // уточняющий атрибут после скобок запроса
                dbuf.Read(&type_int,sizeof(type_int));

                if(type_int==0)
                {
                    ((CaplQLExtent*)obj)->attr_sharp=0;
                }
                else
                {
                    ((CaplQLExtent*)obj)->attr_sharp=
                        m_data->GetAttrDefinitionById(type_int);
                }
                // флаг - читать результат экстента на промежуточный сервер
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLExtent*)obj)->is_res_field=type_int>0;

                //if(load_from_ver_1_3)break;

                // v 1.4
                // ветка с кустом групповых операций подзапросов
                dbuf.Read(&type_int,sizeof(type_int));
                if(type_int==-1)
                {
                    ((CaplQLExtent*)obj)->QLExtents=0;
                }
                else
                {
                    ((CaplQLExtent*)obj)->QLExtents=(CaplQLBranchSelect*)m_all_objects.GetAt(type_int);
                }

                ((CaplQLExtent*)obj)->res_ext.Clear();
                dbuf.Read(&type_int,sizeof(type_int));
                k=type_int;

                for(j=0;j<(int)k;j++)
                {
                    inst=0;
                    aplReadValueFromBuf(dbuf,instmap,&val);
                    val.Get(inst);
                    ((CaplQLExtent*)obj)->res_ext.Add(inst);
                }
                //if(load_from_ver_1_4)break;

                // v 1.5
                aplReadValueFromBuf(dbuf,instmap,&val);
                val.Get(str);
                ((CaplQLExtent*)obj)->SQL=str;
                //if(load_from_ver_1_5)break;

                // v 1.6
                aplReadValueFromBuf(dbuf,instmap,&val);
                val.Get(str);
                ((CaplQLExtent*)obj)->typeSQL=str;
                //if(load_from_ver_1_6 || load_from_ver_1_7)break;

                // v 1.8
                dbuf.Read(&type_BYTE,sizeof(type_BYTE));
                ((CaplQLExtent*)obj)->in_result= type_BYTE==1;
                dbuf.Read(&type_BYTE,sizeof(type_BYTE));
                ((CaplQLExtent*)obj)->with_attr= type_BYTE==1;
                if(!m_load_attr)
                {
                    m_load_attr=((CaplQLExtent*)obj)->in_result && ((CaplQLExtent*)obj)->with_attr;
                }
                break;

            case aplTOGroupOp:
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLGroupOp*)obj)->operation_type=(aplQLGroupRelation)type_int;

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLGroupOp*)obj)->first_op=(CaplQLBranchSelect*)m_all_objects.GetAt(type_int);

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLGroupOp*)obj)->second_op=(CaplQLBranchSelect*)m_all_objects.GetAt(type_int);
                break;

            case aplTOLogOp:
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLLogOp*)obj)->operation_type=(aplQLLogRelation)type_int;

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLLogOp*)obj)->selector=(aplQLLogOpSelector)type_int;
                if(((CaplQLLogOp*)obj)->selector==aplValValue ||
                                    ((CaplQLLogOp*)obj)->selector==aplValInBlobString)
                {
                    aplReadValueFromBuf(dbuf,instmap,&val);
                    ((CaplQLLogOp*)obj)->value_val=new CaplValue();
                    ((CaplQLLogOp*)obj)->value_val->Set(val);
                }
                else if(((CaplQLLogOp*)obj)->selector==aplValAttr)
                {
                    dbuf.Read(&type_int,sizeof(type_int));
                    ((CaplQLLogOp*)obj)->value_attr=m_data->GetAttrDefinitionById(type_int);
                }
                else if(((CaplQLLogOp*)obj)->selector==aplValExtent)
                {
                    dbuf.Read(&type_int,sizeof(type_int));
                    ((CaplQLLogOp*)obj)->value_ext=(CaplQLExtent*)m_all_objects.GetAt(type_int);
                }
                break;

            case aplTOPathElement:
                dbuf.Read(&type_int,sizeof(type_int));
                if(type_int==0)
                {
                    ((CaplQLPathElement*)obj)->attr=0;
                }
                else
                {
                    ((CaplQLPathElement*)obj)->attr=m_data->GetAttrDefinitionById(type_int);
                }
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLPathElement*)obj)->res_entity=m_data->GetEntityById(type_int);

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLPathElement*)obj)->next_element=(CaplQLBranchSelect*)m_all_objects.GetAt(type_int);

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLPathElement*)obj)->operation=(CaplQLLogOp*)m_all_objects.GetAt(type_int);

                if(load_from_ver_1_8)break;
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLPathElement*)obj)->attr_convert=(aplQLLogAttrConvert)type_int;

                if(load_from_ver_1_9)break;

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLPathElement*)obj)->arithmetic=(CaplQLArithmeticOpPath*)m_all_objects.GetAt(type_int);

                if(load_from_ver_2_0)break;

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLPathElement*)obj)->pseudo_attr=(aplQLTypePseudoAttr)type_int;

                break;

            case aplTOArithmeticOpPath:

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLArithmeticOpPath*)obj)->next_element=(CaplQLArithmeticOpPath*)m_all_objects.GetAt(type_int);

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLArithmeticOpPath*)obj)->arithm_op=(aplQLArithmeticOp)type_int;

                dbuf.Read(&type_int,sizeof(type_int));
                if(type_int==0)
                {
                    ((CaplQLArithmeticOpPath*)obj)->attr2=0;
                }
                else
                {
                    ((CaplQLArithmeticOpPath*)obj)->attr2=m_data->GetAttrDefinitionById(type_int);
                }
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLArithmeticOpPath*)obj)->attr_convert = (aplQLLogAttrConvert)type_int;

                dbuf.Read(&type_real,sizeof(type_real));
                ((CaplQLArithmeticOpPath*)obj)->num2 = type_real;

                break;

            case aplTOField:
                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLField*)obj)->base_extent=(CaplQLExtent*)m_all_objects.GetAt(type_int);

                dbuf.Read(&type_int,sizeof(type_int));
                ((CaplQLField*)obj)->path=(CaplQLPathElement*)m_all_objects.GetAt(type_int);
                break;
            }
        }
        // параметры Query
        m_extents.Clear();
        dbuf.Read(&k,sizeof(k));
        for(i=0;i<(int)k;i++)
        {
            dbuf.Read(&type_int,sizeof(type_int));
            m_extents.Add((CaplQLExtent*)m_all_objects.GetAt(type_int));
        }
        dbuf.Read(&type_int,sizeof(type_int));
        m_res_bush=(CaplQLBranchSelect*)m_all_objects.GetAt(type_int);

        dbuf.Read(&type_int,sizeof(type_int));
        m_no_case=(bool)(type_int==1);

        if(!m_pagin.isEmpty())
        {
            CString err = m_pagin.CheckQuery(this);
            if(!err.IsEmpty())
                APL_THROW_EX(APLAPIERR_INVALIDDATAFORMAT, err);
        }
    }
    catch(SaplErrorDescription err)
    {
        delete[] instmap;
        throw(SaplErrorDescription(err, _T(__FILE__), _T(__DATE__), __LINE__, _T(""), __APL_FUNC__, m_class_info));
        return false;
    }
    delete[] instmap;


    return true;
}


CString CaplQLQuery::LogOp2String(CaplQLQuery::aplQLLogRelation log_relation,bool is_rus)
{

    switch(log_relation){
    case CaplQLQuery::aplSMALL:
        return _T("<");
        break;
    case CaplQLQuery::aplBIG:
        return _T(">");
        break;
    case CaplQLQuery::aplSMALLEQUAL:
        return _T("<=");
        break;
    case CaplQLQuery::aplBIGEQUAL:
        return _T(">=");
        break;
    case CaplQLQuery::aplEQUAL:
        if(is_rus)
            return  APL_T("равно");
        else
            return _T("=");
        break;
    case CaplQLQuery::aplNOTEQUAL:
        if(is_rus)
            return  APL_T("не равно");
        else
            return _T("!=");
        break;
    case CaplQLQuery::aplIN:
        if(is_rus)
            return  APL_T("входит");
        else
            return _T(" IN ");
        break;
    case CaplQLQuery::aplNOTIN:
        if(is_rus)
            return  APL_T("не входит");
        else
            return _T(" NOT IN ");
        break;
    case CaplQLQuery::aplLIKE:
        if(is_rus)
            return  APL_T("содержит");
        else
            return _T(" LIKE ");
        break;
    case CaplQLQuery::aplLIKELEFT:
        if(is_rus)
            return  APL_T("содержит слева");
        else
            return _T(" LIKE_LEFT ");
        break;
    case CaplQLQuery::aplLIKERIGHT:
        if(is_rus)
            return  APL_T("содержит справа");
        else
            return _T(" LIKE_RIGHT ");
        break;
    case CaplQLQuery::aplNOTLIKELEFT:
        if(is_rus)
            return  APL_T("не содержит слева");
        else
            return _T(" NOT LIKE_LEFT ");
        break;
    case CaplQLQuery::aplNOTLIKERIGHT:
        if(is_rus)
            return  APL_T("не содержит справа");
        else
            return _T(" NOT LIKE_RIGHT ");
        break;
    case CaplQLQuery::aplNOTLIKE:
        if(is_rus)
            return  APL_T("не содержит");
        else
            return _T(" NOT LIKE ");
        break;
    case CaplQLQuery::aplNOLOGREL:
        if(is_rus)
            return  APL_T("не задано");
        else
            return _T(" NO LOG RELATION ");
        break;
    case CaplQLQuery::aplEXIST:
        // характеристика задана
        if(is_rus)
            return  APL_T("существует");
        else
            return _T(" EXIST ");
        break;
    case CaplQLQuery::aplNOTEXIST:
        //характеристика не задана
        if(is_rus)
            return  APL_T("не существует");
        else
            return _T(" NOT EXIST ");
        break;
    case CaplQLQuery::aplINDIAPAZON:
        //характеристика не задана
        if(is_rus)
            return  APL_T("в диапазоне");
        else
            return _T(" IN DIAPAZON ");
        break;
    case CaplQLQuery::aplEMPTY:
        //массив пустой
        if(is_rus)
            return  APL_T("массив пустой");
        else
            return _T(" AGGR EMPTY ");
        break;
    case CaplQLQuery::aplNOTEMPTY:
        //массив пустой
        if(is_rus)
            return  APL_T("массив не пустой");
        else
            return _T(" AGGR NOT EMPTY ");
        break;
    case CaplQLQuery::aplLOGICALRELATION:
        //массив пустой
        if(is_rus)
            return  APL_T("логическое выражение");
        else
            return _T(" BOOLEAN EXPRESSION ");
        break;
    case CaplQLQuery::aplISNULL:
        //массив пустой
        if(is_rus)
            return  APL_T(" is null ");
        else
            return _T(" is null ");
        break;
    case CaplQLQuery::aplISNOTNULL:
        //массив пустой
        if(is_rus)
            return  APL_T(" is not null ");
        else
            return _T(" is not null ");
        break;
    }
    return _T("");
}

bool CaplQLQuery::Compare(CaplValue &val,CaplInstance* base_inst,CaplQLLogOp* log_op,CaplValue *val_new,aplQLLogRelation def_op_type)
{

    int ival_1=0,ival_2=0,i=0;
    double rval_1=0,rval_2=0;
    bool bval_1,bval_2,flag;
    CaplInstance *instval_1=0,*instval_2=0;
    CString sval_1,sval_2;
    CaplAggr aggrval;
    CaplValue val_1,val_2;
    aplQLLogRelation op_type;

    if(log_op==0 && val_new==0)return false;
    if(log_op!=0)
    {
        if(val_new!=0)return false;
        if(log_op->selector==aplValValue)
        {
            if(log_op->value_val==0) return false;
            val_new=log_op->value_val;
        }
        if(log_op->selector==aplValAttr)
        {
            if(log_op->value_attr==0) return false;
            m_data->GetAttr(base_inst,log_op->value_attr,val_2);
            val_new=&val_2;
        }
        if(log_op->selector==aplValInBlobString)
        {
            // поиск по БЛОБам только в ORACLE
            APL_THROW_EX(APLAPIERR_BADDATA, APL_T("Поиск по БЛОБам возможен только в ORACLE"));
            return false;
        }
        op_type=log_op->operation_type;
    }
    else
    {
        op_type=def_op_type;
//		op_type=aplLIKE;
//		op_type=aplEQUAL;
    }

    switch(val.type)
    {
    case aplINTEGER:
        if(val_new==0)return false;
        if(val_new->type!=aplINTEGER)return false;
        val.Get((int&)ival_1);val_new->Get((int&)ival_2);

        switch(op_type)
        {
        case aplSMALL:// "<"
            return ival_1<ival_2;
            break;
        case aplBIG://">"
            return ival_1>ival_2;
            break;
        case aplSMALLEQUAL:// "<="
            return ival_1<=ival_2;
            break;
        case aplBIGEQUAL://">="
            return ival_1>=ival_2;
            break;
        case aplIN:// "входит"
        case aplLIKE:// "содержит"
        case aplLIKELEFT:// "содержит с левой стороны"
        case aplLIKERIGHT:// "содержит с правой стороны"
        case aplEQUAL://"равно"
            return ival_1==ival_2;
            break;
        case aplNOTEQUAL://"не равно"
        case aplNOTLIKELEFT:// "не содержит с левой стороны"
        case aplNOTLIKERIGHT:// "не содержит с правой стороны"
        case aplNOTIN:// "входит"
            return ival_1!=ival_2;
            break;
        default://aplNOLOGREL=0,
            return false;
        }
        break;

    case aplREAL:
        if(val_new==0)return false;
        if(val_new->type!=aplREAL)return false;
        val.Get((double&)rval_1);val_new->Get((double&)rval_2);

        switch(op_type)
        {
        case aplSMALL:// "<"
            return rval_1<rval_2;
            break;
        case aplBIG://">"
            return rval_1>rval_2;
            break;
        case aplSMALLEQUAL:// "<="
            return rval_1<=rval_2;
            break;
        case aplBIGEQUAL://">="
            return rval_1>=rval_2;
            break;
        case aplEQUAL://"равно"
            return rval_1==rval_2;
            break;
        case aplNOTEQUAL://"не равно"
            return rval_1!=rval_2;
            break;
        case aplIN:// "входит"
        case aplLIKE:// "содержит"
        case aplLIKELEFT:// "содержит с левой стороны"
        case aplLIKERIGHT:// "содержит с правой стороны"
        case aplNOTLIKELEFT:// "не содержит с левой стороны"
        case aplNOTLIKERIGHT:// "не содержит с правой стороны"
        default://aplNOLOGREL=0,
            return false;
        }
        break;

    case aplENUMERATION:
    case aplSTRING:
        if(val_new==0)return false;
        if(val_new->type!=aplSTRING)return false;
        val.Get(sval_1);val_new->Get(sval_2);

        if(m_no_case || (op_type!=aplEQUAL && op_type!=aplNOTEQUAL) )
        {
            sval_1.MakeUpper();
            sval_2.MakeUpper();
        }

        switch(op_type)
        {
        case aplSMALL:// "<"
            return sval_1<sval_2;
            break;
        case aplBIG://">"
            return sval_1>sval_2;
            break;
        case aplSMALLEQUAL:// "<="
            return sval_1<=sval_2;
            break;
        case aplBIGEQUAL://">="
            return sval_1>=sval_2;
            break;
        case aplEQUAL://"равно"
            return sval_1==sval_2;
            break;
        case aplNOTEQUAL://"не равно"
            return sval_1!=sval_2;
            break;
        case aplIN:// "входит"
            return(sval_2.Find(sval_1)!=-1);
            break;
        case aplLIKE:// "содержит"
            return(sval_1.Find(sval_2)!=-1);
            break;
        case aplLIKELEFT:// "содержит с левой стороны"
            return (sval_1.Left(sval_2.GetLength())==sval_2);
            break;
        case aplLIKERIGHT:// "содержит с правой стороны"
            return (sval_1.Right(sval_2.GetLength())==sval_2);
            break;
        case aplNOTLIKELEFT:// "не содержит с левой стороны"
            return (sval_1.Left(sval_2.GetLength())!=sval_2);
            break;
        case aplNOTLIKERIGHT:// "не содержит с правой стороны"
            return (sval_1.Right(sval_2.GetLength())!=sval_2);
            break;
        case aplNOTLIKE:// "не содержит"
            return(sval_1.Find(sval_2)==-1);
            break;
        default://aplNOLOGREL=0,
            return false;
        }
        break;

    case aplINSTANCE:
    case aplSELECT:
        val.Get(instval_1);
        //if(instval_1==0)return false; Пустые атрибуты типа inst тоже надо искать
        if(log_op!=0 && log_op->selector==aplValExtent)
        {
            if(log_op->value_ext==0)return false;

            switch(log_op->operation_type)
            {
            case aplIN:// "входит"
                for(i=0;i<log_op->value_ext->res_ext.GetSize();i++)
                {
                    if(instval_1==log_op->value_ext->res_ext.GetAt(i))
                        return true;
                }
                return false;
            case aplNOTIN:// "не входит"
                flag=true;
                for(i=0;i<log_op->value_ext->res_ext.GetSize();i++)
                {
                    if(instval_1==log_op->value_ext->res_ext.GetAt(i))
                    {
                        flag=false;break;
                    }
                }
                return flag;
            default://aplNOLOGREL=0,
                return false;
            }
        }
        else
        {
            if(val_new==0)return false;
            val_new->Get(instval_2);
            apl_id ival_1=(instval_1==0)?0:instval_1->GetId();
            apl_id ival_2=(instval_2==0)?0:instval_2->GetId();

            switch(op_type)
            {
            case aplEQUAL://"равно"
            case aplLIKE://"равно"
                return(instval_1==instval_2);
                break;
            case aplNOTEQUAL://"не равно"
                return(instval_1!=instval_2);
                break;
            case aplSMALL:// "<"
                return ival_1<ival_2;
                break;
            case aplBIG://">"
                return ival_1>ival_2;
                break;
            case aplSMALLEQUAL:// "<="
                return ival_1<=ival_2;
                break;
            case aplBIGEQUAL://">="
                return instval_1->GetId()>=ival_2;
                break;
            default://aplNOLOGREL=0,
                return false;
            }
        }
        break;

    case aplBOOL:
        if(val_new==0)return false;
        val.Get(bval_1);val_new->Get(bval_2);

        switch(op_type)
        {
        case aplEQUAL://"равно"
            return(bval_1==bval_2);
            break;
        case aplNOTEQUAL://"не равно"
            return(bval_1!=bval_2);
            break;
        default://aplNOLOGREL=0,
            return false;
        }
        break;

    case aplAGGR:
        val.Get(aggrval);
        if(op_type == aplLIKE || op_type == aplEQUAL)
        {
            for(i=0;i<aggrval.GetSize();i++)
            {
                aggrval.GetByIndex(i,val_1);
                if(Compare(val_1,base_inst,0,val_new,op_type))
                    return true;
            }
            return false;
        }
        else if(op_type == aplEMPTY)
        {
            return aggrval.GetSize()==0;
        }
        else if(op_type == aplNOTEMPTY)
        {
            return aggrval.GetSize()!=0;
        }
        else
        {
            return false;
        }
        break;
    default://aplNOTYPE,aplLOGICAL=7,aplBINARY=8, =9,aplBLOB=10,aplENUMERATION)
        return false;
    }
    return false;
}//bool Compare


CString CaplQLQuery::PrintMakeStep(CaplQLBranchSelect *bsel)
{
    CString result=_T("");

    if(bsel==0)
    {
        result=APL_T("<!! bsel==0 !!>");
        return result;
    }
    m_stack_level++;
    if(m_stack_level > MAX_REQURSIVE_IN_QUERY)
    {
        result.Format(APL_T(" <! Превышена глубина вложенности: %i !>"),m_stack_level);
        m_stack_level--;
        return result;
    }


    //	CaplQLField *fld;
    CaplQLGroupOp *gr_op=0;
    CaplQLPathElement *p_el=0;
    //	CaplQLExtent* ext_ql;
    if(bsel->type==aplTOGroupOp)
    {
        gr_op=(CaplQLGroupOp *)bsel;
        if(gr_op->first_op==0 || gr_op->second_op==0)
        {
            result=APL_T("<!! пустая операция !!>");
            m_stack_level--;
            return result;
        }
        CString op_type_str;
        if(gr_op->operation_type==aplAND)
        {
            op_type_str = _T(" AND \n\t\t");
        }
        else
        {
            op_type_str = _T(" OR  \n\t\t");
        }
        result=_T("\n\t\t( ") + PrintMakeStep(gr_op->first_op) + op_type_str + PrintMakeStep(gr_op->second_op) + _T(" )");
    }
    else if(bsel->type==aplTOPathElement)
    {
        p_el=(CaplQLPathElement *)bsel;
        if(p_el->next_element==0)
        {
            result=PrintMakeLogOp(bsel);
        }
        else
        {
            if(p_el->attr==0 || p_el->res_entity==0)
            {
                result=_T("<!! p_el->attr==0 || p_el->res_entity==0 !!>");
                m_stack_level--;
                return result;
            }
            result = _T(".") + p_el->attr->name + _T("->") + p_el->res_entity->name + PrintMakeStep(p_el->next_element);
        }
    }
    else
    {
        result=APL_T("<!! неизвестный тип bsel !!>");
    }
    m_stack_level--;
    return result;
}

CString CaplQLQuery::PrintMakeLogOp(CaplQLBranchSelect *bsel)
{

    CaplStackLogger stack_logger(__APL_FUNC__);

    CaplQLPathElement *p_el=0;
    CaplQLLogOp *log_op=0;
    aplValueType type;
    CaplAggr aggr;

    CString result,name_attr,name_attr_len,buf,tbuf,pref="",operation;
    result="";
    if(bsel==0)
    {
        result=_T("<!! bsel==0 !!>");
        return result;
    }
    m_stack_level++;
    if(m_stack_level > MAX_REQURSIVE_IN_QUERY)
    {
        result.Format(APL_T(" <! Превышена глубина вложенности: %i !>"),m_stack_level);
        m_stack_level--;
        return result;
    }


    try{
        if(bsel->type!=aplTOPathElement)
        {
            result=_T("<!! bsel->type!=aplTOPathElement !!>");
            m_stack_level--;
            return result;
        }
        p_el=(CaplQLPathElement *)bsel;
        log_op=p_el->operation;
        if(log_op==0)
        {
            if(p_el->res_entity==0 || p_el->attr==0)
            {
                result=APL_T("<!! ентити или аттр == 0 !!>");
                m_stack_level--;
                return result;
            }
            result = _T(".") + p_el->attr->name + _T("->") + p_el->res_entity->name;
        }
        else
        {
            // SQL-описание операции
            operation=CaplQLQuery::LogOp2String(log_op->operation_type,false);
            // имя столбца атрибута
            if(p_el->attr==0)
            {
                switch(p_el->pseudo_attr)
                {
                case aplPAUserCreate:result = _T(".#user_create ");break;
                case aplPADateCreate:result = _T(".#date_create ");break;
                case aplPAUserUpdate:result = _T(".#user_update ");break;
                case aplPADateUpdate:result = _T(".#date_update ");break;
                case aplPAInstId:
                default:
                    result = _T(".# ");break;
                }
            }
            else
            {
                type=p_el->attr->type;
                if(p_el->attr_convert==aplValToInt)
                {
                    if(type==aplSTRING)
                    {
                        result = _T(".@TO_INT(") + p_el->attr->name + _T(") ");
                    }
                    else if(type==aplREAL)
                    {
                        result = _T(".@TO_INT(") + p_el->attr->name + _T(") ");
                    }
                }
                else if(p_el->attr_convert==aplValToReal)
                {
                    if(type==aplSTRING)
                    {
                        result = _T(".@TO_REAL(") + p_el->attr->name + _T(") ");
                    }
                    else if(type==aplREAL)
                    {
                        result = _T(".") + p_el->attr->name + _T(" ");
                    }
                    type=aplREAL;
                }
                else if(p_el->attr_convert==aplValToString)
                {
                    result = _T(".@TO_STRING(") + p_el->attr->name + _T(") ");
                }
                else
                {
                    result = _T(".") + p_el->attr->name + _T(" ");
                }
                CaplQLArithmeticOpPath* arithmetic = p_el->arithmetic;
                while(arithmetic!=0)
                {
                    switch(arithmetic->arithm_op)
                    {
                    case aplAOAdd:
                        result += _T("+");
                        break;
                    case aplAOSub:
                        result += _T("-");
                        break;
                    case aplAOMul:
                        result += _T("*");
                        break;
                    case aplAODiv:
                        result += _T("/");
                        break;
                    default:
                        result += _T(" ??? ");
                    }
                    if(arithmetic->attr2==0)
                    {
                        buf.Format(_T(" %f "),arithmetic->num2);
                        result+=buf;
                    }
                    else
                    {
                        if(arithmetic->attr_convert==aplValToInt)
                        {
                            result += _T(".@TO_INT(") + arithmetic->attr2->name + _T(") ");
                        }
                        else if(arithmetic->attr_convert==aplValToReal)
                        {
                            result += _T(".@TO_REAL(") + arithmetic->attr2->name + _T(") ");
                        }
                        else
                        {
                            result += _T(".") + arithmetic->attr2->name + _T(" ");
                        }
                    }
                    arithmetic = arithmetic->next_element;
                }
            }
            if(aplValAttr==log_op->selector)
            {
                result+=operation + _T(" ") + log_op->value_attr->name;
            }
            else if(aplValValue==log_op->selector)
            {
                if(0==log_op->value_val)
                {
                    result+=operation + APL_T(" <!! пустой объект log_op->value_val !!>");
                }
                else
                {
                    log_op->value_val->Print(buf,0,0,aplWIN,0,true);
                    result+=operation + _T(" ") + buf;
                }
            }
            else if(log_op->selector==aplValInBlobString)
            {
                if(log_op->value_val==0)
                {
                    result+=operation + APL_T(" <!! пустой объект log_op->value_val !!>");
                }
                else
                {
                    log_op->value_val->Print(buf);
                    result+=operation + _T(" @BLOB_CONSIST(\"") + buf + _T("\") ");
                }

            }
            else if(aplValNoValue==log_op->selector)
            {
                result+=operation + _T(" ");
            }
            else if(aplValExtent==log_op->selector)
            {
                if(log_op->value_ext==0)
                {
                    result+=operation + _T("<!! value_ext==0 !!>");
                }
                else
                {
                    result+=operation + _T(" #") + log_op->value_ext->name + _T(" ");
                }
            }
            else
            {
                result+=APL_T("<!! Неизвестный тип операнда !!>");
            }
        }
    }
    catch(SaplErrorDescription err)
    {
        m_stack_level--;
        return _T("");
    }

    m_stack_level--;
    return result;
}//CString CaplQLQueryPerformOracle::MakeLogOpNew(CaplQLBranchSelect *bsel){

CString CaplQLQuery::PrintGroupQLExt(CaplQLBranchSelect* bsel)
{
    CaplStackLogger stack_logger(__APL_FUNC__);

    CaplQLExtent *QLext=0;
    CaplQLPathElement *p_el=0;
    CaplQLGroupOp *gr_op=0;
    CaplQLField *fld=0;

    CString result = _T("");


    if(bsel==0)
    {
        result = _T("<!! bsel==0 !!>");
        return result;
    }
    m_stack_level++;
    if(m_stack_level > MAX_REQURSIVE_IN_QUERY)
    {
        result.Format(APL_T(" <! Превышена глубина вложенности: %i !>"),m_stack_level);
        m_stack_level--;
        return result;
    }

    if(bsel->type==aplTOGroupOp)
    {
        gr_op=(CaplQLGroupOp *)bsel;
        result = _T("(") + PrintGroupQLExt(gr_op->first_op);
        if(gr_op->operation_type==aplAND){ result += _T(" AND ");}
        else if(gr_op->operation_type==aplOR){ result += _T(" OR ");}
        else{result += APL_T(" <!! неизвестный тип отношений !!> ");}

        result += PrintGroupQLExt(gr_op->second_op);
        result += _T(")");
    }
    else if(bsel->type==aplTOField)
    {
        fld=(CaplQLField *)bsel;
        // Формируем пути из выходных кустов
        QLext=fld->base_extent;
        if(QLext==0)
        {
            result = _T("<!! QLext==0 !!>");
            m_stack_level--;
            return result;
        }
        result = QLext->name;

        p_el=fld->path;

        while(p_el!=0)
        {
            if(p_el->attr == 0)
            {
                result += _T("<!! p_el->attr == 0 !!>");
                break;
            }
            result += _T(".") + p_el->attr->name;
            bsel=p_el->next_element;
            if(bsel==0)
                break;
            if(bsel->type!=aplTOPathElement)
            {
                result += APL_T("<!! неверный bsel->type!!!! !!>");
                break;
            }
            p_el=(CaplQLPathElement*)bsel;
        }
    }else{
        result = APL_T("<!! неизвестный тип bsel !!>");
    }
    m_stack_level--;
    return result;
}//bool CaplQLQueryPerformOracle::PrintGroupQLExt


// переводит запрос из бинарного вида в строчку
void CaplQLQuery::PrintQuery(CString &query)
{
    CaplStackLogger stack_logger(__APL_FUNC__);
    CaplQLExtent *ext=0;
    CaplQLBranchSelect *bsel_next=0;

    int i_ext,i_inst;
    query = _T("");
    CString tmp;

    if(m_load_attr)
    {
        query = _T("SELECT_LOAD_ALL ");
    }
    else
    {
        query = _T("SELECT ");
    }

    query += PrintGroupQLExt(m_res_bush);
    query+=_T("\n");
    query += _T("FROM");

    for(i_ext=0;i_ext<m_extents.Size;i_ext++)
    {
        query+=_T("\n\t");
        ext= m_extents.GetAt(i_ext);
        if(ext==0)
        {
            query+=APL_T("<!!! пустой экстент !!!>");
            continue;
        }
        query += ext->name;
        query += _T("{");
        if(ext->base_entity!=0)
        {
            query += ext->base_entity->name;

            bsel_next=ext->conditions;
            if(bsel_next!=0)
            {
                query+=PrintMakeStep(bsel_next);
            }
        }
        else if(ext->res_ext.Size > 0)
        {
            for(i_inst=0;i_inst<ext->res_ext.Size;i_inst++)
            {
                tmp.Format(_T("#%i"),ext->res_ext.GetAt(i_inst)->GetId());
                if(i_inst > 0)query+=_T(",");
                query+=tmp;
            }
        }
        else if(ext->QLExtents!=0)
        {
            query += PrintGroupQLExt(ext->QLExtents);
        }
        else if(!ext->SQL.IsEmpty())
        {
            query += _T('&') + ext->typeSQL  + _T('&') + ext->SQL;
        }
        query += _T("}");
        if(ext->attr_sharp != 0)
        {
            query += _T(".") + ext->attr_sharp->name;
            if(ext->res_entity != 0) query += _T("->") + ext->res_entity->name;
        }
    }
    query+=_T("\nEND_SELECT");
    if(!m_pagin.isEmpty()) m_pagin.Print(query);
}


CaplPagin::CaplPagin(int _start, int _size)
{
    m_start=_start; m_size_p=_size; m_size_all=-1;
    m_sort_attrs.Clear();
}

#if __cplusplus >= 201103L
CaplPagin::CaplPagin(int _start, int _size, std::list<CaplAttr*> _sort_attrs, bool _asc)
{Set(_start, _size, _sort_attrs, _asc);}
#else
CaplPagin::CaplPagin(int _start, int _size, const CaplTAggr<CaplAttr*, CaplAttr*, APLAGGR_LIST> &_sort_attrs, bool _asc)
{Set(_start, _size, _sort_attrs, _asc);}
#endif

CaplPagin::CaplPagin(const CaplPagin *prev)
{
    Set(prev);
}

void CaplPagin::Set(const CaplPagin *prev)
{
    if(prev==0){setEmpty(); return;}
    m_start = prev->m_start;
    m_size_p = prev->m_size_p;
    m_size_all = prev->m_size_all;
    m_sort_attrs.Clear();
    m_sort_attrs.Append(prev->m_sort_attrs);
    m_asc = prev->m_asc;
}

#if __cplusplus >= 201103L
void CaplPagin::Set(int _start, int _size, std::list<CaplAttr*> _sort_attrs, bool _asc)
{
	m_start=_start; m_size_p=_size; m_size_all=-1;
	m_sort_attrs.Clear();
	for(auto attr: _sort_attrs)
		m_sort_attrs.Add(attr);

	m_asc = _asc;
}
#endif
void CaplPagin::Set(int _start, int _size, const CaplTAggr<CaplAttr*, CaplAttr*, APLAGGR_LIST> &_sort_attrs, bool _asc)
{
	m_start=_start; m_size_p=_size; m_size_all=-1;
	m_sort_attrs.Append(_sort_attrs);
	m_asc = _asc;
}


bool CaplPagin::isEqual(const CaplPagin *prev, bool withPortion) const
{
    if(prev==0)
    {
        if(isEmpty()) return true;
        return false;
    }
    else if(isEmpty()) return false;

    if(withPortion)
    {
        if(m_start != prev->m_start) return false;
        if(m_size_p != prev->m_size_p) return false;
    }
    if(m_asc != prev->m_asc) return false;
    if(m_sort_attrs.Size != prev->m_sort_attrs.Size) return false;
    for(int i=0; i<m_sort_attrs.Size; i++)
    {
        if(m_sort_attrs[i] != prev->m_sort_attrs[i]) return false;
    }
    return true;
}


#define PAGIN_VER_001 0xFFDDAA01

void CaplPagin::Add2Buf(CaplDataBuf &dbuf)
{
    UINT32 ui32 = PAGIN_VER_001;
    UINT16 ui16;
    dbuf.Add(&ui32, 4);
    ui32 = (UINT32)m_start; dbuf.Add(&ui32, 4);
    ui32 = (UINT32)m_size_p; dbuf.Add(&ui32, 4);
    ui16 = m_asc?1:0;
    dbuf.Add(&ui16, 2);
    ui16 = m_sort_attrs.Size;
    dbuf.Add(&ui16, 2);
    for(int i=0; i<m_sort_attrs.Size; i++)
    {
        CaplAttr* attr = m_sort_attrs[i];
        ui32 = (UINT32)(attr==0?0:attr->id);
        dbuf.Add(&ui32, 4);
    }
}

bool CaplPagin::ReadFromBuf(CaplDataBuf &dbuf, CaplStepData* data)
{
    m_sort_attrs.Clear();
    if(data==0) return false;
    UINT32 ui32;
    UINT16 ui16;
    if(!dbuf.Read(&ui32, 4)) return false;
    if(ui32 != PAGIN_VER_001) return false;
    if(!dbuf.Read(&ui32, 4)) return false;
    m_start = (int)ui32;
    if(!dbuf.Read(&ui32, 4)) return false;
    m_size_p = (int)ui32; // если m_size_p==-1, то запрошена порция до конца массива
    if(!dbuf.Read(&ui16, 2)) return false;
    m_asc = ui16 > 0;

    if(!dbuf.Read(&ui16, 2)) return false;
    for(UINT16 i=0; i<ui16; i++)
    {
        if(!dbuf.Read(&ui32, 4)) return false;
        if(ui32==0) continue;
        CaplAttr* attr = data->GetAttrDefinitionById(ui32);
        if(attr == 0) return false;
        m_sort_attrs.Add(attr);
    }
    return true;
}

CString CaplPagin::CheckQuery(CaplQLQuery* m_query)
{
    // Проверка запросов на условия:
    // 1)	Запрос только SELECT.
    // 2)	В выходной секции указан только один подзапрос
    // 3)	Этот подзапрос типа ентити + условия (без дополнительного атрибута)
    if(m_query == 0)
        return APL_T("Не задан объект запроса");
    if(m_query->m_data == 0)
        return APL_T("В объекте запроса не азадн указатель m_data");
    if(m_query->m_load_attr)
        return APL_T("Пангинация разрешена только для запроса SELECT и недопустима в запросе SELECT_LOAD_ALL");
    if(m_query->m_res_bush == 0)
        return APL_T("Не определен объект результата");
    if(m_query->m_res_bush->type != CaplQLQuery::aplTOField)
        return APL_T("Пангинация разрешена только для одного выходного подзапроса и недопустима при комбинациях подзапросов");
    if(((CaplQLQuery::CaplQLField*)m_query->m_res_bush)->path!=0)
        return APL_T("Пангинация разрешена только для одного выходного подзапроса без уточняющих путей");
    if(((CaplQLQuery::CaplQLField*)m_query->m_res_bush)->base_extent==0)
        return APL_T("Не определено тело подзапроса из объекта результата");
    if(((CaplQLQuery::CaplQLField*)m_query->m_res_bush)->base_extent->base_entity == 0)
        return APL_T("Пангинация разрешена только для одного выходного подзапроса на основе ентити");
    if(((CaplQLQuery::CaplQLField*)m_query->m_res_bush)->base_extent->attr_sharp != 0)
        return APL_T("Пангинация разрешена только для одного выходного подзапроса на основе ентити без уточняющих атрибутов после закрывающей скобки подзапроса");

    CaplEntity *ent = ((CaplQLQuery::CaplQLField*)m_query->m_res_bush)->base_extent->base_entity;
    for(int i=0; i<m_sort_attrs.Size; i++)
    {
        CaplAttr* attr = m_sort_attrs[i];
        if(attr==0)
        {
            CString cstr;
            cstr.Format(APL_T("Не задан атрибут сортировки в позиции %i"), i);
            return cstr;
        }

        if(!m_query->m_data->IsEntityAttr(ent, attr))
        {
            CString cstr;
            cstr.Format(APL_T("Атрибут сортировки '%s' не принадлежит энтити выходного подзапроса '%s'"),
                        (LPCTSTR)attr->name, (LPCTSTR)ent->name);
            return cstr;
        }
        if(attr->type != aplSTRING && attr->type != aplENUMERATION)
        {
            CString cstr;
            cstr.Format(APL_T("Разрешена сортировка только по строковым атрибутам, но атрибут '%s' имеет тип '%s'"),
                        (LPCTSTR)attr->name, (LPCTSTR)GetNameType(attr->type));
            return cstr;
        }
    }

    return _T("");
}

void CaplPagin::Print(CString &info)
{
    CString str;
    if(!info.IsEmpty()) info += _T("  ");
    str.Format(_T("PAGIN: start %u, size_p %u, attrs "), m_start, m_size_p);
    info += str;
    if(m_sort_attrs.Size==0)
        info += _T(" <empty>");
    else for(int i=0; i<m_sort_attrs.Size; i++)
    {
        CaplAttr* attr = m_sort_attrs[i];
        if(attr==0){ info += _T("<zero> ");}
        else info += attr->name + _T(" ");
    }
}

void CaplPagin::GetPortion(aplExtent &ext_in, aplExtent &ext_out, CaplStepData* data)
{
    int i, j;
    m_size_all = ext_in.GetSize();
    ext_out.Clear();
    if(data==0) return;
    int imax = m_start;
    if(m_size_p == -1) imax += m_size_all;
    else imax += m_size_p;

    if(m_sort_attrs.Size==0)
    {
        CaplMap map;
        map.bAutoSort = false;
        for(i = 0; i<ext_in.GetSize(); i++)
        {
            CaplInstance* inst = ext_in[i];
            map.Add(inst->GetId(), inst);
        }
        map.SortIn();
        for(i=m_start; i<imax && i<map.GetSize(); i++ )
        {
            if(m_asc)
                ext_out.Add((CaplInstance*)map[i].out);
            else
                ext_out.Add((CaplInstance*)map[map.GetSize()-1-i].out);
        }
    }
    else
    {
        CaplStrMap map;
        for(i = 0; i<ext_in.GetSize(); i++)
        {
            CString key(_T("")), val;
            CaplInstance* inst = ext_in[i];
            for(j=0; j<m_sort_attrs.Size; j++)
            {
                data->GetAttr(inst, m_sort_attrs[j], val);
                if(!key.IsEmpty())key += _T("     ");
                key+=val;
            }
            map.Add(key.MakeUpper(), inst);
        }
        map.Sort();
        for(i=m_start; i<imax && i<map.GetSize(); i++ )
        {
            if(m_asc)
                ext_out.Add((CaplInstance*)map.GetAt(i)->val);
            else
                ext_out.Add((CaplInstance*)map.GetAt(map.GetSize()-1-i)->val);
        }
    }

}
