=== modified file 'rules/annotations/index_selection.rules'
--- old/rules/annotations/index_selection.rules	2021-02-09 20:22:00 +0000
+++ new/rules/annotations/index_selection.rules	2023-01-05 08:39:54 +0000
@@ -114,6 +114,8 @@
 **                           Fixed conversion of sorting by extent fields with non-constant index.
 ** 042 ECF 20200906          New ORM implementation.
 ** 043 IAS 20210209          Do not use word index field for sorting
+** 044 DDF 20230105          Use hint to set related_buffer annotation to true if
+**                           in a FIND.
 */
 -->
 
@@ -187,6 +189,7 @@
    <worker class="com.goldencode.p2j.schema.SchemaWorker"            namespace="schema" />
    <worker class="com.goldencode.p2j.uast.ProgressPatternWorker"     namespace="prog" />
    <worker class="com.goldencode.p2j.convert.IndexSelectionWorker"   namespace="isw" />
+   <worker class="com.goldencode.p2j.uast.UastHintsWorker"           namespace="uhw" />
    
    <!-- variables -->
    <variable name="record"             type="com.goldencode.ast.Aast" />
@@ -225,6 +228,8 @@
    <variable name="tableCount"         type="java.lang.Integer" />
    <variable name="countingTables"     type="java.lang.Boolean" />
    <variable name="functypes"          type="java.util.ArrayList" />
+   <variable name="inFind"             type="java.lang.Boolean" />
+   <variable name="enableFindFieldRef" type="java.lang.Boolean" />
    
    <!-- function libraries -->
    <include name="common-progress" />
@@ -350,10 +355,20 @@
       <rule>functypes.add(prog.func_raw)</rule>
       <rule>functypes.add(prog.func_memptr)</rule>
       <rule>functypes.add(prog.func_poly)</rule>
+      
+      <rule>inFind = false</rule>
    </init-rules>
    
    <walk-rules>
    
+      <!-- if the type is Find and hint is provided -->
+      <rule>type == prog.KW_FIND
+         <action>enableFindFieldRef = uhw.getUastBoolean("enable_find_field_reference")</action>
+         <rule>enableFindFieldRef != null
+            <action>inFind = enableFindFieldRef</action>
+         </rule>
+      </rule>
+   
       <!-- query-level initialization -->
       <rule>
          (type == prog.open_query or
@@ -549,7 +564,7 @@
                   <!-- in a multi-table query, remember that this field
                        is a reference back to a different but previous
                        buffer in the same query -->
-                  <rule on="false">queryBufferOrder.contains(getNoteLong("refid"))
+                  <rule on="false">queryBufferOrder.contains(getNoteLong("refid")) or inFind
                      <action>putNote("related_buffer", true)</action>
                      <action on="false">putNote("related_buffer", false)</action>
                   </rule>
@@ -611,7 +626,7 @@
                      <!-- in a multi-table query, remember that this field
                           is a reference back to a different but previous
                           buffer in the same query -->
-                     <rule>queryBufferOrder.contains(getNoteLong("refid"))
+                     <rule>queryBufferOrder.contains(getNoteLong("refid")) or inFind
                         <action>putNote("related_buffer", true)</action>
                         <action on="false">putNote("related_buffer", false)</action>
                      </rule>
@@ -1015,6 +1030,11 @@
          <action>extentSortFields.clear()</action>
       </rule>
       
+      <!-- reset flag -->
+      <rule>inFind and type == prog.KW_FIND
+         <action>inFind = false</action>
+      </rule>
+      
    </ascent-rules>
    
    <post-rules>

=== modified file 'rules/annotations/nested_find.rules'
--- old/rules/annotations/nested_find.rules	2022-12-20 10:36:04 +0000
+++ new/rules/annotations/nested_find.rules	2023-01-05 08:40:17 +0000
@@ -13,6 +13,7 @@
 **                           that reference the same buffer. The purpose is to
 **                           create a report where nested finds are classified
 **                           using the NEXT/PREV/LAST/FIRST/WHERE keywords.
+** 002 DDF 20230105          Changed the annotation value stored to the tree id.
 */
  -->
 
@@ -78,7 +79,7 @@
    <variable name="inWhere"          type="java.lang.Boolean" />
    <variable name="bufferName"       type="java.lang.String" />
    <variable name="annotationValue"  type="java.lang.String" />
-   <variable name="annotationObject" type="java.lang.Object" />
+   <variable name="annotationObject" type="com.goldencode.ast.Aast" />
 
    <init-rules>
       <rule>scopedDict = create("com.goldencode.p2j.util.ScopedSymbolDictionary")</rule>
@@ -97,8 +98,7 @@
          <action>recordAst = this.getChildAt(0)</action>
          <rule>recordAst != null
             <action>annotationValue = #(java.lang.String) recordAst.getAnnotation("bufname")</action>
-            <action>annotationObject = recordAst.getAnnotation("bufname")</action>
-            <action>scopedDict.addSymbol(false, annotationValue, annotationObject)</action>
+            <action>scopedDict.addSymbol(false, annotationValue, this.parent)</action>
          </rule>
       </rule>
       
@@ -117,8 +117,9 @@
            previously -->
       <rule>inWhere and scopedDict.size() > 0 and evalLib("fields")
          <action>bufferName = #(java.lang.String) this.getAnnotation("bufname")</action>
-         <rule>scopedDict.lookupSymbol(bufferName) != null
-            <action>putReferenceNote(currentFindAst.getId(), "nested_find", true)</action>
+         <action>annotationObject = #(com.goldencode.ast.Aast) scopedDict.lookupSymbol(bufferName)</action>
+         <rule>annotationObject != null
+            <action>putReferenceNote(currentFindAst.getId(), "nested_find", annotationObject.getId())</action>
          </rule>
       </rule>
    </walk-rules>

=== modified file 'rules/convert/expressions.rules'
--- old/rules/convert/expressions.rules	2021-07-22 19:04:41 +0000
+++ new/rules/convert/expressions.rules	2023-01-05 08:40:40 +0000
@@ -232,6 +232,7 @@
 **                           initialized at the method's execution, and not at the caller's arguments.
 **                           (backed out CA/20210428).
 **     GES 20210722          Exclude Java method calls from built-in OO 4GL processing.
+**     DDF 20230105          Include hint value if exists when the casting to P2JQuery.Parameter is inserted.
 */
 -->
 
@@ -1306,9 +1307,13 @@
                <rule>
                   getNoteBoolean("deferred") and
                   !(isNote("delegated_eval") and getNoteBoolean("delegated_eval"))
-                  <action>lastid = createJavaAst(java.cast, "P2JQuery.Parameter", lastid)</action>
-                  <action>lastid = createJavaAst(java.lambda, "", lastid)</action>
-                  <action>createJavaAst(java.lparens, "", lastid)</action>
+                  <!-- use hint if provided -->
+                  <rule>(uhw.getUastBoolean("enable_find_field_reference") != null) and
+                        uhw.getUastBoolean("enable_find_field_reference")         
+                     <action on="false">lastid = createJavaAst(java.cast, "P2JQuery.Parameter", lastid)</action>
+                     <action on="false">lastid = createJavaAst(java.lambda, "", lastid)</action>
+                     <action on="false">createJavaAst(java.lparens, "", lastid)</action>
+                  </rule>
                </rule>
                
                <!-- dynamic where subscripts in query substitutions need to

=== modified file 'src/com/goldencode/p2j/jmx/FwdJMX.java'
--- old/src/com/goldencode/p2j/jmx/FwdJMX.java	2022-10-31 09:52:21 +0000
+++ new/src/com/goldencode/p2j/jmx/FwdJMX.java	2023-01-05 08:41:30 +0000
@@ -22,6 +22,7 @@
 **                    OrmDataSetParam, OrmDynamicDataSetParam).
 **                  - OpenClient parameter processing (AppserverAddTableMetaData, ApperverGetJavaParameter, 
 **                    AppserverGetOutputParameter).
+**     DDF 20230105 Added a counter for nested find queries (refs: #6709).
 */ 
 
 /*
@@ -524,7 +525,9 @@
       /** Outgoing network traffic */
       NetworkWrites,
       /** Widget attributes flush count */
-      WidgetAttrFlushes;
+      WidgetAttrFlushes,
+      /** Counter for nested find queries */
+      NestedFindQueries;
       
       /** Counter instance */
       private final SimpleLongCounter counter = new SimpleLongCounter();

=== modified file 'src/com/goldencode/p2j/persist/BufferManager.java'
--- old/src/com/goldencode/p2j/persist/BufferManager.java	2022-10-10 09:11:45 +0000
+++ new/src/com/goldencode/p2j/persist/BufferManager.java	2023-01-05 08:41:51 +0000
@@ -466,6 +466,8 @@
 **                           block scope.
 **     CA  20221010          Performance improvements - avoid the ProcedureData lookup, by keeping a parallel 
 **                           stack of this data for THIS-PROCEDURE.  Refs #6826
+**     DDF 20230105          Added functionality for tracking nested find at runtime. A flag from directory.xml
+**                           is stored and used to enable runtime tracking. (refs: #6709)
 */
 
 /*
@@ -530,7 +532,9 @@
 import java.util.logging.*;
 
 import com.goldencode.cache.*;
+import com.goldencode.p2j.cfg.Configuration;
 import com.goldencode.p2j.convert.*;
+import com.goldencode.p2j.directory.DirectoryService;
 import com.goldencode.p2j.oo.lang.*;
 import com.goldencode.p2j.persist.dirty.*;
 import com.goldencode.p2j.persist.id.*;
@@ -698,6 +702,38 @@
 
    /** Track the external scopes pushed on {@link BufferManager}. */
    private final Stack<Boolean> externalScope = new Stack<>();
+   
+   /** Maps a RecordBuffer and the related P2JQuery from a forEach block */
+   private Map<RecordBuffer, P2JQuery> forEachMap = new ConcurrentHashMap<>();
+   
+   /** Configuration value from {@code directory.xml} that enables nested find tracking */
+   public static final boolean ENABLE_NESTED_FIND_TRACKING;
+   
+   static
+   {
+      boolean enableNestedFindTracking = false;
+      
+      if (Configuration.isRuntimeConfig())
+      {
+         DirectoryService ds = DirectoryService.getInstance();
+         if (!ds.bind())
+         {
+            throw new RuntimeException("Directory bind failed");
+         }
+         
+         try
+         {
+            String path = "persistence/enable-nested-find-tracking";
+            enableNestedFindTracking = Utils.getDirectoryNodeBoolean(ds, path, false, false);
+         }
+         finally
+         {
+            ds.unbind();
+         }
+      }
+      
+      ENABLE_NESTED_FIND_TRACKING = enableNestedFindTracking;
+   }
 
    /**
     * Constructor; instances of this class are context-local and must be accessed using the static
@@ -2728,6 +2764,47 @@
    }
    
    /**
+    * Add an element to the forEachMap.
+    * If the key already exists, the value is overridden.
+    * 
+    * @param   rb
+    *          RecordBuffer of a P2JQuery from a forEach block
+    * @param   q
+    *          P2JQuery that is component of a forEach block
+    */
+   public static void addForEachMap(RecordBuffer rb, P2JQuery q)
+   {
+      BufferManager bm = context.get();
+      bm.forEachMap.put(rb, q);
+   }
+   
+   /**
+    * Remove an element from the forEachMap.
+    * 
+    * @param   rb
+    *          RecordBuffer of a P2JQuery from a forEach block.
+    */
+   public static void removeForEachMap(RecordBuffer rb)
+   {
+      BufferManager bm = context.get();
+      bm.forEachMap.remove(rb);
+   }
+   
+   /**
+    * Lookup the given key in the forEachMap.
+    * 
+    * @param   rb
+    *          RecordBuffer that is looked up.
+    *          
+    * @return  true/false
+    */
+   public static boolean lookupForEachValue(RecordBuffer rb) 
+   {
+      BufferManager bm = context.get();
+      return bm.forEachMap.containsKey(rb);
+   }
+   
+   /**
     * Container for batch assign mode information.
     */
    private static class BatchModeData

=== modified file 'src/com/goldencode/p2j/persist/FindQuery.java'
--- old/src/com/goldencode/p2j/persist/FindQuery.java	2021-09-12 01:01:10 +0000
+++ new/src/com/goldencode/p2j/persist/FindQuery.java	2023-01-05 08:41:59 +0000
@@ -63,6 +63,9 @@
 ** 024 ECF 20210806          Corrected CAN-FIND return value in event of fatal error. Fixed nested query
 **                           error detection/reporting.
 **     ECF 20210911          Javadoc fix.
+**     DDF 20230105          Added method for checking if FindQuery is nested in a FOR EACH. Tracking is
+**                           done at runtime only if it's enabled through BufferManager.ENABLE_NESTED_FIND_TRACKING
+**                           value.
 */
 
 /*
@@ -121,6 +124,8 @@
 package com.goldencode.p2j.persist;
 
 import java.util.function.*;
+
+import com.goldencode.p2j.jmx.*;
 import com.goldencode.p2j.persist.lock.*;
 import com.goldencode.p2j.util.*;
 
@@ -161,6 +166,11 @@
    /** Flag indicating whether buffer is updated by query */
    private boolean readOnly = false;
    
+   /** Counter for measuring nested find initializations */
+   private static final SimpleCounter NestedFindCounter = SimpleCounter.getInstance(
+            FwdJMX.Counter.NestedFindQueries
+   ); 
+   
    /**
     * Basic constructor which defaults record lock type to
     * <code>LockType.SHARE</code>.
@@ -415,6 +425,11 @@
       super();
       super.initialize(dmo, where, whereExpr, sort, null, inverse, args, lockType);
       
+      if (BufferManager.ENABLE_NESTED_FIND_TRACKING)
+      {
+         System.out.println("Current FINDQuery nested status: " + Boolean.toString(isNested()));
+      }
+      
       // if the NO-ERROR clause is specified, then a QOEE must not be thrown
       setLenientOffEnd(getBuffer().txHelper.errHlp.isSilent());
       setErrorIfNull(true);
@@ -888,4 +903,46 @@
       
       return true;
    }
+   
+   /**
+    * Check if query is nested in a FOR EACH.
+    * 
+    * @return  true if it's nested, false if not
+    */
+   private boolean isNested()
+   {
+      if (hasWhereExpression())
+      {
+         return false;
+      }
+      
+      Object[] values = getArgs();
+      
+      if (values == null)
+      {
+         return false;
+      }
+      
+      for (Object val : values)
+      {
+         if (!(val instanceof FieldReference))
+         {
+            continue;
+         }
+         
+         final FieldReference ref = (FieldReference) val;
+         RecordBuffer valueRecord = ref.getParentBuffer();
+         if (valueRecord == null)
+         {
+            continue;
+         }
+         
+         if (BufferManager.lookupForEachValue(valueRecord))
+         {
+            NestedFindCounter.update(1L);
+            return true;
+         }
+      }
+      return false;
+   }
 }

=== modified file 'src/com/goldencode/p2j/util/BlockManager.java'
--- old/src/com/goldencode/p2j/util/BlockManager.java	2022-11-25 05:31:36 +0000
+++ new/src/com/goldencode/p2j/util/BlockManager.java	2023-01-05 08:42:11 +0000
@@ -227,6 +227,9 @@
 **     TJD 20220504          Java 11 compatibility minor changes
 **     CA  20221123          If an 'object' instance is assigned in the FINALLY block and also referenced by 
 **                           the RETURN statement, then do not remove it from 'pendingAssign'.
+**     DDF 20230105          Saved the offEndQueries for nested find tracking. offEndQueries are cleared after the
+**                           forEach block is finished, while tracking requires the components to be available
+**                           in the body block.
 */
 
 /*
@@ -293,9 +296,8 @@
 import com.goldencode.p2j.directory.*;
 import com.goldencode.p2j.main.*;
 import com.goldencode.p2j.oo.lang.*;
+import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.security.ContextLocal;
-import com.goldencode.p2j.persist.QueryOffEndException;
-import com.goldencode.p2j.persist.QueryOffEndListener;
 
 // the following are dependencies on the UI package which we would prefer to
 // eliminate if the services can be cleanly abstracted without making the
@@ -10500,6 +10502,7 @@
       
       wa.tm.pushScope(label, ttype, props, false, false, true, true, btype, block);
       
+      Set<P2JQuery> forEachQueries = null;
       try
       {
          try
@@ -10514,6 +10517,22 @@
          }
          finally
          {
+            if (BufferManager.ENABLE_NESTED_FIND_TRACKING)
+            {
+               forEachQueries = wa.tm.getOffEndQueries();
+               for (P2JQuery query : forEachQueries)
+               {
+                  if (query.getRecordBuffers() == null)
+                  {
+                     continue;
+                  }
+                  
+                  for (RecordBuffer rb : query.getRecordBuffers())
+                  {
+                     BufferManager.addForEachMap(rb, query);
+                  }
+               }
+            }
             // after block initialization is finished, the off end listeners
             // for all the buffers in the collected queries are saved
             wa.tm.stopOffEndRegistration();
@@ -10661,6 +10680,21 @@
       
       finally
       {
+         if (forEachQueries != null)
+         {
+            for (P2JQuery query : forEachQueries)
+            {
+               if (query.getRecordBuffers() == null)
+               {
+                  continue;
+               }
+               
+               for (RecordBuffer rb : query.getRecordBuffers())
+               {
+                  BufferManager.removeForEachMap(rb);
+               }
+            }
+         }
          wa.tm.popScope();
       }
    }

=== modified file 'src/com/goldencode/p2j/util/TransactionManager.java'
--- old/src/com/goldencode/p2j/util/TransactionManager.java	2022-10-10 09:11:45 +0000
+++ new/src/com/goldencode/p2j/util/TransactionManager.java	2023-01-05 08:42:30 +0000
@@ -511,6 +511,7 @@
 **                           a set instead of a list, as their order is not important.  Refs #6824
 **     CA  20221010          Performance improvements - avoid the ProcedureData lookup, by keeping a parallel 
 **                           stack of this data for THIS-PROCEDURE.  Refs #6826
+**     DDF 20230105          Added functionality to retrieve offEndQuery value.
 */
 
 /*
@@ -7712,6 +7713,19 @@
    }
    
    /**
+    * Get the offEndQueries using the context local work area.
+    * 
+    * @param   wa
+    *          Context local work area.
+    *       
+    * @return  offEndQueries of the context local work area.
+    */
+   public static Set<P2JQuery> getOffEndQueries(WorkArea wa)
+   {
+      return wa.offEndQueries;
+   }
+   
+   /**
     * Helper to expose transaction state in a way that avoids context local lookups.
     */
    public static class TransactionHelper
@@ -10185,6 +10199,16 @@
          copy.addAll(wa.blocks);
          return copy;
       }
+
+      /**
+       * Get the offEndQueries using the context local work area.
+       * 
+       * @return  offEndQueries of the context local work area.
+       */
+      public Set<P2JQuery> getOffEndQueries()
+      {
+         return TransactionManager.getOffEndQueries(wa);
+      }
    }
    
    /**

