.nf
 
 .nf
 
    ========== licence begin  GPL
    Copyright (c) 2000-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
.fo
 
 
.fo
*****************************************************
Copyright (c) 2000-2005 SAP AG
SAP Database Technology
 
Release :      Date : 2000-11-22
*****************************************************
modname : VAK680
changed : 2000-11-22
module  : Join_Select
 
Author  : ElkeZ
Created : 1985-10-16
*****************************************************
 
Purpose : The module determines the most favourable order for
          table processing and sends the Mess-Buffers that have
          been built by VAK69 to KB
 
Define  :
 
        PROCEDURE
              a680_join (
                    VAR acv                 : tak_all_command_glob;
                    VAR dmli                : tak_dml_info;
                    VAR res_tree            : tgg00_FileId;
                    VAR ak_strat_interface  : tak71_strat_rec;
                    last_pars_part          : boolean;
                    VAR jvrec               : tak68_joinview_rec);
 
        PROCEDURE
              a680search_sequence (
                    VAR acv                 : tak_all_command_glob;
                    VAR dmli                : tak_dml_info;
                    VAR parsk               : tak_parskey;
                    VAR eq_rec              : tak68_eq_record;
                    VAR res_tree            : tgg00_FileId;
                    VAR series              : tak68_sequence;
                    VAR ak_strat_interface  : tak71_strat_rec;
                    VAR jvrec               : tak68_joinview_rec;
                    use_operator_join       : boolean);
 
        PROCEDURE
              a680_first_table_cost (
                    VAR sequence_info    : tak68_sequence_info;
                    VAR table_stats      : tak68_table_stats;
                    first_table          : tsp00_Int2;
                    VAR res_info         : tak68_result_info);
 
        PROCEDURE
              a680standard_cost (
                    VAR acv            : tak_all_command_glob;
                    VAR dmli           : tak_dml_info;
                    config             : tak_sysbufferaddress;
                    VAR table_stats    : tak68_table_stats;
                    jtrans             : tak68_join_transitions;
                    VAR jinfos         : tak68_joininfos;
                    VAR res_info       : tak68_result_info;
                    VAR mul_tabs       : tak68_mult_tabs;
                    VAR lastsuccession : tak68_lastsuccession;
                    VAR lowest_costs   : tsp00_Longreal;
                    VAR sequence_info  : tak68_sequence_info;
                    succ_length        : tsp00_Int2;
                    final_call         : boolean);
 
        PROCEDURE
              a680next_join_eval (
                    VAR acv                 : tak_all_command_glob;
                    VAR jtrans              : tak68_join_transition;
                    VAR table_stat          : tak68_one_table_stat;
                    VAR counted_multiplier  : tsp00_Longreal;
                    VAR reverse_multiplier  : tsp00_Longreal;
                    VAR newsum              : tsp00_Longreal;
                    VAR newpages            : tsp00_Longreal;
                    old_recs_per_respage    : tsp00_Int4;
                    recs_per_respage        : tsp00_Int4;
                    use_operator_join       : boolean);
 
        PROCEDURE
              a680multiplier_get (
                    VAR acv             : tak_all_command_glob;
                    base_ptr            : tak_sysbufferaddress;
                    VAR col_info        : tak00_columninfo;
                    jpropset            : tak_jcolpropset;
                    VAR jmultiplier     : tsp00_Int4;
                    VAR do_col_upd_stat : boolean);
 
        FUNCTION
              a680is_outer_predicate (
                    VAR dmli   : tak_dml_info;
                    st_pos     : tsp00_Int2) : boolean;
 
        PROCEDURE
              a680rollback_temp_jinfo(
                    VAR acv      : tak_all_command_glob;
                    VAR dmli     : tak_dml_info;
                    VAR parsk    : tak_parskey (*ptocConst*);
                    VAR jv_tabid : tgg00_Surrogate (*ptocConst*);
                    info_cnt     : tsp00_Int2;
                    seqsearch_for_exec  : boolean);
 
.CM *-END-* define --------------------------------------
Use     :
 
        FROM
              Scanner : VAK01;
 
        VAR
              a01join_clust_read        : boolean;
              a01_il_b_identifier       : tsp00_KnlIdentifier;
 
      ------------------------------ 
 
        FROM
              AK_universal_semantic_tools : VAK06;
 
        PROCEDURE
              a06a_mblock_init (
                    VAR acv      : tak_all_command_glob;
                    mtype        : tgg00_MessType;
                    m2type       : tgg00_MessType2;
                    VAR tree     : tgg00_FileId);
 
        PROCEDURE
              a06drop_fieldlist_references (VAR fieldlists : tgg00_FieldLists);
 
        PROCEDURE
              a06_systable_get (
                    VAR acv      : tak_all_command_glob;
                    dstate       : tak_directory_state;
                    VAR tableid  : tgg00_Surrogate;
                    VAR base_ptr : tak_sysbufferaddress;
                    all          : boolean;
                    VAR ok       : boolean);
 
        PROCEDURE
              a06determine_username (
                    VAR acv       : tak_all_command_glob;
                    VAR userid    : tgg00_Surrogate;
                    VAR user_name : tsp00_KnlIdentifier);
 
      ------------------------------ 
 
        FROM
              AK_Identifier_Handling : VAK061;
 
        PROCEDURE
              a061get_colname (
                    VAR col_info : tak00_columninfo;
                    VAR colname  : tsp00_KnlIdentifier);
 
      ------------------------------ 
 
        FROM
              AK_error_handling : VAK07;
 
        PROCEDURE
              a07_b_put_error (
                    VAR acv  : tak_all_command_glob;
                    b_err    : tgg00_BasisError;
                    err_code : tsp00_Int4);
 
        PROCEDURE
              a07ak_system_error (
                    VAR acv  : tak_all_command_glob;
                    modul_no : integer;
                    id       : integer);
 
        PROCEDURE
              a07_nb_put_error (
                    VAR acv  : tak_all_command_glob;
                    b_err    : tgg00_BasisError;
                    err_code : tsp00_Int4;
                    VAR n    : tsp00_KnlIdentifier);
 
      ------------------------------ 
 
        FROM
              Systeminfo_cache : VAK10;
 
        PROCEDURE
              a10del_sysinfo (
                    VAR acv     : tak_all_command_glob;
                    VAR syskey  : tgg00_SysInfoKey;
                    VAR b_err   : tgg00_BasisError);
 
        PROCEDURE
              a10new (
                    VAR acv  : tak_all_command_glob;
                    obj_size : tsp00_Int4;
                    VAR p    : tgg00_StackListPtr);
 
        PROCEDURE
              a10dispose (
                    VAR acv : tak_all_command_glob;
                    VAR p   : tgg00_StackListPtr);
 
      ------------------------------ 
 
        FROM
              SQLManager : VAK101;
 
        FUNCTION
              a101_IsTempFile(
                    VAR acv    : tak_all_command_glob;
                    VAR fileId : tgg00_FileId(*ptocConst*)) : boolean;
 
        FUNCTION
              a101_IsExtendedTempFile(
                    VAR acv    : tak_all_command_glob;
                    VAR fileId : tgg00_FileId(*ptocConst*)) : boolean;
 
        PROCEDURE
              a101_InsertColumnIntoSysUpdStatWanted(
                    VAR acv        : tak_all_command_glob;
                    VAR tableSurr  : tgg00_Surrogate(*ptocConst*);
                    VAR columnName : tsp00_KnlIdentifier(*ptocConst*));
 
        FUNCTION
              a101_SetMaxParallelServers (max : tsp00_Int4) : tsp00_Int4;
 
      ------------------------------ 
 
        FROM
              CatalogWrapper : VAK103;
 
        FUNCTION
              a103GetColumn (
                    VAR BaseRec : tak_baserecord;
                    ColIndex    : integer
                    ) : tak00_colinfo_ptr;
 
      ------------------------------ 
 
        FROM
              AK_update_statistics: VAK28;
 
        FUNCTION
              a28prim_pages (
                    VAR acv     : tak_all_command_glob;
                    VAR baserec : tak_baserecord) : tsp00_Int4;
 
        FUNCTION
              a28primrows (
                    VAR acv     : tak_all_command_glob;
                    VAR baserec : tak_baserecord) : tsp00_Int4;
 
        PROCEDURE
              a28rows_and_colstat (
                    VAR acv        : tak_all_command_glob;
                    base_ptr       : tak_sysbufferaddress;
                    VAR col_info   : tak00_columninfo;
                    VAR statistics : tak_column_statistics;
                    VAR prim_rows  : tsp00_Int4;
                    VAR no_cstats  : boolean);
 
      ------------------------------ 
 
        FROM
              AK_universal_show_tools : VAK40;
 
        PROCEDURE
              a40sequence_expl_row (
                    VAR acv  : tak_all_command_glob;
                    VAR line : tsp00_Line;
                    change_to_unicode : boolean);
 
        PROCEDURE
              a40join_expl_row (
                    VAR acv            : tak_all_command_glob;
                    VAR strat          : tsp00_C40;
                    VAR owner          : tsp00_KnlIdentifier;
                    VAR table          : tsp00_KnlIdentifier;
                    recs_found         : tsp00_Longreal;
                    multiplier         : tsp00_Longreal;
                    reverse_multiplier : tsp00_Longreal;
                    new_left_size      : tsp00_Longreal;
                    new_left_recs      : tsp00_Longreal;
                    costs              : tsp00_Longreal);
 
      ------------------------------ 
 
        FROM
              Executing_loop_most : VAK505;
 
        PROCEDURE
              a505const_param_expression  (
                    VAR acv      : tak_all_command_glob;
                    VAR dmli     : tak_dml_info;
                    VAR mblock   : tgg00_MessBlock);
 
      ------------------------------ 
 
        FROM
              DML_Help_Procedures : VAK54;
 
        PROCEDURE
              a54_select_last_part (
                    VAR acv              : tak_all_command_glob;
                    VAR dmli             : tak_dml_info;
                    VAR user_result_tree : tgg00_FileId;
                    last_pars_part       : boolean);
 
        PROCEDURE
              a54_joinview_baserecords (
                    VAR acv  : tak_all_command_glob;
                    VAR dmli : tak_dml_info);
 
      ------------------------------ 
 
        FROM
              Select_List : VAK61;
 
        PROCEDURE
              a61_set_jump (
                    VAR mblock : tgg00_MessBlock;
                    stentrynr  : integer;
                    operator   : tgg00_StackEntryType);
 
        PROCEDURE
              a61_rel_old_table (
                    VAR acv  : tak_all_command_glob;
                    VAR dmli : tak_dml_info;
                    i        : integer);
 
      ------------------------------ 
 
        FROM
              Execute_Where_Part : VAK65;
 
        PROCEDURE
              a65_set_operator (
                    VAR acv  : tak_all_command_glob;
                    operator : tgg00_StackOpType);
 
      ------------------------------ 
 
        FROM
              Join_Select_help_routines : VAK681;
 
        PROCEDURE
              a681get_oj_info (
                    VAR acv         : tak_all_command_glob;
                    VAR dmli        : tak_dml_info);
 
        FUNCTION
              a681tmp_table_length (
                    VAR dmli    : tak_dml_info;
                    VAR jinfos  : tak68_joininfos;
                    tabno       : tsp00_Int2) : tsp00_Int4;
 
        PROCEDURE
              a681opt_conditions (
                    VAR acv    : tak_all_command_glob;
                    VAR dmli   : tak_dml_info;
                    VAR series : tak68_sequence;
                    VAR jinfos : tak68_joininfos;
                    VAR eq_rec : tak68_eq_record);
 
        FUNCTION
              a681keval_key_kind (
                    VAR acv       : tak_all_command_glob;
                    VAR dmli      : tak_dml_info;
                    VAR jinfos    : tak68_joininfos;
                    VAR mul_tabs  : tak68_mult_tabs;
                    start_idx     : tsp00_Int2;
                    dst_side      : tsp00_Int2;
                    is_join_value : boolean) : tak68_one_jointype;
 
        PROCEDURE
              a681key_sort_joinarr (
                    VAR acv      : tak_all_command_glob;
                    VAR dmli     : tak_dml_info;
                    VAR jinfos   : tak68_joininfos;
                    VAR series   : tak68_sequence;
                    ser_pos      : tsp00_Int2;
                    VAR sort_pos : tsp00_Int2;
                    VAR mul_tabs : tak68_mult_tabs);
 
        PROCEDURE
              a681lowest_multiple_strat (
                    VAR acv             : tak_all_command_glob;
                    VAR dmli            : tak_dml_info;
                    config              : tak_sysbufferaddress;
                    jtrans              : tak68_join_transitions;
                    succession          : tak68_succession_ptr;
                    VAR mul_tabs        : tak68_mult_tabs;
                    src_table           : tsp00_Int2;
                    dst_table           : tsp00_Int2;
                    VAR best_strat      : tak68_best_jstrat;
                    processed_table_cnt : tsp00_Int2;
                    update_jtrans       : boolean);
 
        FUNCTION
              a681ieval_inv_kind (
                    VAR acv          : tak_all_command_glob;
                    VAR dmli         : tak_dml_info;
                    VAR jinfos       : tak68_joininfos;
                    VAR mul_tabs     : tak68_mult_tabs;
                    start_idx        : tsp00_Int2;
                    dst_side         : tsp00_Int2;
                    is_join_value    : boolean) : tak68_one_jointype;
 
        PROCEDURE
              a681inv_sort_joinarr (
                    VAR acv      : tak_all_command_glob;
                    VAR dmli     : tak_dml_info;
                    VAR jinfos   : tak68_joininfos;
                    VAR series   : tak68_sequence;
                    ser_pos      : tsp00_Int2;
                    VAR sort_pos : tsp00_Int2;
                    VAR mul_tabs : tak68_mult_tabs);
 
        PROCEDURE
              a681sort_conditions (
                    VAR acv     : tak_all_command_glob;
                    VAR dmli    : tak_dml_info;
                    VAR series  : tak68_sequence);
 
        PROCEDURE
              a681test_outer_qualification (
                    VAR acv     : tak_all_command_glob;
                    VAR dmli    : tak_dml_info;
                    VAR jinfos  : tak68_joininfos);
 
        PROCEDURE
              a681jnew_seq (
                    VAR acv            : tak_all_command_glob;
                    VAR dmli           : tak_dml_info;
                    config             : tak_sysbufferaddress;
                    VAR table_stats    : tak68_table_stats;
                    jtrans             : tak68_join_transitions;
                    VAR sequence_info  : tak68_sequence_info;
                    VAR jinfos         : tak68_joininfos;
                    VAR res_info       : tak68_result_info;
                    VAR mul_tabs       : tak68_mult_tabs;
                    VAR lastsuccession : tak68_lastsuccession);
 
        PROCEDURE
              a681tr_multabs (
                    VAR acv      : tak_all_command_glob;
                    VAR mul_tabs : tak68_mult_tabs;
                    cntfrom      : tsp00_Int2);
 
        PROCEDURE
              a681tr_tabstats (
                    VAR acv           : tak_all_command_glob;
                    VAR dmli          : tak_dml_info;
                    VAR table_stats   : tak68_table_stats;
                    cntfrom           : tsp00_Int2;
                    debug             : boolean);
 
        PROCEDURE
              a681tr_joinvals (
                    VAR acv      : tak_all_command_glob;
                    VAR dmli     : tak_dml_info;
                    jtrans       : tak68_join_transitions;
                    starttab     : tsp00_Int2;
                    stoptab      : tsp00_Int2;
                    table_cnt    : tsp00_Int2);
 
      ------------------------------ 
 
        FROM
              Join_Select_execution : VAK682;
 
        PROCEDURE
              a682save_context (
                    VAR acv           : tak_all_command_glob;
                    VAR dmli          : tak_dml_info;
                    VAR parsk         : tak_parskey;
                    VAR sr_rec        : tak71_strat_rec;
                    VAR series        : tak68_sequence;
                    VAR eq_rec        : tak68_eq_record;
                    VAR res_tree      : tgg00_FileId;
                    use_operator_join : boolean;
                    copy_joininfo     : boolean);
 
        PROCEDURE
              a682_execute_join (
                    VAR acv       : tak_all_command_glob;
                    VAR dmli      : tak_dml_info;
                    VAR series    : tak68_sequence;
                    VAR res_tree  : tgg00_FileId;
                    VAR parsk     : tak_parskey;
                    VAR jvrec     : tak68_joinview_rec;
                    use_old_rescnt: boolean;
                    del_parsinfos : boolean);
 
        PROCEDURE
              a682_execute_join_operator (
                    VAR acv       : tak_all_command_glob;
                    VAR dmli      : tak_dml_info;
                    VAR series    : tak68_sequence;
                    VAR res_tree  : tgg00_FileId;
                    VAR parsk     : tak_parskey;
                    VAR jvrec     : tak68_joinview_rec;
                    use_old_rescnt: boolean;
                    del_parsinfos : boolean);
 
        PROCEDURE
              a682join_MBlock_key (
                    VAR acv         : tak_all_command_glob;
                    VAR dmli        : tak_dml_info;
                    VAR parsk       : tak_parskey;
                    VAR jv_tabid    : tgg00_Surrogate;
                    seqno           : tsp00_Int2;
                    VAR ke          : tgg00_SysInfoKey;
                    use_stmt_parsk  : boolean);
 
        PROCEDURE
              a682_mbuf_to_tmpbuf (
                    VAR acv       : tak_all_command_glob;
                    VAR ke        : tgg00_SysInfoKey;
                    VAR b_err     : tgg00_BasisError;
                    sysinfo_kind  : tak68_mbuf_to_tmpbuf_context);
 
      ------------------------------ 
 
        FROM
              Join2_Select_help_routines : VAK685;
 
        PROCEDURE
              a685init_eq_rec(
                    VAR acv         : tak_all_command_glob;
                    VAR eq_rec      : tak68_eq_record );
 
        PROCEDURE
              a685expand_eq_rec(
                    VAR acv    : tak_all_command_glob;
                    VAR eq_rec : tak68_eq_record );
 
        PROCEDURE
              a685finalize_eq_rec(
                    VAR acv         : tak_all_command_glob;
                    VAR eq_rec      : tak68_eq_record );
 
        PROCEDURE
              a685expand_joinarr(
                    VAR acv    : tak_all_command_glob;
                    VAR dmli   : tak_dml_info);
 
        PROCEDURE
              a685init_multab_rec(
                    VAR acv    : tak_all_command_glob;
                    VAR multab : tak68_mult_tabs );
 
        PROCEDURE
              a685finalize_multab_rec(
                    VAR acv    : tak_all_command_glob;
                    VAR multab : tak68_mult_tabs );
 
        FUNCTION
              a685get_join_trans(
                    VAR dmli    : tak_dml_info;
                    jtrans      : tak68_join_transitions;
                    dim1        : tsp00_Int2;
                    dim2        : tsp00_Int2 ) : tak68_join_transition_ptr;
&       ifdef TRACE
 
      ------------------------------ 
 
        FROM
              join_trace_routines : VAK683;
 
        PROCEDURE
              a683tr_sequence(
                    debug       : tgg00_Debug;
                    VAR dmli    : tak_dml_info;
                    VAR series  : tak68_sequence);
 
        PROCEDURE
              a683tabarr_output(
                    VAR dmli : tak_dml_info);
 
        PROCEDURE
              a683output_joins(
                    debug       : tgg00_Debug;
                    VAR acv     : tak_all_command_glob;
                    VAR dmli    : tak_dml_info;
                    VAR joins   : tak_joinrec);
 
        PROCEDURE
              a683trace_jointype(
                    debug   : tgg00_Debug;
                    desc    : tsp00_Sname;
                    jtype   : tak68_one_jointype);
 
        PROCEDURE
              a683trace_jprops(
                    debug : tgg00_Debug;
                    jprop : tak_jcolpropset);
 
        PROCEDURE
              a683_output (
                    debug    : tgg00_Debug;
                    VAR joins : tak_joinrec);
 
        PROCEDURE
              a683out_equal_record(
                    debug       : tgg00_Debug;
                    VAR eq_rec  : tak68_eq_record);
 
        PROCEDURE
              a683join_transition_trace(
                    debug         : tgg00_Debug;
                    VAR dmli      : tak_dml_info;
                    jtrans        : tak68_join_transitions;
                    VAR table_cnt : tsp00_Int2);
 
        PROCEDURE
              a683multabs_trace (
                    debug        : tgg00_Debug;
                    VAR mul_tabs : tak68_mult_tabs;
                    size_multabs : integer;
                    position     : integer;
                    table_cnt    : integer);
 
        PROCEDURE
              a683tr_newsucc (
                    debug          : tgg00_Debug;
                    succession     : tak68_succession_ptr;
                    start          : integer;
                    stop           : integer;
                    torec          : boolean);
 
        PROCEDURE
              a683tr_tablenames(
                    debug    : tgg00_Debug;
                    VAR acv  : tak_all_command_glob;
                    VAR dmli : tak_dml_info;
                    tablecnt : integer);
&       endif
 
      ------------------------------ 
 
        FROM
              Join2_Select : VAK684;
 
        PROCEDURE
              a684_join (
                    VAR acv                 : tak_all_command_glob;
                    VAR dmli                : tak_dml_info;
                    VAR series              : tak68_sequence;
                    VAR res_tree            : tgg00_FileId;
                    VAR jinfos              : tak68_joininfos;
                    VAR ak_strat_interface  : tak71_strat_rec;
                    VAR jvrec               : tak68_joinview_rec;
                    VAR table_stats         : tak68_table_stats);
 
      ------------------------------ 
 
        FROM
              Join2_Select : VAK690;
 
        PROCEDURE
              a690_join (
                    VAR acv                 : tak_all_command_glob;
                    VAR dmli                : tak_dml_info;
                    VAR series              : tak68_sequence;
                    VAR res_tree            : tgg00_FileId;
                    VAR jinfos              : tak68_joininfos;
                    VAR ak_strat_interface  : tak71_strat_rec;
                    VAR jvrec               : tak68_joinview_rec;
                    VAR table_stats         : tak68_table_stats);
&       ifdef trace
 
      ------------------------------ 
 
        FROM
              Join_aux_functions : VAK687;
 
        PROCEDURE
              a687_check_memory_usage (
                    VAR TransContext : tgg00_TransContext;
                    global_added     : tsp00_Longint;
                    VAR usedbytes1   : tsp00_Longint);
&       endif
 
      ------------------------------ 
 
        FROM
              Build_Strategy : VAK70;
 
        VAR
              a70glob_inv_strats            : tgg07_StratEnumSet;
              a70glob_accessop_known_strats : tgg07_StratEnumSet;
 
        PROCEDURE
              a70strategy (
                    VAR acv          : tak_all_command_glob;
                    VAR dmli         : tak_dml_info;
                    VAR gg_strategy  : tgg07_StrategyInfo;
                    VAR StratInfo_len: tsp00_Int2;
                    VAR eval_info    : tak71_page_eval_rec;
                    config           : tak00_access_configuration);
 
      ------------------------------ 
 
        FROM
              Build_Strategy_2 : VAK71;
 
        PROCEDURE
              a71default_strat (VAR gg_strategy : tgg07_StrategyInfo);
 
      ------------------------------ 
 
        FROM
              Catalog_Select_Optimizer : VAK722;
 
        PROCEDURE
              a722strategy (
                    VAR acv        : tak_all_command_glob;
                    VAR dmli       : tak_dml_info;
                    VAR eval_info  : tak71_page_eval_rec;
                    VAR gg_strategy: tgg07_StrategyInfo;
                    VAR strat_len  : tsp00_Int2);
 
      ------------------------------ 
 
        FROM
              Strategy_Explain : VAK728;
 
        PROCEDURE
              a728_explain (
                    VAR acv          : tak_all_command_glob;
                    VAR dmli         : tak_dml_info;
                    VAR gg_strategy  : tgg07_StrategyInfo;
                    joininfo_ptr     : tak68_join_ptr;
                    VAR morestratbuf : tsp00_MoveObj;
                    morestratbufsize : tsp00_Int4;
                    morestratpos     : tsp00_Int4);
 
        PROCEDURE
              a728explain_no_join_strat_now( VAR acv : tak_all_command_glob);
 
      ------------------------------ 
 
        FROM
              Hint_Handling : VAK80;
 
        PROCEDURE
              a80open_hint_info(
                    VAR acv         : tak_all_command_glob;
                    parskey         : tak_parskey;
                    VAR hintinfo    : tak_sysbufferaddress);
 
        PROCEDURE
              a80close_hint_info( VAR hintinfo : tak_sysbufferaddress );
 
        FUNCTION
              a80is_operator_join(
                    VAR acv         : tak_all_command_glob;
                    VAR dmli        : tak_dml_info) : boolean;
 
      ------------------------------ 
 
        FROM
              filesysteminterface_1 : VBD01;
 
        VAR
              b01niltree_id : tgg00_FileId;
 
      ------------------------------ 
 
        FROM
              FileDir_Wrapper : VBD998;
 
        FUNCTION
              bd998ArePagesClustered(
                    VAR trans     : tgg00_TransContext;
                    VAR tableSurr : tgg00_Surrogate) : boolean;
 
      ------------------------------ 
 
        FROM
              BD_Wrapper : VBD999;
 
        FUNCTION
              bd999GetDataIOBlockCount : tsp00_Int4;
 
        FUNCTION
              bd999GetPageSize : tsp00_Int4;
 
      ------------------------------ 
 
        FROM
              Configuration_Parameter : VGG01;
 
        VAR
              g01vtrace                     : tgg00_VtraceState;
              gg01_operator_join            : boolean;
              gg01_operator_join_costfunc   : boolean;
              gg01_operator_join_sort       : boolean;
 
        FUNCTION
              g01join_tablebuffer : tsp00_Int4;
 
        FUNCTION
              g01optimize_parallel_server : tsp00_Int4;
 
        PROCEDURE
              g01opmsg (
                    msg_prio  : tsp3_priority;
                    msg_type  : tsp3_msg_type;
                    msg_no    : tsp00_Int4;
                    msg_label : tsp00_C8;
                    msg_text  : tsp00_C24;
                    msg_value : tsp00_Int4);
&       ifdef trace
 
        PROCEDURE
              g01abort (
                    msg_no     : tsp00_Int4;
                    msg_label  : tsp00_C8;
                    msg_text   : tsp00_C24;
                    bad_value  : tsp00_Int4);
&       endif
 
      ------------------------------ 
 
        FROM
              Integer_SET : VGG12;
 
        FUNCTION
              gg12InitBitArray(
                    VAR TransContext : tgg00_TransContext;
                    bits             : tsp00_Int4) : tsp00_Addr;
 
        FUNCTION
              gg12GetBit (
                    IntegerSet  : tsp00_Addr;
                    number      : tsp00_Int4) : boolean;
 
        PROCEDURE
              gg12SetBit (
                    IntegerSet  : tsp00_Addr;
                    number      : tsp00_Int4;
                    value       : boolean);
 
        PROCEDURE
              gg12ClearBitArray (
                    IntegerSet  : tsp00_Addr;
                    bits        : tsp00_Int4);
 
        PROCEDURE
              gg12FinalizeBitArray(
                    VAR TransContext : tgg00_TransContext;
                    VAR IntegerSet   : tsp00_Addr);
 
      ------------------------------ 
 
        FROM
              Trace_Help_Procedures: VGG041;
 
        PROCEDURE
              g041join_expl_row_trace (
                    VAR t              : tgg00_TransContext;
                    VAR strat          : tsp00_C40;
                    VAR owner          : tsp00_KnlIdentifier;
                    VAR table          : tsp00_KnlIdentifier;
                    recs_found         : tsp00_Longreal;
                    multiplier         : tsp00_Longreal;
                    new_left_size      : tsp00_Longreal;
                    new_left_recs      : tsp00_Longreal;
                    costs              : tsp00_Longreal);
 
      ------------------------------ 
 
        FROM
              Kernel_move_and_fill : VGG101;
 
        PROCEDURE
              SAPDB_PascalMove  (
                    mod_id         : tsp00_C6;
                    mod_intern_num : tsp00_Int4;
                    size1          : tsp00_Int4;
                    size2          : tsp00_Int4;
                    val1           : tsp00_MoveObjPtr;
                    p1             : tsp00_Int4;
                    val2           : tsp00_MoveObjPtr;
                    p2             : tsp00_Int4;
                    cnt            : tsp00_Int4;
                    VAR e          : tgg00_BasisError);
 
        PROCEDURE
              SAPDB_PascalForcedMove (
                    size1    : tsp00_Int4;
                    size2    : tsp00_Int4;
                    val1     : tsp00_MoveObjPtr;
                    p1       : tsp00_Int4;
                    val2     : tsp00_MoveObjPtr;
                    p2       : tsp00_Int4;
                    cnt      : tsp00_Int4);
 
        PROCEDURE
              SAPDB_PascalForcedFill (
                    size     : tsp00_Int4;
                    m        : tsp00_MoveObjPtr;
                    pos      : tsp00_Int4;
                    len      : tsp00_Int4;
                    fillchar : char);
 
      ------------------------------ 
 
        FROM
              GG_edit_routines: VGG17;
 
        PROCEDURE
              g17int4to_line (
                    int       : tsp00_Int4;
                    with_zero : boolean;
                    int_len   : integer;
                    ln_pos    : integer;
                    VAR ln    : tsp00_Line);
 
        PROCEDURE
              g17nameto_line (
                    n           : tsp00_Name;
                    VAR ln_len  : integer;
                    VAR ln      : tsp00_Line);
 
      ------------------------------ 
 
        FROM
              GG_allocator_interface : VGG941;
 
        FUNCTION
              gg941Allocate(
                    VAR TransContext : tgg00_TransContext;
                    wantedBytes      : integer) : tsp00_Addr;
 
        PROCEDURE
              gg941Deallocate(
                    VAR TransContext : tgg00_TransContext;
                    VAR p            : tsp00_Addr);
 
        PROCEDURE
              gg941CalcStatistics(
                    VAR TransContext : tgg00_TransContext;
                    VAR usedbytes    : tsp00_Longint;
                    VAR maxusedbytes : tsp00_Longint;
                    VAR ctrlbytes    : tsp00_Longint);
 
      ------------------------------ 
 
        FROM
              Single_Select : VKB720;
 
        PROCEDURE
              k720_test_subquery (
                    VAR trans   : tgg00_TransContext;
                    VAR datapart: tgg00_DataPart;
                    datapartsize: tsp00_Int4;
                    VAR mdesc   : tgg00_StackDesc;
                    VAR rec     : tgg00_Rec);
 
      ------------------------------ 
 
        FROM
              RTE_kernel : VEN101;
 
        PROCEDURE
              vnewbuf (count      : tsp00_Int4;
                    VAR available : tsp00_Int4;
                    VAR p         : tsp00_Addr;
                    VAR ok        : boolean);
&       ifdef TRACE
 
        PROCEDURE
              vdebug_break (debug_break_pos : tsp00_Int4);
 
      ------------------------------ 
 
        FROM
              hint_trace_routines : VAK81;
 
        PROCEDURE
              a81debug_access_config (
                    debug       : tgg00_Debug;
                    VAR config  : tak00_access_configuration);
 
      ------------------------------ 
 
        FROM
              Test_Procedures : VTA01;
 
        PROCEDURE
              t01execution_kind (
                    debug     : tgg00_Debug;
                    nam       : tsp00_Sname;
                    ex_kind   : tak_execution_kind);
 
        PROCEDURE
              t01bool (
                    debug    : tgg00_Debug;
                    nam      : tsp00_Sname;
                    curr_bool: boolean);
 
        PROCEDURE
              t01stackdesc (
                    debug          : tgg00_Debug;
                    nam            : tsp00_Sname;
                    stack_addr     : tgg00_StackListPtr;
                    VAR stack_desc : tgg00_StackDesc);
 
        PROCEDURE
              t01messblock (
                    debug         : tgg00_Debug;
                    nam           : tsp00_Sname;
                    VAR m         : tgg00_MessBlock);
 
        PROCEDURE
              t01tabid (
                    debug     : tgg00_Debug;
                    nam       : tsp00_Sname;
                    VAR tabid : tgg00_Surrogate);
 
        PROCEDURE
              t01qual (
                    level     : tgg00_Debug;
                    VAR q_buf : tgg00_QualBuf);
 
        PROCEDURE
              t01int4 (
                    level   : tgg00_Debug;
                    nam     : tsp00_Sname;
                    int     : tsp00_Int4);
 
        PROCEDURE
              t01sname (
                    level   : tgg00_Debug;
                    nam     : tsp00_Sname);
 
        PROCEDURE
              t01name (
                    level   : tgg00_Debug;
                    nam     : tsp00_Name);
 
        PROCEDURE
              t01lidentifier (
                    level       : tgg00_Debug;
                    identifier  : tsp00_KnlIdentifier);
 
        PROCEDURE
              t01treeid  (
                    level    : tgg00_Debug;
                    nam      : tsp00_Sname;
                    VAR tree : tgg00_FileId);
 
        PROCEDURE
              t01real (
                    level  : tgg00_Debug;
                    nam    : tsp00_Sname;
                    r      : tsp00_Longreal;
                    digits : integer);
 
        PROCEDURE
              t01stackentry (
                    level       : tgg00_Debug;
                    VAR st      : tgg00_StackEntry;
                    entry_index : integer);
 
        FUNCTION
              t01trace (layer : tgg00_Debug) : boolean;
 
        PROCEDURE
              t01p2int4 (
                    debug : tgg00_Debug;
                    nam_1 : tsp00_Sname ;
                    int_1 : tsp00_Int4;
                    nam_2 : tsp00_Sname ;
                    int_2 : tsp00_Int4);
 
        PROCEDURE
              t01strat_enum (
                    debug : tgg00_Debug;
                    nam   : tsp00_Sname;
                    strat : tgg07_StratEnum);
 
        PROCEDURE
              t01addr (
                    debug    : tgg00_Debug;
                    nam      : tsp00_Sname;
                    bufaddr  : tsp00_Addr);
&       endif
 
.CM *-END-* use -----------------------------------------
Synonym :
 
        PROCEDURE
              a10new;
 
              tak_sysbufferaddress tgg00_StackListPtr
 
        PROCEDURE
              a10dispose;
 
              tak_sysbufferaddress tgg00_StackListPtr
 
        PROCEDURE
              t01addr;
 
              tsp00_BufAddr tsp00_Addr
 
        PROCEDURE
              vnewbuf;
 
              tsp00_PageAddr tsp00_Addr
 
.CM *-END-* synonym -------------------------------------
***********************************************************
Specification:
List of procedures:
 (No ] Name (calls)
 
(1) a680_join (2, 14, 19)
 
    (2) a680search_sequence (3, 5, 6, 10, 11, a684_join)
        (3) get_all_eq_conditions (4)
            (4) eq_found
        (5) sort_joins
 
        (6) transfer_qualification (7)
            (7) some_and_parts (8)
                (8) next_qualification (9)
                    (9) test_and_move_qual
 
        (10) evaluate_joins
 
        (11) search_lowest (12)
             a681jnew_seq
               a680standard_cost (13)
                 (13) next_join_eval
 
    (14) put_join_pars_info
 
(15) a682copy_joinparsinfo (18, 16)
 
(17) a682_only_ex_join (18, 19)
     (18) get_join_pars_info
 
(19) a682_execute_join (20)
     (20) j_one_record
 
Passed to this module are an array in which the tables specified in the
From part of the Select have been recorded and a Mess-Buffer in which the
output- and qualification stack entries plus data for all tables are contained
together.
The task of this module is to determine the best order in which the
referenced base tables are to be processed. With the aid of VAK69, a
Mess-Buffer must be created for each referenced base table,
with the Mess-Buffer
containing only output- and qualification stack entries that relate to that
table and information on join conditions of that table.
 
The Join is executed in that way that
a normal set select for a table (VKB72) is
issued for the first table in the chosen order. The result records contain in
their key the values that are required for checking the Join conditions of this
first table with the second table in the sequence.
Then, in the case of n referenced base tables, (n-1) Joinselects (VKB74)
are initiated, which each join the result of the hitherto performed Joinselects
(or of the first normal Select) with the next table in the sequence. At any one
time, therefore, only two tables are under consideration: the result of the
previous Join steps and a base table. The result of this Join step, in turn,
has the field values in the key necessary for the next Join step and is thus
incorporated into the next Join step.
The result of the last Join step is the one desired by the user (except in
exceptional cases in which Set functions or similar play a role).
.sp;There are two, very different methods for joining. The first, better method
is used if, from the Join value in the existing intermediate-result table,
conclusions can be made as to the records in the new primary table that have a
matching Join value.
This is the case with equal joins, with there still being subdivisions:
 
1. the Join field in the primary table is the only key field. If one has the
Join value from the intermediate-result table, one can directly access the
record in the primary table that is known to satisfy the Equal Join Condition.
 
2. the Join field in the primary table is the first key field. This means that
an area of the primary table is identified in which all records satisfy the
Join condition.
 
3. the Join field in the primary table is a singly inverted field. If one has
the Join value from the intermediate-result table, this means that the
inversion list is known, in which there are precisely those primary keys whose
records satisfy the Equal Join condition.
 
These three cases are the most favourable, because, in the primary table, only
those records are considered that satisfy at least this one, particularly
selective Join condition.
 
In contrast to these are all other cases, both Equal conditions in which the
field in the primary table is neither the 1st key field nor an inverted field
as well as all other Join conditions (>, <, >=, <=, <>).
Since, because of performance and I/O rate, it is impossible to process the
primary table for each Join value in the intermediate result, this processing
has, exceptionally, been put before the actual Join step.
As in the 1st table of the Join, therefore, the table is searched for
records that satisfy the qualifications for this table; the required fields are
fetched out and the thus produced result records are sorted according to the
fields necessary for joining with the other table and are entered in a second
intermediate result.
In both cases, the search in the first table and also for
this search, it is the case that search strategies (e.g. search via inversion)
are used in accordance with conditions in the Search_Condition that refer to
this table. If a field is part of an Equal join and there are still further
conditions for this field that refer only to one table, these conditions are
also used for the other field of the Equal join and are, if applicable, used in
the strategy.
.CM *-END-* specification -------------------------------
***********************************************************
Description:
 
A680_JOIN
 
This procedure is the main Join-processing procedure and is called by the Set
Select (VAK67) and by VAK59 (updatable Join Views).
The Mess-Buffers are created for the updatable Join Views and are then
simply processed if the View conditions have to be checked for an Insert,
Update or Delete. The Join Mess-Buffers are not newly created each time.
If at least one of the tables in the FROM part of the SELECT represented a
Join View, it applies that atcnt (number of tables in the FROM part) is <
atmaxcounttabs (number of base tables taking part in the SELECT). In the
array in which the tables are described, the Join Views are replaced by their
primary tables (A54_JOINVIEW_BASERECORDS) in order to obtain a 1:1 relationship
between array entries (and thus names of base tables) and Join steps.
In A680SEARCH_SEQUENCE, not only is the order of table processing specified,
but also a Mess-Buffer is constructed directly for each table (VAK69), with
this Mess-Buffer containing all the information necessary for processing.
If the command is not to be executed immediately, only parse information is
stored that describes the parameters to be given by the user and that contains
the result-file name (A54_SELECT_LAST_PART), as well as parse information
containing the base tables used, their file version, home location etc., which
are required again for execution.
This is not a Mess-Buffer that is to be sent to KB.
If the Join is to be executed, A680_EXECUTE_JOIN is called, which processes
in a loop the Mess-Buffers that have been built by VAK69.
 
A680SEARCH_SEQUENCE
 
Called by A680_JOIN to first determine the best table order and the best
strategies for the current join and then start its execution.
This procedure builds a Mess-Buffer
for every table taking part in the join.
Every Mess-Buffer contains only those conditions of the global join
qualification that extend over columns of the respective table (performed by
TRANSFER_QUALIFICATION). All Mess-Buffers are temporarily stored as system
information of type 'emessbuf'.
The same part2 of the Mess-Buffer is used in all Join steps, i.e. the data
are not assigned to individual Join Mess_Buffers, since this would have caused
excessive problems with regard to the separation of parsing and execution. In
each case, therefore, only part1 is changed.
The original part1, containing output- and qualification stack entries of
all tables, is saved, since all build- and store procedures work with the
Mess-Buffer from the ACV, i.e. the latter must contain the new Mess-Buffers.
By means of AK680GET_ALL_EQ_CONDITIONS, an attempt is made to use Equal Join
conditions transitively (a = b AND b = c => a = c) and thus to create new
implicit Join conditions that can be used for processing.
This procedure also determines cases in which conditions that apply to one
Join field can also be used for the associated field.
In SORT_JOINS, the Join conditions are sorted according to their
selectivity ('=' before '<', '>' before '<>'), so that the Join field value of
the most selective condition is in the keys at the beginning and some
valuations that are necessary for determining the order of table processing
find the most selective
condition easier.
A new Mess-Buffer is initialized, with userid and table names being taken
from the array of table descriptions (see A680_JOIN, Join Views). When called
because of updatable Join Views, JV_MAXKEYL specifies the maximum length of a
primary key that must find space in front of the other data (strategy =
j_viewkey). Otherwise, JV_MAXKEYL = 0.
By means of TRANSFER_QUALIFICATION, the qualifications, not output stack
entries, relating to this one table are transferred to the new Mess-Buffer.
The current table description is fetched (A61_REL_OLD_TABLE), and the
strategy record is initialized.
In A70STRATEGY, the best possible strategy for searching in this table is
to be determined. This strategy is required for the first table of the search
sequence still to be determined and for all tables that have to be sorted
according to the Join fields prior to joining with the old intermediate result.
Needed for all tables are the values determined in A70STRATEGY that
specify how many pages (because of I/O) of the table have to be searched in
order to determine all relevant records (ts_pages_searched).
At the same time, a cost value is determined for this search (ts_wholeIO_pages).
The TABLE_STATS array used for determining the order has the following entries:
 
        ts_pages_searched := strat_value * ts_all_pages;
        ts_strat_value    :=  strat_value;
        ts_wholeIO_pages  :=  cost_value;
 
where ts_all_pages stands for the number of pages of the primary table last
determined by UPDATE STATISTICS, strat_value for the percentage of pages to be
searched in the selected strategy, qual_value for the percentage of records
satisfying all conditions.
Part1 of the Mess-Buffer and the strategy are stored in system information
of type emessbuf (or evmessbuf for updatable Join Views).
In EVALUATE_JOINS, the possible transitions from table x to table y are
evaluated on the basis of their access possibilities (direct access or prior
resorting) and on the basis of the result set to be expected (cartesian
products via a large table are more expensive than direct access from a small
table).
In SEARCH_LOWEST, the best order of the tables is determined with the aid
of the matrix in EVALUATE_JOINS.
A684_JOIN receives this order (series) and then completely builds the
Mess-Buffers that have been started, i.e. it inserts output stack entries and,
if necessary, replaces the one-table strategy by a better Join access strategy.
In addition, the output stack entries from the original Mess-Buffer are
transferred to the new Mess-Buffers during execution of VAK69.
This is because only then can the position be known (because
key structure of temporary result records
is dependent on the Join order) at which an output field must be
located.
 
AK680GET_ALL_EQ_CONDITIONS
 
The Join conditions recorded in JOINARR are checked as to whether they describe
Equal Joins that each have only one field as an operand. These Join conditions
are stored in EQ_REC and can then be used in TRANSFER_QUALIFICATION.
Within EQ_REC, an attempt is made to find transitive Join conditions (a = b
AND b = c) and, from them, to form a = c. This is done until EQ_REC is full (20
entries) or until no new condition was added in the previous run through the
loop (old_cnt = eqr_cnt).
If new, artificial conditions have been formed, they are transferred back
to the JOINARR that is used for the order search etc. if this cannot result in
any code-conversion problems (eqi_codetype is the same on both sides and code
conversion not required for any field).
 
AK680EQ_FOUND
 
Descriptions for two Join fields are transferred from which an artificial Equal
condition is to be formed. In this function, it is checked whether this
condition does not already exist anyway (AK680EQ_FOUND = true).
 
SORT_JOINS
 
In this procedure, the Join conditions are sorted according to their
selectivity: '=' to the front, '<>' to the back. Valuations (which Join
condition is there from table x to table y) can then rely on them. Also, the
order of the Join fields in the key of the respective result record is based on
them. And in this array
it is advisable to have the most selective conditions at the
front also in order to be able to select the best Join strategy and thus the
best Join processing.
 
AK680TRANSFER_QUALIFICATION
 
This procedure transfers for the specified table (j_act_recno) all conditions
from the global Mess-Buffer (j_stack_desc) to the single-table Mess-Buffer
(mess_buf), which extend only over columns of this table. (I.e. it skips all
join-conditions and all conditions for others than the given table).
For this purpose, the first and last stack entries of the qualification are
determined and the recursive procedure SOME_AND_PARTS is called. By found =
false, this procedure receives the information that there are still no
qualifications in the new Mess-Buffer, i.e. also that an 'AND' need not be
written after the next qualification that is found.
 
AK680SOME_AND_PARTS
 
This is a recursive procedure that, with the aid of NEXT_QUALIFICATION (fetches
a condition), searches the entire qualification for conditions for this table
and transfers them in the usual manner to the new Mess-Buffer (condition 1; jump
over the rest if condition not satisfied; condition 2; AND). If old_found =
true (there is already a condition in the new Mess-Buffer), a stack-entry place
must remain for the jump_false entry.
A search is made for the next condition. If none exists, the place kept
free for jump_false may have to be made available again.
Otherwise, AK680SOME_AND_PARTS is again called recursively in order to search
for the next conditions starting from the new starting position in the old mess
buffer.
If a condition was found in NEXT_QUALIFICATION, the jump_false and the AND
need not be entered until at the end in order to get the jump right to the end
of the conditions and in order to retain the inverse Polish notation.
 
AK680NEXT_QUALIFICATION
 
In this procedure, a search is made for the next condition relating to the
highest level of the qualification (i.e. that can be combined with all other
conditions by AND). This may also mean that several individual conditions that
are combined by OR are handled at once as one large condition.
To be able to decide whether it is a condition for the currently relevant
table, it is necessary to fetch from the first stack entry describing a field
(tabno is then still -1) the table number belonging to this field
(ecol_tab[ 2 ]).
If more than one field belongs to this condition (Join; there is a further
field stack entry in which ecol_tab[ 2 ] <> tabno), it is not a condition that
should be transferred to the new Mess-Buffer.
It is checked whether this is a large condition at the highest level (end
of qualification : i > maxstarts or end of qualification reachable by jumps
over AND and jump_false). READY indicates that the end of a large condition has
been reached.
 
If FOUND = true, i.e. a large condition has been found that extends only over
one table, this condition must be transferred to the new Mess-Buffer in
AK680TEST_AND_MOVE_QUAL if it relates to the current table. In the case of
conditions that relate to another table, an attempt is made with the aid of
EQ_REC also to use these conditions for the current table if these two tables
have Equal Join conditions.
 
AK680TEST_AND_MOVE_QUAL
 
If the condition defined by STARTS and ST_ENTRY_CNT does not relate to the
current table (J_ACTTABNO), this condition is wandered through in a loop. Each
non-field stack entry is loaded into the new Mess-Buffer. For each field stack
entry (of the non-current table), a search is made in EQ_REC for whether a
stack entry is contained there that describes the same field (the two stack
entries must be identical with the exception of the eop).
If such a field description has been found (EQ_IND <> 0), it is checked
whether the associated field in EQ_REC (in EQ_REC Equal Joins are contained
which have only one field on each side of the operator;
see AK680GET_ALL_EQ_CONDITIONS) describes a field of the
current table. If so, instead of the field contained in the original
qualification, the corresponding field description to which the entry in EQ_REC
refers is loaded into the new Mess-Buffer. However, this is possible only if
there are no problems because of different code types.
CURR_EQ is set to an impossible value (maxint2) in order to get out of the
loop; FOUND remains true.
If an appropriate field has not been found or only one with code-type
problems, FOUND = false.
If, conversely, a condition relating to the current table has been passed
to this procedure, this condition is copied to the new Mess-Buffer;  FOUND
remains true.
If FOUND is still true, it must be recorded in the new Mess-Buffer how far
it has already been filled.
 
AK680EVALUATE_JOINS
 
This procedure computes a 2-dimensional array (JTRANS) that contains for
every ordered pair (t1,t2) of tables the best transition from t1 to t2 (i.e.
the best join-condition joining the two tables together);
 
possible transitions:
 
 
      to single_keyfield
      to unique_field
      to first_keyfield
      to invfield
      to eq_field
      to lt_gt_field
      to ne_field
      to impossible
      to unknown
 
These access possibilities have been sorted from the best to the worst.
 
The information that was already created during the processing of the Search
condition is used in JOINARR to determine the access possibilities between two
tables (JOINTYPE). This is information on whether the Join field is the only or
the first key field (jtonlykey, jtfirstkey) or whether it is an inverted.
 
AK680SET_JOIN_ORDER
This procedure determines the execution order for the join.
The only output parameter is 'series', which contains the sequence of tables
and also relates to the join-conditions that are used for the transitions.
 
If the user has specified the Join order (possible for test purposes) or if the
Mess-Buffers have to be formed for updatable Join Views, an order is not
determined, but the order determined by the FROM part is used.
 
The join order is determined by a a681jnew_seq that minimizes a
cost measure computed for best possible join-orders.
 
If the FROM-list contains more than 'max_window' (4) tables, a slight
modification of the algorithm is applied. It considers only a subset of all
possible execution orders to speed up the optimization process.
4 tables that are next to one another in the FROM part are checked at a
time and a search is made for the best order for them (1st loop FOR i := 1 TO
dmli.d_cntfromtab).
The best combination-of-4 represents the start of the order of processing.
Then, each time, 4 tables directly behind the tables that have already been
processed are put into a good order and are marked as the next tables to be
processed.
 
The record 'lowest' contains all information about the state of the search,
e.g. the best sequence found so far (see description below).
When the search was made for the order, it was also determined at the same
time from which of the previously processed tables there is the best
possibility of access to the next table. This knowledge and the selected Join
strategy are stored in SERIES.
 
If the costs for commands are to be checked (COSTCHECK), this is also done,
since the cost value of the best order is now available.
 
A680NEXT_JOIN_EVAL
 
This procedure estimates the cost for one step of the join - execution.
 
INPUT :
newpages    = size of temporary join-result-table (in pages)
newsum      = accumulated costs so far (in no of page accesses)
jointype    = type of transition (e.g. 'to single key')
ts_all_pages   = size of new table (in pages)
ts_pages_searched=number of pages of the new table that have to
to be accessed to retrieve all qualified records
ts_wholeIO_pages = costs to build a temporary result-table from the
qualified records of the new table
 
ts_recs_per_page=estimated number of records per page of new table
 
recs_per_res_page=estimated number of records per page of temporary
join-result-table
 
OUTPUT:
newpages   = size of new join-result-table
newsum     = costs including the last step
 
For explanation of input-output mapping see in-line comments
within procedure.
 
PUT_JOIN_PARS_INFO
 
This procedure is called by A680_JOIN.
It forms the parse information that describes all the base tables used for
the Join with their file versions etc. This information is again required for
execution and is then transferred back to the ATARR (GET_JOIN_PARS_INFO).
The Join processing order is likewise stored.
 
A680_STRATEGIES
 
This procedure is used by VAK50 in the case of correlated subqueries. In the
execution of these subqueries, data are passed to the kernel
that leads to the
determination of strategies (for the first table and all tables in which
resorting according to the Join field values is necessary). Since this
determination of strategy is not to be performed on each run through a subquery
within the correlated subquery, it is performed once prior to the first
execution. The result (a possibly modified Mess-Buffer) is stored under the
parse id that has been agreed for this correlated  subquery.
The necessary information (table descriptions and sequences) is fetched
(GET_JOIN_PARS_INFO) and the Mess-Buffers are determined and stored by
STRATS_OF_JOINS.
 
.CM *-END-* description ---------------------------------
***********************************************************
.CM -lll-
Code    :
 
 
CONST
      c_debug_output       = true;
      c_update_jtrans      = true; (* a681lowest_multiple_strat *)
      c_final_call         =  true;
      c_change_to_unicode  = true;
      c_seqsearch_for_exec = true;
 
TYPE
      t_stop_reason = ( dont_stop, stop_bof_param );
 
      t_qual_scanner  =  RECORD
            qs_src_pos    : tsp00_Int2;
            qs_dest_pos   : tsp00_Int2;
            qs_src_maxpos : tsp00_Int2;
            qs_filler     : tsp00_Int2;
      END;
 
      (* table_stats describes the characteristic values of the       *)
      (* strategy chosen by a70strategy for one specific table:       *)
      (*                                                              *)
      (*    ts_pages_searched : absolute number of base-table-pages   *)
      (*                     that are expected to be touched          *)
      (*                     applying this strategy                   *)
      (*    ts_wholeIO_pages  : estimated costs for building a        *)
      (*                     result-table of all qualified            *)
      (*                     records of this table                    *)
      (*    ts_all_pages      : absolute number of base-table-pages   *)
      (*    ts_recs_per_page  : estimated number of records per page  *)
      (*                                                              *)
      (* table_stats contains one of the above records for every      *)
      (* table of the Join-FROM-List                                  *)
      (* join-trans contains for every pair t1,t2 of the tables       *)
      (* in the FROM-list the best join-transition, i.e. the best     *)
      (* simple condition of the qualification that connects a        *)
      (* column of t1 with a column of t2; equal-conditions are       *)
      (* of major significance                                        *)
      (* lowinfo contains information about the current state         *)
      (* within the recursive search for the best evaluation-order    *)
      (* of the tables in the from list:                              *)
      (*                                                              *)
      (* si_sequence : contains the best initial ordering found so    *)
      (*             far; it consists of two relevant parts:          *)
      (*              1. an initial sequence that is decided upon al- *)
      (*                 ready and will not change anymore during the *)
      (*                 recursion. (stored in left part of array)    *)
      (*              2. an extension to the initial sequence that    *)
      (*                 may still change in the course of the        *)
      (*                 recursion. (stored in right part of array)   *)
      (* li_newsum  : measure for the costs needed so far to join     *)
      (*              according to the sequence in 'succession'       *)
      (* li_newpages: expected number of result-pages after joining   *)
      (*              the tables from succession                      *)
      (*                                                              *)
 
 
(*------------------------------*) 
 
FUNCTION
      ak680eq_found (
            VAR eq_rec : tak68_eq_record;
            VAR eq1    : tak68_eqfieldinfo;
            VAR eq2    : tak68_eqfieldinfo) : boolean;
 
VAR
      _i : integer;
 
BEGIN
&ifdef TRACE
t01int4 (ak_join,'eq1=        ', eq1.eqi_stackpos);
t01int4 (ak_join,'eq2=        ', eq2.eqi_stackpos);
&endif
_i := 1;
WHILE _i <= eq_rec.eqr_cnt DO
    BEGIN
    IF  (eq1.eqi_fieldid.eqt_whole = eq_rec.eqr_arr^[ _i ][ cak68_left ].
        eqi_fieldid.eqt_whole)
        AND
        (eq2.eqi_fieldid.eqt_whole = eq_rec.eqr_arr^[ _i ][ cak68_right ].
        eqi_fieldid.eqt_whole)
    THEN
        _i := eq_rec.eqr_cnt + 2
    ELSE
        IF  (eq1.eqi_fieldid.eqt_whole = eq_rec.eqr_arr^[ _i ][ cak68_right ].
            eqi_fieldid.eqt_whole)
            AND
            (eq2.eqi_fieldid.eqt_whole = eq_rec.eqr_arr^ [ _i ][ cak68_left ].
            eqi_fieldid.eqt_whole)
        THEN
            _i := eq_rec.eqr_cnt + 2;
        (*ENDIF*) 
    (*ENDIF*) 
    _i := succ(_i);
    END;
(*ENDWHILE*) 
ak680eq_found := _i >= eq_rec.eqr_cnt + 2;
&ifdef TRACE
t01int4 (ak_join, 'eq_found    ', ord(_i >= eq_rec.eqr_cnt + 2));
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680get_all_eq_conditions (
            VAR acv     : tak_all_command_glob;
            VAR dmli    : tak_dml_info;
            VAR eq_rec  : tak68_eq_record);
 
VAR
      _eq1        : tak68_eqfieldinfo;
      _eq2        : tak68_eqfieldinfo;
      _i          : integer;
      _j          : integer;
      _old_cnt    : integer;
      _oldjcnt    : integer;
      _jval_field : tak68_eqfieldid_type;
 
BEGIN
(* insert new entries with op_eq into dmli.d_joins *)
(**)
(* _jval_field describe a constant column type, e.i. "column = 6" *)
_jval_field.eqt_tabno := cak68_join_value;
_jval_field.eqt_colno := 0;
&ifdef TRACE
a683_output (ak_join, dmli.d_joins);
&endif
eq_rec.eqr_cnt := 0;
_i             := 0;
(* loop for all joins, catch given equal conditions *)
WHILE ( _i <= dmli.d_joins.jrc_cnt - 1 ) DO
    BEGIN
    IF  ( dmli.d_joins.jrc_joinarr^[ _i ].jo_op = op_eq )
    THEN
        BEGIN
        (* use only EQ-joins without expression *)
        IF  ((( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_cntstack = 1 )
            AND
            ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ].jop_cntstack = 1 ))
            AND NOT
            ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_outer_join OR
            ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ].jop_outer_join ))
            AND
            ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_tableno <>
            dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ].jop_tableno ))
        THEN
            BEGIN
            IF  ( eq_rec.eqr_cnt + 1 > eq_rec.eqr_capacity )
            THEN
                a685expand_eq_rec( acv, eq_rec );
            (*ENDIF*) 
            IF  ( eq_rec.eqr_cnt + 1 <= eq_rec.eqr_capacity )
            THEN
                BEGIN
                eq_rec.eqr_cnt := succ(eq_rec.eqr_cnt);
                (* insert into left side of eq_rec *)
                WITH eq_rec.eqr_arr^[ eq_rec.eqr_cnt ][ cak68_left ] DO
                    BEGIN
                    eqi_fieldid.eqt_tabno  := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 1 ].jop_tableno;
                    eqi_fieldid.eqt_colno  := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 1 ].jop_fieldno;
                    eqi_stackpos       := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 1 ].jop_startstack;
                    eqi_joinno         := _i;
                    eqi_jrecs          := 1;
                    IF  ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].
                        jop_datatyp in [ dcha, ddate, dtime,dtimestamp ] )
                    THEN
                        eqi_codetype := csp_ascii
                    ELSE
                        eqi_codetype := csp_codeneutral;
                    (*ENDIF*) 
                    END;
                (*ENDWITH*) 
                (* insert into right side of eq_rec *)
                WITH eq_rec.eqr_arr^[ eq_rec.eqr_cnt ][ cak68_right ] DO
                    BEGIN
                    eqi_fieldid.eqt_tabno  := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 2 ].jop_tableno;
                    eqi_fieldid.eqt_colno  := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 2 ].jop_fieldno;
                    eqi_stackpos       := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 2 ].jop_startstack;
                    eqi_joinno := _i;
                    eqi_jrecs  := 2;
                    eqi_codetype := eq_rec.eqr_arr^[ eq_rec.eqr_cnt ][ cak68_left ].eqi_codetype;
                    END;
                (*ENDWITH*) 
                END;
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    _i := succ(_i);
    END;
(*ENDWHILE*) 
_oldjcnt := eq_rec.eqr_cnt;
(* look for transitive joins T1.A = T2.A, T2.A = T3.A, ...  *)
(* but also catch transitions like T1.A = const, const=T2.A *)
REPEAT
    (* UNTIL no new eq_rec entries *)
    _old_cnt := eq_rec.eqr_cnt;
    (* check all real EQ-joins entry among each other                   *)
    (* each with 4 cases: left=left, right=right, lft=right, right=left *)
    FOR _i := 1 TO _old_cnt DO
        FOR _j := 1 TO _old_cnt DO
            IF  _i <> _j
            THEN
                BEGIN
                (* set eqt_tabno=0 and eqt_colno=0 *)
                _eq1.eqi_fieldid.eqt_whole := 0;
                IF  (eq_rec.eqr_arr^[ _i ][ cak68_left ].eqi_fieldid.eqt_whole =
                    eq_rec.eqr_arr^[ _j ][ cak68_left ].eqi_fieldid.eqt_whole)
                    AND
                    (eq_rec.eqr_arr^[ _i ][ cak68_left ].eqi_fieldid.eqt_whole <>
                    _jval_field.eqt_whole)
                THEN
                    BEGIN
                    _eq1 := eq_rec.eqr_arr^[ _i ][ cak68_right ];
                    _eq2 := eq_rec.eqr_arr^[ _j ][ cak68_right ];
                    END
                ELSE
                    IF  (eq_rec.eqr_arr^[ _i ][ cak68_right ].eqi_fieldid.eqt_whole =
                        eq_rec.eqr_arr^[ _j ][ cak68_right ].eqi_fieldid.eqt_whole)
                        AND (eq_rec.eqr_arr^[ _i ][ cak68_right ].eqi_fieldid.eqt_whole <>
                        _jval_field.eqt_whole)
                    THEN
                        BEGIN
                        _eq1 := eq_rec.eqr_arr^[ _i ][ cak68_left ];
                        _eq2 := eq_rec.eqr_arr^[ _j ][ cak68_left ];
                        END
                    ELSE
                        IF  (eq_rec.eqr_arr^[ _i ][ cak68_right ].eqi_fieldid.eqt_whole =
                            eq_rec.eqr_arr^[ _j ][ cak68_left ].eqi_fieldid.eqt_whole)
                            AND (eq_rec.eqr_arr^[ _i ][ cak68_right ].eqi_fieldid.eqt_whole <>
                            _jval_field.eqt_whole)
                        THEN
                            BEGIN
                            _eq1 := eq_rec.eqr_arr^ [ _i ][ cak68_left ];
                            _eq2 := eq_rec.eqr_arr^[ _j ][ cak68_right ];
                            END
                        ELSE
                            IF  (eq_rec.eqr_arr^[ _i ][ cak68_left ].eqi_fieldid.eqt_whole =
                                eq_rec.eqr_arr^[ _j ][ cak68_right ].eqi_fieldid.eqt_whole)
                                AND (eq_rec.eqr_arr^[ _i ][ cak68_left ].eqi_fieldid.eqt_whole <>
                                _jval_field.eqt_whole)
                            THEN
                                BEGIN
                                _eq1 := eq_rec.eqr_arr^[ _i ][ cak68_right ];
                                _eq2 := eq_rec.eqr_arr^ [ _j ][ cak68_left ];
                                END;
                            (*ENDIF*) 
                        (*ENDIF*) 
                    (*ENDIF*) 
                (*ENDIF*) 
                IF  (_eq1.eqi_fieldid.eqt_whole <> 0)
                THEN
                    IF  NOT (ak680eq_found (eq_rec, _eq1, _eq2))
                    THEN
                        BEGIN
                        (* transitive join found                         *)
                        (* -> new join condition can be uses T1.A = T3.A *)
                        (* ak680eq_found ensures the non existents of    *)
                        (* this new join condition                       *)
                        IF  ( eq_rec.eqr_cnt + 1 > eq_rec.eqr_capacity )
                        THEN
                            a685expand_eq_rec( acv, eq_rec );
                        (*ENDIF*) 
                        IF  ( eq_rec.eqr_cnt + 1 <= eq_rec.eqr_capacity )
                        THEN
                            BEGIN
                            eq_rec.eqr_cnt := succ( eq_rec.eqr_cnt );
                            eq_rec.eqr_arr^ [ eq_rec.eqr_cnt ][ cak68_left ] := _eq1;
                            eq_rec.eqr_arr^[ eq_rec.eqr_cnt ][ cak68_right ] := _eq2;
                            END;
                        (*ENDIF*) 
                        END;
                    (*ENDIF*) 
                (*ENDIF*) 
                END;
            (*ENDIF*) 
        (*ENDFOR*) 
    (*ENDFOR*) 
UNTIL
    _old_cnt = eq_rec.eqr_cnt;
(*ENDREPEAT*) 
IF  ( eq_rec.eqr_cnt > _oldjcnt )
THEN
    (* insert new EQ-joins in dmli.d_joins *)
    WHILE (( _oldjcnt < eq_rec.eqr_cnt ) AND ( acv.a_returncode = 0 )) DO
        BEGIN
        _oldjcnt := succ(_oldjcnt);
        IF  (( eq_rec.eqr_arr^[ _oldjcnt ][ cak68_left ].eqi_codetype =
            ( eq_rec.eqr_arr^[ _oldjcnt ][ cak68_right ].eqi_codetype ))
            AND NOT
            ( acv.a_mblock.mb_st^ [ eq_rec.eqr_arr^[ _oldjcnt ][ cak68_left ].
            eqi_stackpos ].eop = op_ascii )
            AND
            NOT ( acv.a_mblock.mb_st^ [ eq_rec.eqr_arr^[ _oldjcnt ][ cak68_right ].
            eqi_stackpos ].eop = op_ascii ))
        THEN
            BEGIN
            (* dmli.d_joins.jrc_cnt used as index points to free position !! *)
            dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                  jo_op := op_eq;
            dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                  jo_col_upd_stat := 0;
            dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                  jo_no_join      := false;
            dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                  jo_partno       := 1;
            WITH eq_rec.eqr_arr^[ _oldjcnt ][ cak68_left ] DO
                (* ensure explicite right constant in left-right sequence *)
                (* joins are expressed in joinarr with                    *)
                (* column-constant or column-column                       *)
                IF  ( dmli.d_joins.jrc_joinarr^[ eqi_joinno ].
                    jo_recs[ eqi_jrecs ].jop_tableno <> cak68_join_value )
                THEN
                    BEGIN
                    WITH eq_rec.eqr_arr^[ _oldjcnt ][ cak68_left ] DO
                        dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                              jo_recs[ 1 ] :=
                              dmli.d_joins.jrc_joinarr^[ eqi_joinno ].
                              jo_recs[ eqi_jrecs ];
                    (*ENDWITH*) 
                    WITH eq_rec.eqr_arr^[ _oldjcnt ][ cak68_right ] DO
                        dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                              jo_recs[ 2 ] :=
                              dmli.d_joins.jrc_joinarr^[ eqi_joinno ].
                              jo_recs[ eqi_jrecs ];
                    (*ENDWITH*) 
                    END
                ELSE
                    BEGIN
                    WITH eq_rec.eqr_arr^[ _oldjcnt ][ cak68_right ] DO
                        dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                              jo_recs[ 1 ] :=
                              dmli.d_joins.jrc_joinarr^[ eqi_joinno ].
                              jo_recs[ eqi_jrecs ];
                    (*ENDWITH*) 
                    WITH eq_rec.eqr_arr^[ _oldjcnt ][ cak68_left ] DO
                        dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                              jo_recs[ 2 ] :=
                              dmli.d_joins.jrc_joinarr^[ eqi_joinno ].
                              jo_recs[ eqi_jrecs ];
                    (*ENDWITH*) 
                    END;
                (*ENDIF*) 
            (*ENDWITH*) 
            (* proof wrong "join" case constant-constant *)
            IF  ( dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                jo_recs[ 1 ].jop_tableno = dmli.d_joins.
                jrc_joinarr^[ dmli.d_joins.jrc_cnt ].jo_recs[ 2 ].
                jop_tableno )
                OR
                ( dmli.d_joins.jrc_joinarr^[ dmli.d_joins.jrc_cnt ].
                jo_recs[ 1 ].jop_tableno = cak68_join_value )
            THEN
                dmli.d_joins.jrc_cnt := pred( dmli.d_joins.jrc_cnt );
            (*ENDIF*) 
            dmli.d_joins.jrc_cnt := succ( dmli.d_joins.jrc_cnt );
            IF  ( dmli.d_joins.jrc_capacity <
                dmli.d_joins.jrc_cnt + 1 (* we have to reserve one free item *)
                )
            THEN
                a685expand_joinarr( acv, dmli );
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        END;
    (*ENDWHILE*) 
&ifdef TRACE
(*ENDIF*) 
t01sname( test_ak, 'd_join 2    ' );
a683_output (ak_join, dmli.d_joins);
a683out_equal_record(ak_join, eq_rec);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680next_join_eval (
            VAR acv                 : tak_all_command_glob;
            VAR jtrans              : tak68_join_transition;(* in/out *)
            VAR table_stat          : tak68_one_table_stat; (* in/out *)
            VAR counted_multiplier  : tsp00_Longreal;         (* in *)
            VAR reverse_multiplier  : tsp00_Longreal;         (* in *)
            VAR newsum              : tsp00_Longreal;         (* in/out *)
            VAR newpages            : tsp00_Longreal;         (* in/out *)
            old_recs_per_respage    : tsp00_Int4;              (* in *)
            recs_per_respage        : tsp00_Int4;              (* in *)
            use_operator_join       : boolean);
 
VAR
      _multiplier             : tsp00_Longreal;
      _new_reads              : tsp00_Longreal;
      _temporary_result_pages : tsp00_Longreal;
      _temporary_result_rows  : tsp00_Longreal;
      _temp_res_dist_val      : tsp00_Longreal;
      _base_table_pages       : tsp00_Longreal;
      _base_table_rows        : tsp00_Longreal;
      _base_table_pages_strat : tsp00_Longreal;
      _base_table_rows_strat  : tsp00_Longreal;
      _base_table_dist_val    : tsp00_Longreal;
      _reads_base_table       : tsp00_Longreal;
      _reads_temp_res         : tsp00_Longreal;
 
BEGIN
_temporary_result_pages := newpages;
_temporary_result_rows  := newpages * old_recs_per_respage;
_base_table_pages       := table_stat.ts_all_pages;
_base_table_rows        := table_stat.ts_all_pages *
      table_stat.ts_recs_per_page;
IF  jtrans.jt_jointype in (* h.b. PTS 1106028 *)
    [to_single_keyfield, to_key, to_unique_field]
THEN
    _multiplier := 1
ELSE
    _multiplier := counted_multiplier;
(*ENDIF*) 
&ifdef TRACE
t01name (ak_join, '..next table join:');
a683trace_jointype(ak_join, 'via         ', jtrans.jt_jointype);
t01real (ak_join, 'count multip', counted_multiplier  , 6);
t01real (ak_join, '_multiplier ', _multiplier  , 6);
t01real (ak_join, 'revers multi', reverse_multiplier  , 6);
t01real (ak_join, 'newsum    in', newsum  , 6);
t01real (ak_join, 'newpages  in', newpages, 6);
t01int4 (ak_join, 'new_recs_prp', recs_per_respage);
t01int4 (ak_join, 'old_recs_prp', old_recs_per_respage);
t01int4 (ak_join, 'recs_p_page ', table_stat.ts_recs_per_page);
t01real (ak_join, 'temp res row', _temporary_result_rows, 6);
t01real (ak_join, 'base     row', _base_table_rows, 6);
t01real (ak_join, 'base   pages', _base_table_pages, 6);
t01real (ak_join, 'strat value ', table_stat.ts_strat_value, 6);
t01real (ak_join, 'pages_srchd ', table_stat.ts_pages_searched, 6);
&endif
(* =========================================== *)
(* first estimate the size of the new          *)
(* temporary result                            *)
(* =========================================== *)
CASE jtrans.jt_jointype OF
    to_single_keyfield,
    to_key,
    to_unique_field,
    to_invfield,
    to_invpart,
    to_all_invfields,
    to_first_keyfield,
    to_keypart,
    to_eq_field  :
        BEGIN
        (* the calculation of the count of rows from the new join result     *)
        (* is  derived from the following formula:                           *)
        (*                                                                   *)
        (*    |R join S| = |R|/D(R) * |S|/D(S) * min (D(R), D(S))            *)
        (*                                                                   *)
        (*    with |R|  = count of tuple from R                              *)
        (*         D(R) = distinct values in R                               *)
        (*                                                                   *)
        (*    and multiplier = |R| / D(R)                                    *)
        (*                                                                   *)
        (* for more information see                                          *)
        (*  http://pwww/SAP_DB_80/Vortrge%20Prof.%20Freytag/JoinCosts.ppt   *)
        (*                                                                   *)
        (* special case: multiplier = 0,                                     *)
        (* i.e. D(R) = 0 ( there are no tuples in R)                         *)
        (*                                                                   *)
        IF  ( _multiplier = 0 ) OR ( reverse_multiplier = 0 )
        THEN
            newpages :=  0
        ELSE
            IF  (_base_table_rows / _multiplier) <
                (_temporary_result_rows / reverse_multiplier)
            THEN
                newpages :=  reverse_multiplier * ( _base_table_rows
                      / recs_per_respage )
            ELSE
                newpages := _multiplier * ( _temporary_result_rows
                      / recs_per_respage );
            (*ENDIF*) 
        (*ENDIF*) 
        END;
    to_lt_gt_field :
        newpages := _temporary_result_rows * ( _base_table_rows / 2
              / recs_per_respage );
    to_ne_field,
    to_cartesian_prod :
        newpages := _temporary_result_rows * ( _base_table_rows
              / recs_per_respage );
    to_mt_join :
        BEGIN
        newpages := cak68_max_valid_real;
        a07ak_system_error( acv, 680, 6 );
        END;
    OTHERWISE
        BEGIN
        newpages := cak68_max_valid_real;
        a07ak_system_error( acv, 680, 5 );
        END;
    END;
(*ENDCASE*) 
IF  (newpages < (1 / recs_per_respage))
THEN
    newpages := (1 / recs_per_respage);
(*ENDIF*) 
;
&ifdef TRACE
t01real (ak_join, 'result rows ', newpages * recs_per_respage, 6);
t01real (ak_join, 'newpages    ', newpages, 6);
&endif
(* =========================================== *)
(* at this point the size of the new table has *)
(* been estimated, now the costs are computed: *)
(* =========================================== *)
CASE jtrans.jt_jointype OF
    to_single_keyfield,
    to_key,
    to_unique_field,
    to_invfield,
    to_invpart,
    to_all_invfields,
    to_first_keyfield,
    to_keypart  :   (* h.b. PTS 1106028 *)
        IF  (_multiplier = 0) OR (reverse_multiplier = 0)
        THEN
            _new_reads :=  0
        ELSE
            BEGIN
            _base_table_dist_val :=
                  _base_table_rows / _multiplier;
            _temp_res_dist_val   :=
                  _temporary_result_rows / reverse_multiplier;
&           ifdef TRACE
            t01real (ak_join, 'DV temp_res ', _temp_res_dist_val, 6);
            t01real (ak_join, 'DV base_tab ', _base_table_dist_val, 6);
&           endif
            IF  _temp_res_dist_val / _base_table_dist_val > 1
            THEN
                _reads_base_table :=  _base_table_pages
            ELSE
                _reads_base_table :=  (_temp_res_dist_val /
                      _base_table_dist_val) * _base_table_pages;
            (*ENDIF*) 
            IF  jtrans.jt_jointype in [to_unique_field, to_invfield,
                to_invpart, to_all_invfields]
            THEN
                _reads_base_table := _reads_base_table * cak68_inv_overhead;
            (*ENDIF*) 
            IF  ( use_operator_join AND gg01_operator_join_costfunc )
            THEN
                _reads_temp_res := 0
            ELSE
                BEGIN
                IF  (_temp_res_dist_val > _base_table_dist_val)
                THEN
                    _reads_temp_res :=
                          ((_temp_res_dist_val - _base_table_dist_val) *
                          (_temporary_result_pages / _temp_res_dist_val))
                          + (_base_table_rows *
                          (_temporary_result_pages / _temp_res_dist_val))
                ELSE
                    _reads_temp_res :=
                          (_base_table_rows / _base_table_dist_val) *
                          _temporary_result_pages;
                (*ENDIF*) 
                END;
            (*ENDIF*) 
            _new_reads := _reads_base_table + _reads_temp_res;
&           ifdef TRACE
            t01real (ak_join, '_reads_base ', _reads_base_table, 6);
            t01real (ak_join, '_reads_temp ', _reads_temp_res, 6);
            t01real (ak_join, '_new_reads  ', _new_reads, 6);
&           endif
            END;
        (*ENDIF*) 
    to_eq_field ,
    to_lt_gt_field ,
    to_ne_field,
    to_cartesian_prod :
        BEGIN
        _base_table_pages_strat := table_stat.ts_pages_searched;
        IF  ( use_operator_join AND gg01_operator_join_costfunc )
        THEN
            BEGIN
            _new_reads := _temporary_result_pages +
                  _temporary_result_rows * _base_table_pages_strat;
            END
        ELSE
            BEGIN
            _base_table_rows_strat  := table_stat.ts_pages_searched *
                  table_stat.ts_recs_per_page;
            _new_reads := _base_table_pages_strat +
                  _temporary_result_pages * _base_table_rows_strat;
            END;
        (*ENDIF*) 
        END;
    OTHERWISE
        (* to_mt_join, to_illegal *)
        (* already error thrown   *)
        _new_reads := cak68_max_valid_real
    END;
(*ENDCASE*) 
newsum := newsum + _new_reads;
(* =========================================== *)
(* costs for storeing the records of the new   *)
(* temporary result                            *)
(* =========================================== *)
newsum   := newsum + newpages;
(* =========================================== *)
(* check for value overflow                    *)
(* =========================================== *)
IF  (newpages < 0) OR (newpages > cak68_max_valid_real)
THEN
    newpages := cak68_max_valid_real;
(*ENDIF*) 
IF  (newsum < 0) OR (newsum > cak68_max_valid_real)
THEN
    newsum   := cak68_max_valid_real;
(*ENDIF*) 
;
&ifdef TRACE
t01real (ak_join, ' _newreads  ', _new_reads, 6);
t01real (ak_join, ' newpages   ', newpages, 6);
t01real (ak_join, ' newsum     ', newsum, 6);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680next_qualification (
            VAR acv              : tak_all_command_glob;
            VAR jinfos           : tak68_joininfos;
            VAR qualscanner      : t_qual_scanner;
            VAR eq_rec           : tak68_eq_record;
            VAR local_qual_found : boolean);
 
VAR
      _ready          : boolean;
      _subquery_found : boolean;
      _exists_found   : boolean;
      _value_found    : boolean;
      _in_case_func   : boolean;
      _st_entry_cnt   : integer;
      _i              : integer;
      _j              : integer;
      _tabno          : integer;
 
BEGIN
&ifdef TRACE
t01int4 (ak_join, 'N_QUALstarts', qualscanner.qs_src_pos);
t01int4 (ak_join, 'src maxpos  ', qualscanner.qs_src_maxpos);
t01int4 (ak_join, 'src pos     ', qualscanner.qs_src_pos);
t01int4 (ak_join, 'dst pos     ', qualscanner.qs_dest_pos);
t01stackdesc (ak_join, 'JI_STACK_DES', jinfos.ji_st_addr, jinfos.ji_stack_desc);
&endif
local_qual_found:= false;
_exists_found   := false;
_subquery_found := false;
_value_found    := false;
_ready          := false;
_in_case_func   := false;
_tabno          := cak_is_undefined;
_i              := qualscanner.qs_src_pos;
WHILE NOT _ready DO
    BEGIN
    CASE jinfos.ji_st_addr^[ _i ].etype OF
        st_subquery :
            BEGIN
            _subquery_found := true;
            IF  (jinfos.ji_st_addr^[ _i ].ecol_tab[ 1 ] = chr (1)) AND
                (jinfos.ji_st_addr^[ _i ].ecol_tab[ 2 ] = chr (1))
            THEN
                _exists_found := true;
            (*ENDIF*) 
            END;
        st_value :
            _value_found := true;
        st_varkey, st_fixkey, st_varcol, st_fixcol,
        st_varlongchar :
            BEGIN
            local_qual_found := true;
            IF  _tabno < 0
            THEN
                _tabno := ord(jinfos.ji_st_addr^[ _i ].ecol_tab[ 2 ])
            ELSE
                IF  (ord(jinfos.ji_st_addr^[ _i ].ecol_tab[ 2 ])) <> _tabno
                THEN
                    local_qual_found := false;
&               ifdef TRACE
                (*ENDIF*) 
            (*ENDIF*) 
            t01int4 (ak_join, 'column found', _i);
&           endif
            END;
        (* PTS 1117523 E.Z. *)
        st_build_in_func :
            IF  (jinfos.ji_st_addr^[ _i ].eop_build_in = op_b_case_start)
            THEN
                _in_case_func := true
            ELSE
                IF  (jinfos.ji_st_addr^[ _i ].eop_build_in = op_b_case_stop)
                THEN
                    _in_case_func := false;
                (*ENDIF*) 
            (*ENDIF*) 
        st_bool :
            local_qual_found := true;
        OTHERWISE;
        END;
    (*ENDCASE*) 
    _i := succ(_i);
    IF  _i > qualscanner.qs_src_maxpos
    THEN
        (* break through while loop *)
        _ready := true
    ELSE
        (* jump behind 'AND' and jump to 'JUMP FALSE'   *)
        (* destination (continuous)                     *)
        (* PTS 1117523 E.Z. *)
        IF  NOT _in_case_func
        THEN
            IF  (jinfos.ji_st_addr^[ _i ].etype = st_jump_false) OR
                (jinfos.ji_st_addr^[ _i ].eop   = op_and)        OR
                (jinfos.ji_st_addr^[ _i ].eop   = op_upd_view_and)
            THEN
                BEGIN
                _j := _i;
                IF  (jinfos.ji_st_addr^[ _i ].eop = op_and) OR
                    (jinfos.ji_st_addr^[ _i ].eop = op_upd_view_and)
                THEN
                    _i := succ(_i);
                (* look ahead for last condition *)
                (*ENDIF*) 
                WHILE ((_j <= qualscanner.qs_src_maxpos) AND NOT _ready) DO
                    IF  (jinfos.ji_st_addr^[ _j ].eop = op_and) OR
                        (jinfos.ji_st_addr^[ _j ].eop = op_upd_view_and)
                    THEN
                        BEGIN
                        _j := succ(_j);
                        IF  _j > qualscanner.qs_src_maxpos
                        THEN
                            (* prepare break through outer while loop *)
                            (* last condition found *)
                            _ready := true
                        (*ENDIF*) 
                        END
                    ELSE
                        IF  ( jinfos.ji_st_addr^[ _j ].etype = st_jump_false )
                        THEN
                            BEGIN
                            _j := _j + jinfos.ji_st_addr^[ _j ].epos;
                            IF  _j > qualscanner.qs_src_maxpos
                            THEN
                                (* prepare break through outer while loop *)
                                (* last condition found *)
                                _ready := true
                            (*ENDIF*) 
                            END
                        ELSE
                            (* break through inner while loop *)
                            _j := qualscanner.qs_src_maxpos + 1;
                        (*ENDIF*) 
                    (*ENDIF*) 
                (*ENDWHILE*) 
                END
            (*ENDIF*) 
        (*ENDIF*) 
    (*ENDIF*) 
    END;
(*ENDWHILE*) 
IF  local_qual_found OR _exists_found OR
    (* tie constant qualification ('AND TRUE' or 'OR FALSE' ... ) *)
    (* to first table                                             *)
    (( jinfos.ji_act_join = 1 ) AND
    ( _tabno = cak_is_undefined ) AND _value_found )
THEN
    BEGIN
    _st_entry_cnt := _i - qualscanner.qs_src_pos;
    local_qual_found := true;
    IF  _subquery_found AND
        ( ssSubquery_egg00 in jinfos.ji_stack_desc.mstack_state )
    THEN
        acv.a_mblock.mb_qual^.mstack_state :=
              acv.a_mblock.mb_qual^.mstack_state + [ ssSubquery_egg00 ];
    (*ENDIF*) 
    IF  ( jinfos.ji_st_addr^[ _i - 1 ].eop in [ op_and, op_upd_view_and ] )
    THEN
        _st_entry_cnt := pred(_st_entry_cnt);
    (* do not copy 'AND' *)
    (*ENDIF*) 
    ak680test_and_move_qual( acv, jinfos, qualscanner, eq_rec,
          _tabno, _st_entry_cnt, local_qual_found );
&   ifdef TRACE
    IF  local_qual_found
    THEN
        t01messblock (ak_join, 'MESSBUFF N_Q', acv.a_mblock);
&   endif
    (*ENDIF*) 
    END;
(*ENDIF*) 
qualscanner.qs_src_pos := _i;
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680set_join_order (
            VAR acv             : tak_all_command_glob;
            VAR dmli            : tak_dml_info;
            config              : tak_sysbufferaddress;
            VAR jinfos          : tak68_joininfos;
            VAR table_stats     : tak68_table_stats;
            jtrans              : tak68_join_transitions;
            VAR series          : tak68_sequence;
            VAR res_info        : tak68_result_info;
            VAR mul_tabs        : tak68_mult_tabs);
 
VAR
      _max_costs           : tsp00_Longreal;
      _sort_pos            : tsp00_Int2;
      _dst_table           : tsp00_Int2;
      _i                   : tsp00_Int2;
      _lowest              : tak68_sequence_info;
      _lastsuccession      : tak68_lastsuccession;
      _jtrans_ptr          : tak68_join_transition_ptr;
      _best_strat          : tak68_best_jstrat;
 
      _cast  : RECORD
            CASE integer OF
                1 :
                    (addr: tsp00_Addr);
                2 :
                    (lptr : tak68_last_values_arr_ptr);
                3 :
                    (sptr : tak68_succession_ptr);
                END;
            (*ENDCASE*) 
 
 
BEGIN
(* ================================================================= *)
(* res_info.recs_per_page gives an estimate of the number of records *)
(* in one page of the join-result-table;                             *)
(* it is assumed that (due to additional fields for join-conditions  *)
(* and arithmetic expressions) the length of a result record is      *)
(* 'sup_field_factor' times bigger than the mere sum of lengths      *)
(* of all output fields (as represented by atintoutpos).             *)
(* Furthermore it is assumed that a page is only 80% filled.         *)
(* ================================================================= *)
&ifdef TRACE
t01bool (ak_join, 'd_outer_join', dmli.d_outer_join);
a683join_transition_trace(ak_join, dmli, jtrans, dmli.d_cntfromtab);
a683multabs_trace (ak_join, mul_tabs, mul_tabs.mt_cnt, 0, dmli.d_cntfromtab);
&endif
_lastsuccession.ls_values   := NIL;
_lastsuccession.ls_sequence := NIL;
_lowest.si_sequence         := NIL;
_cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
      dmli.d_cntfromtab * sizeof(tak68_last_values) );
_lastsuccession.ls_values := _cast.lptr;
&ifdef trace
t01addr( ak_join, 'ls_values   ', _cast.addr );
&endif
_cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
      dmli.d_cntfromtab * sizeof(tak68_succ) );
_lastsuccession.ls_sequence := _cast.sptr;
&ifdef trace
t01addr( ak_join, 'ls_sequence ', _cast.addr );
&endif
_cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
      dmli.d_cntfromtab * sizeof(tak68_succ) );
_lowest.si_sequence := _cast.sptr;
&ifdef trace
t01addr( ak_join, 'si_sequence ', _cast.addr );
&endif
;
IF  (( _lastsuccession.ls_values <> NIL ) AND
    ( _lastsuccession.ls_sequence <> NIL ) AND
    ( _lowest.si_sequence <> NIL ))
THEN
    BEGIN
    (* initialize data structure lowest *)
    FOR _i  := 1 TO dmli.d_cntfromtab DO
        BEGIN
        (* set destination tables as original sequence of tables *)
        _lowest.si_sequence^[ _i ].s_tableno := _i;
        _lowest.si_sequence^[ _i ].s_backup  := 0;
        _lowest.si_sequence^[ _i ].s_resrows := 0;
        (* there's no last succesion *)
        _lastsuccession.ls_sequence^[ _i ].s_tableno := 0;
        _lastsuccession.ls_sequence^[ _i ].s_backup  := 0;
        _lastsuccession.ls_sequence^[ _i ].s_resrows := 0;
        END;
    (*ENDFOR*) 
&   ifdef trace
    a683tr_tablenames( ak_join, acv, dmli, dmli.d_cntfromtab );
&   endif
    IF  ( NOT dmli.d_view )
    THEN
        (* search best join transition *)
        a681jnew_seq( acv, dmli, config, table_stats, jtrans,
              _lowest, jinfos, res_info, mul_tabs, _lastsuccession );
    (*ENDIF*) 
    acv.a_costsum  := 0;
    res_info.ri_old_recs_per_page := 1;
    IF  ( dmli.d_view )
    THEN
        FOR _i := 1 TO dmli.d_cntfromtab DO
            res_info.ri_one_tab_len^[ _i ] :=
                  a681tmp_table_length( dmli, jinfos, _i );
        (*ENDFOR*) 
    (* *** and now with c_update_jtrans *** *)
    (*ENDIF*) 
    _max_costs := cak68_max_real;
    a680standard_cost( acv, dmli, config, table_stats, jtrans, jinfos, res_info,
          mul_tabs, _lastsuccession, _max_costs, _lowest, dmli.d_cntfromtab,
          c_final_call );
&   ifdef TRACE
    t01name( ak_join, 'comp si_sequence  ' );
    a683tr_newsucc( ak_join, _lowest.si_sequence, 1, dmli.d_cntfromtab, true );
    t01sname( ak_join, 'lastsuccessi' );
    a683tr_newsucc( ak_join, _lastsuccession.ls_sequence, 1, dmli.d_cntfromtab, true );
&   endif
    (* ************* PREPARE 'SERIES' ************* *)
    (* *************                  ************* *)
    FOR _i := 1 TO dmli.d_cntfromtab DO
        BEGIN
        series[ _i ].jos_access_mod := [];
        series[ _i ].jos_source     := _lowest.si_sequence^[ _i ].s_tableno;
        series[ _i ].jos_fieldcnt   := 1;
        series[ _i ].jos_indexno    := 0;
        series[ _i ].jos_parallel_server := g01optimize_parallel_server;
        IF  (( config <> NIL ) AND
            ( series[ _i ].jos_source <= config^.shint.hint_joincfg_cnt ))
        THEN
            BEGIN
            series[ _i ].jos_parallel_server :=
                  a101_SetMaxParallelServers( config^.shint.
                  hint_join_config[ series[ _i ].jos_source ].cfg_parallel_server );
&           ifdef trace
            t01int4 (ak_join, 'parall. serv', config^.shint.
                  hint_join_config[ series[ _i ].jos_source ].cfg_parallel_server);
            t01int4 (ak_join, 'set to      ', series[ _i ].jos_parallel_server);
&           endif
            END;
        (*ENDIF*) 
        IF  (( config <> NIL ) AND
            ( series[ _i ].jos_source <= config^.shint.hint_joincfg_cnt ) AND
            ( cj_predefined_buffer in config^.shint.
            hint_join_config[ series[ _i ].jos_source ].cfg_join_switches ))
        THEN
            BEGIN
            series[ _i ].jos_predefined_buf := true;
            series[ _i ].jos_table_buffer   := config^.shint.
                  hint_join_config[ series[ _i ].jos_source ].cfg_bufsize;
&           ifdef trace
            t01int4 (ak_join, 'predef buf  ', series[ _i ].jos_table_buffer );
&           endif
            END
        ELSE
            BEGIN
            series[ _i ].jos_predefined_buf := false;
            series[ _i ].jos_table_buffer   := 0;
            END;
        (*ENDIF*) 
        series[ _i ].jos_expected_recs    :=
              round( table_stats[ series[ _i ].jos_source ].ts_pages_searched *
              table_stats[ series[ _i ].jos_source ].ts_recs_per_page );
        IF  abs(MAX_INT4_SP00 / table_stats[ series[ _i ].jos_source ].ts_recs_per_page)
            > table_stats[ series[ _i ].jos_source ].ts_all_pages
        THEN (* PTS 1127791 M.Ki *)
            series[ _i ].jos_expected_table_rec_reads :=
                  table_stats[ series[ _i ].jos_source ].ts_recs_per_page *
                  table_stats[ series[ _i ].jos_source ].ts_all_pages
        ELSE
            series[ _i ].jos_expected_table_rec_reads := 0;
        (*ENDIF*) 
        series[ _i ].jos_expected_res_recs := _lowest.si_sequence^ [ _i ].s_resrows;
        series[ _i ].jos_invlen := IS_UNDEFINED_GG07;
&       ifdef trace
        t01int4 (ak_join, 'jos_source  ', series[ _i ].jos_source );
        t01real (ak_join, 'ts_pages_sea', table_stats[ series[ _i ].jos_source ].ts_pages_searched, 6);
        t01int4 (ak_join, 'ts_recs_per_', table_stats[ series[ _i ].jos_source ].ts_recs_per_page);
        t01int4 (ak_join, 'jos_expected', series[ _i ].jos_expected_recs );
&       endif
        IF  ( _i = 1 )
        THEN
            BEGIN
            series[ 1 ].jos_joinstrat := strat_key_range;
            series[ 1 ].jos_joinno    := cak68_cartesian_product;
            END
        ELSE
            (* get best join transition information *)
            BEGIN
            _dst_table            := _lowest.si_sequence^[ _i ].s_tableno;
            _best_strat.bj_srctab := _lowest.si_sequence^[ _i ].s_backup;
&           ifdef TRACE
            t01int4 (ak_join, 'table       ', _dst_table);
            t01int4 (ak_join, 'best jn from', _best_strat.bj_srctab);
&           endif
            _jtrans_ptr :=
                  a685get_join_trans( dmli, jtrans, _best_strat.bj_srctab, _dst_table );
            _best_strat.bj_jtype := _jtrans_ptr^.jt_jointype;
            (* set jos_joinstrat, jos_indexno, [jos_fieldcnt,] jos_joinno *)
            (* values for series[ 1 ].* will be put in ak684write_strat_info() *)
            IF  _best_strat.bj_jtype < to_eq_field
            THEN
                BEGIN
                series[ _i ].jos_expected_recs := _jtrans_ptr^.jt_multipl;
                CASE _best_strat.bj_jtype OF
                    to_single_keyfield :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_key_equal;
                        series[ _i ].jos_predefined_buf := true;
                        END;
                    to_key :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_all_keys_equal;
                        series[ _i ].jos_predefined_buf := true;
                        END;
                    to_first_keyfield :
                        series[ _i ].jos_joinstrat := strat_join_key_next;
                    to_keypart :
                        series[ _i ].jos_joinstrat := strat_join_key_range;
                    to_unique_field,
                    to_all_invfields :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_all_inv_equal;
                        IF  ( _best_strat.bj_jtype = to_unique_field )
                        THEN
                            series[ _i ].jos_predefined_buf := true;
                        (*ENDIF*) 
                        END;
                    to_invpart :
                        series[ _i ].jos_joinstrat := strat_join_inv_range;
                    to_mt_join :
                        (* mark multiple transitions           *)
                        series[ _i ].jos_joinstrat := strat_more_than_one;
                    OTHERWISE
                        (* to_invfield *)
                        series[ _i ].jos_joinstrat := strat_join_inv;
                    END;
                (*ENDCASE*) 
                series[ _i ].jos_indexno := _jtrans_ptr^.jt_indexno;
                series[ _i ].jos_joinno  := _jtrans_ptr^.jt_joinno;
                END
            ELSE
                (* _best_strat.bj_jtype >= to_eq_field *)
                BEGIN
                series[ _i ].jos_joinstrat := strat_key_range;
                series[ _i ].jos_indexno   := 0;
                series[ _i ].jos_joinno    := cak68_cartesian_product;
                END;
            (*ENDIF*) 
            END
        (*ENDIF*) 
        END;
    (*ENDFOR*) 
    ;
&   ifdef TRACE
    t01int4 (ak_join, 'costsum     ', acv.a_costsum);
    t01int4 (ak_join, 'costwarning ', acv.a_costwarn_value);
    t01int4 (ak_join, 'costlimit   ', acv.a_costlimit_value);
    t01real (ak_join, 'si_sum      ', _lowest.si_sum, 6);
&   endif
    IF  ( acv.a_costcheck )
    THEN
        BEGIN
        IF  ( acv.a_costsum + _lowest.si_sum + 1 < csp_maxint4 )
        THEN
            acv.a_costsum := acv.a_costsum + trunc(_lowest.si_sum + 1)
        ELSE
            acv.a_costsum := csp_maxint4;
        (*ENDIF*) 
        IF  ( NOT acv.a_intern_explain (* no EXPLAIN command *)
            )
        THEN
            IF  ( acv.a_costsum > acv.a_costlimit_value )
            THEN
                a07_b_put_error( acv, e_costlimit_overflow, 1 )
            ELSE
                IF  (acv.a_costsum > acv.a_costwarn_value) AND
                    (acv.a_comp_type <> at_odbc) AND
                    (acv.a_comp_type <> at_jdbc)
                THEN
                    a07_b_put_error( acv, e_costwarning_overflow, 1 );
                (*ENDIF*) 
            (*ENDIF*) 
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    ;
    _sort_pos := 0;
    FOR _i := 2 TO dmli.d_cntfromtab DO
        BEGIN
        mul_tabs.mt_pos := 0;
        IF  ( series[ _i ].jos_joinstrat = strat_more_than_one )
        THEN
            BEGIN
            _best_strat.bj_jtype      := to_eq_field;
            _best_strat.bj_multiplier := csp_maxint4;
            _best_strat.bj_fieldcnt   := 1;
            _best_strat.bj_indexno    := 0;
            a681lowest_multiple_strat( acv, dmli, config,
                  jtrans, _lowest.si_sequence, mul_tabs,
                  series[ _i - 1 ].jos_source(* src tab *),
                  series[ _i ].jos_source(* dst tab *),
                  _best_strat, _i, NOT c_update_jtrans );
            IF  ( mul_tabs.mt_pos > 0 )
            THEN
                (* multiple table join transition found better than to_eq_field *)
                BEGIN
                series[ _i ].jos_fieldcnt :=
                      mul_tabs.mt_arr^[ mul_tabs.mt_pos ].mtr_fieldcnt;
                series[ _i ].jos_joinno  :=
                      mul_tabs.mt_arr^[ mul_tabs.mt_pos ].mtr_joinno;
                series[ _i ].jos_expected_recs  :=
                      mul_tabs.mt_arr^[ mul_tabs.mt_pos ].mtr_multipl;
                CASE _best_strat.bj_jtype OF
                    to_key :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_all_keys_equal;
                        series[ _i ].jos_predefined_buf := true;
                        END;
                    to_first_keyfield :
                        series[ _i ].jos_joinstrat := strat_join_key_next;
                    to_keypart :
                        series[ _i ].jos_joinstrat := strat_join_key_range;
                    to_unique_field,
                    to_all_invfields :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_all_inv_equal;
                        IF  ( _best_strat.bj_jtype = to_unique_field )
                        THEN
                            series[ _i ].jos_predefined_buf := true;
                        (*ENDIF*) 
                        series[ _i ].jos_indexno  :=
                              mul_tabs.mt_arr^ [ mul_tabs.mt_pos ].mtr_indexno;
                        END;
                    to_invpart :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_inv_range;
                        series[ _i ].jos_indexno   :=
                              mul_tabs.mt_arr^ [ mul_tabs.mt_pos ].mtr_indexno;
                        END;
                    to_invfield :
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_inv;
                        (* update jos_indexno to used index *)
                        series[ _i ].jos_indexno :=
                              mul_tabs.mt_arr^[ mul_tabs.mt_pos ].mtr_indexno;
                        series[ _i ].jos_joinno  :=
                              mul_tabs.mt_arr^[ mul_tabs.mt_pos ].mtr_joinno;
                        END;
                    to_single_keyfield:
                        BEGIN
                        series[ _i ].jos_joinstrat := strat_join_key_equal;
                        series[ _i ].jos_predefined_buf := true;
                        END;
                    OTHERWISE
                        a07ak_system_error( acv, 680, 4 );
                    END;
                (*ENDCASE*) 
&               ifdef TRACE
                t01strat_enum( ak_join, 'jos_joinstra', series[ _i ].jos_joinstrat );
                t01int4 (ak_join, 'jfieldcnt   ', series[ _i ].jos_fieldcnt);
                t01int4 (ak_join, 'jindexno    ', series[ _i ].jos_indexno);
&               endif
                END
            ELSE
                a07ak_system_error( acv, 680, 3 );
            (*ENDIF*) 
            END;
        (* **************************************** *)
        (* jos_indexno points to the mt_arr element *)
        (* which offers a multiple-field strategy   *)
        (* **************************************** *)
        (*ENDIF*) 
        CASE series[ _i ].jos_joinstrat OF
            strat_join_all_keys_equal,
            strat_join_key_range :
                BEGIN
                (* changes mtr_joinno !!! *)
                a681key_sort_joinarr (acv, dmli, jinfos, series,
                      _i (* sequence pos *), _sort_pos, mul_tabs);
                IF  ( mul_tabs.mt_pos > 0 )
                THEN
                    BEGIN
                    (* reset jos_indexno *)
                    series[ _i ].jos_indexno := 0;
                    series[ _i ].jos_joinno  :=
                          mul_tabs.mt_arr^ [ mul_tabs.mt_pos ].mtr_joinno;
                    END;
                (*ENDIF*) 
                END;
            strat_join_all_inv_equal,
            strat_join_inv_range :
                BEGIN
                (* changes mtr_joinno !!! *)
                a681inv_sort_joinarr (acv, dmli, jinfos,
                      series, _i, _sort_pos, mul_tabs);
                IF  ( mul_tabs.mt_pos > 0 )
                THEN
                    BEGIN
                    (* update jos_indexno to used index *)
                    series[ _i ].jos_indexno  :=
                          mul_tabs.mt_arr^ [ mul_tabs.mt_pos ].mtr_indexno;
                    series[ _i ].jos_joinno  :=
                          mul_tabs.mt_arr^ [ mul_tabs.mt_pos ].mtr_joinno;
                    END;
                (*ENDIF*) 
                END;
            OTHERWISE ;
                (* strat_join_key_equal, strat_join_key_next,
                      strat_join_inv *)
            END;
        (*ENDCASE*) 
        END;
    (*ENDFOR*) 
    ;
    FOR _i := 0 TO dmli.d_joins.jrc_cnt - 1 DO
        BEGIN
        dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_outpos := 0;
        dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ].jop_outpos := 0;
        END;
    (*ENDFOR*) 
&   ifdef TRACE
    t01real (ak_join, 'si_sum      ', _lowest.si_sum,3);
    a683tr_sequence( ak_join, dmli, series );
    t01int4 (ak_join, 'costsum     ', acv.a_costsum);
&   endif
    ;
    (* starting with series[ _i ].jos_joinno all join transitions *)
    (* are clustered according following fields                   *)
    END
ELSE
    a07_b_put_error( acv, e_no_more_memory, 1 );
(*ENDIF*) 
IF  ( _lastsuccession.ls_values <> NIL )
THEN
    BEGIN
    _cast.lptr := _lastsuccession.ls_values;
&   ifdef trace
    t01addr( ak_join, 'ls_values   ', _cast.addr );
&   endif
    gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
    END;
(*ENDIF*) 
IF  ( _lastsuccession.ls_sequence <> NIL )
THEN
    BEGIN
    _cast.sptr := _lastsuccession.ls_sequence;
&   ifdef trace
    t01addr( ak_join, 'ls_sequence ', _cast.addr );
&   endif
    gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
    END;
(*ENDIF*) 
IF  ( _lowest.si_sequence <> NIL )
THEN
    BEGIN
    _cast.sptr := _lowest.si_sequence;
&   ifdef trace
    t01addr( ak_join, 'si_sequence ', _cast.addr );
&   endif
    gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680some_and_parts (
            VAR acv         : tak_all_command_glob;
            VAR jinfos      : tak68_joininfos;
            VAR qualscanner : t_qual_scanner;
            VAR eq_rec      : tak68_eq_record;
            old_found       : boolean);
 
VAR
      _new_found    : boolean;
      _stackentrynr : integer;
 
BEGIN
&ifdef TRACE
t01int4 (ak_join, 'qs_src_maxpo', qualscanner.qs_src_maxpos);
t01int4 (ak_join, 'qs_src_pos  ', qualscanner.qs_src_pos);
t01int4 (ak_join, 'qs_dest_pos ', qualscanner.qs_dest_pos);
t01bool (ak_join, 'old_found   ', old_found);
t01qual (ak_join, acv.a_mblock.mb_qual^);
&endif
(* break through condition for recursive function calls *)
IF  ( qualscanner.qs_src_pos <= qualscanner.qs_src_maxpos )
THEN
    BEGIN
    IF  ( acv.a_mblock.mb_qual^.mqual_pos = 0 )
    THEN
        acv.a_mblock.mb_qual^.mqual_pos := acv.a_mblock.mb_qual^.mfirst_free;
    (*ENDIF*) 
    IF  ( old_found )
    THEN
        BEGIN
        _stackentrynr := qualscanner.qs_dest_pos;
&       ifdef TRACE
        (* put dummy qualification separator *)
        WITH acv.a_mblock.mb_st^[ qualscanner.qs_dest_pos ] DO
            BEGIN
            etype    := st_dummy;
            eop      := op_none;
            epos     := 1;
            elen_var := 0;
            ecol_pos := 0
            END;
        (*ENDWITH*) 
&       endif
        (* create gap for operator between conditions *)
        qualscanner.qs_dest_pos := succ(qualscanner.qs_dest_pos);
        acv.a_mblock.mb_qual^.mqual_cnt   :=
              succ(acv.a_mblock.mb_qual^.mqual_cnt);
        acv.a_mblock.mb_qual^.mfirst_free :=
              succ(acv.a_mblock.mb_qual^.mfirst_free);
        END;
    (*ENDIF*) 
    _new_found := false;
    ak680next_qualification (acv, jinfos, qualscanner, eq_rec, _new_found);
&   ifdef TRACE
    t01bool (ak_join, '_new_found  ', _new_found);
&   endif
    IF  ((NOT _new_found) AND old_found)
    THEN
        BEGIN
        (* remove created gap *)
        qualscanner.qs_dest_pos := pred(qualscanner.qs_dest_pos);
        acv.a_mblock.mb_qual^.mqual_cnt   :=
              pred(acv.a_mblock.mb_qual^.mqual_cnt);
        acv.a_mblock.mb_qual^.mfirst_free :=
              pred(acv.a_mblock.mb_qual^.mfirst_free)
        END;
    (*ENDIF*) 
    qualscanner.qs_src_pos := succ(qualscanner.qs_src_pos);
    IF  ( qualscanner.qs_src_pos <= qualscanner.qs_src_maxpos ) AND
        ( jinfos.ji_st_addr^[ qualscanner.qs_src_pos ].etype = st_jump_false )
    THEN
        qualscanner.qs_src_pos := succ(qualscanner.qs_src_pos);
    (*ENDIF*) 
    IF  (old_found AND _new_found)
    THEN
        qualscanner.qs_dest_pos := acv.a_mblock.mb_qual^.mqual_pos +
              acv.a_mblock.mb_qual^.mqual_cnt;
    (*ENDIF*) 
    ak680some_and_parts( acv, jinfos, qualscanner, eq_rec,
          _new_found OR old_found );
    IF  ( old_found AND _new_found )
    THEN
        BEGIN
        a65_set_operator( acv, op_and );
        (* fill created gap with operator *)
        a61_set_jump( acv.a_mblock, _stackentrynr, st_jump_false );
        qualscanner.qs_dest_pos := acv.a_mblock.mb_qual^.mqual_pos +
              acv.a_mblock.mb_qual^.mqual_cnt
        END
    (*ENDIF*) 
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680sort_joins (VAR joins : tak_joinrec);
 
VAR
      _searchop : tgg00_StackOpType;
      _i        : integer;
      _j        : integer;
      _mj       : tak_one_join;
 
BEGIN
_i := 0;
_searchop := op_eq;
WHILE ( _i < joins.jrc_cnt ) DO
    BEGIN
    _j := _i;
    WHILE ( _j < joins.jrc_cnt ) DO
        BEGIN
        IF  ( joins.jrc_joinarr^[ _j ].jo_op = _searchop )
        THEN
            BEGIN
            (* swap [_i] <--> [_j], _i <= _j *)
            _mj                     := joins.jrc_joinarr^[ _i ];
            joins.jrc_joinarr^[ _i ] := joins.jrc_joinarr^[ _j ];
            joins.jrc_joinarr^[ _j ] := _mj;
            _j := joins.jrc_cnt + 2; (* break inner while *)
            _i := succ(_i);
            END
        ELSE
            _j := succ(_j);
        (*ENDIF*) 
        END;
    (*ENDWHILE*) 
    IF  ( _j = joins.jrc_cnt )
    THEN
        BEGIN
        (* switch to op with less precedence , because op *)
        (* with actual precedence wasn't found            *)
        CASE _searchop OF
            op_eq :
                _searchop := op_lt;
            op_lt :
                _searchop := op_le;
            op_le :
                _searchop := op_gt;
            op_gt :
                _searchop := op_ge;
            op_ge :
                _searchop := op_ne;
            op_ne :
                _searchop := op_like;
            op_like :
                _searchop := op_sounds;
            op_sounds :
                _searchop := op_not_like;
            op_not_like :
                _searchop := op_not_sounds;
            OTHERWISE : (* PTS 1113327 *)
                (* expression like "<col> (+) is null" found -> end sort *)
                _i := joins.jrc_cnt + 2;
            END
        (*ENDCASE*) 
        END;
    (*ENDIF*) 
    END;
(*ENDWHILE*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680test_and_move_qual (
            VAR acv              : tak_all_command_glob;
            VAR jinfos           : tak68_joininfos;
            VAR qualscanner      : t_qual_scanner;
            VAR eq_rec           : tak68_eq_record;
            tabno                : integer;
            st_entry_cnt         : integer;
            VAR local_qual_found : boolean);
 
VAR
      _curr_eq      : integer;
      _eq_ind       : integer;
      _i            : integer;
      _st_ptr       : integer;
      _cc93_comp_int: integer;
      (* cc from 1993 otherwise produces *)
      (*                      wrong code *)
      _op           : tgg00_StackOpType;
      _new_codetype : tsp00_CodeType;
      _old_codetype : tsp00_CodeType;
 
BEGIN
&ifdef TRACE
t01int4 (ak_join,'tabno       ', tabno);
t01int4 (ak_join,'ji_acttabno ', jinfos.ji_acttabno);
t01int4 (ak_join,'src pos     ', qualscanner.qs_src_pos);
t01int4 (ak_join,'st_entry_cnt', st_entry_cnt);
t01bool (ak_join,'local qualif', local_qual_found);
&endif
IF  ( tabno <> jinfos.ji_acttabno ) AND ( tabno <> cak_is_undefined )
THEN
    BEGIN
    (* look for transitiv inheritance of local qualification *)
    (* T3.A > X ... AND T1.A = T3.A    ====>  T1.A > X *)
    _i      := qualscanner.qs_src_pos;
    _st_ptr := acv.a_mblock.mb_qual^.mfirst_free;
    WHILE _i < qualscanner.qs_src_pos + st_entry_cnt DO
        BEGIN
&       ifdef TRACE
        t01stackentry (ak_join, jinfos.ji_st_addr^[ _i ], _i);
&       endif
        IF  ( jinfos.ji_st_addr^[ _i ].etype in
            [ st_fixkey, st_varkey, st_fixcol, st_varcol, st_varlongchar ] )
        THEN
            BEGIN
            _curr_eq := 1;
            (* test if table of actual condition has an EQUAL join *)
            WHILE _curr_eq <= eq_rec.eqr_cnt DO
                BEGIN
                _eq_ind := 0;
                _op     := jinfos.ji_st_addr^[ _i ].eop;
&               ifdef TRACE
                t01sname (ak_join, 'DIFF RCC<>CC');
                t01int4 (ak_join, '_curr_eq    ', _curr_eq);
                t01stackentry (ak_join,
                      jinfos.ji_st_addr^[ eq_rec.
                      eqr_arr^[ _curr_eq ][ cak68_left ].eqi_stackpos ],
                      eq_rec.eqr_arr^[ _curr_eq ][ cak68_left ].eqi_stackpos);
                t01stackentry (ak_join, jinfos.ji_st_addr^[ _i ], _i);
&               endif
                WITH jinfos.ji_st_addr^[ eq_rec.eqr_arr^[ _curr_eq ][ cak68_left ].
                     eqi_stackpos ] DO
                    BEGIN
                    _cc93_comp_int :=
                          ord (jinfos.ji_st_addr^[ _i ].ecol_tab[ 2 ]);
                    IF  (etype    = jinfos.ji_st_addr^[ _i ].etype)    AND
                        (epos     = jinfos.ji_st_addr^[ _i ].epos)     AND
                        (elen_var = jinfos.ji_st_addr^[ _i ].elen_var) AND
                        (ord (ecol_tab[ 2 ]) = _cc93_comp_int)
                    THEN
                        BEGIN
                        _old_codetype := eq_rec.eqr_arr^[ _curr_eq ][ cak68_left ].
                              eqi_codetype;
                        _new_codetype := eq_rec.eqr_arr^[ _curr_eq ][ cak68_right ].
                              eqi_codetype;
                        _eq_ind := eq_rec.eqr_arr^[ _curr_eq ][ cak68_right ].
                              eqi_stackpos;
&                       ifdef TRACE
                        t01int4 (ak_join, '1. Variante ', _eq_ind);
&                       endif
                        END
                    (*ENDIF*) 
                    END;
                (*ENDWITH*) 
                WITH jinfos.ji_st_addr^[ eq_rec.eqr_arr^[ _curr_eq ][ cak68_right ].
                     eqi_stackpos ] DO
                    IF  (etype    = jinfos.ji_st_addr^[ _i ].etype)    AND
                        (epos     = jinfos.ji_st_addr^[ _i ].epos)     AND
                        (elen_var = jinfos.ji_st_addr^[ _i ].elen_var) AND
                        (ord (ecol_tab[ 2 ]) = _cc93_comp_int)
                    THEN
                        BEGIN
                        _old_codetype := eq_rec.eqr_arr^[ _curr_eq ][ cak68_right ].
                              eqi_codetype;
                        _new_codetype := eq_rec.eqr_arr^[ _curr_eq ][ cak68_left ].
                              eqi_codetype;
                        _eq_ind := eq_rec.eqr_arr^[ _curr_eq ][ cak68_left ].
                              eqi_stackpos;
&                       ifdef TRACE
                        t01int4 (ak_join, '2. Variante ', _eq_ind);
&                       endif
                        END;
&                   ifdef TRACE
                    (*ENDIF*) 
                (*ENDWITH*) 
                t01int4 (ak_join, '_eq_ind     ', _eq_ind);
&               endif
                IF  ( _eq_ind <> 0 )
                THEN
                    IF  ( ord(jinfos.ji_st_addr^[ _eq_ind ].
                        ecol_tab[ 2 ]) = jinfos.ji_acttabno )
                    THEN
                        BEGIN
                        (* table of condition is actual join table *)
                        (* and has an EQUAL join *)
&                       ifdef TRACE
                        t01sname(ak_join, 'trans found ');
&                       endif
                        IF  ( _old_codetype <> _new_codetype )
                        THEN
                            IF  ( _op <> op_none )
                            THEN
                                (* table of condition has operation *)
                                (* don't create transition          *)
                                _curr_eq := succ(eq_rec.eqr_cnt);
                            (*ENDIF*) 
                        (*ENDIF*) 
                        IF  ( _curr_eq <= eq_rec.eqr_cnt )
                        THEN
                            BEGIN
                            acv.a_mblock.mb_st^[ _st_ptr ] :=
                                  jinfos.ji_st_addr^[ _eq_ind ];
                            _curr_eq := csp_maxint2;
                            acv.a_mblock.mb_st^[ _st_ptr ].eop := _op;
                            END;
                        (*ENDIF*) 
                        END
                    ELSE
                        (* table of condition isn't actual join table *)
                        (* get next EQUAL condition                   *)
                        _curr_eq := succ(_curr_eq)
                    (*ENDIF*) 
                ELSE
                    (* table of condition hasn't EQUAL join *)
                    (* get next EQUAL condition             *)
                    _curr_eq := succ(_curr_eq);
                (*ENDIF*) 
                END; (* _curr_eq <= eqr_cnt *)
            (*ENDWHILE*) 
            IF  ( _curr_eq < csp_maxint2 )
            THEN
                BEGIN
                _i     := csp_maxint2;
                local_qual_found := false;
                END;
            (*ENDIF*) 
            END
        ELSE
            acv.a_mblock.mb_st^[ _st_ptr ] := jinfos.ji_st_addr^[ _i ];
        (*ENDIF*) 
        _i      := succ(_i);
        _st_ptr := succ(_st_ptr);
        END;
    (*ENDWHILE*) 
    END
ELSE
    SAPDB_PascalMove ('VAK680',   1,    
          (jinfos.ji_stack_desc.mst_max * STACK_ENTRY_MXGG00),
          acv.a_mblock.mb_st_size,
          @jinfos.ji_st_addr^,
          ((qualscanner.qs_src_pos - 1) * STACK_ENTRY_MXGG00 + 1),
          @acv.a_mblock.mb_st^,
          ((qualscanner.qs_dest_pos - 1) * STACK_ENTRY_MXGG00 + 1),
          st_entry_cnt * STACK_ENTRY_MXGG00,
          acv.a_returncode);
(*ENDIF*) 
IF  ( local_qual_found )
THEN
    BEGIN
    IF  ( jinfos.ji_stack_desc.mview_pos > 0 )
        AND
        ( qualscanner.qs_src_pos >= jinfos.ji_stack_desc.mview_pos )
    THEN
        BEGIN
        acv.a_mblock.mb_qual^.mview_cnt := st_entry_cnt;
        IF  ( acv.a_mblock.mb_qual^.mview_pos = 0 )
        THEN
            acv.a_mblock.mb_qual^.mview_pos := qualscanner.qs_dest_pos;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    qualscanner.qs_dest_pos := qualscanner.qs_dest_pos + st_entry_cnt;
    acv.a_mblock.mb_qual^.mqual_cnt   :=
          acv.a_mblock.mb_qual^.mqual_cnt + st_entry_cnt;
    acv.a_mblock.mb_qual^.mfirst_free :=
          acv.a_mblock.mb_qual^.mfirst_free + st_entry_cnt
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680transfer_qualification (
            VAR acv    : tak_all_command_glob;
            VAR jinfos : tak68_joininfos;
            VAR eq_rec : tak68_eq_record);
 
VAR
      _found       : boolean;
      _qualscanner : t_qual_scanner;
 
BEGIN
&ifdef TRACE
(* fight against usecheck *)
_qualscanner.qs_filler := 0;
&endif
_qualscanner.qs_src_pos := jinfos.ji_stack_desc.mqual_pos;
IF  ( jinfos.ji_st_addr^[ _qualscanner.qs_src_pos ].etype = st_jump_output )
THEN
    (* jump over output description *)
    _qualscanner.qs_src_pos := _qualscanner.qs_src_pos +
          jinfos.ji_st_addr^[ jinfos.ji_stack_desc.mqual_pos ].epos - 1;
(*ENDIF*) 
_qualscanner.qs_src_maxpos := jinfos.ji_stack_desc.mqual_pos +
      jinfos.ji_stack_desc.mqual_cnt - 1;
_qualscanner.qs_dest_pos := acv.a_mblock.mb_qual^.mfirst_free;
_found := false;
ak680some_and_parts( acv, jinfos, _qualscanner, eq_rec, _found );
IF  ( acv.a_mblock.mb_qual^.mqual_cnt > 0 )
THEN
    acv.a_mblock.mb_qual^.mfirst_free := acv.a_mblock.mb_qual^.mqual_pos +
          acv.a_mblock.mb_qual^.mqual_cnt
ELSE
    acv.a_mblock.mb_qual^.mfirst_free := 1;
(*ENDIF*) 
IF  ( acv.a_mblock.mb_qual^.mview_pos > 0 )
THEN
    acv.a_mblock.mb_qual^.mview_cnt := acv.a_mblock.mb_qual^.mfirst_free -
          acv.a_mblock.mb_qual^.mview_pos;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680evaluate_joins (
            VAR acv         : tak_all_command_glob;
            VAR dmli        : tak_dml_info;
            config          : tak_sysbufferaddress;
            VAR jinfos      : tak68_joininfos;
            jtrans          : tak68_join_transitions;
            VAR mul_tabs    : tak68_mult_tabs (* to be filled *));
 
VAR
      _dst_field      : tsp00_Int4;
      _src_table      : tsp00_Int4;
      _dst_table      : tsp00_Int4;
      _i              : tsp00_Int4;
      _src            : tsp00_Int4;
      _dst            : tsp00_Int4;
      _jtrans_ptr     : tak68_join_transition_ptr;
      _jtrans_ptr1    : tak68_join_transition_ptr;
      _dst_prop       : tak_jcolpropset;
      _old_jointype   : tak68_one_jointype;
      _tmptype        : tak68_one_jointype;
      _tmp            : tak_one_joinpart;
      _join_val_found : boolean;
 
BEGIN
&ifdef TRACE
a683output_joins(ak_join, acv, dmli, dmli.d_joins);
cccprint('AK680 ak680evalu');
&endif
_join_val_found := false;
(* initialize transition table *)
FOR _src := 1 TO dmli.d_cntfromtab DO
    FOR _dst := 1 TO dmli.d_cntfromtab DO
        BEGIN
        _jtrans_ptr := a685get_join_trans( dmli, jtrans, _src, _dst );
        IF  _src = _dst
        THEN
            _jtrans_ptr^.jt_jointype := to_illegal
        ELSE
            _jtrans_ptr^.jt_jointype := to_cartesian_prod;
        (*ENDIF*) 
        _jtrans_ptr^.jt_indexno  := 0;
        _jtrans_ptr^.jt_multipl  := csp_maxint4;
        END;
    (*ENDFOR*) 
(*ENDFOR*) 
FOR _i := 0 TO dmli.d_joins.jrc_cnt - 1 DO
    BEGIN
    (* loop over join conditions *)
&   ifdef TRACE
    t01int4(ak_join, 'joinarr pos ', _i );
&   endif
    IF  ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_tableno =
        cak68_join_value )
    THEN
        (* switch *)
        BEGIN
        _tmp := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ];
        dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ] :=
              dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ];
        dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ] := _tmp;
        END;
    (*ENDIF*) 
    _src_table := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_tableno;
    _dst_table := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ].jop_tableno;
    IF  (_dst_table = cak68_join_value)
    THEN
        BEGIN
        (* S.column <op> <constant> *)
        _dst_table      := _src_table;
        _dst            := 1;
        _join_val_found := true;
        (* observe the involved table *)
        _src            := 2;
        END
    ELSE
        BEGIN
        (* S.column <op> D.column *)
        (* consider right table first *)
        _dst            := 2;
        _join_val_found := false;
        (* observe two involved table  *)
        _src            := 1;
        END;
    (*ENDIF*) 
    _dst_prop  := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ _dst ].jop_propset;
    _dst_field := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ _dst ].jop_fieldno;
    IF  (_dst_table <> cak68_join_value)
        (* avoid <value> op <value> *)
    THEN
        BEGIN
        (* find  out join transitions *)
        WHILE ( _src < 3 ) DO
            BEGIN
            (* work twice, part 1 and 2 *)
            IF  (( _src_table <> _dst_table ) OR _join_val_found )
            THEN
                BEGIN
                _jtrans_ptr :=
                      a685get_join_trans( dmli, jtrans, _src_table, _dst_table );
&               ifdef TRACE
                t01name( ak_join, '##################' );
                IF  _dst = 1
                THEN
                    t01sname(ak_join, 'left site   ')
                ELSE
                    t01sname(ak_join, 'right site  ');
                (*ENDIF*) 
                t01p2int4 (ak_join, '_src_table  ', _src_table,
                      '_dst_table  ', _dst_table);
                a683trace_jointype(ak_join, 'jointype    ',
                      _jtrans_ptr^.jt_jointype);
                a683trace_jprops(ak_join, _dst_prop);
                t01stackentry (ak_join,
                      jinfos.ji_st_addr^[ dmli.d_joins.jrc_joinarr^[ _i ].
                      jo_recs[ 1 ].jop_startstack ],
                      dmli.d_joins.jrc_joinarr^[ _i ].
                      jo_recs[ 1 ].jop_startstack);
                t01stackentry (ak_join,
                      jinfos.ji_st_addr^[ dmli.d_joins.jrc_joinarr^[ _i ].
                      jo_recs[ 2 ].jop_startstack ],
                      dmli.d_joins.jrc_joinarr^[ _i ].
                      jo_recs[ 2 ].jop_startstack);
&               endif
                (* remember initial join transition *)
                _old_jointype := _jtrans_ptr^.jt_jointype;
                IF  (( _jtrans_ptr^.jt_jointype <> to_illegal )
                    OR _join_val_found )
                THEN
                    BEGIN
                    (* work for all 'S.col <op> D.col' or  *)
                    (* 'S.col <op> <constant>'             *)
                    (* if _join_val_found -> _src_table = _dst_table *)
                    IF  ( dmli.d_joins.jrc_joinarr^[ _i ].jo_op = op_eq )
                    THEN
                        (* S.column = <constant> or *)
                        (* S.column = D.column      *)
                        BEGIN
                        (* check for jtonlykey *)
                        IF  ( jtonlykey in _dst_prop )
                        THEN
                            (* key consists of one column *)
                            BEGIN
                            IF  (( config <> NIL ) AND
                                ( _dst_table <= config^.shint.hint_joincfg_cnt ))
                            THEN
                                BEGIN
                                _jtrans_ptr^.jt_jointype :=
                                      a681keval_key_kind( acv, dmli, jinfos,
                                      mul_tabs, _i (* no. in joinarr[] *),
                                      _dst (* no. in jo_recs[] *),
                                      _join_val_found );
                                END
                            ELSE
                                BEGIN
&                               ifdef trace
                                t01name(ak_join, 'to_single_keyfield');
&                               endif
                                _jtrans_ptr^.jt_jointype := to_single_keyfield;
                                _jtrans_ptr^.jt_indexno  := _dst_field;
                                _jtrans_ptr^.jt_multipl  := dmli.d_joins.jrc_joinarr^[ _i ].
                                      jo_recs[ _dst ].jop_multiplier;
                                END;
                            (*ENDIF*) 
                            END
                        ELSE
                            BEGIN
                            (* check for jtfirstkey *)
                            IF  ( jtfirstkey in _dst_prop )
                                AND
                                ((* current jt_jointype is worse *)
                                ( _jtrans_ptr^.jt_jointype > to_first_keyfield ) OR
                                (* 'keyaccess' is given *)
                                (( config <> NIL ) AND
                                ( _dst_table <= config^.shint.hint_joincfg_cnt ) AND
                                ( cj_keyaccess in config^.shint.
                                hint_join_config[ _dst_table ].cfg_join_switches )))
                            THEN
                                (* key consists of more than one column *)
                                BEGIN
                                _jtrans_ptr^.jt_jointype :=
                                      a681keval_key_kind( acv, dmli, jinfos,
                                      mul_tabs, _i (* no. in joinarr[] *),
                                      _dst (* no. in jo_recs[] *),
                                      _join_val_found );
                                IF  ( _jtrans_ptr^.jt_jointype = to_key )
                                THEN
                                    _jtrans_ptr^.jt_multipl := dmli.d_joins.
                                          jrc_joinarr^[ _i ].jo_recs[ _dst ].
                                          jop_multiplier;
                                (*ENDIF*) 
                                END;
                            (*ENDIF*) 
                            ;
                            (* till now we checked for              *)
                            (* jtonlykey  -> to_single_keyfield     *)
                            (* jtfirstkey -> to_key, to_mt_join or  *)
                            (* to_first_keyfield if jrc_joinarr^     *)
                            (* runs over                            *)
                            END;
                        (*ENDIF*) 
                        IF  ( _jtrans_ptr^.jt_jointype < _old_jointype )
                            (* _old_jointype = to_cartesian_prod or to_illegal *)
                        THEN
                            (* we found a better join transition *)
                            BEGIN
                            _jtrans_ptr^.jt_joinno := _i;
                            END;
                        (*ENDIF*) 
                        ;
                        (* till now key access checked      *)
                        (* rememeber actual join transition *)
                        _old_jointype := _jtrans_ptr^.jt_jointype;
                        ;
                        (* check for jtmulti *)
                        IF  ( jtmulti in _dst_prop )
                        THEN
                            BEGIN
                            _tmptype := a681ieval_inv_kind( acv, dmli, jinfos,
                                  mul_tabs, _i, _dst, _join_val_found );
                            IF  ( _tmptype <= _old_jointype )
                                (* only the case for to_unique_field, to_invpart *)
                                OR
                                ( _tmptype = to_mt_join )
                                OR
                                (* there is an index and 'indexaccess' is given *)
                                (( _tmptype <> to_eq_field ) AND
                                ( config <> NIL ) AND
                                ( _dst_table <= config^.shint.hint_joincfg_cnt ) AND
                                ( cj_indexaccess in config^.shint.
                                hint_join_config[ _dst_table ].cfg_join_switches ))
                            THEN
                                _jtrans_ptr^.jt_jointype := _tmptype;
                            (*ENDIF*) 
                            ;
&                           ifdef TRACE
                            a683trace_jointype(ak_join, 'jointype ###',
                                  _jtrans_ptr^.jt_jointype);
&                           endif
                            END;
                        (*ENDIF*) 
                        IF  ( _jtrans_ptr^.jt_jointype >= to_eq_field )
                            OR
                            (* join hint 'noaccesspath' given *)
                            (( config <> NIL ) AND
                            ( _dst_table <= config^.shint.hint_joincfg_cnt ) AND
                            ( cj_noaccesspath in config^.shint.
                            hint_join_config[ _dst_table ].cfg_join_switches ))
                        THEN
                            BEGIN
&                           ifdef trace
                            t01name(ak_join, 'to_eq_field       ');
&                           endif
                            (* at least get to_eq_field *)
                            _jtrans_ptr^.jt_jointype := to_eq_field;
                            _jtrans_ptr^.jt_multipl  := dmli.d_joins.
                                  jrc_joinarr^[ _i ].jo_recs[ _dst ].jop_multiplier;
                            END;
                        (* till now we checked for jtmulti                 *)
                        (* jtmulti  -> to_eq_field, to_invpart, to_mt_join *)
                        (*ENDIF*) 
                        END
                    ELSE
                        BEGIN
                        (* ******* transition of non EQUI-Join  ******* *)
                        IF  ( dmli.d_joins.jrc_joinarr^[ _i ].jo_op in
                            [ op_sounds, op_not_sounds, op_like, op_not_like ] )
                        THEN
                            IF  ( _src_table = dmli.d_joins.jrc_joinarr^[ _i ].
                                jo_recs[ 1 ].jop_tableno )
                            THEN
                                _jtrans_ptr^.jt_jointype := to_ne_field
                            ELSE
                                _jtrans_ptr^.jt_jointype := to_illegal
                            (*ENDIF*) 
                        ELSE
                            BEGIN
                            IF  (( dmli.d_joins.jrc_joinarr^[ _i ].jo_op <> op_ne )
                                AND
                                ( _jtrans_ptr^.jt_jointype > to_lt_gt_field ))
                            THEN
                                _jtrans_ptr^.jt_jointype := to_lt_gt_field
                            ELSE
                                IF  (( dmli.d_joins.jrc_joinarr^[ _i ].
                                    jo_op = op_ne )
                                    AND
                                    ( _jtrans_ptr^.jt_jointype > to_ne_field ))
                                THEN
                                    _jtrans_ptr^.jt_jointype := to_ne_field;
                                (*ENDIF*) 
                            (*ENDIF*) 
                            END;
                        (*ENDIF*) 
                        END;
                    (*ENDIF*) 
                    END;
                (*ENDIF*) 
                IF  ( _jtrans_ptr^.jt_jointype < _old_jointype )
                THEN
                    _jtrans_ptr^.jt_joinno := _i;
                (*ENDIF*) 
                IF  ( _join_val_found )
                THEN
                    BEGIN
                    FOR _dst := 1 TO dmli.d_cntfromtab DO
                        BEGIN
                        _jtrans_ptr1 :=
                              a685get_join_trans( dmli, jtrans, _dst, _dst_table );
                        IF  ( _jtrans_ptr1^.jt_jointype >
                            _jtrans_ptr^.jt_jointype )
                        THEN
                            BEGIN
                            _jtrans_ptr1^.jt_jointype := _jtrans_ptr^.jt_jointype;
                            _jtrans_ptr1^.jt_joinno   := _i;
                            _jtrans_ptr1^.jt_indexno  := _jtrans_ptr^.jt_indexno;
                            END;
                        (*ENDIF*) 
                        END;
                    (*ENDFOR*) 
                    _jtrans_ptr1 :=
                          a685get_join_trans( dmli, jtrans, _dst_table, _dst_table );
                    _jtrans_ptr1^.jt_jointype := to_illegal;
                    _src := 2; (* exit loop *)
                    END
                ELSE
                    BEGIN
                    (* swap join transition *)
                    _dst       := 1;
                    _src_table := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ 2 ].jop_tableno;
                    _dst_table := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ _dst ].jop_tableno;
                    _dst_prop  := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ _dst ].jop_propset;
                    _dst_field := dmli.d_joins.jrc_joinarr^[ _i ].
                          jo_recs[ _dst ].jop_fieldno;
                    END;
                (*ENDIF*) 
                END;
            (*ENDIF*) 
            _src := succ( _src );
            END;
        (*ENDWHILE*) 
        END;
    (*ENDIF*) 
    END;
(*ENDFOR*) 
;
&ifdef TRACE
a683join_transition_trace(ak_join, dmli, jtrans, dmli.d_cntfromtab);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680_join (
            VAR acv                 : tak_all_command_glob;
            VAR dmli                : tak_dml_info;
            VAR res_tree            : tgg00_FileId;
            VAR ak_strat_interface  : tak71_strat_rec;
            last_pars_part          : boolean;
            VAR jvrec               : tak68_joinview_rec);
 
VAR
      _series            : tak68_sequence;
      _eq_rec            : tak68_eq_record;
      _i                 : tsp00_Int2;
&     ifdef trace
      _joinarr_capacity_old : tsp00_Int2;
      _usedbytes1        : tsp00_Longint;
      _maxusedbytes      : tsp00_Longint;
      _ctrlbytes         : tsp00_Longint;
&     endif
      _aux_count_literals: tsp00_Int2;
      _aux_fieldlists    : tgg00_FieldLists;
      _use_operator_join : boolean;
 
BEGIN
&ifdef trace
(* we could increase global memory for d_joins *)
_joinarr_capacity_old := dmli.d_joins.jrc_capacity;
gg941CalcStatistics( acv.a_transinf.tri_trans,
      _usedbytes1, _maxusedbytes, _ctrlbytes );
vdebug_break( 55555 );
&endif
a685init_eq_rec( acv, _eq_rec );
_aux_count_literals := acv.a_count_literals;
acv.a_count_literals := 0;
IF  ( acv.a_returncode = 0 )
THEN
    BEGIN
    _use_operator_join := a80is_operator_join( acv, dmli );
    IF  ( dmli.d_cntfromtab < dmli.d_maxcounttabs )
    THEN
        (* SELECT with join-views in from_part *)
        a54_joinview_baserecords (acv, dmli);
    (*ENDIF*) 
    ;
&   ifdef TRACE
    t01int4( ak_join, 'd_reclen    ', dmli.d_reclen );
    t01execution_kind( ak_join, 'a_ex_kind   ', acv.a_ex_kind );
    t01execution_kind( ak_join, 'a_init_ex_ki', acv.a_init_ex_kind );
    t01int4( ak_join, 'expl kind   ', ord( acv.a_explain_kind ));
    t01bool( ak_join, 'a_intern_exp', acv.a_intern_explain );
    t01bool( ak_join, 'vtr_Strategy', g01vtrace.vtrStrategy_gg00 );
    t01bool( ak_join, 'a_outer_join', acv.a_outer_join );
    a683_output (ak_join, dmli.d_joins);
    t01messblock (ak_join, 'OLD MESSBUFF', acv.a_mblock);
    t01treeid (ak_join, 'res_tree    ', res_tree);
    a683tabarr_output (dmli);
&   endif
    IF  ( acv.a_outer_join AND ( dmli.d_joins.jrc_cnt > 0 ))
    THEN
        a681get_oj_info( acv, dmli );
    (*ENDIF*) 
    IF  ( acv.a_returncode = 0 ) AND NOT ( dmli.d_view )
    THEN
        BEGIN
        ak680get_all_eq_conditions (acv, dmli, _eq_rec);
        ak680sort_joins (dmli.d_joins);
&       ifdef TRACE
        t01name( ak_join, 'START->geteq->sort' );
        a683_output(ak_join, dmli.d_joins);
&       endif
        END;
    (*ENDIF*) 
    a680search_sequence (acv, dmli, acv.a_pars_last_key, _eq_rec, res_tree,
          _series, ak_strat_interface, jvrec, _use_operator_join);
    IF  ( NOT dmli.d_view )
    THEN
        BEGIN
        _aux_fieldlists := acv.a_mblock.mb_fieldlists;
        IF  ( acv.a_returncode = 0 )
        THEN
            IF  ( acv.a_ex_kind = only_parsing )
            THEN
                BEGIN
                acv.a_mblock.mb_qual^.mtree := res_tree;
                IF  ( NOT acv.a_intern_explain )
                THEN
                    BEGIN
                    a682save_context (acv, dmli, acv.a_pars_last_key,
                          ak_strat_interface, _series, _eq_rec, res_tree,
                          _use_operator_join, false);
                    END;
                (*ENDIF*) 
                _i := acv.a_mblock.mb_data_len ;
                a06a_mblock_init (acv, m_select, mm_with_join, b01niltree_id);
                acv.a_mblock.mb_data_len  := _i;
                acv.a_mblock.mb_fieldlists := _aux_fieldlists;
                a54_select_last_part (acv, dmli, res_tree, last_pars_part);
                END
            ELSE
                BEGIN
                a06drop_fieldlist_references (_aux_fieldlists);
                IF  ( _use_operator_join )
                THEN
                    BEGIN
                    a682_execute_join_operator (acv, dmli, _series, res_tree,
                          acv.a_pars_last_key, jvrec, false, c_seqsearch_for_exec );
                    END
                ELSE
                    a682_execute_join (acv, dmli, _series, res_tree,
                          acv.a_pars_last_key, jvrec, false, c_seqsearch_for_exec );
                (*ENDIF*) 
                END
            (*ENDIF*) 
        ELSE
            BEGIN
            a06drop_fieldlist_references (_aux_fieldlists);
            acv.a_mblock.mb_fieldlists[ cgg_idx_literal_valuefieldlist]     := NIL;
            acv.a_mblock.mb_fieldlists[ cgg_idx_pars_result_valuefieldlist] := NIL;
            END;
        (*ENDIF*) 
        END
    ELSE
        (* CREATE VIEW with join statement *)
        (* no context saved *)
        ;
    (*ENDIF*) 
    END;
(*ENDIF*) 
a685finalize_eq_rec( acv, _eq_rec );
acv.a_count_literals := _aux_count_literals;
&ifdef trace
a687_check_memory_usage( acv.a_transinf.tri_trans,
      ( dmli.d_joins.jrc_capacity - _joinarr_capacity_old ) *
      sizeof( dmli.d_joins.jrc_joinarr^[0] ), _usedbytes1 );
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680search_sequence (
            VAR acv                 : tak_all_command_glob;
            VAR dmli                : tak_dml_info;
            VAR parsk               : tak_parskey;
            VAR eq_rec              : tak68_eq_record;
            VAR res_tree            : tgg00_FileId;
            VAR series              : tak68_sequence; (* uninitialized *)
            VAR ak_strat_interface  : tak71_strat_rec;
            VAR jvrec               : tak68_joinview_rec;
            use_operator_join       : boolean);
 
CONST
      _c_needed_stck = 6;
 
VAR
      _jtrans         : tak68_join_transitions;
      _table_stats    : tak68_table_stats_ptr;
      _jinfos         : tak68_joininfos;
      _res_info       : tak68_result_info;
      _mul_tabs       : tak68_mult_tabs;
      _old_return     : tsp00_Int4;
      _syskey         : tgg00_SysInfoKey;
      _src_tab        : tsp00_Int2;
      _dst_tab        : tsp00_Int2;
      _hintinfo       : tak_sysbufferaddress;
      _stop_search    : t_stop_reason;
      _aux_addr       : tgg00_StackListPtr;
 
      _cast           : RECORD
            CASE integer OF
                1 :
                    (addr: tsp00_Addr);
                2 :
                    (cptr : tak68_evalarr_ptr);
                3 :
                    (tptr : tak68_table_stats_ptr);
                4 :
                    (jptr : tak68_join_transitions);
                5 :
                    (rptr : tgg00_RecPtr );
                END;
            (*ENDCASE*) 
 
      _aux_fieldlists     : tgg00_FieldLists;
 
BEGIN
_table_stats:= NIL;
_jtrans     := NIL;
(* reserve memory for table statistics *)
_cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
      dmli.d_cntfromtab * sizeof(tak68_one_table_stat) );
_table_stats := _cast.tptr;
&ifdef trace
t01addr( ak_join, '_table_stats', _cast.addr );
&endif
(* reserve memory for informations about temporary result sets *)
ak680init_result_info( acv, dmli.d_cntfromtab, _res_info );
(* reserve memory for informations about join transitions *)
_cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
      dmli.d_cntfromtab * dmli.d_cntfromtab * sizeof(tak68_join_transition) );
_jtrans := _cast.jptr;
&ifdef trace
t01addr( ak_join, '_jtrans     ', _cast.addr );
&endif
(* reserve memory for informations about possible join transitions *)
a685init_multab_rec( acv, _mul_tabs );
IF  (( _table_stats = NIL ) OR ( _jtrans = NIL ))
THEN
    a07_b_put_error( acv, e_no_more_memory, 1 );
(*ENDIF*) 
IF  ( acv.a_returncode = 0 )
THEN
    BEGIN
    (* call from ak682part1_only_ex_join() *)
    _jinfos.ji_seqsearch_for_exec  := (acv.a_ex_kind = only_parsing) AND
          (acv.a_init_ex_kind = only_executing);
    _hintinfo := NIL;
    IF  ( _jinfos.ji_seqsearch_for_exec )
        (* call from ak682part1_only_ex_join() *)
    THEN
        BEGIN
        (* change stack code *)
        IF  ( ssSubquery_egg00 in acv.a_mblock.mb_qual^.mstack_state )
        THEN
            BEGIN
&           ifdef trace
            t01stackdesc (ak_join, 'K720_TEST  1',
                  acv.a_mblock.mb_qual^.mst_addr,
                  acv.a_mblock.mb_qual^.mstack_desc);
&           endif
            _cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
                  sizeof( tgg00_Rec ) );
            IF  ( _cast.addr <> NIL )
            THEN
                BEGIN
                k720_test_subquery( acv.a_mblock.mb_trns^,
                      acv.a_mblock.mb_data^,
                      acv.a_mblock.mb_data_size,
                      acv.a_mblock.mb_qual^.mstack_desc, _cast.rptr^ );
                gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
                END
            ELSE
                a07_b_put_error( acv, e_no_more_memory, 1 );
            (*ENDIF*) 
&           ifdef trace
            t01stackdesc (ak_join, 'K720_TEST  2',
                  acv.a_mblock.mb_qual^.mst_addr,
                  acv.a_mblock.mb_qual^.mstack_desc);
&           endif
            END;
        (*ENDIF*) 
        IF  ( ssConstParamExpr_egg00 in acv.a_mblock.mb_qual^.mstack_state )
        THEN
            a505const_param_expression ( acv, dmli, acv.a_mblock);
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    ;
    (* asure NULL value at pos mb_data_len in datapart *)
    (* ak684add_not_null_qualification(), Join_AccessOperator.GetNullRecord() *)
    IF  ( acv.a_mblock.mb_data^.
        mbp_buf[ acv.a_mblock.mb_data_len ] <> csp_undef_byte )
    THEN
        BEGIN
        IF  ( acv.a_mblock.mb_data_len < acv.a_mblock.mb_data_size )
        THEN
            BEGIN
            acv.a_mblock.mb_data_len := succ (acv.a_mblock.mb_data_len );
            acv.a_mblock.mb_data^.
                  mbp_buf[ acv.a_mblock.mb_data_len ] := csp_undef_byte;
            END
        ELSE
            BEGIN
            a07_b_put_error( acv, e_too_many_mb_data, 1 );
            g01opmsg( sp3p_knldiag, sp3m_error, csp3_ak_msg,
                  csp3_n_join, 'TOO MANY DATA (VAK680,1)', 1 );
            END;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    ;
    (* backup original stack description *)
    _jinfos.ji_parskey             := parsk;
    _jinfos.ji_stack_desc          := acv.a_mblock.mb_qual^.mstack_desc;
    _jinfos.ji_stack_desc.mst_max  := _c_needed_stck +
          acv.a_mblock.mb_qual^.mfirst_free - 1;
    IF  ( _jinfos.ji_stack_desc.mst_max > acv.a_mblock.mb_qual^.mst_max )
    THEN
        _jinfos.ji_stack_desc.mst_max := acv.a_mblock.mb_qual^.mst_max;
    (*ENDIF*) 
    a10new( acv, (_jinfos.ji_stack_desc.mst_max * STACK_ENTRY_MXGG00),
          _jinfos.ji_st_addr );
&   ifdef TRACE
    t01int4 (ak_join, 'jrc_cnt     ', dmli.d_joins.jrc_cnt);
    t01int4 (ak_join, 'jstdesc free', _jinfos.ji_stack_desc.mfirst_free);
    t01int4 (ak_join, 'jstdesc max ', _jinfos.ji_stack_desc.mst_max);
    t01int4 (ak_join, 'qual    max ', acv.a_mblock.mb_qual^.mst_max);
    t01int4 (ak_join, 'new  (size) ',
          (_jinfos.ji_stack_desc.mst_max * STACK_ENTRY_MXGG00));
    t01bool( ak_join, 'gg01_oper_jo', gg01_operator_join );
    t01bool( ak_join, 'gg01_op_sort', gg01_operator_join_sort );
&   endif
    IF  ( _jinfos.ji_st_addr = NIL )
    THEN
        a07_b_put_error (acv, e_cachedirectory_full, 1)
    ELSE
        BEGIN
        (* backup original stack entries *)
        SAPDB_PascalMove ('VAK680',   2,    
              acv.a_mblock.mb_st_size,
              _jinfos.ji_stack_desc.mst_max * STACK_ENTRY_MXGG00,
              @acv.a_mblock.mb_st^, 1,
              @_jinfos.ji_st_addr^, 1,
              (acv.a_mblock.mb_qual^.mfirst_free - 1) * STACK_ENTRY_MXGG00,
              acv.a_returncode);
        (* tie saved stack description with saved stack entries *)
        _jinfos.ji_stack_desc.mst_addr := _jinfos.ji_st_addr;
        _jinfos.ji_mb_data_len := acv.a_mblock.mb_data_len ;
        _jinfos.ji_use_operator_join := use_operator_join;
        _old_return            := acv.a_returncode;
        ;
        a80open_hint_info( acv, parsk, _hintinfo );
        _syskey.slinkage := cak_temp_info_linkage;
        series[ 1 ].jos_source := 0;
        _mul_tabs.mt_cnt       := 0;
        _stop_search           := dont_stop;
        _jinfos.ji_act_join    := 0;
        IF  ( ssSubquery_egg00 in acv.a_mblock.mb_qual^.mstack_state ) OR
            ( ssConstParamExpr_egg00 in acv.a_mblock.mb_qual^.mstack_state )
        THEN
            (* fired on original messblock in first REPEAT loop *)
            _stop_search := stop_bof_param
        ELSE
            (* messblock was restored *)
            WHILE ( acv.a_returncode = 0 ) AND
                  ( _jinfos.ji_act_join < dmli.d_cntfromtab ) AND
                  ( _stop_search = dont_stop ) DO
                BEGIN
                ak680prepare_join( acv, dmli, _hintinfo, eq_rec, _jinfos,
                      jvrec, _table_stats^,
                      _res_info, _syskey, _stop_search );
                END;
            (*ENDWHILE*) 
        (*ENDIF*) 
        ;
&       ifdef trace
        t01name( ak_join, 'all tables process' );
&       endif
        (* all tables was processed *)
        IF  ( _stop_search = dont_stop )
        THEN
            (* we found all one table strategies *)
            BEGIN
            IF  ( acv.a_returncode = 0 )
            THEN
                BEGIN
                (* fill _jtrans, _mul_tabs *)
                ak680evaluate_joins( acv, dmli, _hintinfo, _jinfos,
                      _jtrans, _mul_tabs );
                ;
                IF  ( acv.a_intern_explain OR g01vtrace.vtrStrategy_gg00 )
                THEN
                    BEGIN
                    a681tr_tabstats (acv, dmli, _table_stats^,
                          dmli.d_cntfromtab, NOT c_debug_output);
                    _src_tab := 1;
                    WHILE _src_tab <= dmli.d_cntfromtab DO
                        BEGIN
                        _dst_tab := _src_tab + 16 - 1;
                        IF  _dst_tab > dmli.d_cntfromtab
                        THEN
                            _dst_tab := dmli.d_cntfromtab;
                        (*ENDIF*) 
                        a681tr_joinvals (acv, dmli, _jtrans, _src_tab, _dst_tab,
                              dmli.d_cntfromtab);
                        _src_tab := _dst_tab + 1;
                        END;
                    (*ENDWHILE*) 
                    a681tr_multabs (acv, _mul_tabs, dmli.d_cntfromtab);
&                   ifdef trace
                    a681tr_tabstats (acv, dmli, _table_stats^,
                          dmli.d_cntfromtab, c_debug_output);
                    a683multabs_trace (ak_join, _mul_tabs, _mul_tabs.mt_cnt, 0,
                          dmli.d_cntfromtab);
&                   endif
                    END;
                (*ENDIF*) 
                END;
            (*ENDIF*) 
            IF  ( acv.a_returncode = 0 )
            THEN
                ak680set_join_order( acv, dmli, _hintinfo, _jinfos,
                      _table_stats^, _jtrans, series, _res_info, _mul_tabs );
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        IF  ( _hintinfo <> NIL )
        THEN
            a80close_hint_info( _hintinfo );
        (*ENDIF*) 
        IF  ( _old_return = 0 ) AND
            (( acv.a_returncode <> 0 ) OR ( _stop_search <> dont_stop )) AND
            ( _syskey.slinkage <> cak_temp_info_linkage )
        THEN
            BEGIN
            a680rollback_temp_jinfo( acv, dmli, _jinfos.ji_parskey,
                  jvrec.jv_tabid, ord( _syskey.slinkage[ 2 ]),
                  _jinfos.ji_seqsearch_for_exec);
            END;
        (*ENDIF*) 
        IF  (( acv.a_returncode = 0 ) AND ( _stop_search = dont_stop ))
        THEN
            BEGIN
&           ifdef TRACE
            t01int4 (ak_join, 'jrc_cnt     ', dmli.d_joins.jrc_cnt);
&           endif
            IF  ( dmli.d_joins.jrc_cnt > 1 )
            THEN
                a681opt_conditions (acv, dmli, series, _jinfos, eq_rec);
            (*ENDIF*) 
            ;
&           ifdef TRACE
            t01int4( ak_join, 'jrc_cnt     ', dmli.d_joins.jrc_cnt );
            a683tr_sequence( ak_join, dmli, series );
&           endif
            IF  ( dmli.d_joins.jrc_cnt > 0 )
            THEN
                a681sort_conditions (acv, dmli, series);
            (*ENDIF*) 
            ;
&           ifdef TRACE
            a683_output (ak_join, dmli.d_joins);
            t01int4( ak_join, 'ji_use_opera', ord(_jinfos.ji_use_operator_join));
&           endif
            acv.a_mblock.mb_data_len := _jinfos.ji_mb_data_len;
            IF  ( _jinfos.ji_use_operator_join )
            THEN
                BEGIN
                a690_join( acv, dmli, series, res_tree, _jinfos,
                      ak_strat_interface, jvrec, _table_stats^ );
                ak680calc_buffers( acv, dmli, series );
                END
            ELSE
                a684_join( acv, dmli, series, res_tree, _jinfos,
                      ak_strat_interface, jvrec, _table_stats^ );
            (*ENDIF*) 
&           ifdef trace
            a683tr_sequence( ak_join, dmli, series );
&           endif
            ;
            (* restore original messblock *)
            _aux_addr := acv.a_mblock.mb_st;
            _aux_fieldlists := acv.a_mblock.mb_fieldlists;
            a06a_mblock_init (acv, m_select, mm_with_join, res_tree );
            acv.a_mblock.mb_st := _aux_addr;
            acv.a_mblock.mb_fieldlists := _aux_fieldlists;
            acv.a_mblock.mb_qual^.mstack_desc := _jinfos.ji_stack_desc;
            acv.a_mblock.mb_data_len  := _jinfos.ji_mb_data_len;
            SAPDB_PascalMove ('VAK680',   3,    
                  _jinfos.ji_stack_desc.mst_max * STACK_ENTRY_MXGG00,
                  acv.a_mblock.mb_st_size,
                  @_jinfos.ji_st_addr^, 1,
                  @acv.a_mblock.mb_st^, 1,
                  (acv.a_mblock.mb_qual^.mfirst_free - 1) * STACK_ENTRY_MXGG00,
                  acv.a_returncode);
            END
        ELSE
            BEGIN
            IF  (( acv.a_returncode = 0 ) AND ( _stop_search = stop_bof_param )
                AND acv.a_intern_explain )
            THEN
                a728explain_no_join_strat_now( acv );
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        a10dispose( acv, _jinfos.ji_st_addr );
        END;
    (*ENDIF*) 
    END;
(*ENDIF*) 
;
(* free memory for informations about possible join transitions *)
a685finalize_multab_rec( acv, _mul_tabs );
(* free memory for informations about join transitions *)
IF  ( _jtrans <> NIL )
THEN
    BEGIN
    _cast.jptr := _jtrans;
&   ifdef trace
    t01addr( ak_join, '_jtrans     ', _cast.addr );
&   endif
    gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
    END;
(*ENDIF*) 
;
(* free memory for informations about temporary result sets *)
ak680finalize_result_info( acv, _res_info );
;
(* free memory for table statistics *)
IF  ( _table_stats <> NIL )
THEN
    BEGIN
    _cast.tptr := _table_stats;
&   ifdef trace
    t01addr( ak_join, '_table_stats', _cast.addr );
&   endif
    gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
    END;
(*ENDIF*) 
;
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680_first_table_cost (
            VAR sequence_info    : tak68_sequence_info; (* IN/OUT *)
            VAR table_stats      : tak68_table_stats; (* IN *)
            first_table          : tsp00_Int2;
            VAR res_info         : tak68_result_info (* IN/OUT *));
 
BEGIN
(* set record length of first result table *)
res_info.ri_rec_len := res_info.ri_one_tab_len^[ first_table ];
(* set record per page in first result table *)
IF  (res_info.ri_rec_len > 0 )
THEN
    res_info.ri_recs_per_page := trunc (cak_page80percent /
          (res_info.ri_rec_len * cak68_sup_field_factor)) + 1
ELSE
    res_info.ri_recs_per_page := 1; (* unexpected value in dmli *)
(*ENDIF*) 
&ifdef TRACE
t01sname (ak_join, '============');
t01int4 (ak_join, '1. join step', first_table);
t01int4 (ak_join, 'all pages   ', table_stats[ first_table ].ts_all_pages);
t01real (ak_join, 'recs/page   ', table_stats[ first_table ].ts_recs_per_page, 3);
t01real (ak_join, 'pages_srchd ', table_stats[ first_table ].ts_pages_searched, 3);
t01real (ak_join, 'recs/respage', res_info.ri_recs_per_page, 6);
t01int4 (ak_join, 'resrec len  ', res_info.ri_rec_len);
&endif
(* set page count in first result table *)
sequence_info.si_pages := (table_stats[ first_table ].ts_pages_searched *
      table_stats[ first_table ].ts_recs_per_page) /
      res_info.ri_recs_per_page;
(* set costs for building first result table *)
sequence_info.si_sum   := table_stats[ first_table ].ts_wholeIO_pages;
&ifdef TRACE
t01real (ak_join, 'result pages', sequence_info.si_pages, 6);
t01real (ak_join, 'result costs', sequence_info.si_sum, 6);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680ins_col_upd_stat (
            VAR acv  : tak_all_command_glob;
            VAR dmli : tak_dml_info;
            tabno    : tsp00_Int2);
 
VAR
      _i           : integer;
      _j           : integer;
      _k           : integer;
      _l           : integer;
      _fieldno     : tsp00_Int2;
      _same_tables : tak_joinset;
      _colname     : tsp00_KnlIdentifier;
 
BEGIN
&ifdef TRACE
t01tabid (ak_join, 'btreeid.tabi',
      dmli.d_sparr.pbasep^.sbase.btreeid.fileTabId_gg00);
&endif
_i           := 0;
_fieldno     := 0;
_k           := tabno; (* *** join step *** *)
_j           := _k;
_same_tables := [];
IF  (NOT (dmli.d_sparr.pbasep^.sbase.btablekind in
    [ twithkey, twithoutkey ])) OR
    (* no base table *)
    (a101_IsExtendedTempFile (acv, dmli.d_sparr.pbasep^.sbase.btreeid))
    (* no temporary table *)
THEN
    BEGIN
    (* avoide further steps *)
    _i := dmli.d_joins.jrc_cnt;
    _j := dmli.d_cntfromtab + 1;
    END;
(*ENDIF*) 
WHILE (_j <= dmli.d_cntfromtab) DO
    BEGIN
&   ifdef TRACE
    t01tabid (ak_join,
          'otreeid.tabi', dmli.d_tabarr^[ _j ].otreeid.fileTabId_gg00);
&   endif
    IF  (dmli.d_sparr.pbasep^.sbase.btreeid.fileTabId_gg00 =
        dmli.d_tabarr^[ _j ].otreeid.fileTabId_gg00)
    THEN
        _same_tables := _same_tables + [ _j ];
    (*ENDIF*) 
    _j := succ (_j);
    END;
(*ENDWHILE*) 
WHILE (_i < dmli.d_joins.jrc_cnt) DO
    BEGIN
    WITH dmli.d_joins.jrc_joinarr^[ _i ] DO
        BEGIN
        _k := 1;
        IF  (dmli.d_joins.jrc_joinarr^[ _i ].jo_op = op_eq)
        THEN
            WHILE (_k <= 2) DO
                (* work twice, left and right side *)
                BEGIN
                IF  (jo_recs[ _k ].jop_tableno  = tabno) AND
                    (jo_recs[ _k ].jop_cntstack = 1) AND
                    ((jo_col_upd_stat = _k)     OR
                    ( jo_col_upd_stat = 3)       )
                THEN
                    BEGIN
                    _fieldno := jo_recs[ _k ].jop_fieldno;
                    _j       := _i;
                    (* loop over remaining joins              *)
                    (* maintain jo_col_upd_stat for each join *)
                    (* if the same table,column is referenced *)
                    (* adjust jo_col_upd_stat                 *)
                    WHILE (_j < dmli.d_joins.jrc_cnt) DO
                        WITH dmli.d_joins.jrc_joinarr^[ _j ] DO
                            BEGIN
                            _l := 1;
                            (* examine left and right side if neccessary *)
                            WHILE (jo_col_upd_stat >= _l) AND (_l <= 2) DO
                                BEGIN
                                IF  (jo_recs[ _l ].jop_tableno in _same_tables)
                                    AND
                                    (jo_recs[ _l ].jop_fieldno = _fieldno)
                                THEN
                                    BEGIN
                                    jo_col_upd_stat := jo_col_upd_stat - _l;
                                    _l               := 3; (* exit while *)
                                    END
                                ELSE
                                    _l := succ (_l);
                                (*ENDIF*) 
                                END;
                            (*ENDWHILE*) 
                            _j := succ (_j);
                            END;
                        (*ENDWITH*) 
                    (*ENDWHILE*) 
                    dmli.d_colbuf := a103GetColumn ( dmli.d_sparr.pbasep^.sbase,
                          dmli.d_sparr.pbasep^.sbase.bfirstcolind );
                    WHILE (dmli.d_colbuf^.creccolno <> _fieldno) AND
                          (acv.a_returncode = 0) DO
                        BEGIN
&                       IFDEF TRACE
                        t01int4 (ak_join, 'cnextind    ', dmli.d_colbuf^.cnextind);
                        t01int4 (ak_join, 'd_index     ', dmli.d_index);
                        t01int4 (ak_join, 'creccolno   ', dmli.d_colbuf^.creccolno);
&                       ENDIF
                        IF  (dmli.d_colbuf^.cnextind <> 0)
                        THEN
                            dmli.d_colbuf := a103GetColumn ( dmli.d_sparr.pbasep^.sbase,
                                  dmli.d_colbuf^.cnextind )
                        ELSE
                            a07ak_system_error( acv, 680, 1 );
                        (*ENDIF*) 
                        END;
                    (*ENDWHILE*) 
                    IF  ( acv.a_returncode = 0 )
                    THEN
                        BEGIN
                        a061get_colname (dmli.d_colbuf^, _colname);
&                       IFDEF TRACE
                        t01int4 (ak_join, 'fno         ', _fieldno);
                        t01lidentifier (ak_join, _colname);
&                       ENDIF
                        a101_InsertColumnIntoSysUpdStatWanted (acv,
                              dmli.d_sparr.pbasep^.sbase.bsurrogate,
                              _colname);
                        END;
                    (*ENDIF*) 
                    IF  (acv.a_returncode <> 0)
                    THEN
                        BEGIN
                        _i := dmli.d_joins.jrc_cnt; (* exit while *)
                        _j := _i;     (* exit while *)
                        END;
                    (* actual table found *)
                    (*ENDIF*) 
                    _k := 3; (* exit while *)
                    END;
                (* try next join part *)
                (*ENDIF*) 
                _k := succ (_k);
                END;
            (*ENDWHILE*) 
        (*ENDIF*) 
        END;
    (*ENDWITH*) 
    _i := succ (_i);
    END;
(*ENDWHILE*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680standard_cost (
            VAR acv            : tak_all_command_glob;
            VAR dmli           : tak_dml_info;
            config             : tak_sysbufferaddress;
            VAR table_stats    : tak68_table_stats;
            jtrans             : tak68_join_transitions;
            VAR jinfos         : tak68_joininfos;
            VAR res_info       : tak68_result_info;
            VAR mul_tabs       : tak68_mult_tabs;
            VAR lastsuccession : tak68_lastsuccession;
            VAR lowest_costs   : tsp00_Longreal;
            VAR sequence_info  : tak68_sequence_info;
            succ_length        : tsp00_Int2;
            final_call         : boolean);
 
VAR
      _l                      : tsp00_Int2;
      _i                      : tsp00_Int2;
      _new_sequence_idx       : tsp00_Int2;
      _src_table              : tsp00_Int2;
      _newseq_table           : tsp00_Int2;
      _reverse_jrec_site      : tsp00_Int2;
      _counted_multiplier     : tsp00_Longreal;
      _reverse_multiplier     : tsp00_Longreal;
      _factor                 : tsp00_Longreal;
      _jtrans_ptr             : tak68_join_transition_ptr;
      _jtrans_ptr1            : tak68_join_transition_ptr;
      (*_already_sequenced_tabs : SET OF 1..cak00_maxsources;*)
      _already_sequenced_tabs : tsp00_Addr;
      _best_strat             : tak68_best_jstrat;
      _aux_jointype           : tak68_one_jointype;
 
BEGIN
&ifdef TRACE
t01sname(ak_join, 'eval seq   :');
a683tr_newsucc (ak_join, sequence_info.si_sequence, 1, succ_length, true);
t01sname(ak_join, 'last seq   :');
a683tr_newsucc (ak_join, lastsuccession.ls_sequence, 1, dmli.d_cntfromtab, true);
&endif
_new_sequence_idx := 1;
IF  ( NOT final_call )
THEN
    (* find out common start sequence *)
    WHILE (_new_sequence_idx < succ_length) AND
          (sequence_info.si_sequence^[ _new_sequence_idx ].s_tableno =
          lastsuccession.ls_sequence^[ _new_sequence_idx ].s_tableno) DO
        _new_sequence_idx := succ(_new_sequence_idx);
    (*ENDWHILE*) 
(*ENDIF*) 
_src_table := sequence_info.si_sequence^[ 1 ].s_tableno;
IF  ( _new_sequence_idx = 1 )
THEN
    BEGIN
    a680_first_table_cost (sequence_info, table_stats, _src_table, res_info);
    lastsuccession.ls_values^[ 1 ].lv_newsum      := sequence_info.si_sum;
    lastsuccession.ls_values^[ 1 ].lv_newpages    := sequence_info.si_pages;
    lastsuccession.ls_values^[ 1 ].lv_res_rec_len := res_info.ri_rec_len;
    lastsuccession.ls_values^[ 1 ].lv_recs_per_respage :=
          res_info.ri_recs_per_page;
    lastsuccession.ls_values^[ 1 ].lv_old_recs_per_respage :=
          res_info.ri_old_recs_per_page;
    lastsuccession.ls_sequence^[ 1 ] := sequence_info.si_sequence^[ 1 ];
    _new_sequence_idx := 2;
&   ifdef trace
    t01name (ak_join, 'values from optimi');
&   endif
    END
ELSE
    BEGIN
    (* get computed values from common start sequence *)
    sequence_info.si_sum          :=
          lastsuccession.ls_values^[ _new_sequence_idx - 1 ].lv_newsum;
    sequence_info.si_pages        :=
          lastsuccession.ls_values^[ _new_sequence_idx - 1 ].lv_newpages;
    res_info.ri_rec_len           :=
          lastsuccession.ls_values^[ _new_sequence_idx - 1 ].lv_res_rec_len;
    res_info.ri_recs_per_page     :=
          lastsuccession.ls_values^[ _new_sequence_idx - 1 ].lv_recs_per_respage;
    res_info.ri_old_recs_per_page :=
          lastsuccession.ls_values^[ _new_sequence_idx - 1 ].lv_old_recs_per_respage;
&   ifdef trace
    t01int4 (ak_join, 'values from ', _new_sequence_idx - 1);
    t01real (ak_join, 'value is    ', sequence_info.si_sum, 6);
&   endif
    END;
(*ENDIF*) 
IF  ( final_call AND ( acv.a_intern_explain OR g01vtrace.vtrStrategy_gg00 ))
THEN
    (* put first table *)
    ak680explain_join( acv, dmli, to_illegal, _src_table,
          table_stats[ _src_table ].ts_pages_searched *
          table_stats[ _src_table ].ts_recs_per_page (* records found *),
          1(*multipl*), 1(*reverse_multiplier*),
          sequence_info.si_pages (* new pages *),
          sequence_info.si_pages * res_info.ri_recs_per_page (* new records *),
          sequence_info.si_sum );
(*ENDIF*) 
_i := _new_sequence_idx;
(* compute costs for join sequence in sequence_info.si_sequence *)
(* write sequence in lastsuccession until costs are worse than  *)
(* earlier sequence                                             *)
IF  ( succ_length > 1 )
THEN
    BEGIN
    _already_sequenced_tabs :=
          gg12InitBitArray( acv.a_transinf.tri_trans, cak00_maxsources );
    IF  ( _already_sequenced_tabs = NIL )
    THEN
        a07_b_put_error( acv, e_no_more_memory, 1 );
    (*ENDIF*) 
    WHILE ( _i <= succ_length ) AND ( acv.a_returncode = 0 ) DO
        BEGIN
&       ifdef trace
        t01int4(ak_join, 'JOIN STEP   ', _i);
&       endif
        _newseq_table  := sequence_info.si_sequence^[ _i ].s_tableno;
        _best_strat.bj_srctab     := sequence_info.si_sequence^[ _i - 1 ].s_tableno;
        _best_strat.bj_multiplier := csp_maxint4;
        _best_strat.bj_fieldcnt   := 1;
        _best_strat.bj_indexno    := 0;
        _best_strat.bj_jtype      := to_cartesian_prod;
        res_info.ri_rec_len := res_info.ri_rec_len +
              res_info.ri_one_tab_len^[ _newseq_table ];
        res_info.ri_old_recs_per_page := res_info.ri_recs_per_page;
        IF  (res_info.ri_rec_len > 0 )
        THEN
            res_info.ri_recs_per_page := trunc (cak_page80percent /
                  (res_info.ri_rec_len * cak68_sup_field_factor)) + 1;
        (*ENDIF*) 
        FOR _l := 1 TO _i - 1 DO
            BEGIN
            _src_table := sequence_info.si_sequence^[ _l ].s_tableno;
            _jtrans_ptr :=
                  a685get_join_trans( dmli, jtrans, _src_table, _newseq_table );
            IF  _jtrans_ptr^.jt_jointype = to_mt_join
            THEN
                BEGIN
                a681lowest_multiple_strat( acv, dmli, config, jtrans,
                      sequence_info.si_sequence,
                      mul_tabs, _src_table, _newseq_table(*dst table*),
                      _best_strat, _i - 1, final_call);
                END
            ELSE
                IF  ( _best_strat.bj_jtype > _jtrans_ptr^.jt_jointype )
                    OR
                    ((_best_strat.bj_jtype = _jtrans_ptr^.jt_jointype ) AND
                    ( _best_strat.bj_multiplier > _jtrans_ptr^.jt_multipl ))
                THEN
                    BEGIN
                    _best_strat.bj_jtype      := _jtrans_ptr^.jt_jointype;
                    _best_strat.bj_multiplier := _jtrans_ptr^.jt_multipl;
                    _best_strat.bj_indexno    := _jtrans_ptr^.jt_indexno;
                    _best_strat.bj_srctab     := _src_table;
                    END;
                (*ENDIF*) 
            (*ENDIF*) 
            END;
        (*ENDFOR*) 
        _src_table := _best_strat.bj_srctab;
        _jtrans_ptr :=
              a685get_join_trans( dmli, jtrans, _src_table, _newseq_table );
        IF  (( _jtrans_ptr^.jt_jointype = to_cartesian_prod )
            OR
            ( _jtrans_ptr^.jt_jointype = to_illegal ))
        THEN
            BEGIN
            _counted_multiplier := csp_maxint4;
            _reverse_multiplier := csp_maxint4;
            END
        ELSE
            BEGIN
            _counted_multiplier := _best_strat.bj_multiplier;
            _reverse_multiplier := csp_maxint4;
            (* _already_sequenced_tabs := []; *)
            gg12ClearBitArray( _already_sequenced_tabs, cak00_maxsources );
            (* h.b. PTS 1106028 *)
            FOR _l := 1 TO _i - 1 DO
                BEGIN
                (* _already_sequenced_tabs := _already_sequenced_tabs +
                      [sequence_info.si_sequence^[ _l ].s_tableno]; *)
                gg12SetBit( _already_sequenced_tabs,
                      sequence_info.si_sequence^[ _l ].s_tableno, true );
&               ifdef trace
                t01int4 (ak_join, 'already_sequ',
                      sequence_info.si_sequence^[ _l ].s_tableno);
&               endif
                END;
            (*ENDFOR*) 
            FOR _l := 0 TO dmli.d_joins.jrc_cnt - 1 DO
                WITH dmli.d_joins.jrc_joinarr^[ _l ] DO
                    IF  (_newseq_table = jo_recs[ 1 ].jop_tableno) OR
                        (_newseq_table = jo_recs[ 2 ].jop_tableno)
                    THEN
                        BEGIN
                        IF  (_newseq_table = jo_recs[ 1 ].jop_tableno)
                        THEN
                            _reverse_jrec_site := 2
                        ELSE
                            _reverse_jrec_site := 1;
                        (*ENDIF*) 
                        WITH jo_recs[ _reverse_jrec_site ] DO
                            IF  ((jop_tableno = cak68_join_value) OR
                                gg12GetBit( _already_sequenced_tabs, ord( jop_tableno )))
                            THEN
                                BEGIN
                                _factor := 1;
                                IF  jop_tableno <> cak68_join_value
                                THEN
                                    _factor := (sequence_info.si_pages * res_info.ri_old_recs_per_page) /
                                          (table_stats[jop_tableno].ts_all_pages * table_stats[jop_tableno].ts_recs_per_page);
                                (*ENDIF*) 
                                IF  (_reverse_multiplier > (jop_multiplier * _factor))
                                THEN
                                    _reverse_multiplier := jop_multiplier * _factor;
                                (*ENDIF*) 
                                IF  (_reverse_multiplier < 1)
                                THEN
                                    _reverse_multiplier := 1;
&                               ifdef trace
                                (*ENDIF*) 
                                t01real (ak_join, 'r_mult new  ', _reverse_multiplier, 3);
&                               endif
                                END;
                            (*ENDIF*) 
                        (*ENDWITH*) 
                        END;
                    (*ENDIF*) 
                (*ENDWITH*) 
            (*ENDFOR*) 
            END;
        (*ENDIF*) 
        _src_table := _best_strat.bj_srctab;
        _jtrans_ptr :=
              a685get_join_trans( dmli, jtrans, _src_table, _newseq_table );
        _aux_jointype := _jtrans_ptr^.jt_jointype;
        (* store best transition type from predecessors *)
        sequence_info.si_sequence^[ _i ].s_backup := _best_strat.bj_srctab;
        _jtrans_ptr1 :=
              a685get_join_trans( dmli, jtrans, _best_strat.bj_srctab, _newseq_table );
        _jtrans_ptr1^.jt_jointype := _best_strat.bj_jtype;
        (* _best_strat.bj_srctab contains the table with best join *)
        (* transitions from predecessors to actual table  *)
        res_info.ri_recs_per_page := ak680curr_recs_per_respage (jinfos,
              dmli.d_joins, sequence_info.si_sequence, _i);
        IF  ( final_call )
        THEN
            BEGIN
            IF  ( trunc(sequence_info.si_pages * res_info.ri_old_recs_per_page) > MAX_INT4_SP00 )
            THEN
                sequence_info.si_sequence^[ _i - 1 ].s_resrows := MAX_INT4_SP00
            ELSE
                sequence_info.si_sequence^[ _i - 1 ].s_resrows :=
                      trunc(sequence_info.si_pages * res_info.ri_old_recs_per_page);
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        a680next_join_eval (acv, _jtrans_ptr^ (*in/out*),
              table_stats[ _newseq_table ](*in/out*),
              _counted_multiplier (*in*),
              _reverse_multiplier (*in*),
              sequence_info.si_sum (*in/out*),
              sequence_info.si_pages (*in/out*),
              res_info.ri_old_recs_per_page (*in*),
              res_info.ri_recs_per_page (*in*),
              jinfos.ji_use_operator_join);
        _jtrans_ptr^.jt_jointype := _aux_jointype;
&       ifdef trace
        IF  ( final_call )
        THEN
            BEGIN
            ak680explain_join (acv, dmli,
                  _jtrans_ptr^.jt_jointype,
                  _newseq_table,
                  table_stats[ _newseq_table ].ts_pages_searched *
                  table_stats[ _newseq_table ].ts_recs_per_page,
                  _counted_multiplier, _reverse_multiplier,
                  sequence_info.si_pages,
                  sequence_info.si_pages * res_info.ri_recs_per_page,
                  sequence_info.si_sum);
            END;
&       else
        (*ENDIF*) 
        IF  ( final_call AND
            ( acv.a_intern_explain OR g01vtrace.vtrStrategy_gg00 ))
        THEN
            ak680explain_join (acv, dmli,
                  _jtrans_ptr^.jt_jointype,
                  _newseq_table,
                  table_stats[ _newseq_table ].ts_pages_searched *
                  table_stats[ _newseq_table ].ts_recs_per_page,
                  _counted_multiplier, _reverse_multiplier,
                  sequence_info.si_pages,
                  sequence_info.si_pages * res_info.ri_recs_per_page,
                  sequence_info.si_sum);
        (*ENDIF*) 
        ;
&       endif
&       ifdef TRACE
        t01real (ak_join,'calc sum    ', sequence_info.si_sum, 6);
&       endif
        lastsuccession.ls_values^[ _i ].lv_newsum               :=
              sequence_info.si_sum;
        lastsuccession.ls_values^[ _i ].lv_newpages             :=
              sequence_info.si_pages;
        lastsuccession.ls_values^[ _i ].lv_res_rec_len          :=
              res_info.ri_rec_len;
        lastsuccession.ls_values^[ _i ].lv_recs_per_respage     :=
              res_info.ri_recs_per_page;
        lastsuccession.ls_values^[ _i ].lv_old_recs_per_respage :=
              res_info.ri_old_recs_per_page;
        lastsuccession.ls_sequence^[ _i ] := sequence_info.si_sequence^[ _i ];
        IF  ( sequence_info.si_sum > lowest_costs )
        THEN
            BEGIN
&           ifdef trace
            t01name(ak_join, 'WORSE SEQ PRAEFIX ');
&           endif
            IF  ( succ_length < dmli.d_cntfromtab )
            THEN
                BEGIN
                (* use this computed value for further calculations, *)
                (* because sequence isn't complete                   *)
                FOR _l := _i + 1 TO dmli.d_cntfromtab DO
                    BEGIN
                    lastsuccession.ls_sequence^[ _l ].s_tableno := 0;
                    END;
                (*ENDFOR*) 
                END
            ELSE
                (* don't use computed worse value for further calculations *)
                (* mark last computed worse value                          *)
                lastsuccession.ls_sequence^[ _i ].s_tableno := 0;
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        IF  (NOT final_call) AND (sequence_info.si_sum > lowest_costs)
        THEN
            (* stop computing of cost, if cost bigger than sequence_info costs *)
            _i := succ_length;
        (*ENDIF*) 
        _i := succ (_i);
        END;
    (*ENDWHILE*) 
    gg12FinalizeBitArray( acv.a_transinf.tri_trans, _already_sequenced_tabs );
    END;
&ifdef trace
(*ENDIF*) 
t01real (ak_join, 'calc seqcost', sequence_info.si_sum, 6);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680explain_join (
            VAR acv            : tak_all_command_glob;
            VAR dmli           : tak_dml_info;
            jointype           : tak68_one_jointype;
            tabno              : integer;
            recs_found         : tsp00_Longreal;
            multiplier         : tsp00_Longreal;
            reverse_multiplier : tsp00_Longreal;
            new_pages          : tsp00_Longreal;
            new_recs           : tsp00_Longreal;
            costs              : tsp00_Longreal);
 
VAR
      _owner    : tsp00_KnlIdentifier;
      _table    : tsp00_KnlIdentifier;
      _strat    : tsp00_C40;
      _base_ptr : tak_sysbufferaddress;
      _f_ok     : boolean;
 
BEGIN
IF  ( g01vtrace.vtrStrategy_gg00 ) OR ( acv.a_explain_kind = ex_join )
THEN
    BEGIN
    CASE jointype OF
        to_single_keyfield :
            _strat := 'JOIN VIA SINGLE KEY                     ';
        to_unique_field    :
            _strat := 'JOIN VIA UNIQUE INDEX                   ';
        to_keypart  :
            _strat := 'JOIN VIA KEY RANGE                      ';
        to_key      :
            _strat := 'JOIN VIA KEY                            ';
        to_first_keyfield  :
            _strat := 'JOIN VIA FIRST KEY COLUMN               ';
        to_invfield :
            _strat := 'JOIN VIA SINGLE INDEX                   ';
        to_all_invfields :
            _strat := 'JOIN VIA MULTIPLE INDEX                 ';
        to_invpart :
            _strat := 'JOIN VIA MULTIPLE INDEX RANGE           ';
        to_eq_field :
            _strat := 'JOIN VIA EQUAL COLUMN (SORTING)         ';
        to_lt_gt_field :
            _strat := 'JOIN VIA LESS OR GREATER COLUMN(SORTING)';
        to_ne_field,
        to_cartesian_prod :
            _strat := 'JOIN VIA SORTING                        ';
        to_mt_join:
            _strat := 'JOIN VIA MORE THAN ONE FIELD            ';
        OTHERWISE
            _strat := bsp_c40;
        END;
    (*ENDCASE*) 
    IF  ( oisreference in dmli.d_tabarr^[tabno].ospecialname )
    THEN
        BEGIN
        _owner := a01_il_b_identifier;
        _table := dmli.d_tabarr^[tabno].oreference
        END
    ELSE
        BEGIN
        IF  ( oispartjoinview in dmli.d_tabarr^[tabno].ospecialname )
        THEN
            BEGIN
            a06_systable_get( acv, d_release,
                  dmli.d_tabarr^[tabno].otreeid.fileTabId_gg00,
                  _base_ptr, true, _f_ok );
            IF  ( _f_ok )
            THEN
                BEGIN
                _table := _base_ptr^.sbase.btablen^;
                a06determine_username( acv, _base_ptr^.sbase.bauthid, _owner );
                END
            ELSE
                a07_nb_put_error( acv, e_unknown_tablename,
                      1, dmli.d_tabarr^[tabno].otable );
            (*ENDIF*) 
            END
        ELSE
            BEGIN
            _owner := dmli.d_tabarr^[tabno].ouser;
            _table := dmli.d_tabarr^[tabno].otable
            END;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    IF  ( recs_found < 1 )
    THEN
        recs_found := 1;
    (*ENDIF*) 
    IF  ( new_pages < 1 )
    THEN
        new_pages := 1;
    (*ENDIF*) 
    IF  ( new_recs < 1 )
    THEN
        new_recs := 1;
    (*ENDIF*) 
    IF  ( g01vtrace.vtrStrategy_gg00 )
    THEN
        g041join_expl_row_trace( acv.a_mblock.mb_trns^, _strat, _owner,
              _table, recs_found, multiplier, new_pages, new_recs, costs );
    (*ENDIF*) 
    IF  ( acv.a_intern_explain ) AND ( acv.a_explain_kind = ex_join )
    THEN
        a40join_expl_row( acv, _strat, _owner, _table, recs_found,
              multiplier, reverse_multiplier, new_pages, new_recs, costs )
    (*ENDIF*) 
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680only_inv_prepare (
            VAR acv           : tak_all_command_glob;
            VAR dmli          : tak_dml_info;
            VAR jinfos        : tak68_joininfos;
            VAR strat         : tgg07_StrategyInfo;
            tabno             : integer;
            VAR mblock_changed: boolean);
 
VAR
      _ix           : integer;
      _ixj          : integer;
      _record_pos   : integer;
      _out_stop     : integer;
 
BEGIN
&ifdef TRACE
t01stackdesc (ak_join, 'MBUFFSTACK 1',
      acv.a_mblock.mb_qual^.mst_addr, acv.a_mblock.mb_qual^.mstack_desc);
t01stackdesc (ak_join, 'ji_stack_des',
      jinfos.ji_st_addr, jinfos.ji_stack_desc);
&endif
mblock_changed :=
      ((jinfos.ji_stack_desc.mfirst_free * 2) <
      acv.a_mblock.mb_qual^.mst_max - acv.a_mblock.mb_qual^.mfirst_free);
IF  mblock_changed
THEN
    BEGIN
    _record_pos   := cgg_rec_key_offset + RESCNT_MXGG04 + 1;
    (* get first stack entry ??, create gap for st_jump_out *)
    IF  ( acv.a_mblock.mb_qual^.mqual_pos <> 0 )
    THEN
        acv.a_mblock.mb_qual^.mst_addr^[ acv.a_mblock.mb_qual^.mfirst_free ] :=
              acv.a_mblock.mb_qual^.mst_addr^[ acv.a_mblock.mb_qual^.mqual_pos ];
    (*ENDIF*) 
    acv.a_mblock.mb_qual^.mqual_pos := acv.a_mblock.mb_qual^.mfirst_free;
    _ix       := succ (acv.a_mblock.mb_qual^.mfirst_free);
    IF  (jinfos.ji_st_addr^[ jinfos.ji_stack_desc.mqual_pos ].etype =
        st_jump_output)
    THEN
        _out_stop := jinfos.ji_st_addr^
              [ jinfos.ji_stack_desc.mqual_pos ].epos +
              jinfos.ji_stack_desc.mqual_pos - 2
    ELSE
        _out_stop := jinfos.ji_stack_desc.mfirst_free - 1;
    (*ENDIF*) 
&   ifdef TRACE
    t01int4 (ak_join, 'mqual_pos   ', jinfos.ji_stack_desc.mqual_pos);
    t01int4 (ak_join, 'etype       ',
          ord (jinfos.ji_st_addr^ [ jinfos.ji_stack_desc.mqual_pos ].etype));
    t01int4 (ak_join, '_out_stop   ', _out_stop);
&   endif
    (* *** transfer outputfields *** *)
    FOR _ixj := jinfos.ji_stack_desc.mqual_pos TO _out_stop DO
        BEGIN
        IF  (((ord(jinfos.ji_st_addr^[ _ixj ].ecol_tab[ 2 ])) = tabno)
            AND
            (jinfos.ji_st_addr^[ _ixj ].etype in
            [ st_fixkey, st_varkey, st_fixcol, st_varcol, st_varlongchar ]))
        THEN
            BEGIN
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ] :=
                  jinfos.ji_st_addr^[ _ixj ];
            _ix := succ (_ix);
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ] :=
                  acv.a_mblock.mb_qual^.mst_addr^[ _ix - 1];
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].etype    := st_output;
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].eop_out  := op_o_none;
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].epos     := _record_pos;
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].ecol_pos := 0;
            _record_pos := _record_pos +
                  acv.a_mblock.mb_qual^.mst_addr^[ _ix ].elen_var;
            _ix := succ (_ix);
            END;
        (*ENDIF*) 
        END;
    (*ENDFOR*) 
    (* *** transfer joinqualifications as outputfields *** *)
    FOR _ixj := _out_stop + 1 TO
          jinfos.ji_stack_desc.mqual_pos + jinfos.ji_stack_desc.mqual_cnt - 1 DO
        BEGIN
        IF  (((ord(jinfos.ji_st_addr^[ _ixj ].ecol_tab[ 2 ])) = tabno)
            AND
            (jinfos.ji_st_addr^[ _ixj ].etype in
            [ st_fixkey, st_varkey, st_fixcol, st_varcol, st_varlongchar ]))
        THEN
            BEGIN
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ] :=
                  jinfos.ji_st_addr^[ _ixj ];
            (* remove index flag *)
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].ecol_tab [ 1 ] := chr (0);
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].ecol_tab [ 2 ] := chr (tabno);
            IF  (NOT (acv.a_mblock.mb_qual^.mst_addr^[ _ix ].eop =
                op_order_desc_ascii ))
            THEN
                acv.a_mblock.mb_qual^.mst_addr^[ _ix ].eop := op_none;
            (*ENDIF*) 
            _ix := succ (_ix);
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ]
                  := acv.a_mblock.mb_qual^.mst_addr^[ _ix - 1];
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].etype    := st_output;
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].eop_out  := op_o_none;
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].epos     := _record_pos;
            acv.a_mblock.mb_qual^.mst_addr^[ _ix ].ecol_pos := 0;
            _record_pos := _record_pos +
                  acv.a_mblock.mb_qual^.mst_addr^[ _ix ].elen_var;
            _ix := succ (_ix);
            END;
        (*ENDIF*) 
        END;
    (*ENDFOR*) 
    acv.a_mblock.mb_qual^.mfirst_free := _ix;
    acv.a_mblock.mb_qual^.mqual_cnt   := acv.a_mblock.mb_qual^.mfirst_free -
          acv.a_mblock.mb_qual^.mqual_pos + 1;
    a61_set_jump (acv.a_mblock, acv.a_mblock.mb_qual^.mqual_pos, st_jump_output);
    (* *** copy qualification *** *)
    FOR _ixj := 1 TO acv.a_mblock.mb_qual^.mqual_pos - 1 DO
        BEGIN
        acv.a_mblock.mb_qual^.mst_addr^[ _ix ] :=
              acv.a_mblock.mb_qual^.mst_addr^[ _ixj ];
        _ix := succ (_ix);
        END;
    (*ENDFOR*) 
    acv.a_mblock.mb_qual^.mfirst_free := _ix;
    acv.a_mblock.mb_qual^.mqual_cnt   := acv.a_mblock.mb_qual^.mfirst_free -
          acv.a_mblock.mb_qual^.mqual_pos;
    (* remark stack move size for correct         *)
    (* stack positions of internal stack pointers *)
    strat.str_stack_output_offs := - acv.a_mblock.mb_qual^.mqual_pos -
          acv.a_mblock.mb_qual^.mst_addr^[ acv.a_mblock.mb_qual^.mqual_pos ].epos + 2;
    END;
(*ENDIF*) 
;
&ifdef TRACE
t01stackdesc (ak_join, 'MBUFFSTACK 2', acv.a_mblock.mb_qual^.mst_addr,
      acv.a_mblock.mb_qual^.mstack_desc);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680_reset_only_inv (
            VAR acv           : tak_all_command_glob;
            is_inv_only_strat : boolean);
 
VAR
      _ix      : integer;
      _ixj     : integer;
 
BEGIN
&ifdef TRACE
t01stackdesc (ak_join, 'MBUFFSTACK 1', acv.a_mblock.mb_qual^.mst_addr,
      acv.a_mblock.mb_qual^.mstack_desc);
&endif
_ixj := 1;
IF  ( NOT is_inv_only_strat )
THEN
    BEGIN
    (* *** copy qualification: could be modified by *** *)
    (* ***  a70strategy, e.g. IN -> =              *** *)
    FOR _ix := acv.a_mblock.mb_qual^.mqual_pos +
          acv.a_mblock.mb_qual^.mst_addr^[ acv.a_mblock.mb_qual^.mqual_pos ].
          epos - 1
          TO acv.a_mblock.mb_qual^.mqual_pos +
          acv.a_mblock.mb_qual^.mqual_cnt - 1 DO
        BEGIN
        acv.a_mblock.mb_qual^.mst_addr^[ _ixj ] :=
              acv.a_mblock.mb_qual^.mst_addr^[ _ix ];
        _ixj      := succ (_ixj);
        END;
    (*ENDFOR*) 
    END
ELSE
    BEGIN
    (* *** in vak684 a70strategy will be called 2.nd time  *** *)
    END;
(*ENDIF*) 
acv.a_mblock.mb_qual^.mqual_cnt   := pred (acv.a_mblock.mb_qual^.mqual_pos);
acv.a_mblock.mb_qual^.mfirst_free := acv.a_mblock.mb_qual^.mqual_pos;
acv.a_mblock.mb_qual^.mqual_pos   := 1;
;
&ifdef TRACE
t01stackdesc (ak_join, 'MBUFFSTACK 2', acv.a_mblock.mb_qual^.mst_addr,
      acv.a_mblock.mb_qual^.mstack_desc);
&endif
END;
 
(*------------------------------*) 
 
FUNCTION
      ak680curr_recs_per_respage (
            VAR jinfos : tak68_joininfos;
            VAR joins  : tak_joinrec;
            succession : tak68_succession_ptr;
            join_step  : integer) : integer;
 
VAR
      _xi            : integer;
      _out_stop      : integer;
      _res_rec_len   : integer;
      _joined_tables : tak_joinset;
 
BEGIN
_res_rec_len := 0;
_joined_tables   := [];
FOR _xi := 1 TO join_step DO
    _joined_tables := _joined_tables + [ succession^[ _xi ].s_tableno ];
(*ENDFOR*) 
IF  (jinfos.ji_stack_desc.mst_addr^
    [ jinfos.ji_stack_desc.mqual_pos ].etype = st_jump_output)
THEN
    _out_stop := jinfos.ji_stack_desc.
          mst_addr^[ jinfos.ji_stack_desc.mqual_pos ].epos +
          jinfos.ji_stack_desc.mqual_pos - 2
ELSE
    _out_stop := jinfos.ji_stack_desc.mqual_pos;
(*ENDIF*) 
_xi := jinfos.ji_stack_desc.mqual_pos;
(* *** length of outputfields *** *)
WHILE _xi <= _out_stop DO
    BEGIN
    IF  ( jinfos.ji_stack_desc.mst_addr^[ _xi ].etype in
        [ st_fixkey, st_varkey, st_fixcol, st_varcol, st_varlongchar ] ) AND
        ((ord (jinfos.ji_stack_desc.mst_addr^[ _xi ].ecol_tab[ 2 ]))
        in _joined_tables)
    THEN
        BEGIN
        REPEAT
            _xi := succ (_xi);
        UNTIL
            (_xi >= _out_stop) OR
            (jinfos.ji_stack_desc.mst_addr^[ _xi ].etype = st_output);
        (*ENDREPEAT*) 
        _res_rec_len := _res_rec_len +
              jinfos.ji_stack_desc.mst_addr^[ _xi ].elen_var;
        END;
    (*ENDIF*) 
    _xi := succ (_xi);
    END;
(*ENDWHILE*) 
&ifdef TRACE
t01int4 (ak_join, 'only output ', _res_rec_len);
&endif
(* *** length of joinqualificationfields *** *)
FOR _xi := 0 TO joins.jrc_cnt - 1 DO
    BEGIN
    IF  (joins.jrc_joinarr^[ _xi ].jo_recs[ 1 ].
        jop_tableno  in _joined_tables)
        AND
        (joins.jrc_joinarr^[ _xi ].jo_recs[ 2 ].
        jop_tableno <> cak68_join_value) AND
        (NOT (joins.jrc_joinarr^[ _xi ].jo_recs[ 2 ].
        jop_tableno in _joined_tables))
    THEN
        _res_rec_len := _res_rec_len +
              joins.jrc_joinarr^[ _xi ].jo_recs[ 1 ].jop_inoutlen;
    (*ENDIF*) 
    IF  (joins.jrc_joinarr^[ _xi ].jo_recs[ 2 ].
        jop_tableno  in _joined_tables)
        AND
        (joins.jrc_joinarr^[ _xi ].jo_recs[ 1 ].
        jop_tableno <> cak68_join_value) AND
        (NOT (joins.jrc_joinarr^[ _xi ].jo_recs[ 1 ].
        jop_tableno in _joined_tables))
    THEN
        _res_rec_len := _res_rec_len +
              joins.jrc_joinarr^[ _xi ].jo_recs[ 2 ].jop_inoutlen;
    (*ENDIF*) 
    END;
(*ENDFOR*) 
_res_rec_len := _res_rec_len + cgg_rec_key_offset + RESCNT_MXGG04;
&ifdef TRACE
t01int4 (ak_join, 'outp + qual ',
      _res_rec_len - cgg_rec_key_offset - RESCNT_MXGG04);
t01int4 (ak_join, 'total reclen', _res_rec_len);
&endif
ak680curr_recs_per_respage := trunc (cak_page80percent /
      (_res_rec_len * cak68_sup_field_factor)) + 1;
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680multiplier_get (
            VAR acv             : tak_all_command_glob;
            base_ptr            : tak_sysbufferaddress;
            VAR col_info        : tak00_columninfo;
            jpropset            : tak_jcolpropset;
            VAR jmultiplier     : tsp00_Int4;
            VAR do_col_upd_stat : boolean);
 
VAR
      _colstat_required : boolean;
      _prim_rows        : tsp00_Int4;
      _col_stat         : tak_column_statistics;
 
BEGIN
do_col_upd_stat := false;
a28rows_and_colstat (acv, base_ptr, col_info, _col_stat, _prim_rows,
      _colstat_required);
&ifdef TRACE
t01int4 (ak_sem, 'c_values    ', _col_stat.c_values);
t01int4 (ak_sem, 'c_pages     ', _col_stat.c_pages );
t01int4 (ak_sem, '_prim_rows  ', _prim_rows);
&endif
IF  _colstat_required
THEN
    do_col_upd_stat := true;
(*ENDIF*) 
IF  _col_stat.c_values <> cak_is_undefined
THEN
    IF  _col_stat.c_values = 0
    THEN
        jmultiplier := 0
    ELSE
        IF  (csp_maxint4) < (_prim_rows / _col_stat.c_values)
        THEN
            jmultiplier := csp_maxint4
        ELSE
            jmultiplier := trunc((_prim_rows / _col_stat.c_values))
        (*ENDIF*) 
    (*ENDIF*) 
ELSE
    BEGIN
    IF  (jtonlykey in jpropset)
    THEN
        jmultiplier := cak68_only_field_multiplier
    ELSE
        jmultiplier := cak68_eq_field_multiplier;
    (*ENDIF*) 
    END;
(*ENDIF*) 
&ifdef TRACE
t01real (ak_join, 'jmultiplier ', jmultiplier, 6);
t01bool (ak_join, 'do_upd_stat ', do_col_upd_stat);
&endif
END;
 
(*------------------------------*) 
 
FUNCTION
      a680is_outer_predicate (
            VAR dmli   : tak_dml_info;
            st_pos     : tsp00_Int2) : boolean;
 
VAR
      _i          : integer;
      _found      : boolean;
      _is_outer   : boolean;
 
BEGIN (* PTS 1113327 *)
_i        := 0;
_found    := false;
_is_outer := false;
WHILE (_i < dmli.d_joins.jrc_cnt) AND NOT _found DO
    BEGIN
    WITH dmli.d_joins.jrc_joinarr^[ _i ] DO
        _found := (jo_recs[ 1 ].jop_startstack <= st_pos) AND
              ((jo_recs[ 1 ].jop_startstack + jo_recs[ 1 ].jop_cntstack) >= st_pos)
              OR
              (jo_recs[ 2 ].jop_startstack <= st_pos) AND
              ((jo_recs[ 2 ].jop_startstack + jo_recs[ 2 ].jop_cntstack) >= st_pos);
    (*ENDWITH*) 
    IF  NOT _found
    THEN
        _i := succ(_i);
    (*ENDIF*) 
    END;
(*ENDWHILE*) 
IF  ( _found )
THEN
    IF  ( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].
        jop_startstack <= st_pos )
        AND
        (( dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].
        jop_startstack + dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].
        jop_cntstack ) >= st_pos )
    THEN
        _is_outer := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 1 ].jop_outer_join
    ELSE
        _is_outer := dmli.d_joins.jrc_joinarr^[ _i ].jo_recs[ 2 ].jop_outer_join;
    (*ENDIF*) 
(*ENDIF*) 
a680is_outer_predicate := _is_outer;
&ifdef TRACE
t01int4 (ak_join, 'st_pos      ', st_pos);
t01int4 (ak_join, 'joinarr pos ', _i);
a683_output (ak_join, dmli.d_joins);
t01bool (ak_join, '_is_outer   ', _is_outer);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680prepare_join(
            VAR acv             : tak_all_command_glob;
            VAR dmli            : tak_dml_info;
            config              : tak_sysbufferaddress;
            VAR eq_rec          : tak68_eq_record;
            VAR jinfos          : tak68_joininfos;
            VAR jvrec           : tak68_joinview_rec;
            VAR table_stats     : tak68_table_stats;
            VAR res_info        : tak68_result_info;
            VAR syskey          : tgg00_SysInfoKey;
            VAR stop_search     : t_stop_reason);
 
VAR
      _gg_strategy         : tgg07_StrategyInfo;
      _page_eval_info      : tak71_page_eval_rec;
      _config              : tak00_access_configuration;
      _h                   : integer;
      _movelen             : integer;
      _all_rows            : integer;
      _prim_pages          : tsp00_Int4;
      _b_err               : tgg00_BasisError;
      _StratInfo_len       : tsp00_Int2;
      _mblock_changed      : boolean;
      _aux_fieldlists      : tgg00_FieldLists;
      _available           : tsp00_Int4;
      _ok                  : boolean;
 
BEGIN
_b_err               := e_ok;
jinfos.ji_act_join := succ( jinfos.ji_act_join );
jinfos.ji_acttabno := jinfos.ji_act_join;
_aux_fieldlists := acv.a_mblock.mb_fieldlists;
(* MBLOCK initialization *)
IF  ( oisshowview in dmli.d_tabarr^[jinfos.ji_act_join].ospecialname )
THEN
    a06a_mblock_init( acv, m_show, mm_with_join,
          dmli.d_tabarr^[ jinfos.ji_act_join ].otreeid )
ELSE
    a06a_mblock_init( acv, m_select, mm_with_join,
          dmli.d_tabarr^[ jinfos.ji_act_join ].otreeid );
(*ENDIF*) 
(* restore original data length *)
acv.a_mblock.mb_data_len         := jinfos.ji_mb_data_len;
acv.a_mblock.mb_data^.mbp_keylen := jvrec.jv_maxkeyl;
acv.a_mblock.mb_fieldlists       := _aux_fieldlists;
&ifdef TRACE
t01int4 (ak_join, 'mmmmmf_free1', acv.a_mblock.mb_qual^.mfirst_free);
&endif
(* get (only) qualifications for one table *)
ak680transfer_qualification( acv, jinfos, eq_rec );
&ifdef TRACE
t01int4 (ak_join, 'mmmmmf_free2', acv.a_mblock.mb_qual^.mfirst_free);
t01messblock (ak_join, 'MESSBUFF    ', acv.a_mblock);
&endif
IF  ( dmli.d_acttabindex <> jinfos.ji_act_join )
THEN
    a61_rel_old_table( acv, dmli, jinfos.ji_act_join );
(*ENDIF*) 
IF  ( acv.a_returncode = 0 ) AND dmli.d_joins.jrc_col_upd AND ( NOT dmli.d_view )
THEN
    ak680ins_col_upd_stat( acv, dmli, jinfos.ji_acttabno );
(*ENDIF*) 
IF  ( acv.a_returncode = 0 )
THEN
    BEGIN
    IF  ( acv.a_mblock.mb_strat_len MOD ALIGNMENT_GG00 <> 0 )
    THEN
        acv.a_mblock.mb_strat_len := acv.a_mblock.mb_strat_len -
              (acv.a_mblock.mb_strat_len MOD ALIGNMENT_GG00) +
              ALIGNMENT_GG00;
    (*ENDIF*) 
    ;
    (* initialize strategy record *)
    a71default_strat( _gg_strategy );
    _gg_strategy.str_rowno := dmli.d_rowno;
    IF  ( jinfos.ji_use_operator_join )
    THEN
        _gg_strategy.str_build_result := false
    ELSE
        _gg_strategy.str_build_result := true;
    (*ENDIF*) 
    _gg_strategy.str_ordering  := (dmli.d_distinct <> no_distinct);
    _gg_strategy.str_all_files := (acv.a_recursive_state = rs_last_select);
    _gg_strategy.str_strategy  := strat_undecided;
    table_stats[ jinfos.ji_act_join ].ts_inv_only_strat := false;
    _prim_pages  := a28prim_pages (acv, dmli.d_sparr.pbasep^.sbase);
    dmli.d_tabarr^[ jinfos.ji_act_join ].opages := _prim_pages;
    IF  ( _prim_pages > 0 )
    THEN
        BEGIN
        table_stats[ jinfos.ji_act_join ].ts_all_pages := _prim_pages;
        _all_rows := a28primrows (acv, dmli.d_sparr.pbasep^.sbase);
        IF  _all_rows < 1 (* not expected *)
        THEN
            _all_rows := 1;
        (*ENDIF*) 
        table_stats[ jinfos.ji_act_join ].ts_recs_per_page :=
              _all_rows DIV _prim_pages
        END
    ELSE
        BEGIN
        table_stats[ jinfos.ji_act_join ].ts_all_pages := 0;
        _all_rows  := cak_initrows;
        IF  ( dmli.d_sparr.pbasep^.sbase.bavgrowlen > 0 )
        THEN
            table_stats[ jinfos.ji_act_join ].
                  ts_recs_per_page := cak_page80percent DIV
                  dmli.d_sparr.pbasep^.sbase.bavgrowlen
        ELSE
            table_stats[ jinfos.ji_act_join ].
                  ts_recs_per_page := 1; (* unexpected value *)
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    IF  ( table_stats[ jinfos.ji_act_join ].ts_recs_per_page = 0 )
    THEN
        table_stats[ jinfos.ji_act_join ].
              ts_recs_per_page := 1; (* unexpected value *)
    (* ============================ *)
    (* recs_per_page represents the *)
    (* estimated number of records  *)
    (* per page of this table       *)
    (* ============================ *)
&   ifdef TRACE
    (*ENDIF*) 
    t01sname (ak_join, '------------');
    t01int4  (ak_join, '   TABLENO  ', jinfos.ji_act_join);
    t01int4  (ak_join, 'prime-pages ',
          table_stats[ jinfos.ji_act_join ].ts_all_pages);
    t01int4  (ak_join, 'prime-rows  ', _all_rows);
    t01bool (ak_join, 'd_view      ', dmli.d_view );
&   endif
    IF  ( dmli.d_view )
    THEN
        BEGIN
        (* CREATE VIEW statement                        *)
        (* stackentries for the view have to be created *)
        (* compose strategy for views                   *)
        IF  ( jinfos.ji_act_join = 1 )
        THEN
            BEGIN
            _gg_strategy.str_strategy := strat_viewkey;
            FOR _h := 0 TO MAX_COLPOSARR_IDX_GG07 DO
                BEGIN
                _gg_strategy.str_key_in_range.skir_keystart[ _h ] := 0;
                _gg_strategy.str_key_in_range.skir_keystop[ _h ]  := 0
                END;
            (*ENDFOR*) 
            _gg_strategy.str_key_in_range.skir_strat_props := [ ksp_exact_match ]
            END
        ELSE
            _gg_strategy.str_strategy := strat_join_viewkey;
        (*ENDIF*) 
        _gg_strategy.str_use_rowno   := false;
        _gg_strategy.str_corr_single := false;
        _gg_strategy.str_cnt_strat   := 1;
        _gg_strategy.str_key_len     := 2; (* not used*)
        _gg_strategy.str_rec_len     := cgg_rec_key_offset +
              RESCNT_MXGG04; (*   "    *)
        _StratInfo_len := STRATEGY_START_MXGG07 +
              sizeof(_gg_strategy.str_key_in_range);
        table_stats[ jinfos.ji_act_join ].ts_pages_searched := 0;
        table_stats[ jinfos.ji_act_join ].ts_strat_value    := 0;
        table_stats[ jinfos.ji_act_join ].ts_wholeIO_pages  := 1;
        (* initialization for updateable join-views; *)
        (* insignificant as join-order is fix anyway *)
        END
    ELSE
        BEGIN
        _h := dmli.d_inoutpos;
&       ifdef TRACE
        t01int4 (ak_join, 'd_inoutpos  ', _h);
        t01int4 (ak_join, 'ji_act_join ', jinfos.ji_act_join);
&       endif
        (* calculate size of first join(select) result *)
        res_info.ri_one_tab_len^[ jinfos.ji_act_join ] :=
              a681tmp_table_length (dmli, jinfos, jinfos.ji_act_join);
        dmli.d_inoutpos := res_info.ri_one_tab_len^[ jinfos.ji_act_join ];
        _gg_strategy.str_rec_len := res_info.ri_one_tab_len^[ jinfos.ji_act_join ];
        ;
        IF  ( oisshowview in dmli.d_tabarr^[ jinfos.ji_act_join ].ospecialname )
        THEN
            BEGIN
            a722strategy (acv, dmli, _page_eval_info, _gg_strategy, _StratInfo_len);
            table_stats[ jinfos.ji_act_join ].ts_strat_value :=
                  _page_eval_info.pev_readIO_pages / _prim_pages;
            IF  ( table_stats[ jinfos.ji_act_join ].ts_strat_value > 1 )
            THEN
                table_stats[ jinfos.ji_act_join ].ts_strat_value := 1;
            (*ENDIF*) 
            ;
&           ifdef trace
            t01strat_enum( ak_join, 'act strategy', _gg_strategy.str_strategy );
            t01int4 (ak_join, 'cost   pages', _page_eval_info.pev_wholeIO_pages );
            t01int4 (ak_join, 'result pages', _page_eval_info.pev_readIO_pages );
            t01real (ak_join, 'strat value ',
                  table_stats[ jinfos.ji_act_join ].ts_strat_value, 3 );
&           endif
            END
        ELSE
            BEGIN
            _mblock_changed := false;
            (* only qualifications lays on a_mblock *)
            (* so far (ak680transfer_qualification) *)
            (* add output columns for               *)
            (* jinfos.ji_act_join                  *)
            ak680only_inv_prepare( acv, dmli, jinfos, _gg_strategy,
                  jinfos.ji_act_join, _mblock_changed );
            IF  (( config <> NIL ) AND
                ( jinfos.ji_acttabno <= config^.shint.hint_joincfg_cnt ))
            THEN
                BEGIN
                _config.cfg_switches := config^.shint.
                      hint_join_config[ jinfos.ji_acttabno ].cfg_access_switches;
                _config.cfg_indexno := config^.shint.
                      hint_join_config[ jinfos.ji_acttabno ].cfg_indexno;
&               ifdef trace
                a81debug_access_config( ak_join, _config );
&               endif
                END
            ELSE
                BEGIN
                _config.cfg_switches := [];
                _config.cfg_indexno  := 0;
                END;
            (*ENDIF*) 
            IF  ( NOT _mblock_changed )
            THEN
                BEGIN
                _config.cfg_switches :=
                      _config.cfg_switches + [ cs_disable_inv_only ];
                END;
            (*ENDIF*) 
            ;
            (* call one table optimizer *)
            IF  ( jinfos.ji_use_operator_join )
            THEN
                BEGIN
                _config.cfg_switches := _config.cfg_switches - [ cs_build_result ];
                _config.cfg_switches := _config.cfg_switches + [ cs_operator_join ];
                a70strategy( acv, dmli, _gg_strategy,
                      _StratInfo_len, _page_eval_info, _config );
                END
            ELSE
                a70strategy( acv, dmli, _gg_strategy,
                      _StratInfo_len, _page_eval_info, _config );
            (*ENDIF*) 
            table_stats[ jinfos.ji_act_join ].ts_inv_only_strat :=
                  ( _gg_strategy.str_strategy in a70glob_inv_strats ) AND
                  ( isp_inv_only in _gg_strategy.str_inv_in_range.
                  siir_strat_props );
            IF  ( _page_eval_info.pev_readIO_rows_min <> IS_UNDEFINED_GG07 ) AND
                ( _page_eval_info.pev_readIO_rows_min <> MAX_INT4_SP00 )
            THEN
                table_stats[ jinfos.ji_act_join ].ts_strat_value :=
                      _page_eval_info.pev_readIO_rows_min / _all_rows
            ELSE
                BEGIN
                IF  ( _page_eval_info.pev_readIO_rows = IS_UNDEFINED_GG07 )
                THEN
                    table_stats[ jinfos.ji_act_join ].ts_strat_value :=
                          _page_eval_info.pev_readIO_pages / _prim_pages
                ELSE
                    table_stats[ jinfos.ji_act_join ].ts_strat_value :=
                          _page_eval_info.pev_readIO_rows / _all_rows;
                (*ENDIF*) 
                END;
            (*ENDIF*) 
            IF  ( table_stats[ jinfos.ji_act_join ].ts_strat_value > 1 )
            THEN
                table_stats[ jinfos.ji_act_join ].ts_strat_value := 1;
            (*ENDIF*) 
            ;
&           ifdef TRACE
            t01strat_enum( ak_join, 'act strategy', _gg_strategy.str_strategy );
            t01int4 (ak_join, 'cost   pages', _page_eval_info.pev_wholeIO_pages );
            t01int4 (ak_join, 'result pages', _page_eval_info.pev_readIO_pages );
            t01int4 (ak_join, 'result rows ', _page_eval_info.pev_readIO_rows );
            t01int4 (ak_join, 'res min rows', _page_eval_info.pev_readIO_rows_min );
            t01real (ak_join, 'strat value ',
                  table_stats[ jinfos.ji_act_join ].ts_strat_value, 3 );
            t01bool( ak_join, 'inv only    ',
                  table_stats[ jinfos.ji_act_join ].ts_inv_only_strat );
            t01real (ak_join, 'record_found',
                  _all_rows * table_stats[ jinfos.ji_act_join ].ts_strat_value, 3);
&           endif
            IF  ( _mblock_changed )
            THEN
                ak680_reset_only_inv( acv,
                      table_stats[ jinfos.ji_act_join ].ts_inv_only_strat );
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        dmli.d_inoutpos := _h;
        (* PTS 1111348 *)
        IF  ( jinfos.ji_seqsearch_for_exec ) AND
            ( _gg_strategy.str_strategy  = strat_undecided )
        THEN
            a07_b_put_error (acv, e_unknown_strategy, 1);
        (*ENDIF*) 
        IF  ( acv.a_returncode = 0 )
        THEN
            BEGIN
            IF  ( _gg_strategy.str_strategy = strat_undecided )
            THEN
                stop_search := stop_bof_param
            ELSE
                IF  ( jinfos.ji_use_operator_join AND
                    ( NOT ( _gg_strategy.str_strategy in
                    a70glob_accessop_known_strats )))
                THEN
                    BEGIN
                    (* we need fetch strategies for new join execution *)
                    a07ak_system_error( acv, 680, 2 );
                    END;
                (*ENDIF*) 
            (*ENDIF*) 
            table_stats[ jinfos.ji_act_join ].ts_wholeIO_pages :=
                  _page_eval_info.pev_wholeIO_pages;
            table_stats[ jinfos.ji_act_join ].ts_pages_searched :=
                  table_stats[ jinfos.ji_act_join ].ts_strat_value *
                  table_stats[ jinfos.ji_act_join ].ts_all_pages;
            (* PTS 1112102 *)
            IF  table_stats[ jinfos.ji_act_join ].ts_pages_searched
                < 1 / table_stats[ jinfos.ji_act_join ].ts_recs_per_page
            THEN
                table_stats[ jinfos.ji_act_join ].ts_pages_searched :=
                      1 / table_stats[ jinfos.ji_act_join ].ts_recs_per_page;
&           ifdef trace
            (*ENDIF*) 
            t01real (ak_join, 'pages_search',
                  table_stats[ jinfos.ji_act_join ].ts_pages_searched, 3);
            t01int4 (ak_join, 'all_pages   ',
                  table_stats[ jinfos.ji_act_join ].ts_all_pages);
            t01real (ak_join, 'strat_value ',
                  table_stats[ jinfos.ji_act_join ].ts_strat_value,3);
            t01int4 (ak_join, 'cost_value  ',
                  table_stats[ jinfos.ji_act_join ].ts_wholeIO_pages);
&           endif
            (* transfer of values from evaluation_rec *)
            (* to table statistics array              *)
            END;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    table_stats[ jinfos.ji_act_join ].ts_strategy := _gg_strategy.str_strategy;
    (* allocat space for cluster IO *)
    IF  (a01join_clust_read OR _gg_strategy.str_use_clusterIO)        AND
        NOT acv.a_intern_explain AND ( bd999GetDataIOBlockCount > 0 ) AND
        (acv.a_transinf.tri_trans.trClusterIOPtr_gg00 = NIL)          AND
        NOT a101_IsTempFile(acv, dmli.d_sparr.pbasep^.sbase.btreeid)  AND
        bd998ArePagesClustered( acv.a_transinf.tri_trans, dmli.d_sparr.pbasep^.sbase.btreeid.fileTabId_gg00)
    THEN
        BEGIN
        _ok := true;
        _available := 0;
        vnewbuf (bd999GetDataIOBlockCount, _available, acv.a_transinf.tri_trans.trClusterIOPtr_gg00, _ok);
        IF  _ok
        THEN
            acv.a_transinf.tri_trans.trClusterIOSize_gg00 := bd999GetDataIOBlockCount * bd999GetPageSize;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    IF  ( acv.a_mblock.mb_qual^.mfirst_free >= acv.a_mblock.mb_st_max )
    THEN
        a07_b_put_error( acv, e_too_many_mb_stackentries, 1 )
    ELSE
        IF  ( acv.a_mblock.mb_strat_len + _StratInfo_len >= acv.a_mblock.mb_strat_size )
        THEN
            a07_b_put_error( acv, e_too_many_mb_strat, 1 );
        (*ENDIF*) 
    (*ENDIF*) 
    IF  ( acv.a_returncode = 0 )
    THEN
        BEGIN
        (* build syskey *)
        a682join_MBlock_key( acv, dmli, jinfos.ji_parskey,
              jvrec.jv_tabid, jinfos.ji_act_join, syskey,
              jinfos.ji_seqsearch_for_exec );
        IF  ( stop_search <> dont_stop )
        THEN
            BEGIN
            (* optimal sequence cannot be evaluated at      *)
            (* parse time, stop search and restore mess_buf *)
&           ifdef trace
            t01int4( ak_join, 'search abort', ord( stop_search ));
&           endif
            (* restore mblock description *)
            acv.a_mblock.mb_qual^.mstack_desc := jinfos.ji_stack_desc;
            (* restore mblock qualification *)
            acv.a_mblock.mb_qual^.mst_addr := acv.a_mblock.mb_st;
            SAPDB_PascalMove ('VAK680',   4,    
                  (jinfos.ji_stack_desc.mst_max * STACK_ENTRY_MXGG00),
                  acv.a_mblock.mb_st_size,
                  @jinfos.ji_st_addr^, 1, @acv.a_mblock.mb_st^, 1,
                  ((jinfos.ji_stack_desc.mfirst_free - 1) * STACK_ENTRY_MXGG00),
                  acv.a_returncode);
            END
        ELSE
            BEGIN
            IF  ( _StratInfo_len <= 0 )
            THEN
                _b_err := e_unknown_strategy
            ELSE
                BEGIN
                (* save strategy *)
                IF  ( _gg_strategy.str_strategy = strat_more_than_one )
                THEN (* parts of strategy already in mess_buf *)
                    _movelen := STRATEGY_START_MXGG07
                ELSE
                    _movelen := _StratInfo_len;
                (*ENDIF*) 
                (* put strategy on data part *)
                SAPDB_PascalMove ('VAK680',   5,    
                      sizeof( _gg_strategy ),
                      acv.a_mblock.mb_strat_size,
                      @_gg_strategy, 1,
                      @acv.a_mblock.mb_strat^,
                      acv.a_mblock.mb_strat_len  + 1,
                      _movelen,
                      acv.a_returncode);
                (* create STRATEGY stack entry *)
                acv.a_mblock.mb_qual^.mstrat_pos  :=
                      acv.a_mblock.mb_qual^.mfirst_free;
                acv.a_mblock.mb_qual^.mstrat_cnt  := 1;
                acv.a_mblock.mb_qual^.mfirst_free :=
                      succ ( acv.a_mblock.mb_qual^.mfirst_free );
                WITH acv.a_mblock.
                     mb_st^ [ acv.a_mblock.mb_qual^.mstrat_pos ] DO
                    BEGIN
                    etype         := st_strat;
                    eop           := op_none;
                    epos          := acv.a_mblock.mb_strat_len  + 1;
                    elen_var      := _StratInfo_len;
                    ecol_tab[ 1 ] := chr(0);
                    ecol_tab[ 2 ] := chr(0)
                    END;
                (*ENDWITH*) 
                acv.a_mblock.mb_strat_len  :=
                      acv.a_mblock.mb_strat_len  + _StratInfo_len;
&               ifdef TRACE
                t01messblock (ak_join, 'MESSBUFF    ', acv.a_mblock);
&               endif
                (* put strategy to AK cache *)
                a682_mbuf_to_tmpbuf(acv, syskey, _b_err, mtc_search_sequence);
                IF  ( _b_err = e_ok )
                THEN
                    IF  ( dmli.d_joins.jrc_cnt > 0 )
                        AND
                        acv.a_outer_join
                        AND
                        ( jinfos.ji_acttabno in dmli.d_oj_tables )
                    THEN
                        (* check, if outer join is neccessary *)
                        a681test_outer_qualification (acv, dmli, jinfos);
                    (*ENDIF*) 
                (*ENDIF*) 
                END;
            (*ENDIF*) 
            IF  ( _b_err <> e_ok )
            THEN
                a07_b_put_error( acv, _b_err, 1 );
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        END
    (*ENDIF*) 
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      a680rollback_temp_jinfo(
            VAR acv             : tak_all_command_glob;
            VAR dmli            : tak_dml_info;
            VAR parsk           : tak_parskey;
            VAR jv_tabid        : tgg00_Surrogate;
            info_cnt            : tsp00_Int2;
            seqsearch_for_exec  : boolean);
 
VAR
      _i              : tsp00_Int2;
      _aux_return     : tsp00_Int2;
      _aux_errorpos   : tsp00_Int4;
      _syskey         : tgg00_SysInfoKey;
      _b_err          : tgg00_BasisError;
 
BEGIN
_aux_return   := acv.a_returncode;
_aux_errorpos := acv.a_errorpos;
acv.a_returncode := 0;
a682join_MBlock_key( acv, dmli, parsk, jv_tabid, 0,
      _syskey, seqsearch_for_exec );
(* destroy created cache entries *)
FOR _i := 1 TO info_cnt DO
    BEGIN
    _syskey.slinkage[ 2 ] := chr( _i );
    a10del_sysinfo( acv, _syskey, _b_err );
    END;
(*ENDFOR*) 
acv.a_returncode := _aux_return;
acv.a_errorpos   := _aux_errorpos;
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680calc_buffers(
            VAR acv       : tak_all_command_glob;
            VAR dmli      : tak_dml_info;
            VAR series    : tak68_sequence);
 
VAR
      _y        : tsp00_Longreal;
      _free_buf : tsp00_Int4;
      _buf_unit : tsp00_Longreal;
      _lnln     : tsp00_Int4;
      _i        : tsp00_Int2;
      _output   : tsp00_C24;
      _output1  : tsp00_Name;
      _ln       : tsp00_Line;
 
BEGIN
_y := 0;
_free_buf := g01join_tablebuffer;
&ifdef trace
t01int4( ak_join, 'whole buffer', _free_buf);
&endif
FOR _i := 1 TO dmli.d_cntfromtab DO
    BEGIN
    IF  ( series[ _i ].jos_expected_recs = 0 )
    THEN
        series[ _i ].jos_expected_recs := 1;
    (*ENDIF*) 
    IF  ( series[ _i ].jos_predefined_buf )
    THEN
        BEGIN
        (* jos_table_buffer already contains usable value *)
        _free_buf := _free_buf - series[ _i ].jos_table_buffer;
&       ifdef trace
        t01int4( ak_join, 'predef buf  ', series[ _i ].jos_table_buffer );
&       endif
        END
    ELSE
        BEGIN
        (* reminder: jos_table_buffer contains _gg_strategy.str_rec_len*)
        _y := _y + ( series[ _i ].jos_expected_recs *
              (series[ _i ].jos_table_buffer/2) );
&       ifdef trace
        t01int4( ak_join, 'rec len     ', series[ _i ].jos_table_buffer );
&       endif
        END;
    (*ENDIF*) 
    END;
(*ENDFOR*) 
IF  ( _y <> 0 )
THEN
    BEGIN
    _buf_unit := _free_buf / _y;
&   ifdef trace
    t01real( ak_join, '_buf_unit   ', _buf_unit, 6 );
&   endif
    FOR _i := 1 TO dmli.d_cntfromtab DO
        BEGIN
        (* jos_table_buffer already contains usable value *)
        IF  ( NOT series[ _i ].jos_predefined_buf )
        THEN
            series[ _i ].jos_table_buffer :=
                  round( series[ _i ].jos_expected_recs *
                  (series[ _i ].jos_table_buffer/2) * _buf_unit );
        (*ENDIF*) 
        ;
&       ifdef trace
        t01int4( ak_join, 'buffer size ', series[ _i ].jos_table_buffer );
        IF  ( series[ _i ].jos_expected_recs < 0 )
        THEN
            g01abort ( -9999, csp3_n_join,
                  'WRONG JOS_EXPECTED_RECS ', series[ _i ].jos_expected_recs);
&       endif
        (*ENDIF*) 
        END;
    (*ENDFOR*) 
    END
ELSE
    (* nothing to distribute *)
    ;
(*ENDIF*) 
IF  ( acv.a_intern_explain OR g01vtrace.vtrStrategy_gg00 ) AND
    ( acv.a_explain_kind = ex_sequence )
THEN
    BEGIN
    _output1 := 'BYTES             ';
    SAPDB_PascalForcedFill (sizeof (_ln), @_ln, 1, sizeof (_ln), bsp_c1);
    _output := 'BUFFER SIZE PER TASK :  ';
    SAPDB_PascalForcedMove (sizeof(_output), sizeof(_ln),
          @_output, 1, @_ln, 1, sizeof(_output));
    g17int4to_line ( g01join_tablebuffer, false, 10, 24, _ln);
    _lnln := 35;
    g17nameto_line( _output1, _lnln, _ln );
    a40sequence_expl_row (acv, _ln, c_change_to_unicode);
    ;
    FOR _i := 1 TO dmli.d_cntfromtab DO
        BEGIN
        SAPDB_PascalForcedFill (sizeof (_ln), @_ln, 1, sizeof (_ln), bsp_c1);
        _output := 'TABLE                   ';
        SAPDB_PascalForcedMove (sizeof(_output), sizeof(_ln),
              @_output, 1, @_ln, 1, sizeof(_output));
        g17int4to_line ( series[ _i ].jos_source , false, 2, 7, _ln);
        _ln[ 10 ] := ':';
        g17int4to_line ( series[ _i ].jos_table_buffer , false, 10, 12, _ln);
        _lnln := 23;
        IF  ( series[ _i ].jos_predefined_buf )
        THEN
            _output1 := 'bytes             '
        ELSE
            _output1 := 'BYTES             ';
        (*ENDIF*) 
        g17nameto_line( _output1, _lnln, _ln );
        _output1 := ' (recs est.       ';
        g17nameto_line( _output1, _lnln, _ln );
        g17int4to_line ( series[ _i ].jos_expected_recs , false, 10,
              _lnln + 2, _ln);
        _lnln := 51;
        _ln[ _lnln ] := ')';
        a40sequence_expl_row (acv, _ln, c_change_to_unicode);
        END;
    (*ENDFOR*) 
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680init_result_info(
            VAR acv       : tak_all_command_glob;
            table_cnt     : tsp00_Int4;
            VAR res_info  : tak68_result_info);
 
VAR
 
      _cast  : RECORD
            CASE integer OF
                1 :
                    (addr: tsp00_Addr);
                2 :
                    (iptr : tak68_int2_arr_ptr);
                END;
            (*ENDCASE*) 
 
 
BEGIN
res_info.ri_one_tab_len    := NIL;
_cast.addr := gg941Allocate( acv.a_transinf.tri_trans,
      table_cnt * sizeof(tsp00_Int2) );
res_info.ri_one_tab_len := _cast.iptr;
&ifdef trace
t01addr( ak_join, 'ri_one_tab_l', _cast.addr );
&endif
IF  ( res_info.ri_one_tab_len = NIL )
THEN
    a07_b_put_error( acv, e_no_more_memory, 1 );
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak680finalize_result_info(
            VAR acv       : tak_all_command_glob;
            VAR res_info  : tak68_result_info);
 
VAR
 
      _cast  : RECORD
            CASE integer OF
                1 :
                    (addr: tsp00_Addr);
                2 :
                    (iptr : tak68_int2_arr_ptr);
                END;
            (*ENDCASE*) 
 
 
BEGIN
IF  ( res_info.ri_one_tab_len <> NIL )
THEN
    BEGIN
    _cast.iptr := res_info.ri_one_tab_len;
&   ifdef trace
    t01addr( ak_join, 'ri_one_tab_l', _cast.addr );
&   endif
    gg941Deallocate( acv.a_transinf.tri_trans, _cast.addr );
    END;
(*ENDIF*) 
END;
 
.CM *-END-* code ----------------------------------------
.SP 2 
***********************************************************
.PA 
