.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 : VAK724
changed : 2000-11-22
module  : Build_Strategy_term_structure
 
Author  : ElkeZ
Created : 1985-10-16
*****************************************************
 
Purpose : module for recognization of term structure
 
Define  :
 
        PROCEDURE
              a724all_L1_terms (
                    VAR acv         : tak_all_command_glob;
                    VAR dmli        : tak_dml_info;
                    VAR access_info : tak70_strategy_record;
                    VAR L1_terms    : tak70_term );
 
.CM *-END-* define --------------------------------------
 
Use     :
 
        FROM
              Build_Strategy : VAK70;
 
        VAR
              a70_glob_zstrat_rec : tak70_strategy_record;
 
      ------------------------------ 
 
        FROM
              Build_Strategy_2 : VAK71;
 
        VAR
              a71blankline            : tsp00_Line;
 
        PROCEDURE
              a71key_strat (
                    VAR acv         : tak_all_command_glob;
                    VAR access_info : tak70_strategy_record;
                    start           : tsp00_Int2;
                    stop            : tsp00_Int2);
 
        PROCEDURE
              a71index_strat (
                    VAR acv         : tak_all_command_glob;
                    VAR access_info : tak70_strategy_record;
                    start           : tsp00_Int2;
                    stop            : tsp00_Int2);
 
      ------------------------------ 
 
        FROM
              AK_universal_semantic_tools : VAK06;
 
        PROCEDURE
              a06find_colinfo (
                    base_ptr        : tak_sysbufferaddress;
                    VAR stack_entry : tgg00_StackEntry;
                    VAR colinfo_ptr : tak00_colinfo_ptr);
 
      ------------------------------ 
 
        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);
 
      ------------------------------ 
 
        FROM
              AK_Index : VAK24;
 
        FUNCTION
              a24LookForFunctionBasedIndex (
                    VAR acv        : tak_all_command_glob;
                    stackStart     : integer;
                    stackEnd       : integer;
                    VAR stackNext  : integer) : boolean;
 
      ------------------------------ 
 
        FROM
              Trace_Strategy_2 : VAK727;
 
        PROCEDURE
              a727L1_terms_trace (
                    VAR acv      : tak_all_command_glob;
                    VAR L1_terms : tak70_term);
 
      ------------------------------ 
 
        FROM
              Configuration_Parameter : VGG01;
 
        VAR
              g01vtrace    : tgg00_VtraceState;
 
      ------------------------------ 
 
        FROM
              Select_Help_Procedures : VGG04;
 
        PROCEDURE
              g04init_select_fields (
                    VAR sel       : tgg00_SelectFieldsParam;
                    data_addr     : tsp00_MoveObjPtr;
                    data_size     : tsp00_Int4;
                    work_st_addr  : tgg00_StackListPtr;
                    work_st_max   : tsp00_Int2;
                    work_buf_addr : tsp00_MoveObjPtr;
                    work_buf_size : tsp00_Int4;
                    curr_sqlmode  : tsp00_SqlMode;
                    fieldlistptr  : tgg00_FieldListsPtr);
 
        FUNCTION
              g04iseffective_value (
                    VAR stackentry : tgg00_StackEntry;
                    VAR mblock     : tgg00_MessBlock) : boolean;
 
      ------------------------------ 
 
        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
              g17sname_to_line (
                    n             : tsp00_Sname;
                    VAR ln_len    : integer;
                    VAR ln        : tsp00_Line (*ptocSynonym char**));
 
      ------------------------------ 
 
        FROM
              Trace_Help_Procedures : VGG041;
 
        PROCEDURE
              g041c30_to_trace (
                    VAR t : tgg00_TransContext;
                    msg : tsp00_C30);
 
        PROCEDURE
              g041name_to_trace (
                    VAR t  : tgg00_TransContext;
                    name   : tsp00_Name);
 
        PROCEDURE
              g041int4_to_trace (
                    VAR t  : tgg00_TransContext;
                    name   : tsp00_Name;
                    intval : tsp00_Int4);
 
        PROCEDURE
              g041line_to_trace (
                    VAR t      : tgg00_TransContext;
                    VAR line   : tsp00_Line);
 
        PROCEDURE
              g041p2int4_to_trace (
                    VAR t   : tgg00_TransContext;
                    name1   : tsp00_Name;
                    intval1 : tsp00_Int4;
                    name2   : tsp00_Name;
                    intval2 : tsp00_Int4);
 
      ------------------------------ 
 
        FROM
              KB_get : VKB71;
 
        PROCEDURE
              k71qual_handling (
                    VAR t              : tgg00_TransContext;
                    VAR sel            : tgg00_SelectFieldsParam;
                    with_view          : boolean;
                    check_new_rec      : boolean;
                    VAR stack_desc     : tgg00_StackDesc;
                    VAR err_st_ptr     : tgg00_StEntryAddr;
                    VAR unqualified    : boolean);
 
      ------------------------------ 
 
        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_PascalFill  (
                    mod_id         : tsp00_C6;
                    mod_intern_num : tsp00_Int4;
                    size           : tsp00_Int4;
                    m              : tsp00_MoveObjPtr;
                    pos            : tsp00_Int4;
                    len            : tsp00_Int4;
                    fillchar       : char;
                    VAR e          : tgg00_BasisError);
 
      ------------------------------ 
 
        FROM
              Trace : VBD120;
 
        PROCEDURE
              b120MessBlockTrace (
                    VAR Trans     : tgg00_TransContext;
                    TraceType     : tgg00_VtraceType;
                    VAR MessBlock : tgg00_MessBlock);
 
      ------------------------------ 
 
        FROM
              RTE_kernel : VEN101;
 
        PROCEDURE
              vfrawopen (
                    VAR hostfile   : tsp00_VFilename;
                    VAR hostfileno : tsp00_Int4;
                    VAR error      : tsp00_VfReturn;
                    VAR errtext    : tsp00_ErrText);
 
        PROCEDURE
              vfwrite (hostfileno : tsp00_Int4;
                    buf           : tsp_vf_bufaddr;
                    VAR error     : tsp00_VfReturn;
                    VAR errtext   : tsp00_ErrText);
 
        PROCEDURE
              vfclose (hostfileno : tsp00_Int4;
                    VAR error     : tsp00_VfReturn;
                    VAR errtext   : tsp00_ErrText);
&       ifdef TRACE
 
      ------------------------------ 
 
        FROM
              Test_Procedures : VTA01;
 
        PROCEDURE
              t01bool (
                    debug    : tgg00_Debug;
                    nam      : tsp00_Sname;
                    curr_bool: boolean);
 
        PROCEDURE
              t01int4 (
                    debug    : tgg00_Debug;
                    nam      : tsp00_Sname;
                    int      : tsp00_Int4);
 
        PROCEDURE
              t01line (
                    level  : tgg00_Debug;
                    VAR ln : tsp00_Line);
 
        PROCEDURE
              t01name (
                    level : tgg00_Debug;
                    nam : tsp00_Name);
 
        PROCEDURE
              t01sname (
                    level : tgg00_Debug;
                    nam : tsp00_Sname);
 
        PROCEDURE
              t01p2int4 (
                    debug : tgg00_Debug;
                    nam_1 : tsp00_Sname;
                    int_1 : tsp00_Int4;
                    nam_2 : tsp00_Sname;
                    int_2 : tsp00_Int4);
 
        PROCEDURE
              t01stackentry (
                    debug          : tgg00_Debug;
                    VAR st         : tgg00_StackEntry;
                    entry_index    : integer);
 
      ------------------------------ 
 
        FROM
              Trace_Strategy_1 : VAK725;
 
        PROCEDURE
              a725output_access_info (
                    level           : tgg00_Debug;
                    nam             : tsp00_Sname;
                    VAR access_info : tak70_strategy_record);
 
        PROCEDURE
              a725L1_terms_output (
                    level        : tgg00_Debug;
                    VAR L1_terms : tak70_term);
&       endif
 
.CM *-END-* use -----------------------------------------
 
Synonym :
 
.CM *-END-* synonym -------------------------------------
***********************************************************
Description:
 
A724ALL_L1_TERMS
----------------------------------
 
Processes the entire qualification, with consideration being given at the
highest level only to terms that are ANDed.
 
After the beginning of the qualification ('qs_start') and the end have been
determined ('qs_stop'), it is checked first of all whether there is a negation
at the highest level. Although, in this case, the entire qualification cannot
be used for finding a strategy, this stack entry is ignored and the rest of the
condition is analyzed normally.
After full analysis, the information in 'z_strat_rec' and 'L1_TERMS'
is destroyed and only in the case of a cost estimate is the value of
'z_qual_value' determined as a complement to the original one. This makes it
possible in the case of an 'EXPLAIN' to give at least an approximate indication
of the costs.
 
If there is no 'AND' at the highest level (last stack entry is 'op_or'), there
cann't be a simple Top_level_term. To indicate this, 'lv_levelstart' = 2 is set;
otherwise, = 1. The other parts of 'levelrec' are given the following entries
and have the following significance:
 
lv_levelfound : Specifies the nesting depth of the current condition; is initialized
with 0.
 
lv_levelstart : Specifies whether there is an 'AND' or an 'OR' at the highest
level or not; see above for initialization.
 
lv_levelend : ARRAY of BOOLEAN, specifies for each level (1 .. c_level_max)
whether the level is finished or not. The entry is 'false' for all.
 
The processing of conditions which are combined by 'AND' is performed
in a While loop by AK724ONE_L1_term until there are no more conditions ('qs_actpos'
 > 'qs_stop').
 
Since, in various cases, the counters of L1_TERMS have counted on although no
entries have been made, the contents of L1_TERMS are corrected after the While
loop. Then, the negation is handled at the highest level (see above).
 
AK724ONE_L1_TERM
----------------------------------
 
In this procedure, top-level conditions that can be used in the search for
a strategy (on key fields or inverted fields) are entered in 'zstrat';
conditions of other levels (up to at most level 3 (= c_level_max)) are put in
L1_TERMS. All lower levels are jumped, as is a negated term at the second or
third level. In the case of a negated, single condition, no entries are made in
'z_start' or L1_TERMS. In the case of a cost estimate, however, the complement of
the calculated costs is entered in 'ps_qual_value' (cost value of the current
condition).
 
Sequence of actions
 
(1) Calling of AK724ANALYZE_CONDITION in order to analyze the current condition.
After it has been called, 'qs_actpos' is at the next boolean operator ('op_and',
'op_or', 'op_not') or jump command ('st_jump_true', 'st_jump_false' ). If a
usable condition has been found, 'ps_startpos' identifies the beginning and 'ps_stoppos'
identifies the end of the condition, and 'ps_keyfield' and 'ps_indexfield' indicate
whether a keyfield or an inverted field has been found.
 
(2) Handling of a negated condition (see above).
 
(3) Calling of AK724CHECK_LEVEL, in order to determine the nesting depth of the
current condition. AK724CHECK_LEVEL returns 'true' if there was a term at level 1,
2, 3, with 'lv_levelfound' indicating the level. If there was a lower-level term,
CHECK_LEVEL returns 'false' and has already jumped the corresponding stack
entries, just as in the case of a negated term ('qs_actpos' is then at the first stack
entry after that term).
 
(4) If AK724CHECK_LEVEL was successful, the following actions are performed
depending on 'lv_levelfound':
 
 
4.1 : If there was a key field ('keyfield' = 'true'), it is entered in
'z_strat' with A71KEY_STRAT. If there was an inverted field, it is entered in
'z_strat' with A71INDEX_STRAT. If a cost estimate is to be performed,'
z_qual_value' is also determined.
 
4.2 : If there was neither a key field nor an inverted field and nor is a cost
estimate desired, the condition must not be entered in L1_TERMS as a level-2-
term. If a condition has already been entered under the current value of
'cntor' (i.e. on the same second level), this entry must be destroyed, since,
in the case of an OR link, all terms must be usable (see also OR_QUAL).
Otherwise, the condition is entered in L1_TERMS with AK724BUILD_LOWER_PREDS.
 
(5) The control variable 'qs_actpos' is shifted until there is once again the start of
a condition.
 
These actions are repeated until either a top-level term has been found or
until the last term of a second level was the current term, or if the control
variable is 'qs_actpos' > 'qs_stop'. In this case, control is returned to A724ALL_L1_TERMS.
 
AK724ANALYZE_CONDITION
----------------------------------
 
This procedure is called in AK724ONE_L1_TERM. It starts with stpos at the
first stack entry of a condition.
Only those conditions are of interest for a strategy that contain only one
field on the left-hand side of the operator and that contain only one factor
(one value), no expression and no column on the right-hand side.
The first stack entry is checked for field description; field, keyfield
are initialized.
A search is made for the first operator after this stack entry. If it is a
comparison operator and there is only one value in front of it, it is a
condition that can be used for the strategy (an expression would lead to an
arithmetic operator or similar, e.g. op_concat, op_plus).
The Like conditions must be handled in a special manner, because they may
contain DUMMY stack entries (see VAK65), which are used only for the strategy,
but not for the processing of the qualification.
stpos remains at the first stack entry (of the field description).
If the condition is not one that can be used (NOT field), a jump is made to
the end of that condition (outside of ANALYZE_CONDITION, a jump is made to the
start of the next condition).
If the condition can be used for finding a strategy and if it contains only
one value, it is checked whether the condition is suitable for a key field (IS
NULL is, for example, not suitable) or whether this is a singly inverted field
(ecol_tab[ 1 ] > chr(0)). If so, this condition can be taken for the strategy
search ('indexfield'), provided that this field is not to be updated with an
expression or was specified in FOR UPDATE (upd_col_set).
The condition may also be suitable if the field is part of a multiple index
(ecol_tab[ 1 ] = chr(cak00_col_with_inv)). The check for upd_col_set is performed in
A71_MINDEX_ARR. It cannot yet be performed in ANALYZE_CONDITION, because the
field number is not known for these fields.
If a cost estimate is required, it must be performed for the condition that
has just been found.
Conditions with more than one value (BETWEEN, IN) that can be used for the
strategy search are checked as to whether they are simple values or expressions
(for BETWEEN 2 values => 2 stack entries between the field stack entry and the
last value; for IN as many value stack entries as are specified in the IN stack
entry as list elements (elen_var)).
The conditions with the predicates 'NOTBETWEEN' and 'NOTIN' are
not usable for a strategy ('keyfield' and 'indexfield' to 'false'), but,
since a cost estimate exists for these conditions, this case is also dealt
with.
The conditions are suitable for finding a strategy only if there is an
inverted field (see above) or a key field.
If it is a Like condition, it must be considered that the field stack entry
is followed by 3 value stack entries. These may possibly then be used for Equal
or Between strategies (LIKE 'A*B' => for the strategy: BETWEEN 'A' AND 'A
chr(255) chr(255) ..').
Interrogation for inversions must be performed in this case also.
At the end of the procedure, and if a usable condition has been found,
'ps_startpos' is on the field stack entry and 'ps_stoppos' is on the stack entry that contains
the operator, i.e.the last one. These two entries are used in A71KEY_STRAT,
A71INDEX_STRAT and AK724BUILD_LOWER_PREDS.
 
AK724BUILD_LOWER_PREDS
----------------------------------
 
In this procedure, a condition that could be used for a strategy but that is
not at the highest level of the Search Condition is included in an array so
that it can be processed in OR_QUAL and A71ONE_RECURSION (in the analysis of
the OR parts).
If all conditions are to be evaluated (costcheck), conditions that are
not at the highest level and that are also not useful for finding a strategy,
are also entered in L1_TERMS by this procedure.
 
The decision at which level the next condition is to be entered is
specified by the entry in 'lv_levelend':
 
If, after a lv_levelend, no further term is entered at the next level, 'cntor'
and, for each OR, 'cntterm' may possibly be one too high. Therefore, after the
entire qualification has been processed, this is put right in A724ALL_L1_TERMS (see
A724ALL_L1_TERMS).
 
AK724CHECK_LEVEL
----------------------------------
 
Checks the nesting depth of the current condition.
 
The nesting depth is determined on the basis of the necessary jumps over the
Jump entries in the stack or the boolean operators. The nesting depth is
incremented from 1 to 3 (= c_level_max) in a While loop and that number of
jumps is performed. If, afterwards, the end of the qualification has been
reached ('lpos' > 'stop'), there was a term at the level that has just been
checked. If a boolean operator is jumped in a jump, this level is finished and
'lv_levelend' is set accordingly.
 
If the end has not been reached with the jumps, there are two possibilities:
 
(1) The operator 'NOT' has been found, which cannot be jumped in the While
loop. In this case, 'lpos' is moved to after the 'NOT' (+ 1) with the stack
pointer and AK724SKIP_STACK_ENTRIES is called in order to skip the stack entries.
Since the 'NOT' may come after a term at any level, the call is necessary in
order to discover the level at which the negation occurred. Since at least one
jump has already been performed in the While loop, the results of
SKIP_STACK_ENTRIES are each increased by one and 'lv_levelend' is set (the entire
term has been skipped).
 
(2) If there was no 'NOT' and, in addition, the end of the qualification has
not been reached with three jumps, there was a term at a level > 'c_level_max'
that is also skipped with AK724SKIP_STACK_ENTRIES.
 
In both cases, there was no condition that is usable for the strategy.
Therefore, AK724CHECK_LEVEL is set to 'false'.
 
As a special case, it is also necessary to intercept conditions that do not fit
into the alternating scheme of AND and OR combinations.
These are all conditions of
the form:
 
BED1 OR NOT (BED2 OR BED3)
and
BED1 AND NOT (BED2 AND BED3)
 
Both qualifications lead to stack entries in which a 'jump_true' (or a
'jump_false') leads to a 'NOT', which is followed directly by an 'op_or' or
'jump_true' (or 'op_and' or 'jump_false'). In these cases, the pointer is
shifted to after the 'NOT' directly in the While loop (in order to check the
depth); Ak724CHECK_LEVEL is set to 'false' and 'done' indicates that everything has
been done.
 
AK724SKIP_STACK_ENTRIES
----------------------------------
 
Skips parts of the qualification.
 
Is called either by AK724CHECK_LEVEL in order to skip a negated term or a term with
deeper nesting than 'c_level_max', or by ONE_AND if there was a level-2-
condition that cannot be used for the strategy and, therefore, the entire
level-2-expression is unusable. A distinction is made between the two cases on
the basis of 'lv_levelfound', which, in the first case, is greater than
'c_level_max', with it being smaller in the second case. Since, in the second
case, only one jump over an OR condition is required, this is performed
directly. Conversely, in the first case, the pointer is moved by the amount of
'epos' (there can only be jump entries) until a level-3-term is again reached
(Ak724CHECK_LEVEL is successful).
 
In addition, a further check has been incorporated here to check whether the
pointer is in fact being moved. For, in the case of unexpected stack entries
(that cannot be recognized either in CHECK_LEVEL or in ANALYZE_CONDITION), an
endless loop occurs when CHECK_LEVEL is called recursively. This is possible,
for example, if new stack structures are created by new functionality (as
happened in the case 'STAMP'). This emergency exit therefore prevents forgotten
changes from leading to an endless loop in this module.
 
.CM *-END-* description ---------------------------------
***********************************************************
.CM -lll-
Code    :
 
 
CONST
      c_level_max =  3;
      c_skip      = true;
 
TYPE
      t_access_kind_set = PACKED SET OF (
            ak_key,
            ak_inv,
            ak_const_TRUE,
            ak_const_FALSE);
 
      t_pred_scanner = RECORD
            ps_startpos    : tsp00_Int2;
            ps_stoppos     : tsp00_Int2;
            ps_access_kind : t_access_kind_set;
            ps_filler      : tsp00_C3;
      END;
 
 
      t_leveltype = RECORD
            lv_predicate   : t_pred_scanner;
            lv_levelfound  : tsp00_Int2;
            lv_levelstart  : tsp00_Int2;
            lv_levelend    : ARRAY [ 1..c_level_max ] OF boolean;
      END;
 
 
      t_qual_scanner = RECORD
            qs_start  : tsp00_Int2;
            qs_stop   : tsp00_Int2;
            qs_actpos : tsp00_Int2;
      END;
 
 
 
(*------------------------------*) 
 
PROCEDURE
      a724all_L1_terms (
            VAR acv         : tak_all_command_glob;
            VAR dmli        : tak_dml_info;
            VAR access_info : tak70_strategy_record;
            VAR L1_terms    : tak70_term );
 
VAR
      _ix                : tsp00_Int2;
      _levelrec          : t_leveltype;
      _qualscanner       : t_qual_scanner;
      _L1_predicates_col : tak_columnset;
      _term_negated      : boolean;
 
BEGIN
(* bear in mind:                                 *)
(* TERM   -> L1TERM AND L1TERM [ AND L1TERM .. ] *)
(* L1TERM -> L2TERM OR  L2TERM [ OR  L2TERM .. ] *)
(* L2TERM -> L3TERM AND L3TERM [ AND L3TERM .. ] *)
(*                                               *)
(* a TERM is a PREDICATE if it doesn't contains  *)
(* boolean operators                             *)
_L1_predicates_col    := [];
_term_negated         := false;
_qualscanner.qs_start := acv.a_mblock.mb_qual^.mqual_pos;
_qualscanner.qs_stop  := acv.a_mblock.mb_qual^.mqual_pos +
      acv.a_mblock.mb_qual^.mqual_cnt - 1;
IF  ( _qualscanner.qs_start <= _qualscanner.qs_stop )
THEN
    BEGIN
    (* jump over output list *)
    IF  ( acv.a_mblock.mb_st^[ _qualscanner.qs_start ].etype = st_jump_output )
    THEN
        _qualscanner.qs_start := _qualscanner.qs_start +
              acv.a_mblock.mb_st^[ _qualscanner.qs_start ].epos - 1;
    (*ENDIF*) 
    IF  ( acv.a_mblock.mb_st^[ _qualscanner.qs_stop ].etype = st_truth )
    THEN
        (* ALTER TABLE ADD <constraint definition> *)
        (* ALTER TABLE ADD <column definition with constraint> *)
        (* no search for AND *)
        (* no better strategy than *)
        _qualscanner.qs_stop  := pred( _qualscanner.qs_start );
    (*ENDIF*) 
    IF  ( acv.a_mblock.mb_st^[ _qualscanner.qs_stop ].eop = op_not )
    THEN
        BEGIN
        (* negation on top level *)
        _qualscanner.qs_stop := pred( _qualscanner.qs_stop );
        _term_negated        := true;
        END;
    (*ENDIF*) 
    IF  ( acv.a_mblock.mb_st^[ _qualscanner.qs_stop ].eop = op_or )
    THEN
        (* OR term on top level, i.e. L2TERM *)
        _levelrec.lv_levelstart := 2
    ELSE
        (* AND term on top level, i.e. L1TERM *)
        _levelrec.lv_levelstart := 1;
    (*ENDIF*) 
&   ifdef TRACE
    t01int4 (ak_strat, 'lv_levelstrt', _levelrec.lv_levelstart);
&   endif
    END;
(* initialize predicate nesting information *)
(*ENDIF*) 
;
IF  ( g01vtrace.vtrStrategy_gg00 )
THEN
    BEGIN
    g041name_to_trace( acv.a_transinf.tri_trans,
          '>> A724ALL_L1_TERM' );
    END;
(*ENDIF*) 
;
(* initialize predicate nesting information *)
_levelrec.lv_levelfound := 0;
FOR _ix := 1 TO c_level_max DO
    BEGIN
    _levelrec.lv_levelend[ _ix ] := false;
    END;
(*ENDFOR*) 
_qualscanner.qs_actpos := _qualscanner.qs_start;
WHILE ( _qualscanner.qs_actpos < _qualscanner.qs_stop ) DO
    BEGIN
    ak724one_L1_term( acv, dmli, access_info, _levelrec,
          L1_terms, _qualscanner, _L1_predicates_col );
    (* ak724one_L1_term stops qualification loop in case of *)
    (* a. end of qualification        or                    *)
    (* b. lv_levelend[2] = TRUE       or                    *)
    (* c. lv_levelfound = 1                                 *)
    _qualscanner.qs_start := _qualscanner.qs_actpos;
    END;
(*ENDWHILE*) 
IF  ( L1_terms.trm_L1termcnt > 0 )
THEN
    BEGIN
    IF  ( L1_terms.trm_L1termcnt > cak70_max_L1terms )
    THEN
        L1_terms.trm_L1termcnt := cak70_max_L1terms;
    (*ENDIF*) 
    IF  ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
        l1t_L2pL3tcnt = 0 ) OR
        ( NOT ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
        l1t_is_usable ))
    THEN
        BEGIN
        L1_terms.trm_L1termcnt := pred( L1_terms.trm_L1termcnt );
&       ifdef trace
        t01sname(ak_strat, 'rm one L1ter');
        a725L1_terms_output( ak_strat, L1_terms );
&       endif
        END;
    (*ENDIF*) 
    ;
    FOR _ix := 0 TO L1_terms.trm_L1termcnt - 1 DO
        BEGIN
        IF  ( L1_terms.trm_L1terms[ _ix ].
            l1t_L2terms[ L1_terms.trm_L1terms[ _ix ].l1t_L2pL3tcnt - 1 ].
            l2l3p_predcnt = 0 )
        THEN
            BEGIN
            L1_terms.trm_L1terms[ _ix ].l1t_L2pL3tcnt :=
                  pred( L1_terms.trm_L1terms[ _ix ].l1t_L2pL3tcnt );
&           ifdef trace
            t01name(ak_strat, 'rm last empty L2L3');
&           endif
            END;
        (*ENDIF*) 
        IF  ( L1_terms.trm_L1terms[ _ix ].l1t_L2pL3tcnt = 1 ) AND
            ( L1_terms.trm_L1terms[ _ix ].
            l1t_L2terms[ L1_terms.trm_L1terms[ _ix ].l1t_L2pL3tcnt - 1 ].
            l2l3p_predcnt > 0 )
        THEN
            (* there have to be minimal 2 terms 'A OR B'                  *)
            (* l2l3p_predcnt = 0 means: there are L4 terms!! - see vak726 *)
            BEGIN
            ak724ak_system_error( acv, 1 );
            END;
        (*ENDIF*) 
        END;
    (*ENDFOR*) 
    END;
(*ENDIF*) 
IF  ( _term_negated )
THEN
    BEGIN
    L1_terms.trm_L1termcnt     := 0;
    access_info.srec_keyaccess := a70_glob_zstrat_rec.srec_keyaccess;
    access_info.srec_invaccess := a70_glob_zstrat_rec.srec_invaccess;
    access_info.srec_query_prop.qps_switches :=
          access_info.srec_query_prop.qps_switches - [ qp_no_result_set ];
    END;
(*ENDIF*) 
;
&ifdef TRACE
a725output_access_info( ak_strat, 'A724 strat  ', access_info );
a725L1_terms_output( ak_strat, L1_terms );
t01name( ak_strat, '-END OF STRUCTURE-' );
&endif
IF  ( g01vtrace.vtrStrategy_gg00 )
THEN
    BEGIN
    a727L1_terms_trace( acv, L1_terms );
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724one_L1_term (
            VAR acv               : tak_all_command_glob;
            VAR dmli              : tak_dml_info;
            VAR access_info       : tak70_strategy_record;
            VAR levelrec          : t_leveltype;
            VAR L1_terms          : tak70_term;
            VAR qualscanner       : t_qual_scanner;
            VAR L1_pred_cols      : tak_columnset);
 
VAR
      _col_ptr          : tak00_colinfo_ptr;
      _condition_found  : boolean;
      _col_found        : boolean;
      _negated_term     : boolean;
 
BEGIN
&ifdef TRACE
t01int4 (ak_strat, 'qs_start    ', qualscanner.qs_start );
t01int4 (ak_strat, 'qs_stop     ', qualscanner.qs_stop  );
&endif
ak724focus_next_l1term( L1_terms );
qualscanner.qs_actpos       := qualscanner.qs_start;
REPEAT
    BEGIN
    _negated_term := false;
    ak724analyze_condition( acv, dmli.d_sparr, qualscanner.qs_actpos,
          levelrec.lv_predicate, qualscanner.qs_stop );
    (* ======================================================== *)
    (* checks whether condition that begins at position         *)
    (* 'qs_actpos' in the qual-stack is eligible for strategy   *)
    (* computes :                                               *)
    (*            ps_keyfield                                   *)
    (*            ps_startpos                                   *)
    (*            ps_stoppos                                    *)
    (* afterwards                                               *)
    (* qs_actpos points to first stack entry after an eligible  *)
    (* condition (if one has been detected) or to the next      *)
    (* boolean operator (if none detected)                      *)
    (* ======================================================== *)
    IF  ( qualscanner.qs_actpos <= qualscanner.qs_stop ) AND
        ( acv.a_mblock.mb_st^[ qualscanner.qs_actpos ].eop = op_not )
    THEN (* recently analyzed condition is operand of a NOT. *)
        BEGIN
&       ifdef trace
        t01sname(ak_strat, 'skip op_not ');
&       endif
        levelrec.lv_predicate.ps_access_kind  :=
              levelrec.lv_predicate.ps_access_kind - [ ak_key, ak_inv ];
        qualscanner.qs_actpos     := succ( qualscanner.qs_actpos );
        _negated_term         := true;
        END;
    (*ENDIF*) 
    IF  ( ak724check_level( acv, levelrec, qualscanner.qs_actpos,
        qualscanner.qs_stop, c_skip ))
    THEN
        BEGIN
        (* term exists in level 1, 2 or 3 *)
&       ifdef TRACE
        t01int4 (ak_strat, 'act. LEVEL: ', levelrec.lv_levelfound);
        t01stackentry( ak_strat, acv.a_mblock.
              mb_st^[ levelrec.lv_predicate.ps_startpos ],
              levelrec.lv_predicate.ps_startpos );
&       endif
        CASE levelrec.lv_levelfound OF
            1 :
                BEGIN
                _col_found := false;
                IF   ( acv.a_mblock.
                    mb_st^[ levelrec.lv_predicate.ps_startpos ].etype in
                    [ st_fixkey, st_varkey, st_fixcol, st_varcol,
                    st_varlongchar ] )
                THEN
                    BEGIN
                    a06find_colinfo( dmli.d_sparr.pbasep, acv.a_mblock.
                          mb_st^[ levelrec.lv_predicate.ps_startpos ], _col_ptr );
                    IF  ( _col_ptr <> NIL )
                    THEN
                        BEGIN
                        _col_found := true;
                        IF  ( NOT
                            ( _col_ptr^.creccolno in L1_pred_cols ))
                        THEN
                            BEGIN
                            L1_pred_cols := L1_pred_cols +
                                  [ _col_ptr^.creccolno ];
                            access_info.srec_L1_pred_cnt :=
                                  succ(access_info.srec_L1_pred_cnt);
                            END;
                        (*ENDIF*) 
                        END
                    ELSE
                        BEGIN
                        (* correlation column from correlated subquery *)
&                       ifdef trace
                        t01stackentry( ak_strat, acv.a_mblock.
                              mb_st^[ levelrec.lv_predicate.ps_startpos ],
                              levelrec.lv_predicate.ps_startpos );
&                       endif
                        END;
                    (*ENDIF*) 
                    END
                ELSE
                    IF  (acv.a_mblock.mb_st^[ levelrec.lv_predicate.ps_startpos ].etype        = st_build_in_func  ) AND
                        (acv.a_mblock.mb_st^[ levelrec.lv_predicate.ps_startpos ].eop_build_in = op_b_user_def_func)
                    THEN
                        _col_found := true
                    ELSE
                        BEGIN
                        (* e.g. ROWNUM <= x, VALUE <op> VALUE *)
                        (* do nothing             *)
                        END;
                    (*ENDIF*) 
                (*ENDIF*) 
                IF  (( ak_key in levelrec.lv_predicate.ps_access_kind ) OR
                    ( ak_inv in levelrec.lv_predicate.ps_access_kind ))
                THEN
                    BEGIN
                    IF  ( ak_key in levelrec.lv_predicate.ps_access_kind )
                        AND
                        ( NOT ( cs_keyscan in
                        access_info.srec_config.cfg_switches ))
                    THEN
                        a71key_strat( acv, access_info,
                              levelrec.lv_predicate.ps_startpos,
                              levelrec.lv_predicate.ps_stoppos );
                    (*ENDIF*) 
                    IF  ( ak_inv in levelrec.lv_predicate.ps_access_kind )
                        AND
                        ( NOT ( cs_indexscan in
                        access_info.srec_config.cfg_switches ))
                    THEN
                        a71index_strat( acv, access_info,
                              levelrec.lv_predicate.ps_startpos,
                              levelrec.lv_predicate.ps_stoppos );
                    (*ENDIF*) 
                    IF  ( NOT _col_found )
                    THEN
                        BEGIN
                        IF  ( NOT ( qp_correlation_key in access_info.
                            srec_query_prop.qps_switches ))
                        THEN
                            a07_b_put_error( acv, e_old_fileversion, 1 )
                        ELSE
                            BEGIN
                            (* predicate for correlation test *)
                            access_info.srec_unusable_L1_pred := true;
                            END;
                        (*ENDIF*) 
                        END;
                    (*ENDIF*) 
                    END
                ELSE
                    BEGIN
                    IF  ( _col_found )
                    THEN
                        access_info.srec_unusable_L1_pred := true;
                    (*ENDIF*) 
                    IF  ( ak_const_FALSE in levelrec.lv_predicate.ps_access_kind )
                    THEN
                        access_info.srec_query_prop.qps_switches :=
                              access_info.srec_query_prop.qps_switches +
                              [ qp_no_result_set ];
                    (*ENDIF*) 
                    END;
                (*ENDIF*) 
                END;
            2 :
                BEGIN
                access_info.srec_L23_predicates := true;
                IF  (( NOT ( ak_key in levelrec.lv_predicate.ps_access_kind )) AND
                    ( NOT ( ak_inv in levelrec.lv_predicate.ps_access_kind )))
                THEN
                    BEGIN
&                   ifdef TRACE
                    t01name( ak_strat, 'level 2 knockout  ' );
&                   endif
                    ak724skip_stack_entries( acv, qualscanner.qs_actpos,
                          levelrec, qualscanner.qs_stop );
                    levelrec.lv_levelend[ 2 ] := true;
                    END
                ELSE
                    BEGIN
                    IF  ( L1_terms.trm_L1termcnt <= cak70_max_L1terms )
                    THEN
                        ak724build_lower_preds( acv, L1_terms, levelrec );
                    (*ENDIF*) 
                    END;
                (*ENDIF*) 
                ak724focus_next_l2term( L1_terms );
                END;
            3 :
                BEGIN
&               ifdef trace
                t01name(ak_strat, 'add pred for lvl 3');
&               endif
                access_info.srec_L23_predicates := true;
                IF  ( L1_terms.trm_L1termcnt <= cak70_max_L1terms )
                THEN
                    ak724build_lower_preds( acv, L1_terms, levelrec );
                (*ENDIF*) 
                IF  ( levelrec.lv_levelend[ 3 ] )
                THEN
                    ak724focus_next_l2term( L1_terms );
                (*ENDIF*) 
                END;
            OTHERWISE
                BEGIN
                END
            END
        (*ENDCASE*) 
        END
    ELSE
        (* term exists in level > 3 *)
        BEGIN
&       ifdef TRACE
        t01sname (ak_strat, 'LEVEL > 3   ');
&       endif
        access_info.srec_L23_predicates := true;
        IF  ( levelrec.lv_levelend[ 3 ] )
        THEN
            ak724focus_next_l2term( L1_terms );
        (*ENDIF*) 
        END; (* level > 3 *)
    (*ENDIF*) 
    _condition_found := false;
    WHILE NOT _condition_found AND ( qualscanner.qs_actpos <= qualscanner.qs_stop ) DO
        BEGIN
        IF  ( acv.a_mblock.mb_st^[ qualscanner.qs_actpos ].etype in
            [ st_jump_false, st_jump_true ] ) OR
            ( acv.a_mblock.mb_st^[ qualscanner.qs_actpos ].eop in
            [ op_and, op_upd_view_and, op_or ] )
        THEN
            qualscanner.qs_actpos := succ( qualscanner.qs_actpos )
        ELSE
            BEGIN
            _condition_found := true;
&           ifdef trace
            t01int4 (ak_strat, 'act. pos    ', qualscanner.qs_actpos);
&           endif
            END;
        (*ENDIF*) 
        END;
    (*ENDWHILE*) 
    IF  ( _negated_term AND ( qp_no_result_set in
        access_info.srec_query_prop.qps_switches ))
    THEN
        BEGIN
        access_info.srec_query_prop.qps_switches :=
              access_info.srec_query_prop.qps_switches - [ qp_no_result_set ];
        END;
    (*ENDIF*) 
&   ifdef TRACE
    t01sname (ak_strat, '------------');
    a725L1_terms_output( ak_strat, L1_terms );
    ak724print_levelinfo(levelrec);
&   endif
    END
UNTIL
    ( levelrec.lv_levelend[ 2 ] OR ( levelrec.lv_levelfound = 1 ) OR
    ( qualscanner.qs_actpos > qualscanner.qs_stop ))
(*ENDREPEAT*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724build_lower_preds (
            VAR acv        : tak_all_command_glob;
            VAR L1_terms   : tak70_term;
            VAR levelrec   : t_leveltype);
 
BEGIN
(* 0 < L1_terms.trm_L1termcnt <= cak70_max_predicates assured *)
&ifdef TRACE
t01bool (ak_strat, 'KEYFIELD    ', ak_key in levelrec.lv_predicate.ps_access_kind );
t01bool (ak_strat, 'INDEXFIELD  ', ak_inv in levelrec.lv_predicate.ps_access_kind);
t01int4 (ak_strat, 'cnt L1 Terms', L1_terms.trm_L1termcnt);
&endif
IF  ( g01vtrace.vtrStrategy_gg00 )
THEN
    BEGIN
    g041name_to_trace( acv.a_transinf.tri_trans,
          '>> AK724BUILD_LOWE' );
    ak724trace_levelinfo(acv, levelrec);
    END;
(*ENDIF*) 
IF  L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_is_usable
THEN
    BEGIN
    IF  ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
        l1t_L2pL3tcnt = 0 )
    THEN
        BEGIN
&       ifdef TRACE
        t01sname ( ak_strat, 'init l2t    ');
&       endif
        (* initialize first level-2-term of actual level-1-term *)
        L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
              l1t_L2pL3tcnt := 1;
        L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
              l1t_L2terms[ 0 ].l2l3p_predcnt := 0
        END;
    (*ENDIF*) 
    ;
&   ifdef TRACE
    t01int4 ( ak_strat, 'cnt L2p L3t ',
          L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_L2pL3tcnt);
&   endif
    IF  ( ak_key in levelrec.lv_predicate.ps_access_kind ) OR
        ( ak_inv in levelrec.lv_predicate.ps_access_kind )
    THEN
        BEGIN
        IF  ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_L2pL3tcnt - 1 ].l2l3p_predcnt < cak70_max_predicates )
        THEN
            BEGIN
&           ifdef TRACE
            t01sname ( ak_strat, 'add predicat' );
&           endif
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt :=
                  succ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt );
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].
                  l2l3p_preds[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt - 1 ].
                  pred_start      := levelrec.lv_predicate.ps_startpos;
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].
                  l2l3p_preds[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt - 1 ].
                  pred_stop       := levelrec.lv_predicate.ps_stoppos;
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].
                  l2l3p_preds[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt - 1 ].
                  pred_key        :=
                  ( ak_key in levelrec.lv_predicate.ps_access_kind );
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].
                  l2l3p_preds[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt - 1 ].
                  pred_inv        :=
                  ( ak_inv in levelrec.lv_predicate.ps_access_kind );
            END;
        (*ENDIF*) 
        ;
&       ifdef TRACE
        t01int4 ( ak_strat, 'cnt predicat',
              L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
              l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
              l1t_L2pL3tcnt - 1 ].l2l3p_predcnt);
&       endif
        END;
    (*ENDIF*) 
    ;
&   ifdef TRACE
    t01int4 ( ak_strat, 'cnt L2p L3t ', L1_terms.
          trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_L2pL3tcnt);
&   endif
    END
ELSE
    BEGIN
&   ifdef TRACE
    t01sname(ak_strat, 'l2term unuse');
&   endif
    END
(*ENDIF*) 
;
&ifdef TRACE
a725L1_terms_output (ak_strat, L1_terms);
&endif
IF  ( g01vtrace.vtrStrategy_gg00 )
THEN
    BEGIN
    a727L1_terms_trace( acv, L1_terms );
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724analyze_condition (
            VAR acv               : tak_all_command_glob;
            VAR sparr             : tak_syspointerarr;
            VAR stpos             : tsp00_Int2;
            VAR predicate       : t_pred_scanner;
            stop                  : tsp00_Int2);
 
VAR
      _col_ptr                : tak00_colinfo_ptr;
      _ix                     : tsp00_Int4;
      _access_path            : boolean;
      _supported_pred         : boolean;
      _functionBasedIndexFound: boolean;
      _constant_expr          : boolean;
 
BEGIN
&ifdef trace
FOR _ix := stpos TO stop DO
    t01stackentry (ak_strat, acv.a_mblock.mb_st^[_ix], _ix);
(*ENDFOR*) 
&endif
_access_path             := false;
_supported_pred          := false;
_constant_expr           := false;
_functionBasedIndexFound := false;
predicate.ps_startpos    := stpos;
predicate.ps_access_kind := [];
(* check first stack entry *)
CASE acv.a_mblock.mb_st^[ stpos ].etype OF
    st_fixkey, st_varkey,
    st_fixprimkey, st_varprimkey :
        BEGIN
        _access_path := true;
        predicate.ps_access_kind := predicate.ps_access_kind + [ ak_key ];
        END;
    st_fixcol, st_varcol,
    st_fixinv, st_varinv,
    st_varlongchar:
        _access_path := true;
    st_value:
        BEGIN
        IF  ( g04iseffective_value( acv.a_mblock.mb_st^[ stpos ], acv.a_mblock ) )
        THEN
            _constant_expr := true;
        (*ENDIF*) 
        END;
    OTHERWISE
        BEGIN
        END
    END;
(*ENDCASE*) 
IF  ( _access_path OR _constant_expr )
THEN
    BEGIN
&   ifdef trace
    t01bool( ak_strat, 'column found', _access_path );
    t01bool( ak_strat, 'const expres', _constant_expr );
&   endif
    _ix := stpos;
    IF  ( _access_path ) AND ( acv.a_mblock.mb_st^[ stpos ].eop <> op_none )
    THEN
        (* BAD: other column function found *)
        BEGIN
        _access_path := false;
&       ifdef trace
        t01sname( ak_strat, 'col function' );
&       endif
        END;
    (*ENDIF*) 
    _ix    := succ( _ix );
    ;
    (* search for next operator and skip 'like'-dummies *)
    (* also stop for operands in expressions, i.e. for 'value + col = value' *)
    (* stop on '+' operand                                                   *)
    WHILE ( _ix <= stop )
          AND
          (( acv.a_mblock.mb_st^[ _ix ].eop   = op_none )  OR
          ( acv.a_mblock.mb_st^[ _ix ].etype = st_dummy )) DO
        BEGIN
        IF  ( acv.a_mblock.mb_st^[ _ix ].etype in
            [ st_fixkey, st_varkey, st_fixcol, st_varcol, st_varlongchar ] )
        THEN
            (* BAD: "column op column"  condition found *)
            (* we need "column op value" conditions     *)
            BEGIN
            _access_path := false;
            _constant_expr := false;
&           ifdef trace
            t01name( ak_strat, 'multi table predic' );
&           endif
            END
        ELSE
            BEGIN
            IF  ( acv.a_mblock.mb_st^[ _ix ].etype = st_value ) AND
                NOT g04iseffective_value( acv.a_mblock.mb_st^[ _ix ], acv.a_mblock )
            THEN
                _constant_expr := false;
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        _ix := succ( _ix )
        END;
    (*ENDWHILE*) 
    IF  ( _ix <= stop )
    THEN
        BEGIN
        IF  NOT ( acv.a_mblock.mb_st^[ _ix ].eop in
            [ op_null, op_eq, op_eq_all, op_gt, op_ge, op_lt, op_le, op_ne,
            op_in, op_not_in, op_between, op_not_between, op_like, op_not_like,
            op_true, op_false, op_get_sub_value ])
            (* none compare operator *)
        THEN
            BEGIN
            (* expression found *)
            _access_path   := false;
            _constant_expr := false;
            END;
        (*ENDIF*) 
        IF  ( acv.a_mblock.mb_st^[ _ix ].etype in
            [ st_fixkey, st_varkey, st_fixcol, st_varcol, st_varlongchar ] )
        THEN
            (* last column of predicate is column, 'value = column' *)
            _constant_expr := false;
        (*ENDIF*) 
        IF  ( acv.a_mblock.mb_st^[ _ix ].etype = st_value ) AND
            NOT g04iseffective_value( acv.a_mblock.mb_st^[ _ix ], acv.a_mblock )
        THEN
            _constant_expr := false;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    IF  sparr.pbasep^.sbase.bindexexist AND
        (
        (acv.a_mblock.mb_st^[ _ix ].etype = st_result)
        OR
        ( (acv.a_mblock.mb_st^[ _ix ].etype = st_build_in_func) AND
        (acv.a_mblock.mb_st^[ _ix ].eop_build_in = op_b_uni_trans) )
        )
    THEN
        BEGIN
        _functionBasedIndexFound :=
              a24LookForFunctionBasedIndex (acv, stpos, stop, _ix);
        IF  _functionBasedIndexFound
        THEN
            BEGIN
            _access_path := true;
            stpos        := _ix - 1;
            predicate.ps_startpos  := stpos;
            END
        ELSE
            _access_path := false;
        (*ENDIF*) 
        predicate.ps_access_kind := predicate.ps_access_kind - [ ak_key ];
        END;
    (*ENDIF*) 
    ;
    predicate.ps_stoppos  := _ix;
    IF  ( acv.a_mblock.mb_st^[ _ix ].etype = st_subquery ) AND
        ( acv.a_mblock.mb_st^[ _ix ].eop = op_get_sub_value )
    THEN
        (* operator lays behind *)
        predicate.ps_stoppos := succ(predicate.ps_stoppos) ;
    (*ENDIF*) 
    ;
    IF  ( _constant_expr )
    THEN
        BEGIN
        (* evaluate constant expression *)
        ak724predicate_test( acv, predicate );
        END;
    (*ENDIF*) 
    ;
    (* _ix position of operator, check operator support *)
    IF  ( _access_path AND ( _ix <= stop ))
    THEN
        BEGIN
        IF  (( acv.a_mblock.mb_st^[ _ix ].etype = st_subquery ) AND
            (
            (( acv.a_mblock.mb_st^[ _ix   ].ecol_tab[ 1 ] = chr( 0 ) (* no ALL*)
            ) AND ( acv.a_mblock.mb_st^[ _ix ].eop in [ op_eq, op_in ] ))
            OR
            (( acv.a_mblock.mb_st^[ _ix ].eop = op_get_sub_value ) AND
            ( acv.a_mblock.mb_st^[ _ix + 1 ].etype = st_op ) AND
            ( acv.a_mblock.mb_st^[ _ix + 1 ].eop in [ op_eq, op_in ]))
            ))
            (* subquery predicate *)
            (*                    *)
            OR
            (* 'normal' predicate *)
            (*                    *)
            (
            (( acv.a_mblock.mb_st^[ _ix ].etype = st_value ) OR
            (* separate operator stack entry *)
            (( acv.a_mblock.mb_st^ [ _ix   ].etype = st_op )     AND
            (acv.a_mblock.mb_st^  [ _ix-1 ].etype = st_value )))
            AND
            (( acv.a_mblock.mb_st^[ _ix ].eop in
            [ op_null, op_eq, op_eq_all, op_gt, op_ge, op_lt, op_le, op_ne ] )
            OR
            (( acv.a_mblock.mb_st^[ _ix ].eop = op_in ) AND
            ( _ix - predicate.ps_startpos - 1 =
            acv.a_mblock.mb_st^[ _ix ].elen_var ))
            OR
            (( acv.a_mblock.mb_st^[ _ix ].eop = op_between ) AND
            ( _ix - predicate.ps_startpos = 2 ))
            OR
            (( _ix > 2 ) AND
            ( acv.a_mblock.mb_st^[ _ix - 2 ].etype = st_dummy ) AND
            ( acv.a_mblock.mb_st^[ _ix - 1 ].etype = st_value ) AND
            ( acv.a_mblock.mb_st^[ _ix     ].eop   = op_like )))
            )
        THEN
            BEGIN
            _supported_pred := true;
            IF  ( acv.a_mblock.mb_st^[ _ix ].etype = st_subquery ) AND
                ( acv.a_mblock.mb_st^[ _ix ].eop = op_get_sub_value )
            THEN
                (* operator lays behind *)
                _ix := succ(_ix) ;
            (*ENDIF*) 
            predicate.ps_stoppos  := _ix;
            IF  ( _functionBasedIndexFound OR
                ( ord(acv.a_mblock.mb_st^[ predicate.ps_startpos ].
                ecol_tab[ 1 ]) = cak00_col_with_inv ))
            THEN
                predicate.ps_access_kind := predicate.ps_access_kind + [ ak_inv ];
            (*ENDIF*) 
            ;
            (* step behind predicate ???? *)
            IF  (( acv.a_mblock.mb_st^[ _ix ].etype = st_value )
                OR
                (( acv.a_mblock.mb_st^[ _ix ].etype = st_op ) AND
                ( acv.a_mblock.mb_st^[ _ix ].eop = op_in )))
                AND
                ( acv.a_mblock.mb_st^[ _ix ].ecol_tab[ 2 ] <> chr( 0 ) )
            THEN
                BEGIN
&               ifdef trace
                t01sname( ak_strat, 'skip ecoltab' );
&               endif
                stpos := _ix + ord( acv.a_mblock.mb_st^[ _ix ].ecol_tab[ 2 ] );
                END
            ELSE
                stpos := _ix;
            (*ENDIF*) 
            ;
            (* step behind last operand *)
            stpos := succ( stpos );
&           ifdef TRACE
            t01bool (ak_strat, 'KEYFIELD    ', ak_key in predicate.ps_access_kind );
            t01bool (ak_strat, 'INDEXFIELD  ', ak_inv in predicate.ps_access_kind );
            t01bool (ak_strat, 'TRUE        ', ak_const_TRUE in predicate.ps_access_kind );
            t01bool (ak_strat, 'FALSE       ', ak_const_FALSE in predicate.ps_access_kind );
&           endif
            END;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    END;
(*ENDIF*) 
;
IF  (( NOT _access_path ) OR ( NOT _supported_pred ))
THEN
    BEGIN
&   ifdef trace
    t01int4 (ak_strat, 'NO ACCESS   ', predicate.ps_startpos );
    t01int4 (ak_strat, 'stpos       ', stpos );
    t01name (ak_strat, 'skipping irrelevan');
&   endif
    (* loop to next condition *)
    ak724next_predicate( acv.a_mblock, stpos, stop );
    _access_path            := false;
    predicate.ps_access_kind:= predicate.ps_access_kind - [ ak_key, ak_inv ];
&   ifdef TRACE
    t01int4 (ak_strat, 'new stpos   ', stpos );
    t01bool (ak_strat, 'TRUE  pred  ', ak_const_TRUE in predicate.ps_access_kind );
    t01bool (ak_strat, 'FALSE pred  ', ak_const_FALSE in predicate.ps_access_kind );
&   endif
    END
ELSE
    (* it seems to be a usable condition *)
    BEGIN
&   ifdef TRACE
    t01int4 (ak_strat, 'ACCESS PATH ', stpos );
    t01bool (ak_strat, 'KEYFIELD    ', ak_key in predicate.ps_access_kind );
    t01bool (ak_strat, 'INDEXFIELD  ', ak_inv in predicate.ps_access_kind );
    t01int4 (ak_strat, 'ps_startpos ', predicate.ps_startpos );
    t01int4 (ak_strat, 'ps_stoppos  ', predicate.ps_stoppos );
&   endif
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724skip_stack_entries (
            VAR acv     : tak_all_command_glob;
            VAR pos      : tsp00_Int2;
            VAR levelrec : t_leveltype ;
            stop         : tsp00_Int2);
 
VAR
      _circle : boolean;
      _oldpos : tsp00_Int2;
 
BEGIN
(* precondition: levelrec.lv_levelfound is 'c_level_max + 1' or '2' *)
(* conditions with a level more than c_level_max are skipped        *)
(* this is repeated until level c_level_max is reached              *)
&ifdef TRACE
t01int4 (ak_strat, 'pos to skip ', pos);
t01int4( ak_strat, 'skip level  ', levelrec.lv_levelfound );
&endif
_oldpos := pos;
_circle := true;
IF  (levelrec.lv_levelfound > c_level_max)
THEN
    BEGIN
    WHILE _circle AND
          NOT ak724check_level (acv, levelrec, pos, stop, NOT c_skip) DO
        BEGIN
        IF  (acv.a_mblock.mb_st^[ pos ].etype in
            [ st_jump_false, st_jump_true ])
        THEN
            BEGIN
            pos := pos + acv.a_mblock.mb_st^[ pos ].epos;
&           ifdef trace
            t01int4( ak_strat, 'jump pos to ', pos );
&           endif
            END;
        (*ENDIF*) 
        IF  (_oldpos = pos)
        THEN
            BEGIN
            _circle := false;
            pos    := stop + 1
            END
        ELSE
            _oldpos := pos
        (*ENDIF*) 
        END;
    (*ENDWHILE*) 
    END
ELSE
    (* lv_levelfound <= c_level_max *)
    BEGIN
    (* One term of level 2 is skipped. Therefore only a *)
    (* jump_true, oder an op_or can be found            *)
    IF  NOT (
        ( acv.a_mblock.mb_st^[ pos ].etype = st_jump_true )
        OR
        (( acv.a_mblock.mb_st^[ pos ].etype = st_op ) AND
        (acv.a_mblock.mb_st^[ pos ].eop = op_or ))
        )
    THEN
        ak724ak_system_error( acv, 2 );
    (*ENDIF*) 
    IF  ( acv.a_mblock.mb_st^[ pos ].etype = st_jump_true )
    THEN
        pos := pos + acv.a_mblock.mb_st^[ pos ].epos;
    (*ENDIF*) 
    WHILE ( acv.a_mblock.mb_st^[ pos ].etype = st_op ) AND
          ( acv.a_mblock.mb_st^[ pos ].eop = op_or ) AND
          ( pos <= stop ) DO
        pos := succ( pos );
    (*ENDWHILE*) 
    END;
(*ENDIF*) 
;
&ifdef TRACE
t01int4 (ak_strat, 'skiped to   ', pos);
&endif
END;
 
(*------------------------------*) 
 
FUNCTION
      ak724check_level (
            VAR acv      : tak_all_command_glob;
            VAR levelrec : t_leveltype;
            VAR pos      : tsp00_Int2;
            stop         : tsp00_Int2;
            skip         : boolean) :  boolean;
 
VAR
      _testlvl       : tsp00_Int2;
      _ix            : tsp00_Int2;
      _lpos          : tsp00_Int2;
      _jmp           : tsp00_Int2;
      _knockout      : boolean;
      _qual_scanned  : boolean;
      _keep_lvl      : boolean;
 
BEGIN
(* 'pos' is after the considered predicat *)
&ifdef trace
t01name (ak_strat, 'open all levels   ');
&endif
FOR _ix := 1 TO c_level_max DO
    BEGIN
    levelrec.lv_levelend[ _ix ] := false;
    END;
(*ENDFOR*) 
&ifdef TRACE
t01int4 (ak_strat, 'pos in      ', pos);
t01int4 (ak_strat, 'pos to stop ', stop);
&endif
(* step to predicate end and leap over unused/unneccessary stack entryies *)
ak724next_predicate( acv.a_mblock, pos, stop );
(**)
&ifdef trace
t01int4 (ak_strat, 'pos to check', pos);
&endif
_testlvl       := 0;
_qual_scanned := false;
_knockout     := false;
(* step to c_level_max *)
WHILE ( _testlvl < c_level_max ) AND
      ( NOT _qual_scanned ) AND ( NOT _knockout ) AND
      ( acv.a_returncode = 0 ) DO
    BEGIN
&   ifdef TRACE
    t01int4 (ak_strat, 'test level  ', succ(_testlvl));
&   endif
    _testlvl := succ(_testlvl);
    _lpos    := pos;
    _ix      := _testlvl;
    IF  ( _lpos <= stop )
    THEN
        BEGIN
        WHILE ( _ix >= levelrec.lv_levelstart ) DO
            (* skip possibly non existent 1. level         *)
            (* level 1 don't exist if qualification        *)
            (* looks like "<term> OR <term>" in WHERE part *)
            (* i.e. work for all existant levels           *)
            BEGIN
&           ifdef TRACE
            t01int4(ak_strat, 'check pos   ', _lpos);
&           endif
            IF  ( _ix MOD 2 = 0 )
            THEN
                (* even level, i.e. OR qualifications *)
                BEGIN
&               ifdef trace
                t01sname(ak_strat, 'OR level    ');
&               endif
                _keep_lvl := true;
                WHILE ( _keep_lvl ) AND ( _lpos <= stop )  DO
                    BEGIN
                    _keep_lvl := false;
                    IF  ( acv.a_mblock.mb_st^[ _lpos ].etype = st_jump_true )
                        OR
                        (* 'col <> value' handling!         *)
                        (* --> (col < value) OR (col > value)   *)
                        (( _lpos < stop ) AND
                        ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                        ( acv.a_mblock.mb_st^[ _lpos ].eop = op_or ) AND
                        ( acv.a_mblock.mb_st^[ _lpos + 1 ].etype = st_jump_true ))
                    THEN
                        BEGIN
                        IF  NOT ( acv.a_mblock.mb_st^[ _lpos ].etype = st_jump_true )
                        THEN
                            BEGIN
&                           ifdef TRACE
                            t01sname(ak_strat, 'special <>  ');
&                           endif
                            (* 'col <> value' handling! *)
                            (* count 'col <> value'     *)
                            _lpos := succ( _lpos );
                            END;
                        (*ENDIF*) 
                        _jmp := acv.a_mblock.mb_st^[ _lpos ].epos;
&                       ifdef trace
                        t01int4(ak_strat, 'jmp relative', _jmp );
&                       endif
                        (* left OR participant found *)
                        IF  ( _lpos + _jmp < stop ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp ].etype = st_op ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp ].eop = op_not ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp + 1 ].etype = st_op ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp + 1 ].eop = op_or )
                        THEN
                            BEGIN
                            (* prerequisite: <term> OR NOT <C1 OR C2> *)
                            (* and <term> was relevant for strategy   *)
                            (* we check level of condition C1         *)
                            _ix              := 0; (* break through outer while loop*)
                            ak724check_level := false;
                            _knockout        := true;
                            pos              := _lpos + _jmp + 1;
&                           ifdef TRACE
                            t01name(ak_strat, 'NOT <OR> knockout ');
&                           endif
                            END
                        ELSE
                            BEGIN
                            _lpos := _lpos + _jmp;
                            ;
                            WHILE ( _lpos <= stop ) AND
                                  ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                                  ( acv.a_mblock.mb_st^[ _lpos ].eop = op_or ) DO
                                _lpos := succ( _lpos );
                            (*ENDWHILE*) 
                            ;
&                           ifdef TRACE
                            t01name(ak_strat, 'lft OR, jump rght ');
                            t01int4(ak_strat, 'new _lpos   ', _lpos);
&                           endif
                            _keep_lvl := true;
                            END;
                        (*ENDIF*) 
                        END
                    ELSE
                        BEGIN
                        IF  ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                            ( acv.a_mblock.mb_st^[ _lpos ].eop = op_or )
                        THEN
                            BEGIN
                            (* right OR participant found *)
                            levelrec.lv_levelend[ _ix ] := true;
&                           ifdef trace
                            t01int4(ak_strat, 'CLOSE LEVEL ', _ix );
&                           endif
                            WHILE ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                                  ( acv.a_mblock.mb_st^[ _lpos ].eop = op_or ) AND
                                  ( _lpos <= stop ) DO
                                _lpos := succ( _lpos );
                            (*ENDWHILE*) 
&                           ifdef TRACE
                            t01sname(ak_strat, 'END OR lvl  ');
                            t01int4(ak_strat, 'new _lpos   ', _lpos);
&                           endif
                            _keep_lvl := true;
                            END
                        ELSE
                            BEGIN
&                           ifdef trace
                            t01name(ak_strat, 'not in act. level ');
&                           endif
                            END;
                        (*ENDIF*) 
                        END;
                    (*ENDIF*) 
                    END;
                (*ENDWHILE*) 
                END
            ELSE
                BEGIN
                (* odd level, i.e. AND qualifications *)
&               ifdef trace
                IF  ( _ix = 1 )
                THEN
                    t01name(ak_strat, 'first AND level   ')
                ELSE
                    t01name(ak_strat, 'second AND level  ');
                (*ENDIF*) 
&               endif
                _keep_lvl := true;
                WHILE ( _keep_lvl ) AND ( _lpos <= stop )  DO
                    BEGIN
                    _keep_lvl := false;
                    IF  ( acv.a_mblock.mb_st^[ _lpos ].etype = st_jump_false )
                        OR
                        (* special 'col IS NOT NULL' handling!         *)
                        (* added by ak684add_not_null_qualification()  *)
                        (( _lpos < stop ) AND
                        ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                        ( acv.a_mblock.mb_st^[ _lpos ].eop = op_and ) AND
                        ( acv.a_mblock.mb_st^[ _lpos + 1 ].etype = st_jump_false ))
                    THEN
                        BEGIN
                        IF  NOT ( acv.a_mblock.mb_st^[ _lpos ].etype = st_jump_false )
                        THEN
                            BEGIN
&                           ifdef TRACE
                            t01name(ak_strat, 'special AND hndlng');
&                           endif
                            _lpos := succ( _lpos );
                            END;
                        (*ENDIF*) 
                        _jmp := acv.a_mblock.mb_st^[ _lpos ].epos;
&                       ifdef trace
                        t01int4(ak_strat, 'jmp relative', _jmp );
&                       endif
                        (* left AND participant found *)
                        IF  ( _lpos + _jmp < stop ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp ].etype = st_op ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp ].eop = op_not ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp + 1 ].etype = st_op ) AND
                            ( acv.a_mblock.mb_st^[ _lpos + _jmp + 1 ].eop = op_and )
                        THEN
                            BEGIN
                            (* prerequisite: <term> AND NOT <C1 AND C2>      *)
                            (* <term> don't have to be relevant for strategy *)
                            (* we check level for C1                         *)
                            _ix              := 0; (* break through outer while loop*)
                            ak724check_level := false;
                            _knockout        := true;
                            pos              := _lpos + _jmp + 1;
&                           ifdef TRACE
                            t01name(ak_strat, 'NOT <AND> knockout');
&                           endif
                            END
                        ELSE
                            BEGIN
                            _lpos := _lpos + _jmp;
                            ;
                            WHILE ( _lpos <= stop ) AND
                                  ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                                  ( acv.a_mblock.mb_st^[ _lpos ].eop in
                                  [ op_and, op_upd_view_and ] ) DO
                                _lpos := succ( _lpos );
                            (*ENDWHILE*) 
                            ;
&                           ifdef TRACE
                            t01name(ak_strat, 'lft AND, jump rght');
                            t01int4(ak_strat, 'new _lpos   ', _lpos);
&                           endif
                            _keep_lvl := true;
                            END;
                        (*ENDIF*) 
                        END
                    ELSE
                        BEGIN
                        IF  ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                            ( acv.a_mblock.mb_st^[ _lpos ].eop in
                            [ op_and, op_upd_view_and ] )
                        THEN
                            BEGIN
                            (* right AND participant found *)
                            levelrec.lv_levelend[ _ix ] := true;
&                           ifdef trace
                            t01int4(ak_strat, 'CLOSE LEVEL ', _ix );
&                           endif
                            WHILE ( acv.a_mblock.mb_st^[ _lpos ].etype = st_op ) AND
                                  ( acv.a_mblock.mb_st^[ _lpos ].eop in
                                  [ op_and, op_upd_view_and ] ) AND
                                  ( _lpos <= stop ) DO
                                _lpos := succ( _lpos );
                            (*ENDWHILE*) 
&                           ifdef TRACE
                            t01sname(ak_strat, 'END AND lvl ');
                            t01int4(ak_strat, 'new _lpos   ', _lpos);
&                           endif
                            _keep_lvl := true;
                            END
                        ELSE
                            BEGIN
&                           ifdef trace
                            t01name(ak_strat, 'not in act. level ');
&                           endif
                            END;
                        (*ENDIF*) 
                        END;
                    (*ENDIF*) 
                    END;
                (*ENDWHILE*) 
                END; (* odd level *)
            (*ENDIF*) 
            ;
            (* maybe we are on one level above *)
            (* try to jump this level          *)
            _ix := pred(_ix);
&           ifdef trace
            IF  _ix >= levelrec.lv_levelstart
            THEN
                t01int4(ak_strat, 'switch level', _ix)
            ELSE
                (* break WHILE loop *)
                t01name(ak_strat, 'jump test complete');
            (*ENDIF*) 
&           endif
            END; (* step through levels *)
        (*ENDWHILE*) 
        END;
    (*ENDIF*) 
    ;
&   ifdef trace
    IF  ( _lpos <> stop + 1 )
    THEN
        BEGIN
        t01int4(ak_strat, 'chck nxt lvl', succ(_testlvl));
        t01p2int4(ak_strat, 'last _lpos  ', _lpos, 'last _ix    ', _ix );
        END;
&   endif
    (*ENDIF*) 
    IF  ( _lpos = stop + 1 )
    THEN
        BEGIN
&       ifdef TRACE
        t01sname(ak_strat, 'qual scanned');
&       endif
        _qual_scanned := true; (* break through while loop *)
        FOR _ix := 1 TO c_level_max - 1 DO
            BEGIN
            IF      levelrec.lv_levelend[ _ix   ] AND
                NOT levelrec.lv_levelend[ _ix+1 ] AND
                NOT ( _ix = _testlvl )
            THEN
                BEGIN
                (* 1 closed AND 2 open AND act.level == 1 *)
                (* >>> open level 1                       *)
                (* OR *)
                (* 2 closed AND 3 open AND act.level == 2 *)
                (* >>> open level 2                       *)
&               ifdef trace
                t01int4(ak_strat, 'reopen lvl  ', _ix);
&               endif
                levelrec.lv_levelend[ _ix ] := false;
                END;
            (*ENDIF*) 
            END;
        (*ENDFOR*) 
        END
    ELSE
        (* we are still in qualification        *)
        (* try to jump out with one level above *)
        (* initial jump in if lv_levelstart = 2 *)
        BEGIN
&       ifdef trace
        t01name(ak_strat, 'open all levels   ');
&       endif
        FOR _ix := 1 TO c_level_max DO
            BEGIN
            levelrec.lv_levelend[ _ix ] := false;
            END;
        (*ENDFOR*) 
        _qual_scanned := false;
        IF  ( _lpos > stop )
        THEN
            ak724ak_system_error( acv, 5 );
        (*ENDIF*) 
        END
    (*ENDIF*) 
    END;
(*ENDWHILE*) 
IF  ( acv.a_returncode = 0 )
THEN
    BEGIN
    IF  ( NOT _qual_scanned AND ( acv.a_mblock.mb_st^[ _lpos ].eop = op_not ))
    THEN
        BEGIN
&       ifdef TRACE
        t01sname(ak_strat, 'low lvl NOT ');
        t01int4(ak_strat,  'level found ', _testlvl);
&       endif
        ak724check_level := false;
        levelrec.lv_levelfound := c_level_max + 1;
        _lpos            := succ(_lpos); (* skip NOT stack entry *)
        ak724skip_stack_entries (acv, _lpos, levelrec, stop);
        pos             := _lpos;
&       ifdef trace
        t01int4(ak_strat,  'lv_levelfoun', levelrec.lv_levelfound);
&       endif
        IF ( levelrec.lv_levelfound < c_level_max )
        THEN
            levelrec.lv_levelfound := levelrec.lv_levelfound + 1
        ELSE
            levelrec.lv_levelfound := c_level_max + 1;
        END
    ELSE
        BEGIN
        IF  ( _qual_scanned AND NOT _knockout )
        THEN
            BEGIN
&           ifdef TRACE
            t01sname(ak_strat, 'no knockout ');
&           endif
            levelrec.lv_levelfound := _testlvl;
            ak724check_level       := true;
            (* close all level except the found level *)
            FOR _ix := levelrec.lv_levelfound TO c_level_max - 1 DO
                BEGIN
&               ifdef trace
                t01int4(ak_strat, 'close level ', _ix+1);
&               endif
                levelrec.lv_levelend[ _ix + 1 ] := true;
                END;
            (*ENDFOR*) 
            END
        ELSE
            (* NOT _qual_scanned OR _knockout *)
            BEGIN
            IF  ( NOT _qual_scanned )
            THEN
                BEGIN
&               ifdef TRACE
                t01sname(ak_strat, 'irrelev lvl ');
&               endif
                ak724check_level       := false;
                levelrec.lv_levelfound := c_level_max + 1;
                IF  ( skip )
                THEN
                    BEGIN
&                   ifdef TRACE
                    t01name(ak_strat, 'skip irrelev level');
&                   endif
                    ak724skip_stack_entries(acv, pos, levelrec, stop);
                    (* all lower predicates counts for c_level_max *)
                    END
                (*ENDIF*) 
                END;
            (*ENDIF*) 
            END;
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    END;
(*ENDIF*) 
;
&ifdef TRACE
t01int4(ak_strat, 'pos out     ', pos );
ak724print_levelinfo(levelrec);
&endif
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724focus_next_l1term(
            VAR L1_terms   : tak70_term );
 
BEGIN
IF  ( L1_terms.trm_L1termcnt < cak70_max_L1terms )
THEN
    BEGIN
&   ifdef trace
    t01name(ak_strat, 'focus on next l1t ');
&   endif
    IF  ( L1_terms.trm_L1termcnt = 0 )
    THEN
        BEGIN
        L1_terms.trm_L1termcnt := 1;
&       ifdef trace
        t01name(ak_strat, 'init first slot   ');
&       endif
        END
    ELSE
        BEGIN
        IF  ( NOT L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_is_usable )
            OR
            (L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_L2pL3tcnt = 0 )
        THEN
            BEGIN
            (* reuse actual slot *)
&           ifdef trace
            t01name(ak_strat, 'reuse actual slot ');
&           endif
            END
        ELSE
            L1_terms.trm_L1termcnt := succ(L1_terms.trm_L1termcnt);
        (*ENDIF*) 
        END;
    (*ENDIF*) 
    L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_L2pL3tcnt := 0;
    L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_is_usable := true;
    END
ELSE
    BEGIN
    IF  ( NOT L1_terms.trm_L1terms[ MAX_L1TERMS_IDX_AK70 ].l1t_is_usable )
        OR
        ( L1_terms.trm_L1terms[ MAX_L1TERMS_IDX_AK70 ].l1t_L2pL3tcnt = 0 )
    THEN
        BEGIN
&       ifdef trace
        t01name(ak_strat, 'reuse actual slot ');
&       endif
        L1_terms.trm_L1terms[ MAX_L1TERMS_IDX_AK70 ].l1t_is_usable := true;
        L1_terms.trm_L1terms[ MAX_L1TERMS_IDX_AK70 ].l1t_L2pL3tcnt := 0;
        END
    ELSE
        BEGIN
&       ifdef trace
        t01sname (ak_strat, 'no more spac');
&       endif
        L1_terms.trm_L1termcnt := cak70_max_L1terms + 1;
        END;
    (*ENDIF*) 
    END;
(*ENDIF*) 
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724focus_next_l2term(
            VAR L1_terms   : tak70_term );
 
BEGIN
IF  (( L1_terms.trm_L1termcnt > 0 ) AND
    ( L1_terms.trm_L1termcnt <= cak70_max_L1terms ))
THEN
    BEGIN
    (* a level-2 term comprises one level-2 predicate   *)
    (* OR several level-3 predicates                    *)
    IF  ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
        l1t_L2pL3tcnt < cak70_max_L2preds_L3terms )
    THEN
        BEGIN
&       ifdef trace
        t01name(ak_strat, 'focus on next l2t ');
&       endif
        IF  (( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_L2pL3tcnt = 0 ) OR
            ( L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
            l1t_L2pL3tcnt - 1 ].l2l3p_predcnt = 0 ))
        THEN
            BEGIN
            (* last level-2-slot is empty -> level-1-term unusable *)
&           ifdef trace
            t01name( ak_strat, 'disable L1 term   ');
&           endif
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_is_usable := false;
            END
        ELSE
            BEGIN
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_L2pL3tcnt :=
                  succ(L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_L2pL3tcnt);
            (* initialize next predicate structure *)
            L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2terms[ L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].
                  l1t_L2pL3tcnt - 1 ].l2l3p_predcnt := 0;
            END;
        (*ENDIF*) 
        END
    ELSE
        BEGIN
&       ifdef trace
        t01sname( ak_strat, 'no more spac');
        t01name( ak_strat, 'disable L1 term   ');
&       endif
        (* there's no more space *)
        L1_terms.trm_L1terms[ L1_terms.trm_L1termcnt - 1 ].l1t_is_usable := false;
        END;
    (*ENDIF*) 
    END
ELSE
    BEGIN
    (* focus for level1-term out of space *)
    END;
(*ENDIF*) 
END;
 
&ifdef TRACE
(*------------------------------*) 
 
PROCEDURE
      ak724print_levelinfo(VAR levelrec : t_leveltype);
 
CONST
      _c_line_start  = 17;
      _c_line_offset = 20;
 
VAR
      _i, _tmp  : tsp00_Int4;
      _line     : tsp00_Line;
 
BEGIN
t01name(ak_strat, '--- levelinfo --- ');
t01p2int4(ak_strat, 'act. level  ', levelrec.lv_levelfound,
      'level start ', levelrec.lv_levelstart);
_line := a71blankline;
_tmp  := 0;
g17sname_to_line( 'level state:', _tmp, _line );
FOR _i := 0 TO c_level_max - 1 DO
    IF  _i * _c_line_offset < sizeof(tsp00_Line)
    THEN
        BEGIN
        g17int4to_line( _i + 1, false, 2, _c_line_start + _i*_c_line_offset, _line);
        _tmp := _c_line_start + _i*_c_line_offset + 2;
        IF  ( levelrec.lv_levelend[ _i + 1 ] )
        THEN
            g17sname_to_line( '(closed)    ', _tmp, _line )
        ELSE
            g17sname_to_line( '(open)      ', _tmp, _line );
        (*ENDIF*) 
        END;
    (*ENDIF*) 
(*ENDFOR*) 
t01line(ak_strat, _line);
END;
 
&endif
(*------------------------------*) 
 
PROCEDURE
      ak724trace_levelinfo(
            VAR acv      : tak_all_command_glob;
            VAR levelrec : t_leveltype);
 
CONST
      _c_line_start  = 17;
      _c_line_offset = 20;
 
VAR
      _i, _tmp  : tsp00_Int4;
      _line     : tsp00_Line;
 
BEGIN
g041name_to_trace( acv.a_transinf.tri_trans, '--- levelinfo --- ' );
g041p2int4_to_trace( acv.a_transinf.tri_trans,
      'act. level        ', levelrec.lv_levelfound,
      'level start       ', levelrec.lv_levelstart );
_line := a71blankline;
_tmp  := 0;
g17sname_to_line( 'level state:', _tmp, _line );
FOR _i := 0 TO c_level_max - 1 DO
    IF  _i * _c_line_offset < sizeof(tsp00_Line)
    THEN
        BEGIN
        g17int4to_line( _i+1, false, 2, _c_line_start + _i*_c_line_offset, _line);
        _tmp := _c_line_start + _i*_c_line_offset + 2;
        IF  ( levelrec.lv_levelend[ _i + 1 ] )
        THEN
            g17sname_to_line( '(closed)    ', _tmp, _line )
        ELSE
            g17sname_to_line( '(open)      ', _tmp, _line );
        (*ENDIF*) 
        END;
    (*ENDIF*) 
(*ENDFOR*) 
g041line_to_trace(acv.a_transinf.tri_trans, _line);
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724ak_system_error (
            VAR acv  : tak_all_command_glob;
            id       : integer);
 
VAR
      _ilen       : tsp00_Int4;
      _ix         : tsp00_Int4;
      _hostfileno : tsp00_Int4;
      _textpos    : tsp00_Int4;
      _dmp_pos    : tsp00_Int4;
      _movelen    : tsp00_Int4;
      _error      : tsp00_VfReturn;
      _errtext    : tsp00_ErrText;
      _dmp_buf    : tsp00_Page;
      _b_err      : tgg00_BasisError;
 
      _hostfile   : RECORD
            CASE boolean OF
                true :
                    (fn : tsp00_VFilename);
                false :
                    (c16 : tsp00_C16);
                END;
            (*ENDCASE*) 
 
 
BEGIN
(* dump SQL statement *)
IF  (( acv.a_cmd_part <> NIL ) AND
    (* a70strategy call from a680search_sequence() *)
    ( acv.a_init_ex_kind <> only_executing ))
THEN
    BEGIN
    FOR _ix := 1 TO sizeof(_hostfile.fn) DO
        _hostfile.fn[_ix] := ' ';
    (*ENDFOR*) 
    _hostfile.c16 := 'SQL.dump        ';
    vfrawopen( _hostfile.fn, _hostfileno, _error, _errtext );
    IF  ( _error = vf_ok )
    THEN
        BEGIN
        _dmp_pos := 1;
        (* generic code for _dmp_pos <> 1 *)
        _textpos := 1;
        WHILE ( _textpos < acv.a_cmd_part^.sp1p_buf_len ) AND
              ( _error = vf_ok ) DO
            BEGIN
            IF  ( acv.a_cmd_part^.sp1p_buf_len - _textpos + 1 )
                > (sizeof( _dmp_buf ) - _dmp_pos + 1)
            THEN
                _movelen := sizeof(_dmp_buf) - _dmp_pos + 1
            ELSE
                _movelen := acv.a_cmd_part^.sp1p_buf_len - _textpos + 1;
            (*ENDIF*) 
            SAPDB_PascalMove ('VAK724',   1,    
                  acv.a_cmd_part^.sp1p_buf_size, sizeof(_dmp_buf),
                  @acv.a_cmd_part^.sp1p_buf, _textpos,
                  @_dmp_buf, _dmp_pos, _movelen, _b_err);
            _dmp_pos := _dmp_pos + _movelen;
            IF  ( sizeof(_dmp_buf) > _dmp_pos - 1 )
            THEN
                SAPDB_PascalFill ('VAK724',   2,    
                      sizeof(_dmp_buf), @_dmp_buf, _dmp_pos,
                      sizeof(_dmp_buf) - _dmp_pos + 1, bsp_c1,
                      _b_err);
            (*ENDIF*) 
            vfwrite (_hostfileno, @_dmp_buf, _error, _errtext);
            _dmp_pos := 1;
            _textpos  := _textpos + _movelen;
            END;
        (*ENDWHILE*) 
        vfclose( _hostfileno, _error, _errtext );
        END;
    (*ENDIF*) 
    END;
(*ENDIF*) 
_ilen := acv.a_mblock.mb_data_len;
b120MessBlockTrace (acv.a_mblock.mb_trns^, ak_send, acv.a_mblock);
acv.a_mblock.mb_data_len := _ilen;
a07ak_system_error( acv, 724, id );
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724predicate_test (
            VAR acv       : tak_all_command_glob;
            VAR predicate : t_pred_scanner );
 
VAR
      _sel           : tgg00_SelectFieldsParam;
      _err_st_ptr    : tgg00_StEntryAddr;
      _aux_err       : tgg00_BasisError;
      _unqualified   : boolean;
      _qual_pos      : tsp00_Int2;
      _qual_cnt      : tsp00_Int2;
      _optimize_pos  : tsp00_Int2;
      _ix            : tsp00_Int2;
 
BEGIN
&ifdef trace
FOR _ix := predicate.ps_startpos TO predicate.ps_stoppos DO
    t01stackentry (ak_strat, acv.a_mblock.mb_st^[_ix], _ix);
(*ENDFOR*) 
&endif
g04init_select_fields( _sel, @acv.a_mblock.mb_data^.mbp_buf,
      acv.a_mblock.mb_data_size, acv.a_work_st_addr,
      acv.a_work_st_max, acv.a_work_buf_addr,
      acv.a_work_buf_size, acv.a_sqlmode,
      @acv.a_mblock.mb_fieldlists);
_unqualified := false;
_sel.sfp_bd_mess_type    := acv.a_mblock.mb_type;
_sel.sfp_bd_mess2_type   := acv.a_mblock.mb_type2;
_sel.sfp_result_wanted   := false;
_sel.sfp_m_result_addr   := NIL;
_sel.sfp_m_result_size   := 0;
_sel.sfp_primkey_addr    := NIL;
_sel.sfp_first_qual      := true;
_sel.sfp_filler2         := false;
_sel.sfp_result_length   := 0;
_sel.sfp_rec_addr        := NIL;
_sel.sfp_rec_len         := 0;
_sel.sfp_rec_key_len     := 0;
_sel.sfp_acv_addr        := @acv;
acv.a_mblock.mb_qual^.mst_addr := acv.a_mblock.mb_st;
acv.a_mblock.mb_qual^.mst_max  := acv.a_mblock.mb_st_max;
;
_qual_pos := acv.a_mblock.mb_qual^.mqual_pos;
_qual_cnt := acv.a_mblock.mb_qual^.mqual_cnt;
_optimize_pos := acv.a_mblock.mb_qual^.mst_optimize_pos;
acv.a_mblock.mb_qual^.mqual_pos := predicate.ps_startpos;
acv.a_mblock.mb_qual^.mqual_cnt :=
      predicate.ps_stoppos - predicate.ps_startpos + 1;
;
_aux_err := acv.a_mblock.mb_trns^.trError_gg00;
acv.a_mblock.mb_trns^.trError_gg00   := e_ok;
k71qual_handling (acv.a_mblock.mb_trns^, _sel, false, false,
      acv.a_mblock.mb_qual^.mstack_desc, _err_st_ptr, _unqualified);
acv.a_mblock.mb_qual^.mqual_pos := _qual_pos;
acv.a_mblock.mb_qual^.mqual_cnt := _qual_cnt;
acv.a_mblock.mb_qual^.mst_optimize_pos := _optimize_pos;
&ifdef trace
t01bool (ak_strat, '_unqualified', _unqualified );
&endif
IF  ( _unqualified )
THEN
    predicate.ps_access_kind := predicate.ps_access_kind + [ ak_const_FALSE ]
ELSE
    predicate.ps_access_kind := predicate.ps_access_kind + [ ak_const_TRUE ];
(*ENDIF*) 
IF  ( NOT ( acv.a_mblock.mb_trns^.trError_gg00 in [ e_ok, e_qual_violation ] ))
THEN
    predicate.ps_access_kind := predicate.ps_access_kind -
          [ ak_const_FALSE, ak_const_TRUE ];
(*ENDIF*) 
acv.a_mblock.mb_trns^.trError_gg00   := _aux_err;
END;
 
(*------------------------------*) 
 
PROCEDURE
      ak724next_predicate(
            VAR mblock  : tgg00_MessBlock;
            VAR stpos   : tsp00_Int2;
            stop        : tsp00_Int2 );
 
BEGIN
(* loop to next condition *)
WHILE ( stpos <= stop )
      AND
      ( mblock.mb_st^[ stpos ].etype <> st_jump_false )
      AND
      ( mblock.mb_st^[ stpos ].etype <> st_jump_true )
      AND
      NOT (( mblock.mb_st^[ stpos ].etype = st_op ) AND
      ( mblock.mb_st^[ stpos ].eop = op_and ))
      AND
      NOT (( mblock.mb_st^[ stpos ].etype = st_op ) AND
      ( mblock.mb_st^[ stpos ].eop = op_or ))
      DO
    (* PTS 1117523 E.Z. *)
    IF  ( mblock.mb_st^[stpos].etype = st_build_in_func ) AND
        ( mblock.mb_st^[stpos].eop_build_in = op_b_case_start )
    THEN
        stpos := stpos + mblock.mb_st^[ stpos ].epos + 1
    ELSE
        stpos := succ( stpos );
    (*ENDIF*) 
(*ENDWHILE*) 
;
(* stpos points how to first boolean stack operator  *)
(* following the condition / or to 'stop+1'          *)
END;
 
.CM *-END-* code ----------------------------------------
.SP 2 
***********************************************************
.PA 
