/*!
 * @file
 * @brief implements Join_JoinOperator
 *
 * @author GertG
 * @ingroup Join
 *
 * @par last changed by:
 * <br>
 * $Author: d026026 $ $DateTime: 2007/04/10 17:59:46 $
 *
 * @sa Join_JoinOperator.hpp
 */
/*

    ========== licence begin  GPL
    Copyright (c) 2002-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



*/
#include "gsp00.h"
#include "ggg00.h"
#include "ggg07.h"
#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_Assertions.hpp"
#include "SAPDBCommon/SAPDB_RangeCode.hpp"
#include "SQLManager/SQLMan_Context.hpp"
#include "SQLManager/SQLMan_MessBlock.hpp"
#include "Join/Join_JoinOperator.hpp"
#include "Join/Join_TableAccessOperator.hpp"
#include "Join/Join_InvAccessOperator.hpp"
#include "Join/Join_InvAccessOperatorEx.hpp"
#include "Join/Join_InvSelectIterator.hpp"
#include "Join/Join_HashAccessAllocator.hpp"
#include "Join/Join_HashAccessOperator.hpp"
#include "Join/Join_NopAccessOperator.hpp"
#include "Join/Join_SortOperator.hpp"
#include "Join/Join_LegacyAccessOperator.hpp"
#include "hak682.h"
#include "hak683.h"
#include "hak01.h"
#include "hak07.h"
#include "hak70.h"
#include "hkb74.h"
#include "hsp30.h"
#include "hgg02.h"
#include "hgg20.h"
#include "hta01.h"
#include "hta01_3.h"
#include "hsp49.h"
#include "hkb71.h"
#include "hgg04.h"
#include "hbd01.h"
#include "hbd01_1.h"
#include "hbd07.h"
#include "hkb720.h"
#include "hbd21.h"

/* ******************** PUBLIC MEMBERS ********************* */
/*!
 * @param acv [in] global context
 * @param dmli [in] statement context
 * @param parsk [in] parskey to be used
 * @param jvrec [in] join view record
 * @param use_old_rescnt [in] delete 'CreateFile' flag of result file?
 * @param del_parsinfos [in] delete parse infos?
 * @param sequence [in] reference to join table sequence array
 * @param act_join [in] actual join number for which operator is created
 */
Join_JoinOperator::Join_JoinOperator(
        SQLMan_Context&             acv,
        tak_dml_info&               dmli,
        const tak_parskey&          parsk,
        const tak68_joinview_rec&   jvrec,
        const pasbool               use_old_rescnt,
        const pasbool               del_parsinfos,
        const tak68_sequence&       sequence,
        const SAPDB_Int4            act_join ):
IOperator(acv), m_LeftOp(0), m_RightOp(0), m_LeftRec(0), 
m_RightRec(0), m_JoinRec(0), m_NullRec(0),
m_GetLeftTupel(true), m_RightRecConvArrCnt(0),
m_LastRightDefBytePos(0), m_JoinLenPart(0), m_IsDescendingIndex(false),
m_RightAccessStrat(sequence[act_join-1].jos_joinstrat),
m_RightAccessInvLen(sequence[act_join-1].jos_invlen),
m_LeftRecChanged(false), m_Filter(0), m_LeftRecInserted(false),
m_AuxRightOuterJoinFile(b01niltree_id), m_AppendRightOuterJoinRecords(false),
m_TableForRightOuterJoinScanStat(never_scanned), m_AuxRightOuterKeyLen(0),
m_JoinRecBufferSize(0), m_OutputFilter(false)
{
    if ( 0 == m_acv.a_returncode )
    {
        SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::ctor", Join_Trace, 1 );

        SAPDBTRACE_WRITELN( Join_Trace, 3, "act join : " << act_join
                << "\taccess : " << sequence[act_join-1].jos_joinstrat );

        tgg00_BasisError _b_err;

        _b_err = extract_joininfo( act_join, (act_join == dmli.d_cntfromtab) );
        m_ResultKeyLen = join_keylen();

        if ( m_JoinDesc.gi_linkrec.kbjr_right_oj )
        {
            m_AuxRightOuterKeyLen = get_AuxRightOuterKeyLen();
            SAPDBTRACE_WRITELN( Join_Trace, 3, "m_AuxRightOuterKeyLen : " << m_AuxRightOuterKeyLen );
        }

        if ( e_ok == _b_err )
        {
            // reserve extra space (failure tolerance)
            //m_JoinRecBufferSize = sizeof(tgg00_Rec);
            if ( m_JoinDesc.gi_result_info.n_rec_len > m_JoinDesc.gi_result_info.n_res_rec_len )
                m_JoinRecBufferSize = m_JoinDesc.gi_result_info.n_rec_len + m_AuxRightOuterKeyLen + 20;
            else
                m_JoinRecBufferSize = m_JoinDesc.gi_result_info.n_res_rec_len + m_AuxRightOuterKeyLen + 20;
            if (!( m_JoinRec = (tgg00_Rec*) m_acv.GetAllocator().Allocate( m_JoinRecBufferSize )))
                _b_err = e_no_more_memory;
            else
            {
                SAPDBTRACE_WRITELN( Join_Trace, 3, m_JoinRecBufferSize << " bytes for join buffer allocated" );
                SAPDBTRACE_WRITELN( Join_Trace, 3, "allocate m_JoinRec @ " << (void*)m_JoinRec );
            }
        }

        if ( e_ok == _b_err )
            if ( m_acv.GetMessBlock().ResQualPos() > 0 && m_acv.GetMessBlock().ResQualCount() > 0 )
                _b_err = this->create_filter( m_acv.GetMessBlock().HasResultOutputCols() );

        if ( e_ok == _b_err )
        {
            SAPDBTRACE_WRITELN( Join_Trace, 3, "create right AccessOp" );
            // create new right AccessOperator with actual acv.a_mblock
            m_RightOp = this->AccessOperatorFactory(
                acv,
                sequence[act_join-1].jos_table_buffer,
                sequence[act_join-1].jos_expected_table_rec_reads,
                sequence[act_join-1].jos_parallel_server,
                sequence[act_join-1].jos_source);
            if ( ! m_RightOp )
                _b_err = e_no_more_memory;

            if ( 0 == acv.a_returncode && e_ok == _b_err )
            {
                // get next acv.a_mblock
                a682j_one_record( acv, dmli, act_join-1,
                        sequence[act_join-1-1].jos_source, parsk,
                        jvrec, use_old_rescnt, del_parsinfos );

                if ( 0 == acv.a_returncode )
                {
                    if ( 2 == act_join )
                    {
                        SAPDBTRACE_WRITELN( Join_Trace, 3, "create left AccessOp" );
                        m_LeftOp = this->AccessOperatorFactory(
                            acv,
                            sequence[act_join-1-1].jos_table_buffer,
                            sequence[act_join-1-1].jos_expected_table_rec_reads,
                            sequence[act_join-1-1].jos_parallel_server,
                            sequence[act_join-1-1].jos_source );
                        if ( ! m_LeftOp )
                            _b_err = e_no_more_memory;
                    }
                    else
                    {
                        SAPDBTRACE_WRITELN( Join_Trace, 3, "create left JoinOp" );
                        // create new left JoinOperator
                        if (!(m_LeftOp = new( m_acv.GetAllocator() )
                                    Join_JoinOperator( acv, dmli,
                                        parsk, jvrec, use_old_rescnt,
                                        del_parsinfos,
                                        sequence, act_join-1 )))
                            _b_err = e_no_more_memory;
                    }
                }
            }
        }

        // clean up
        if ( _b_err != e_ok )
            a07_b_put_error( acv, _b_err, 1 );
    }
}

/*!
 *
 */
Join_JoinOperator::~Join_JoinOperator()
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::dtor", Join_Trace, 1 );

    // release space for stack entries
    SAPDBTRACE_WRITELN( Join_Trace, 8, "deallocate m_Filter @ " << (void*)m_JoinRec );
    if ( m_Filter  ) m_acv.GetAllocator().Deallocate( m_Filter );
    SAPDBTRACE_WRITELN( Join_Trace, 8, "deallocate m_LeftOp @ " << (void*)m_LeftOp );
    if ( m_LeftOp  ) destroy( m_LeftOp,  m_acv.GetAllocator() );
    SAPDBTRACE_WRITELN( Join_Trace, 8, "deallocate m_RightOp @ " << (void*)m_RightOp );
    if ( m_RightOp ) destroy( m_RightOp, m_acv.GetAllocator() );
    SAPDBTRACE_WRITELN( Join_Trace, 8, "deallocate m_JoinRec @ " << (void*)m_JoinRec );
    if ( m_JoinRec ) m_acv.GetAllocator().Deallocate( m_JoinRec );
    SAPDBTRACE_WRITELN( Join_Trace, 8, "deallocate m_NullRec @ " << (void*)m_NullRec );
    if ( m_NullRec ) m_acv.GetAllocator().Deallocate( m_NullRec );
    // make save forgotten Close()
    if ( m_JoinDesc.gi_linkrec.kbjr_right_oj && NIL_PAGE_NO_GG00 != m_AuxRightOuterJoinFile.fileRoot_gg00() )
        b01destroy_file( m_acv.TransContext(), m_AuxRightOuterJoinFile );
}

/*!
 *
 */
tgg00_BasisError Join_JoinOperator::Open()
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::Open", Join_Trace, 1 );
    tgg00_BasisError _e;
    // open outer stream
    _e = m_LeftOp->Open();
    if ( e_ok == _e ) _e = init();
    if ( e_ok == _e ) _e = IOperator::Open();
    return _e;
}

/*!
 * @param startkeys [in] restrict record stream to records with key greater/equal than startkey
 * @param stopkeys [in] restrict record stream to records with key lower/equal than stopkey
 */
tgg00_BasisError Join_JoinOperator::Open(
        const Join_TwoKeys& startkeys,
        const Join_TwoKeys& stopkeys )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::Open", Join_Trace, 1 );
    tgg00_BasisError _e;
    // open outer stream
    _e = m_LeftOp->Open( startkeys, stopkeys );
    if ( e_ok == _e ) _e = init();
    if ( e_ok == _e ) _e = IOperator::Open( startkeys, stopkeys );
    return _e;
}


/*!
 * @param recptr [in/out] pointer to memory should be filled with record
 * @return information code <tt>[e_ok, e_no_next_record]</tt> / error code
 */
tgg00_BasisError Join_JoinOperator::Next( tgg00_Rec*& recptr )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::Next", Join_Trace, 1 );
    tgg00_BasisError _b_err = e_ok;

    do
    {
        if ( ! m_AppendRightOuterJoinRecords )
        {
            if ( m_GetLeftTupel )
            {
                m_GetLeftTupel    = false;
                m_LeftRecChanged  = false;
                m_LeftRecInserted = false;
                SAPDBTRACE_WRITELN( Join_Trace, 3, "<<get next left record>>" );
                _b_err = m_LeftOp->Next( m_LeftRec );
                if ( e_no_next_record == _b_err )
                {
                    if ( m_JoinDesc.gi_linkrec.kbjr_right_oj )
                    {
                        m_AppendRightOuterJoinRecords = true;
                    }
                    else
                    {
                        // don't close left file to ensure repeatable Next() operation
                        //m_LeftOp->Close();
                        return _b_err;
                    }
                }
                else if ( e_ok == _b_err )
                {
                    SAPDBTRACE_WRITELN( Join_Trace, 3, "left record (len=" << m_LeftOp->GetRecordLength() << ") @ " << (void *) this << " is: " );
                    SAPDBTRACE_IF( Join_Trace, 3,
                    t01buf( td_always, m_LeftRec, 1, m_LeftOp->GetRecordLength() <= 500 ? m_LeftOp->GetRecordLength() : 500 ));
                    if ( a70glob_join_strats.includes( m_RightAccessStrat ) )
                        // set m_Startkeys, m_Stopkeys
                        _b_err = prepare_right_key();
                }
            }

            if ( e_ok == _b_err )
            {
                SAPDB_Bool _joining = false;

                if ( ! m_RightOp->IsOpened() )
                {
                    if ( a70glob_join_strats.includes( m_RightAccessStrat ) )
                    {
                        if ( m_JoinDesc.gi_linkrec.kbjr_right_oj && never_scanned == m_TableForRightOuterJoinScanStat )
                        {
                            // scan for RIGHT OUTER JOIN handling
                            SAPDBTRACE_WRITELN( Join_Trace, 5, "scan for RIGHT OUTER JOIN" );
                            _b_err = m_RightOp->Open();
                        }
                        else
                            _b_err = m_RightOp->Open( m_Startkeys, m_Stopkeys );
                    }
                    else
                        // use one table strategy
                        _b_err = m_RightOp->Open();
                }

                SAPDBTRACE_WRITELN( Join_Trace, 3, "after right open - _b_err: " << SAPDBTrace::BasisError(_b_err) );
                do
                {
                    SAPDBTRACE_WRITELN( Join_Trace, 3, "<<get next right record>> " << m_RightAccessStrat );
                    if  ( e_ok == _b_err ) 
                        _b_err = m_RightOp->Next( m_RightRec );
                    if ( e_ok == _b_err )
                    {
                        if ( never_scanned == m_TableForRightOuterJoinScanStat )
                            m_TableForRightOuterJoinScanStat = while_scanning;

                        if ( _joining = join( _b_err ) )
                            m_LeftRecInserted = true;

                        if ( m_JoinDesc.gi_linkrec.kbjr_right_oj )
                        {
                            if ( !_joining && while_scanning == m_TableForRightOuterJoinScanStat )
                                _b_err = update_roj_auxfile( add_record );
                            if ( _joining && already_scanned == m_TableForRightOuterJoinScanStat )
                                _b_err = update_roj_auxfile( delete_record );
                        }

                        // test filter after joining !!!
                        if ( _joining && m_Filter )
                            _joining = filter( m_JoinRec, _b_err );
                    }

                    // test for command canceling
                    if ( m_acv.TransContext().trRteCommPtr_gg00->to_cancel )
                        _b_err = e_cancelled;
                }
                while ( e_ok == _b_err && !_joining );

                if ( e_no_next_record == _b_err )
                {
                    m_RightOp->Close();
                    m_GetLeftTupel = true;
                    if ( while_scanning == m_TableForRightOuterJoinScanStat )
                        m_TableForRightOuterJoinScanStat = already_scanned;
                    if ( !m_LeftRecInserted && m_JoinDesc.gi_linkrec.kbjr_left_oj )
                    {
                        SAPDBTRACE_WRITELN( Join_Trace, 5, "append LEFT OUTER record " );
                        /* !!! LEFT OUTER JOIN !!! */
                        m_RightOp->GetNullRecord( m_RightRec );
                        _b_err = build_join_rec();
                        if ( m_Filter )
                        {
                            if ( ! filter( m_JoinRec, _b_err ) ) 
                                _b_err = e_no_next_record;
                        }
                    }
                }
            }
        }

        if ( m_AppendRightOuterJoinRecords )
        {
            SAPDBTRACE_WRITELN( Join_Trace, 5, "append RIGHT OUTER record " );
            _b_err = get_roj_record( recptr );
            if ( e_no_next_record == _b_err )
            {
                // don't close left file to ensure repeatable Next() operation
                // m_LeftOp->Close();
                return _b_err;
            }
        }
    }
    while ( e_no_next_record == _b_err );

    if ( !m_AppendRightOuterJoinRecords ) recptr = m_JoinRec;

    SAPDBTRACE_WRITELN( Join_Trace, 3, "end of Next @ " << (void *)this << " (returns " << SAPDBTrace::BasisError(_b_err) << ")");

    return _b_err;
}

/*!
 *
 */
void Join_JoinOperator::Close()
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::Close", Join_Trace, 1 );
    // close outer stream
    if ( m_LeftOp->IsOpened() )
        m_LeftOp->Close();

    // close inner stream
    if ( m_RightOp->IsOpened() )
        m_RightOp->Close();

    if ( IsOpened() && m_JoinDesc.gi_linkrec.kbjr_right_oj && NIL_PAGE_NO_GG00 != m_AuxRightOuterJoinFile.fileRoot_gg00() )
    {
        b01destroy_file( m_acv.TransContext(), m_AuxRightOuterJoinFile );
        m_AuxRightOuterJoinFile.fileRoot_gg00() = NIL_PAGE_NO_GG00;
    }
    IOperator::Close();
}

/*!
 * @param record [in/out] record to be filled
 */
void Join_JoinOperator::GetRecordTemplate( tgg00_Rec*& record )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::GetRecordTemplate", Join_Trace, 1 );
    SAPDBTRACE_IF( Join_Trace, 5, memset( m_JoinRec, ':', m_JoinRecBufferSize ) );
    record = m_JoinRec;
    record->recLen_gg00() = GetRecordLength();
    record->recKeyLen_gg00() = GetKeyLength();
    record->recVarcolOffset_gg00() = 0;
    record->recVarcolCnt_gg00() = 0;

    if ( m_OutputFilter )
    {
        tgg00_StackEntry *_err_st_entry;
        pasbool _unqualified = false;

        m_SelFields.sfp_result_length() = 0;
        m_SelFields.sfp_m_result_len()  = 0;
        m_SelFields.sfp_m_result_cnt()  = 1;

        m_SelFields.sfp_rec_addr()      = reinterpret_cast<tsp00_BufAddr>(record);
        m_SelFields.sfp_m_result_size() = m_JoinRecBufferSize;
        m_SelFields.sfp_m_result_addr() = reinterpret_cast<tsp00_MoveObjPtr>(record);
        m_acv.TransContext().trError_gg00 = e_ok;

        // hide real qualification, expose only output description
        SAPDB_Int4 _qual_cnt = m_FilterDesc.mqual_cnt;
        m_FilterDesc.mqual_cnt = (*m_Filter)[ m_FilterDesc.mqual_pos - 1 ].epos() - 1;

        k71qual_handling( m_acv.TransContext(), m_SelFields,
                m_FilterDesc.mview_cnt > 0, false, m_FilterDesc,
                _err_st_entry, _unqualified );

        m_FilterDesc.mqual_cnt = _qual_cnt;
    }
    SAPDBTRACE_IF( Join_Trace, 5,
    t01buf( td_always, record, 1, record->recLen_gg00() <= 300 ? record->recLen_gg00() : 300 ));
}

/*!
 * @param record [in/out] record to be filled
 */
tgg00_BasisError Join_JoinOperator::GetNullRecord( tgg00_Rec*& recptr ) 
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::GetNullRecord", Join_Trace, 1 );

    if ( ! m_NullRec )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 7, "create NULL record" );
        tgg00_BasisError _b_err = e_ok;
        SAPDB_UInt4 _nullbuflen;

        _nullbuflen = (this->GetRecordLength() > this->join_reclen()) ? this->GetRecordLength() : this->join_reclen() ;
        
        // reserve extra space (failure tolerance)
        if (!( m_NullRec = (tgg00_Rec*) 
                    m_acv.GetAllocator().Allocate( _nullbuflen += 20 )))
            return e_no_more_memory;
        SAPDBTRACE_WRITELN( Join_Trace, 0, "allocate m_NullRec @ " << (void*)m_NullRec << " with length of " << _nullbuflen );
        SAPDBTRACE_IF( Join_Trace, 3, SAPDB_MemFillNoCheck( m_NullRec, ':', _nullbuflen ) );
        tgg00_Rec *_tmpl, *_tmpr, *_tmpj;
        SAPDB_UInt4 _tmpb = m_JoinRecBufferSize;
        _tmpl = m_LeftRec; _tmpr = m_RightRec; _tmpj = m_JoinRec;
        m_JoinRec = m_NullRec;
        m_JoinRecBufferSize = _nullbuflen;
        m_LeftOp->GetNullRecord( m_LeftRec ); 
        m_RightOp->GetNullRecord( m_RightRec ); 
        build_join_rec();
        if ( m_Filter && m_OutputFilter ) (void) filter( m_JoinRec, _b_err );
        m_LeftRec = _tmpl; m_RightRec = _tmpr; m_JoinRec = _tmpj;
        m_JoinRecBufferSize = _tmpb;
    }
    recptr = m_NullRec;
    SAPDBTRACE_IF( Join_Trace, 5, 
    t01buf( td_always, recptr, 1, recptr->recLen_gg00() <= 300 ? recptr->recLen_gg00() : 300 ));
    return e_ok;
}

/* ******************* PROTECTED MEMBERS ******************* */


/* ******************** PRIVATE MEMBERS ******************** */
/*!
 *
 */
tgg00_BasisError Join_JoinOperator::extract_joininfo(
        SAPDB_Int4 act_join,
        SAPDB_Bool is_last_join )
{
    // extract join info and adjust messblock for BD calls
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::extract_joininfo", Join_Trace, 3 );

    // following code from kb74init_join()
    k74get_join_infos( m_acv.GetMessBlock(), m_JoinDesc );
    k74get_new_positions( m_acv.GetMessBlock(), m_JoinDesc, is_last_join );

    switch ((SAPDB_Int1) m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[1].kboj_op)
    {
        case cgg07_left_len_eq_right_len:
            SAPDBTRACE_WRITELN( Join_Trace, 5, "cgg07_left_len_eq_right_len" );
            // get length from i-1.th temp result
            m_JoinLenPart = m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[0].kboj_len;
            break;
        case cgg07_left_len_le_right_len:
            SAPDBTRACE_WRITELN( Join_Trace, 5, "cgg07_left_len_lt_right_len" );
            // get length from i-1.th temp result
            m_JoinLenPart = m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[0].kboj_len;
            break;
        case cgg07_left_len_gt_right_len:
            SAPDBTRACE_WRITELN( Join_Trace, 5, "cgg07_left_len_gt_right_len" );
            // get length from i.th base table
            m_JoinLenPart = m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[1].kboj_len;
            break;
        default :
            SAPDBTRACE_WRITELN( Join_Trace, 5, "m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[1].kboj_op" << " = " << m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[1].kboj_op );
    }
    SAPDBTRACE_WRITELN( Join_Trace, 5, "m_JoinLenPart: " << m_JoinLenPart );

    if ( a70glob_join_strats.includes( m_RightAccessStrat ) )
    {
        // get information on changing data types ascii<->ebcdic before joining
        if ( a70glob_join_key_strats.includes( m_RightAccessStrat ) )
        {
            k74check_conversions( m_acv.GetMessBlock(),
                      m_RightRecConvArr, m_RightRecConvArrCnt,
                      m_LastRightDefBytePos, m_JoinLenPart);
        }
        else
        {
            k74check_inv_conversions( m_acv.GetMessBlock(),
                      m_RightRecConvArr, m_RightRecConvArrCnt,
                      m_LastRightDefBytePos, m_IsDescendingIndex, m_JoinLenPart);
        }
    }
    else
        k74last_defined_byte_pos( m_acv.GetMessBlock(), m_JoinDesc.gi_linkrec, m_LastRightDefBytePos );

    if ( m_JoinDesc.gi_linkrec.kbjr_right_oj )
    {
        // determine auxiliary RIGHT OUTER JOIN file name
        g04build_temp_tree_id( m_AuxRightOuterJoinFile, m_acv.TransContext() );
        m_AuxRightOuterJoinFile.fileTfnTemp_gg00().becomes( ttfnOuterJoin_egg00 );
        m_AuxRightOuterJoinFile.fileTempCnt_gg00() = act_join;
        m_AuxRightOuterJoinFilePos.tpsPno_gg00     = NIL_PAGE_NO_GG00;
        m_AuxRightOuterJoinKey.len() = 0;
    }

    return m_acv.TransContext().trError_gg00;
}

/*!
 *
 */
tgg00_BasisError Join_JoinOperator::create_filter( SAPDB_Bool is_outputfilter )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::create_filter", Join_Trace, 3 );

    tgg00_BasisError _b_err = e_ok;
    m_FilterDesc = m_acv.GetMessBlock().mb_qual()->mstack_desc();
    if (!( m_Filter = (tgg00_StackList*)
                m_acv.GetAllocator().Allocate( m_acv.GetMessBlock().ResQualCount() * sizeof( tgg00_StackEntry ) )))
        _b_err = e_no_more_memory;
    SAPDBTRACE_WRITELN( Join_Trace, 0, "allocate m_Filter @ " << (void*)m_Filter );
    memcpy( m_Filter, (tgg00_StackEntry *)m_acv.GetMessBlock().mb_st() + ( m_acv.GetMessBlock().ResQualPos() - 1 ),
            m_acv.GetMessBlock().ResQualCount() * sizeof( tgg00_StackEntry ) );

    m_FilterDesc.mst_addr()         = m_Filter;
    m_FilterDesc.mqual_cnt = m_acv.GetMessBlock().ResQualCount();
    m_FilterDesc.mqual_pos = 1;
    m_FilterDesc.mst_max() = m_FilterDesc.mqual_cnt;
    m_FilterDesc.mfirst_free() = m_FilterDesc.mqual_cnt + 1;
    m_FilterDesc.mst_optimize_pos() = 0;
    m_FilterDesc.mstrat_pos         =  m_FilterDesc.mstrat_cnt     = 0;
    m_FilterDesc.mresqual_pos()     =  m_FilterDesc.mresqual_cnt() = 0;
    m_FilterDesc.mcol_pos           =  m_FilterDesc.mcol_cnt       = 0;
    m_FilterDesc.mmult_pos          =  m_FilterDesc.mmult_cnt      = 0;
    m_FilterDesc.mview_pos          =  m_FilterDesc.mview_cnt      = 0;
    m_FilterDesc.mupd_pos           =  m_FilterDesc.mupd_cnt       = 0;
    m_FilterDesc.mlink_pos          =  m_FilterDesc.mlink_cnt      = 0;
    m_FilterDesc.mstring_pos        =  m_FilterDesc.mstring_cnt    = 0;
    m_FilterDesc.mtrigger_pos       =  m_FilterDesc.mtrigger_cnt   = 0;
    m_FilterDesc.minvqual_pos       =  m_FilterDesc.minvqual_cnt   = 0;

    SAPDBTRACE_IF( Join_Trace, 3,
    t01stackdesc( td_always, "copied rqual", m_FilterDesc.mst_addr(), m_FilterDesc ));

    g04init_select_fields( m_SelFields, 
            m_acv.GetMessBlock().DataPtr(),
            m_acv.GetMessBlock().DataLength(),
            m_acv.GetMessBlock().mb_valuearr(),
            m_acv.GetMessBlock().mb_validx_max(), 
            m_acv.a_work_st_addr, 
            m_acv.a_work_st_max, 
            m_acv.a_work_buf_addr, 
            m_acv.a_work_buf_size,
            m_acv.a_sqlmode );
    m_SelFields.sfp_bd_mess_type().becomes( m_select );
    m_SelFields.sfp_result_wanted() = true;
    m_SelFields.sfp_m_result_size() = m_JoinRecBufferSize;
    m_SelFields.sfp_first_qual()      = true;
    m_SelFields.sfp_rows_read()       = 0;
    m_SelFields.sfp_rows_qual()       = 0;
    m_SelFields.sfp_acv_addr()        = reinterpret_cast<tsp00_Addr>( &m_acv );
    m_SelFields.sfp_oldrec_addr()     = 0;
    m_SelFields.sfp_primkey_addr()    = 0;
    //SAPDBTRACE_IF( Join_Trace, 3, t01selfields( td_always, "m_SelFields ", m_SelFields ));
    SAPDBTRACE_WRITELN( Join_Trace, 3, "is output filter " << is_outputfilter );
    if ( m_OutputFilter = is_outputfilter /* assignment wanted!!*/ )
    {
        if ( -1 != (*m_Filter)[ m_FilterDesc.mqual_pos - 1 ].ecol_pos() )
            // join key for incomplete result record description
            m_ResultKeyLen = (*m_Filter)[ m_FilterDesc.mqual_pos - 1 ].elen_var();
    }
    SAPDBTRACE_WRITELN( Join_Trace, 3, "m_ResultKeyLen: " << m_ResultKeyLen );


    /* remove result qualification from message block */
    m_acv.GetMessBlock().FirstFree() = m_acv.GetMessBlock().ResQualPos();
    m_acv.GetMessBlock().ResQualPos() = m_acv.GetMessBlock().ResQualCount() = 0;

    if ( m_acv.a_mblock.mb_qual()->msubquery() )
    {
        tgg00_Rec* _rec = reinterpret_cast<tgg00_Rec*>( m_acv.GetAllocator().Allocate( sizeof(tgg00_Rec) ) );;
        if ( _rec )
        {
            k720_test_subquery( m_acv.TransContext(),
                *reinterpret_cast<tgg00_DataPartPtr>(m_acv.GetMessBlock().DataPtr()), 
                m_acv.GetMessBlock().DataLength(),
                m_FilterDesc, *_rec );
            _b_err = m_acv.TransContext().trError_gg00;
            m_acv.GetAllocator().Deallocate( _rec );
        }
        else
            _b_err = e_no_more_memory;
    }
    return _b_err;
}

/*!
 *
 */
tgg00_BasisError Join_JoinOperator::prepare_right_key()
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::prepare_right_key", Join_Trace, 3 );
    SAPDBERR_ASSERT_STATE( a70glob_join_strats.includes( m_RightAccessStrat ) );

    tgg00_BasisError _b_err = e_ok;
    SAPDB_Int4 _fillbyte;

    SAPDBTRACE_WRITELN( Join_Trace, 5, "join len is : " << m_JoinLenPart << "\taccess: " << m_RightAccessStrat );

    if ( a70glob_join_key_strats.includes( m_RightAccessStrat ) )
    {
        switch ( m_RightAccessStrat )
        {
            // complete key access
            case strat_join_viewkey:
            case strat_join_key_equal:
            case strat_join_all_keys_equal:
                SAPDBTRACE_WRITELN( Join_Trace, 5, "m_LastRightDefBytePos : " << m_LastRightDefBytePos );
                // set primary stopkey, last key is cutted off
                m_Stopkeys.reckey.len() = s30lnr_defbyte( m_LeftRec,
                        m_LeftRec->buf()[ m_LastRightDefBytePos - 1 ],
                        m_LastRightDefBytePos + 1,
                        m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[0].kboj_len -
                        m_LastRightDefBytePos + cgg_rec_key_offset ) +
                    m_LastRightDefBytePos - cgg_rec_key_offset;

                memcpy( &m_Stopkeys.reckey.k(),
                        ((SAPDB_Byte*)m_LeftRec) + cgg_rec_key_offset,
                        m_Stopkeys.reckey.len() );
                // ascii/ebcdic, descending conversion
                convert_key( m_Stopkeys.reckey );
                // set primary startkey
                m_Startkeys.reckey.len() = m_Stopkeys.reckey.len();
                memcpy( &m_Startkeys.reckey.k(),
                        &m_Stopkeys.reckey.k(), m_Startkeys.reckey.len() );
                break;
            // key part access
            case strat_join_key_next:
            case strat_join_key_range:
                // set primary startkey
                m_Startkeys.reckey.len() = m_JoinLenPart;
                memcpy( &m_Startkeys.reckey.k(),
                        ((SAPDB_Byte*)m_LeftRec) + cgg_rec_key_offset, m_Startkeys.reckey.len() );
                convert_key( m_Startkeys.reckey );
                // set primary stopkey
                m_Stopkeys.reckey.len() = sizeof(m_Stopkeys.reckey.k());
                memcpy( &m_Stopkeys.reckey.k(),
                        &m_Startkeys.reckey.k(), m_Startkeys.reckey.len() );
                // fill stopkey with 0xFF
                memset( ((SAPDB_Byte*)&m_Stopkeys.reckey.k()) + m_Startkeys.reckey.len(), 0xFF,
                        sizeof(m_Stopkeys.reckey.k()) - m_Startkeys.reckey.len() );
                break;
            default:
                SAPDBTRACE_WRITELN( Join_Trace, 1, "not implemented: " << __FILE__ << " line " << __LINE__ );
                _b_err = e_not_implemented;
        }
    }
    else if ( a70glob_join_inv_strats.includes( m_RightAccessStrat ) )
    {
        switch ( m_RightAccessStrat )
        {
            // complete index access
            case strat_join_inv:
            case strat_join_all_inv_equal:
                // set index stopkey
                SAPDBTRACE_WRITELN( Join_Trace, 5, "m_LastRightDefBytePos : " << m_LastRightDefBytePos );
                m_Stopkeys.listkey.len() = s30lnr_defbyte( m_LeftRec,
                        m_LeftRec->buf()[ m_LastRightDefBytePos - 1 ],
                        m_LastRightDefBytePos + 1,
                        m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[0].kboj_len -
                        m_LastRightDefBytePos + cgg_rec_key_offset ) +
                    m_LastRightDefBytePos - cgg_rec_key_offset;
                memcpy( &m_Stopkeys.listkey.k(),
                        ((SAPDB_Byte*)m_LeftRec) + cgg_rec_key_offset,
                        m_Stopkeys.listkey.len());
                SAPDBTRACE_WRITELN( Join_Trace, 5, "build inv len: " << m_Stopkeys.listkey.len()
                        << "\tis descending: " << m_IsDescendingIndex
                        << "\nindex length: " << m_RightAccessInvLen );
                // ascii/ebcdic, descending conversion
                convert_key( m_Stopkeys.listkey );
                if ( m_IsDescendingIndex &&
                     m_RightAccessInvLen > m_Stopkeys.listkey.len() )
                {
                    // we have to fill invkey for descending indexes with define
                    // bytes because descending key are blown up to full length
                    if ( (0xFF - csp_unicode_def_byte) == m_Stopkeys.listkey.k()[ m_LastRightDefBytePos - 1 - cgg_rec_key_offset ] )
                    {
                        g20unifill( sizeof(m_Stopkeys.listkey.k()),
                                &m_Stopkeys.listkey.k(),
                                m_Stopkeys.listkey.len()+1,
                                m_RightAccessInvLen - m_Stopkeys.listkey.len(),
                                csp_unicode_blank );
                        for ( int _ix = m_Stopkeys.listkey.len() + 1;
                              _ix <= m_RightAccessInvLen; ++_ix )
                            m_Stopkeys.listkey.k()[ _ix - 1 ] = 0xFF - m_Stopkeys.listkey.k()[ _ix - 1 ];
                    }
                    else
                    {
                        memset( (SAPDB_Byte*)&m_Stopkeys.listkey.k() + m_Stopkeys.listkey.len(),
                                m_Stopkeys.listkey.k()[ m_LastRightDefBytePos - cgg_rec_key_offset - 1 ],
                                m_RightAccessInvLen - m_Stopkeys.listkey.len() );
                    }
                    m_Stopkeys.listkey.len() = m_RightAccessInvLen;
                    SAPDBTRACE_WRITELN( Join_Trace, 5, "set listkey.len(): " << m_Stopkeys.listkey.len() );
                }
                // set index startkey
                m_Startkeys.listkey.len() = m_Stopkeys.listkey.len();
                memcpy( &m_Startkeys.listkey.k(),
                        &m_Stopkeys.listkey.k(), m_Startkeys.listkey.len() );
                break;
            // index part access
            case strat_join_inv_range:
                // set index startkey
                m_Startkeys.listkey.len() = m_JoinLenPart;
                memcpy( &m_Startkeys.listkey.k(),
                        ((SAPDB_Byte *)m_LeftRec) + cgg_rec_key_offset, m_JoinLenPart );
                // ascii/ebcdic, descending conversion
                convert_key( m_Startkeys.listkey );
                // set index stopkey
                m_Stopkeys.listkey.len() = sizeof(m_Stopkeys.listkey.k());
                memcpy( &m_Stopkeys.listkey.k(), &m_Startkeys.listkey.k(),
                        m_Startkeys.listkey.len() );
                _fillbyte = 0xFF;
                memset( ((SAPDB_Byte *)&m_Stopkeys.listkey.k()) + m_JoinLenPart, _fillbyte,
                        sizeof(m_Stopkeys.reckey.k()) - m_JoinLenPart );
                break;
            default:
                SAPDBTRACE_WRITELN( Join_Trace, 1, "not implemented: " << __FILE__ << " line " << __LINE__ );
                _b_err = e_not_implemented;
        }

        // set primary start/stopkey
        m_Startkeys.reckey.len() = 0;
        m_Stopkeys.reckey.len() = 0;
    }
    return _b_err;
}

/*!
 *
 */
SAPDB_Bool Join_JoinOperator::join( tgg00_BasisError& b_err )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::join", Join_Trace, 3 );

    //SAPDB_UInt2 _joincnt = m_JoinDesc.gi_linkrec.kbjr_compare_start;
    SAPDB_UInt2 _joincnt = 1;
    SAPDB_Bool _fullfilled = true;

    while ( _joincnt <= m_JoinDesc.gi_linkrec.kbjr_jointrans_cnt &&
            _fullfilled && e_ok == b_err )
    {
        tgg00_StackOpType _stackop = m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[0].kboj_op;
        tsp00_LcompResult _lc_result;
        SAPDB_UInt4 _lpos, _rpos;
        char _defbyteL, _defbyteR;

        _lpos = m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[0].kboj_recpos;
        _rpos = m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[1].kboj_recpos;
        SAPDBTRACE_WRITELN( Join_Trace, 5, "_lpos: " << _lpos << " _rpos: " << _rpos );

        // compare columns for join transition
        switch ( _stackop )
        {
            case op_like:
            case op_not_like:
                {
                    tgg00_Rec *_like_pattern = 0;
                    SAPDB_Int4 _change_pos, _ix;
                    if ( m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].
                         kbji_parts[0].kboj_reverse_operands )
                    {
                        SAPDBTRACE_WRITELN( Join_Trace, 5, "reverse operands " << "\tleft record changed: " << m_LeftRecChanged );
                        if ( ! m_LeftRecChanged )
                        {
                            m_LeftRecChanged = true;
                            _like_pattern = m_LeftRec;
                            _change_pos   = _lpos;
                            _ix           = 0;
                        }
                    }
                    else
                    {
                        _like_pattern = m_RightRec;
                        _change_pos   = _rpos;
                        _ix           = 1;
                    }
                    pasbool _ok;
                    if ( _like_pattern )
                    {
                        if ( csp_unicode_def_byte == _like_pattern->buf()[ _change_pos - 1 ] )
                        {
                            s49uni_build_pattern(
                                  _like_pattern, _change_pos + 1,
                                  _change_pos + m_JoinDesc.gi_linkrec.
                                  kbjr_jarr[_joincnt-1].kbji_parts[ _ix ].kboj_len,
                                  (char *)csp_unicode_blank, false,
                                  sqlm_ansi, _ok );
                        }
                        else
                        {
                            s49build_pattern(
                                  _like_pattern,
                                  ( csp_ascii_blank == _like_pattern->buf()[ _change_pos - 1 ] ),
                                  _change_pos + 1, _change_pos +
                                  m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].
                                  kbji_parts[ _ix ].kboj_len,
                                  bsp_c1, false, false,
                                  sqlm_ansi, _ok );
                        }
                        if ( ! _ok )
                        {
                            if ( sqlm_ansi == m_acv.GetMessBlock().SqlMode() )
                                m_acv.TransContext().trError_gg00 = e_illegal_escape_sequence;
                            else
                                m_acv.TransContext().trError_gg00 = e_invalid_pattern;
                        }
                    }
                }
                // fall through
            case op_sounds:
            case op_not_sounds:
                {
                    SAPDB_Int4 _compare_ok = 0;
                    tkb07_buffer_description _workbuf;

                    _workbuf.buffer_addr = m_acv.a_work_buf_addr;
                    _workbuf.buffer_size = m_acv.a_work_buf_size;
                    _workbuf.buffer_len  = 0;
                    if ( m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].
                         kbji_parts[0].kboj_reverse_operands )
                    {
                        k71join_comparison(m_SelFields, _stackop, _workbuf,
                              m_RightRec, _rpos,
                              m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[ 1 ].kboj_len,
                              m_LeftRec, _lpos,
                              m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[ 0 ].kboj_len,
                              _compare_ok );
                    }
                    else
                    {
                        k71join_comparison(m_SelFields, _stackop, _workbuf,
                              m_LeftRec, _lpos,
                              m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[ 0 ].kboj_len,
                              m_RightRec, _rpos,
                              m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[ 1 ].kboj_len,
                              _compare_ok );
                    }
                    _fullfilled = ( cgg04_is_true == _compare_ok );
                }
                break;
            default:
                SAPDB_Bool _restore_defbyte = false;

                if ( 1 == _joincnt &&
                     m_LastRightDefBytePos != -1 &&
                     (SAPDB_Int1)m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].
                     kbji_parts[1].kboj_op != cgg07_left_len_eq_right_len )
                {
                    SAPDBTRACE_WRITELN( Join_Trace, 5, "adjust define bytes\t" << "m_LastRightDefBytePos: " << m_LastRightDefBytePos );
                    // possibly we have to compare more than one column
                    _restore_defbyte = true;
                    _defbyteL = m_LeftRec->buf()[_lpos-1];
                    _defbyteR = m_RightRec->buf()[_rpos-1];
                    SAPDBTRACE_WRITELN( Join_Trace, 5, "_defbyteL: " << _defbyteL << "\t_defbyteR: " << _defbyteR );
                    m_LeftRec->buf()[_lpos-1] = m_LeftRec->buf()[ m_LastRightDefBytePos - 1 ];
                    m_RightRec->buf()[_rpos-1] = m_RightRec->buf()[ m_LastRightDefBytePos - 1 ];
                }
                s30luc( m_LeftRec, _lpos,
                        m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[0].kboj_len,
                        m_RightRec, _rpos,
                        m_JoinDesc.gi_linkrec.kbjr_jarr[_joincnt-1].kbji_parts[1].kboj_len,
                        _lc_result );
                SAPDBTRACE_IF( Join_Trace, 3,
                t01comp_result( td_always, "comp result ", _lc_result ));
                if ( _restore_defbyte )
                {
                    m_LeftRec->buf()[_lpos-1]  = _defbyteL;
                    m_RightRec->buf()[_rpos-1] = _defbyteR;
                }
                switch( _stackop )
                {
                    case op_eq:
                        if ( l_equal == _lc_result )
                            _fullfilled = true;
                        else
                            _fullfilled = false;
                        break;
                    case op_le:
                        if ( l_equal == _lc_result || l_less == _lc_result )
                            _fullfilled = true;
                        else
                            _fullfilled = false;
                        break;
                    case op_lt:
                        if ( l_less == _lc_result )
                            _fullfilled = true;
                        else
                            _fullfilled = false;
                        break;
                    case op_ge:
                        if ( l_equal == _lc_result || l_greater ==_lc_result )
                            _fullfilled = true;
                        else
                            _fullfilled = false;
                        break;
                    case op_gt:
                        if ( l_greater == _lc_result )
                            _fullfilled = true;
                        else
                            _fullfilled = false;
                        break;
                    case op_ne :
                        if ( l_equal != _lc_result && l_undef != _lc_result )
                            _fullfilled = true;
                        else
                            _fullfilled = false;
                        break;
                    default:
                        _fullfilled = false;
                        SAPDBTRACE_WRITELN( Join_Trace, 1, "operator " << _stackop << " not implemented: " << __FILE__ << " line " << __LINE__ );
                        b_err = e_not_implemented;
                }
        }
        ++_joincnt;
    }
    SAPDBTRACE_WRITELN( Join_Trace, 3, "is join: " << _fullfilled );
    if ( _fullfilled )
        b_err = build_join_rec();

    return _fullfilled;
}

/*!
 *
 */
void Join_JoinOperator::convert_key( tgg00_Lkey& key ) const
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::convert_key", Join_Trace, 3 );
    SAPDBTRACE_WRITELN( Join_Trace, 3, "m_RightRecConvArrCnt " << m_RightRecConvArrCnt );
    SAPDBTRACE_IF( Join_Trace, 5, t01buf( td_always, key.k(), 1, key.len() <= 100 ? key.len() : 100 ));

    // take conversions from m_RightRecConvArr and adjust key
    for (SAPDB_UInt2 _i = 0; _i < m_RightRecConvArrCnt; ++_i)
    {
        SAPDBTRACE_WRITELN( Join_Trace, 3, "ct_epos: " << m_RightRecConvArr[ _i ].ct_epos << "\tct_elenvar: " << m_RightRecConvArr[ _i ].ct_elenvar << "\tct_codeno: " << m_RightRecConvArr[ _i ].ct_codeno );
        if ( m_RightRecConvArr[ _i ].ct_epos - cgg_rec_key_offset - 1 <
             m_JoinLenPart )
        {
            if ( m_RightRecConvArr[ _i ].ct_codeno != 0 )
                s30map( g02codetables.tables[ m_RightRecConvArr[ _i ].ct_codeno - 1 ],
                        key.k(),
                        m_RightRecConvArr[ _i ].ct_epos - cgg_rec_key_offset,
                        key.k(),
                        m_RightRecConvArr[ _i ].ct_epos - cgg_rec_key_offset,
                        m_RightRecConvArr[ _i ].ct_elenvar );
            if ( m_RightRecConvArr[ _i ].ct_is_desc )
            {
                SAPDBTRACE_WRITELN( Join_Trace, 5, "convert desc" );
                // build revers index key for descending indexes
                for ( SAPDB_Int4 _jx = m_RightRecConvArr[ _i ].ct_epos - cgg_rec_key_offset;
                      _jx <= m_RightRecConvArr[ _i ].ct_epos + m_RightRecConvArr[ _i ].ct_elenvar - cgg_rec_key_offset - 1;
                      ++_jx )
                {
                    key.k()[ _jx - 1 ] = char(0xFF - key.k()[ _jx - 1 ]);
                }
            }
        }
    }
    SAPDBTRACE_IF( Join_Trace, 5, t01buf( td_always, key.k(), 1, key.len() <= 100 ? key.len() : 100 ));
}

/*!
 *
 */
tgg00_BasisError Join_JoinOperator::build_join_rec( mt_build_type build_type )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::build_join_rec", Join_Trace, 3 );
    
    SAPDBTRACE_IF( Join_Trace, 5, SAPDB_MemFillNoCheck( m_JoinRec, ':', m_JoinRecBufferSize ) );
    SAPDBTRACE_WRITELN( Join_Trace, 5, "build for " << ((join_result == build_type) ? "join result" : "join aux file")  );

    tgg00_BasisError _b_err = e_ok;
    SAPDB_Int4 _ix;
    SAPDB_UInt4 _offset = 0;
    
    if ( join_auxfile == build_type )
    {
        _offset = m_AuxRightOuterKeyLen;
        m_JoinRec->recLen_gg00()    = join_reclen() + m_AuxRightOuterKeyLen;
        m_JoinRec->recKeyLen_gg00() = m_AuxRightOuterKeyLen;
        // write key
        SAPDB_MemCopyNoCheck( &m_JoinRec->recBody_gg00(), (SAPDB_Byte*)&m_RightRec->buf() + (m_RightOp->GetRecordLength() - m_AuxRightOuterKeyLen), m_AuxRightOuterKeyLen );
    }
    else
    {
        m_JoinRec->recLen_gg00()    = join_reclen();
        m_JoinRec->recKeyLen_gg00() = join_keylen();
    }
    m_JoinRec->recVarcolOffset_gg00() = 0;
    m_JoinRec->recVarcolCnt_gg00()    = 0;

    // move from left record (i-1. temp result) to join record (i. temp result)
#ifdef SAPDB_SLOW
    if (m_JoinDesc.gi_n_pos_s.rposcnt + m_JoinDesc.gi_n_pos_s.lposcnt >=
        m_JoinDesc.gi_n_pos_s.rposcnt + 1 )
        SAPDBTRACE_WRITELN( Join_Trace, 5, "move i-1. temp result --> i. temp result" );
#endif
    for ( _ix = m_JoinDesc.gi_n_pos_s.rposcnt + 1; 
         _ix <= m_JoinDesc.gi_n_pos_s.rposcnt + 
         m_JoinDesc.gi_n_pos_s.lposcnt; 
         ++_ix )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 5, "onfrom: " \
                << m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onfrom 
                << " onto: " << m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset 
                << " onlen: " << m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen);
        SAPDBERR_ASSERT_STATE( m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + 
                _offset + m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen - 1 <= 
                m_JoinRecBufferSize );

        SAPDB_RangeMove( __FILE__, 1,
                sizeof(tgg00_Rec), m_JoinRecBufferSize,
                m_LeftRec, m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onfrom,
                m_JoinRec, m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset,
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen, _b_err );
        SAPDBTRACE_IF( Join_Trace, 5, 
        t01buf( td_always, m_JoinRec, 
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset,
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset + 
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen - 1));
    }
    
    // move from right record (i. base table) to join record (i. temp result)
#ifdef SAPDB_SLOW
    if ( m_JoinDesc.gi_n_pos_s.rposcnt >= 1 )
        SAPDBTRACE_WRITELN( Join_Trace, 5, "move i. table --> i. temp result" );
#endif
    for ( _ix = 1; _ix <= m_JoinDesc.gi_n_pos_s.rposcnt; ++_ix )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 5, "onfrom: " \
                << m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onfrom 
                << " onto: " << m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset 
                << " onlen: " << m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen);
        SAPDBERR_ASSERT_STATE( m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + 
                _offset + m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen - 1 <= 
                m_JoinRecBufferSize );
        SAPDB_RangeMove( __FILE__, 2,
                sizeof(tgg00_Rec), m_JoinRecBufferSize,
                m_RightRec, abs( m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onfrom ),
                m_JoinRec, m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset,
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen, _b_err );
        SAPDBTRACE_IF( Join_Trace, 5, 
        t01buf( td_always, m_JoinRec, 
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset,
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onto + _offset +
                m_JoinDesc.gi_n_pos_s.posarr[_ix-1].onlen - 1));
    }

    // move from join record (i. temp result) to join record (i. temp result)
#ifdef SAPDB_SLOW
    if ( m_JoinDesc.gi_copy_info.n_j_cnt >= 1 )
        SAPDBTRACE_WRITELN( Join_Trace, 5, "move i. temp result --> i. temp result" );
#endif
    for ( _ix = 1; _ix <= m_JoinDesc.gi_copy_info.n_j_cnt; ++_ix )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 5, "jfrom: " \
                << m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jfrom + _offset 
                << " jto: " << m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jto + _offset 
                << " jlen: " << m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jlen );
        SAPDBERR_ASSERT_STATE( m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jto + 
                _offset + m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jlen - 1 <= 
                m_JoinRecBufferSize );
        if ( m_JoinDesc.gi_copy_info.n_j_arr[ _ix-1 ].jfrom <= 0 )
        {
            for ( SAPDB_Int4 _iy = 0; 
                 _iy <= m_JoinDesc.gi_copy_info.n_j_arr[ _ix-1 ].jlen - 1; ++_iy )
            {
                m_JoinRec->buf()
                    [ m_JoinDesc.gi_copy_info.n_j_arr[ _ix-1 ].jto + _offset + _iy - 1] = 
                    char (0xFF - m_JoinRec->buf()
                    [ abs( m_JoinDesc.gi_copy_info.n_j_arr[ _ix-1 ].jfrom ) + _offset + _iy - 1]);
            }
        }
        else
        {
            if ( m_JoinDesc.gi_copy_info.n_j_arr[ _ix-1 ].jfrom != 
                 m_JoinDesc.gi_copy_info.n_j_arr[ _ix-1 ].jto )
            {
                SAPDB_RangeMove( __FILE__, 3,
                        m_JoinRecBufferSize, m_JoinRecBufferSize,
                        m_JoinRec, m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jfrom + _offset,
                        m_JoinRec, m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jto + _offset,
                        m_JoinDesc.gi_copy_info.n_j_arr[_ix-1].jlen, _b_err );
            }
        }
    }
    // write defined result counter
#ifdef SAPDB_SLOW
    if ( join_auxfile != build_type && Join_Trace.TracesLevel( 3 ) )
    {
        SAPDB_MemFillNoCheck( (SAPDB_Byte*)&m_JoinRec->recKey_gg00().k() + m_JoinRec->recKeyLen_gg00() - RESCNT_MXGG04, 0x99, RESCNT_MXGG04 );
    }
#endif
    return _b_err;
}

/*!
 *
 */
SAPDB_Bool Join_JoinOperator::filter( tgg00_Rec* recptr, tgg00_BasisError& b_err )
{
    // filter and transform (i.e. functions) record
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::filter", Join_Trace, 3 );
    tgg00_StackEntry *_err_st_entry;
    pasbool _unqualified = false;

    m_SelFields.sfp_m_result_addr() = reinterpret_cast<tsp00_MoveObjPtr>(recptr);
    m_SelFields.sfp_rec_addr()      = reinterpret_cast<tsp00_BufAddr>(recptr);
    m_SelFields.sfp_rec_len()       = recptr->recLen_gg00();
    m_SelFields.sfp_rec_key_len()   = recptr->recKeyLen_gg00();
    m_SelFields.sfp_result_length() = 0;
    m_SelFields.sfp_m_result_len()  = 0;
    m_SelFields.sfp_m_result_cnt()  = 1;

    SAPDBTRACE_WRITELN( Join_Trace, 7, "before filter: " << m_OutputFilter );
    SAPDBTRACE_IF( Join_Trace, 7,
    t01buf( td_always, recptr, 1, recptr->recLen_gg00() <= 300 ? recptr->recLen_gg00() : 300 ));
    if ( m_OutputFilter )
    {
        tsp00_Buf _resbuf;
        SAPDBTRACE_IF( Join_Trace, 5, memset( &_resbuf, ':', sizeof(_resbuf) ));
        m_SelFields.sfp_m_result_size() = sizeof(_resbuf);
        m_SelFields.sfp_m_result_addr() = reinterpret_cast<tsp00_MoveObjPtr>(&_resbuf);
        SAPDB_Int4 _old_result_len = m_SelFields.sfp_result_length();
        m_acv.TransContext().trError_gg00 = e_ok;

        k71qual_handling( m_acv.TransContext(), m_SelFields,
                m_FilterDesc.mview_cnt > 0, false, m_FilterDesc,
                _err_st_entry, _unqualified );
        // write result length
        recptr->recLen_gg00() = GetRecordLength();
        recptr->recKeyLen_gg00() = GetKeyLength();

        if ( _unqualified )
            m_SelFields.sfp_result_length() = _old_result_len;
        else
        {
            if ( -1 == (*m_Filter)[ m_FilterDesc.mqual_pos - 1 ].ecol_pos() )
            {
                SAPDBTRACE_WRITELN( Join_Trace, 7, "transfer constant expression" );
                /* we don't have complete output qualification of record */
                /* ak684transfer_const_expressions() */
                /* ak684out_filter_stack()           */
                for ( SAPDB_Int4 _ix = m_FilterDesc.mqual_pos + 1;
                        _ix <= (*m_Filter)[ m_FilterDesc.mqual_pos - 1 ].epos() +
                        m_FilterDesc.mqual_pos - 2; ++_ix )
                {
                    if ( st_output == (*m_Filter)[ _ix - 1 ].etype() )
                    {
                        SAPDBTRACE_WRITELN( Join_Trace, 5, "pos: " << (*m_Filter)[ _ix - 1 ].epos() << " len: " << (*m_Filter)[ _ix - 1 ].elen_var());
                        memcpy(
                                (SAPDB_Byte*)recptr + (*m_Filter)[ _ix - 1 ].epos() - 1,
                                (SAPDB_Byte*)&_resbuf + (*m_Filter)[ _ix - 1 ].epos() - 1,
                                (*m_Filter)[ _ix - 1 ].elen_var()
                                );
                    }
                }
            }
            else
            {
                memcpy( (SAPDB_Byte*)recptr + cgg_rec_key_offset,
                        (SAPDB_Byte*)&_resbuf + cgg_rec_key_offset,
                        recptr->recLen_gg00() );
            }
            // write defined result counter
            SAPDBTRACE_IF( Join_Trace, 3, memset( (SAPDB_Byte*)&recptr->recKey_gg00().k() + recptr->recKeyLen_gg00() - RESCNT_MXGG04, 0x99, RESCNT_MXGG04 ) );
        }
    }
    else
    {
        m_SelFields.sfp_m_result_size() = m_JoinRecBufferSize;
        m_SelFields.sfp_m_result_addr() = reinterpret_cast<tsp00_MoveObjPtr>(recptr);

        k71qual_handling( m_acv.TransContext(), m_SelFields,
                m_FilterDesc.mview_cnt > 0, false, m_FilterDesc,
                _err_st_entry, _unqualified );
        // write result length
        recptr->recLen_gg00() = GetRecordLength();
        recptr->recKeyLen_gg00() = GetKeyLength();
    }
    SAPDBTRACE_WRITELN( Join_Trace, 3, "k71qual_handling: " << SAPDBTrace::BasisError(m_acv.TransContext().trError_gg00) );

#ifdef SAPDB_SLOW
    if ( e_ok == m_acv.TransContext().trError_gg00 )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 7, "after filter: " );
        SAPDBTRACE_IF( Join_Trace, 7,
        t01buf( td_always, recptr, 1, recptr->recLen_gg00() <= 300 ? recptr->recLen_gg00() : 300 ));
    }
#endif

    switch ( m_acv.TransContext().trError_gg00 )
    {
        case e_ok:
            return true; break;
        case e_qual_violation:
            m_acv.TransContext().trError_gg00 = e_ok;
            return false; break;
        default:
            b_err = m_acv.TransContext().trError_gg00;
            return false;
    }
}

/*!
 *
 */
tgg00_BasisError Join_JoinOperator::get_roj_record( tgg00_Rec*& recptr )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::get_roj_record", Join_Trace, 3 );
    SAPDB_Bool _qualified;

    // potential right outer join accelaration: write only filtered right outer join records!!

    if ( never_scanned == m_TableForRightOuterJoinScanStat )
    {
        // left table delivers no records, therfore m_AuxRightOuterJoinFile
        // is empty but we have to select whole right table
        tgg00_BasisError _b_err = e_ok;

        if ( ! m_RightOp->IsOpened() ) _b_err = m_RightOp->Open();

        /* !!! RIGHT OUTER JOIN for empty left table !!! */
        // tie NULL record on left side
        m_LeftOp->GetNullRecord( m_LeftRec );

        do
        {
            if ( e_ok == _b_err )
                _b_err = m_RightOp->Next( m_RightRec );

            if ( e_ok == _b_err )
            {
                _qualified = true;
                _b_err = build_join_rec( );
                if ( m_Filter ) _qualified = filter( m_JoinRec, _b_err );
            }
        }
        while ( e_ok == _b_err && ! _qualified );

        if  ( e_no_next_record == _b_err)
        {
            m_RightOp->Close();
            m_GetLeftTupel = true;
        }
        recptr = m_JoinRec;
        return _b_err;
    }
    else
    {
        tgg00_BdSetResultRecord _set_result;
        _set_result.bd_key_check_len = 0;
        _set_result.bd_max_rec_cnt   = 1;
        _set_result.bd_max_fill_len  = m_JoinRecBufferSize;
        _set_result.bd_next          = true;
        _set_result.bd_drop_page     = false;

        do
        {
            // fill m_JoinRec with unmatched right record
            b07cnext_record( m_acv.TransContext(), m_AuxRightOuterJoinFile,
                    m_AuxRightOuterJoinKey, _set_result, m_AuxRightOuterJoinFilePos,
                    m_JoinRec );
            if ( e_key_not_found == m_acv.TransContext().trError_gg00 )
                m_acv.TransContext().trError_gg00 = e_ok;
            if ( e_ok == m_acv.TransContext().trError_gg00 )
            {
                SAPDBTRACE_IF( Join_Trace, 3,
                t01buf( td_always, m_JoinRec, 1, m_JoinRec->recLen_gg00() <= 300 ? m_JoinRec->recLen_gg00() : 300 ));
                // skip over key of m_AuxRightOuterJoinFile
                recptr = reinterpret_cast<tgg00_Rec*>( (SAPDB_Byte*) m_JoinRec + m_AuxRightOuterKeyLen );
                recptr->recLen_gg00()    = join_reclen();
                recptr->recKeyLen_gg00() = join_keylen();
                recptr->recVarcolOffset_gg00() = 0;
                recptr->recVarcolCnt_gg00()    = 0;
                _qualified = true;
                if ( m_Filter ) _qualified = filter( recptr, m_acv.TransContext().trError_gg00 );
            }
        }
        while ( e_ok == m_acv.TransContext().trError_gg00 && ! _qualified );

        return m_acv.TransContext().trError_gg00;
    }
    // never reach this code
}

/*!
 *
 */
tgg00_BasisError Join_JoinOperator::update_roj_auxfile( mt_roj_action action )
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::update_roj_auxfile", Join_Trace, 3 );

    SAPDBTRACE_WRITELN( Join_Trace, 3, "action: " << (( add_record == action ) ? "add" : "delete" ));

    if ( add_record == action )
    {
        // add record with no join to auxiliary file
        // temporary tie NULL record on m_LeftRec
        tgg00_Rec * _tmp = m_LeftRec;
        m_LeftOp->GetNullRecord( m_LeftRec );
        m_acv.TransContext().trError_gg00 = build_join_rec( join_auxfile );
        m_LeftRec = _tmp;
        SAPDBTRACE_IF( Join_Trace, 3, 
        t01buf( td_always, m_JoinRec, 1, m_JoinRec->recLen_gg00() <= 300 ? m_JoinRec->recLen_gg00() : 300 ));
        b07cadd_record( m_acv.TransContext(), m_AuxRightOuterJoinFile, *m_JoinRec );
    }
    else
    {
        // remove joined record from auxiliary file
        tgg00_Lkey _key;
        _key.len() = m_AuxRightOuterKeyLen;
        memcpy( &_key.k(), (SAPDB_Byte*)&m_RightRec->buf() + m_RightOp->GetRecordLength() - m_AuxRightOuterKeyLen, m_AuxRightOuterKeyLen );
        SAPDBTRACE_IF( Join_Trace, 3,
        t01key( td_always, "key         ", _key ));
        b07cdel_record( m_acv.TransContext(), m_AuxRightOuterJoinFile, _key );
        // ignore already joined records
        if ( e_key_not_found == m_acv.TransContext().trError_gg00 )
            m_acv.TransContext().trError_gg00 = e_ok;

    }
    return m_acv.TransContext().trError_gg00;
}

/*!
 *
 */
SAPDB_UInt4 Join_JoinOperator::get_AuxRightOuterKeyLen()
{
    SAPDBTRACE_METHOD_DEBUG( "Join_JoinOperator::get_AuxRightOuterKeyLen", Join_Trace, 3 );
    SAPDBERR_ASSERT_STATE( m_acv.GetMessBlock().HasOutputCols() );

    SAPDB_Int4 _stop = m_acv.GetMessBlock().QualPos();
    SAPDB_Int4 _i = m_acv.GetMessBlock().StackEntry( m_acv.GetMessBlock().QualPos()-1 ).epos() +
        m_acv.GetMessBlock().QualPos() - 1;

    // loop over output columns, look for output genertated by ak684key_columns_stack()
    while ( _i > _stop )
    {
        --_i;
        if ( ( st_varkey == m_acv.GetMessBlock().StackEntry( _i-1 ).etype() ||
               st_fixkey == m_acv.GetMessBlock().StackEntry( _i-1 ).etype()) &&
             st_output == m_acv.GetMessBlock().StackEntry( (_i-1)+1 ).etype() &&
             op_o_output_outer_join == m_acv.GetMessBlock().StackEntry( (_i-1)+1 ).eop_out() )
        {
            SAPDBTRACE_WRITELN( Join_Trace, 5, "VAR KEY at pos " << _i << "\tkey len " << m_acv.GetMessBlock().StackEntry( _i-1 ).ecol_pos() );
            return m_acv.GetMessBlock().StackEntry( _i-1 ).ecol_pos();
        }
    }
    a07_b_put_error( m_acv, e_key_does_not_exist, 1 );
    SAPDBERR_ASSERT_STATE( false );
    return 0;
}

IOperator* Join_JoinOperator::AccessOperatorFactory(
    SQLMan_Context&                acv,
    const SAPDB_UInt4&             buffersize,
    const SAPDB_UInt4&             expectedRecordCount,
    const SAPDB_Int4&              maxServerTasks,
    const SAPDB_UInt2              tabno)
{
    SAPDBTRACE_METHOD_DEBUG(
        "Join_JoinOperator::AccessOperatorFactory",
        Join_Trace, 3 );

    Join_AccessDesc accessDesc( acv );
    Join_IAccessOperator*     accessOp     = 0;
    Join_HashAccessOperator*  hashAccessOp = 0;
    SAPDB_Byte*               recordBuffer = 0;
    SAPDB_UInt                recBufSize   = buffersize;

    SAPDBTRACE_WRITELN( Join_Trace, 3, "access strategy : " << accessDesc.Strategy()->str_strategy << 
            "\t buffersize: " << buffersize );

    if ( ! ( accessDesc.IsKnownAccess() || accessDesc.IsJoinAccess() ))
    {
        m_acv.ThrowError( e_unknown_strategy );
        return 0;
    }

    if ( strat_inv_range_merge_fetch == accessDesc.Strategy()->str_strategy )
    {
        m_acv.ThrowError( e_not_implemented );
        return 0;
    }


    if  ( strat_no_result == accessDesc.Strategy()->str_strategy )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_NopAccessOperator" );
        accessOp = new ( m_acv.GetAllocator())
            Join_NopAccessOperator(
                acv,
                tabno,
                !m_JoinDesc.gi_linkrec.kbjr_right_oj,
                recordBuffer,
                recBufSize );
    } else if ( strat_more_than_one == accessDesc.Strategy()->str_strategy || 
                strat_catalog       == accessDesc.Strategy()->str_strategy ) 
    {
        SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_LegacyOperator" );
        accessOp = new (m_acv.GetAllocator())
            Join_LegacyAccessOperator(
                acv,
                tabno,
                recordBuffer,
                recBufSize );
    } else if  ( accessDesc.Strategy()->str_access_mod.includes( strmod_sorted ) )
    {
        SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_SortOperator" );
        if  ( strat_more_than_one == accessDesc.OneTableStrategy()->str_strategy ||
              strat_catalog       == accessDesc.OneTableStrategy()->str_strategy )
            accessOp = new (m_acv.GetAllocator())
                Join_LegacyAccessOperator(
                    acv,
                    tabno,
                    recordBuffer,
                    recBufSize );
        else
            accessOp = new (m_acv.GetAllocator())
                Join_SortOperator(
                    acv,
                    tabno,
                    recordBuffer,
                    recBufSize );
    } else
    {
        if ( accessDesc.UseHashJoin() && a01use_join_hashtable ) 
        {
            SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_HashOperator" );
            // create hash access operator
            SAPDB_UInt4 expectedTableSize =
                static_cast<SAPDB_UInt4>(
                    expectedRecordCount * accessDesc.GetRecordLength() * 1.1 );
            if ( Join_Trace.TracesLevel( 1 ) )
            {
                SAPDBTrace_Stream traceStream( &Join_Trace.GetTracePrinter() );
                traceStream
                    << "Join_JoinOperator - hash access:" << NewLine
                    << "  Expected record count: " << expectedRecordCount << NewLine
                    << "  Record length        : "
                    << accessDesc.GetRecordLength() << NewLine
                    << "  Expected table size  : " << expectedTableSize << NewLine;
                }
            if ( expectedTableSize < BUF8K_MXSP00 ) {
                SAPDBTRACE_WRITELN(
                    Join_Trace, 1,
                    "Corrected expected table size from "
                    << expectedTableSize << " to "
                    << BUF8K_MXSP00 << " bytes" );
                expectedTableSize = BUF8K_MXSP00;
            }
            // m_JoinDesc.gi_linkrec.kbjr_jarr[0] contains
            // length of join transition key that can be used
            // during bd access
            SAPDB_Int2 joinTransitionKeyLength =
                m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[1]
                .kboj_recpos
                + m_JoinDesc.gi_linkrec.kbjr_jarr[0].kbji_parts[1]
                .kboj_len - cgg_rec_key_offset - 1;
            SAPDB_Int2 lastKeyColStartPos = m_LastRightDefBytePos
                - cgg_rec_key_offset - 1;
            Join_HashAccessOperator::UniqueKeys uniqueKeys =
                (accessDesc.Strategy()->str_strategy == strat_join_key_equal)
                || (accessDesc.Strategy()->str_strategy == strat_join_all_keys_equal)
                || (accessDesc.Strategy()->str_strategy == strat_join_inv)
                || (accessDesc.Strategy()->str_strategy == strat_join_all_inv_equal)
                ? Join_HashAccessOperator::NoDuplicateKeys
                : Join_HashAccessOperator::AllowDuplicateKeys;

            Join_HashAccessOperator::KeySizes keySizes = 
                (accessDesc.Strategy()->str_strategy == strat_join_key_next)
                || (accessDesc.Strategy()->str_strategy == strat_join_key_range)
                ? Join_HashAccessOperator::FixSizedKeys
                : Join_HashAccessOperator::VarSizedKeys;

            hashAccessOp =
                new (m_acv.GetAllocator()) Join_HashAccessOperator(
                    acv,
                    tabno,
                    a70glob_join_key_strats.includes( accessDesc.Strategy()->str_strategy ),
                    uniqueKeys,
                    keySizes,
                    expectedTableSize,
                    joinTransitionKeyLength,
                    lastKeyColStartPos);
            if ( hashAccessOp ) {
                // make access operator use hash record buffer and
                // size if it could be allocated
                recordBuffer = hashAccessOp->GetRecordBuffer();
                if ( 0 != recordBuffer ) {
                    recBufSize = expectedTableSize;
                }
            }
        }

        if ( accessDesc.IsIndexAccess() || accessDesc.IsJoinInvAccess() ) 
        {
            SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_InvAccessOperatorEx" );
            if ( accessDesc.UseParallelIndexAccess() && 
                 maxServerTasks > 0 )
            {
                accessOp = new (m_acv.GetAllocator())
                    Join_InvAccessOperatorEx(
                        acv,
                        tabno,
                        !m_JoinDesc.gi_linkrec.kbjr_right_oj,
                        recordBuffer,
                        recBufSize,
                        maxServerTasks );
            } else {
                SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_InvAccessOperator" );
                accessOp = new (m_acv.GetAllocator())
                    Join_InvAccessOperator(
                        acv,
                        tabno,
                        !m_JoinDesc.gi_linkrec.kbjr_right_oj,
                        recordBuffer,
                        recBufSize );
            }
        } else 
        {
            SAPDBTRACE_WRITELN( Join_Trace, 3, "create Join_TableAccessOperator" );
            accessOp = new (m_acv.GetAllocator())
                Join_TableAccessOperator(
                    acv,
                    tabno,
                    !m_JoinDesc.gi_linkrec.kbjr_right_oj,
                    recordBuffer,
                    recBufSize );
        }
    }

    if ( accessOp 
         && (( strat_join_key_equal
               == accessDesc.Strategy()->str_strategy)
             ||
             ( strat_join_all_keys_equal
               == accessDesc.Strategy()->str_strategy)
             ||
             ( strat_join_viewkey
               == accessDesc.Strategy()->str_strategy)) ) {
        accessOp->SetMaximumRowsReadAtATime( 1 );
    }

    if ( hashAccessOp && accessOp ) 
    {
        hashAccessOp->SetAccessOperator( accessOp );
        accessOp = hashAccessOp;
    }

    if ( ! accessOp ) 
    {
        destroy( hashAccessOp,  m_acv.GetAllocator() );
        m_acv.TransContext().trError_gg00 = e_no_more_memory;
        if ( Join_Trace.TracesLevel( 1 ) )
        {
            SAPDBTrace_Stream traceStream( &Join_Trace.GetTracePrinter() );
            traceStream
                << "Join_JoinOperator:: access operator creation failed: no more memory";
        }
    }
    return accessOp;
}
