=== modified file 'src/com/goldencode/p2j/persist/AdaptiveQuery.java'
--- src/com/goldencode/p2j/persist/AdaptiveQuery.java	2023-03-07 15:59:58 +0000
+++ src/com/goldencode/p2j/persist/AdaptiveQuery.java	2023-03-09 12:31:10 +0000
@@ -349,6 +349,8 @@
 **                           set is created, ProgressiveResults can no longer be the type returned.
 **     RAA 20230221          If the query will be executed lazy, then it is no longer registered as a 
 **                           listener.
+**     RAA 20230302          Added a condition that checks if the buffer is active before seeing if the query
+**                           supports lazy mode. This is part of the registration as a listener process.
 */
 
 /*
@@ -531,6 +533,9 @@
    /** Determines if this will be unregistered from ChangeBroker on cleanup. */
    private boolean unregisterOnCleanup = false;
    
+   /** Flag to check the ability to fetch the records in a lazy manner, without server-side invalidation */
+   private Boolean lazyMode = null;
+   
    /**
     * Default constructor. Initialization is deferred until <code>initialize()</code> is
     * called.
@@ -559,7 +564,7 @@
       
       adapter = new ResultsAdapter(new ResultsSource());
       
-      ChangeBroker.get().addListener(this);
+      registerRecordChangeListeners(null);
       
       TransactionManager.registerOffEndQuery(this);
       
@@ -592,7 +597,7 @@
       
       adapter = new ResultsAdapter(new ResultsSource());
       
-      ChangeBroker.get().addListener(this);
+      registerRecordChangeListeners(null);
       
       TransactionManager.registerOffEndQuery(this);
       
@@ -665,12 +670,7 @@
          addWhereExpression(whereExpr);
       }
       
-      ChangeBroker.get().addListener(this);
-//      buffer persistence may not be active yet - what do we do? what about scrolling?
-//      if (buffer == null || !buffer.getPersistence().supportsLazyQueryResultsMode())
-//      {
-//         ChangeBroker.get().addListener(this);
-//      }
+      registerRecordChangeListeners(null);
       
       return this;
    }
@@ -1110,19 +1110,24 @@
    {
       closed = false;
       
-      int scope = BufferManager.get().findNearestExternal();
-      RecordBuffer[] buffers = getRecordBuffers();
-      
-      for (RecordBuffer buffer : buffers)
-      {
-         scope = ((scope == -1)
-                  ? buffer.getScopeOpenDepth()
-                  : Math.min(scope, buffer.getScopeOpenDepth()));
-      }
-      
-      if (scope != -1)
-      {
-         registerRecordChangeListeners(scope);
+      // rule out the scope computation and registering if this is a lazy query anyway
+      // lazy queries don't require server-side invalidation as it supports it at the database level
+      if (shouldRegisterRecordChangeListeners())
+      {
+         int scope = BufferManager.get().findNearestExternal();
+         RecordBuffer[] buffers = getRecordBuffers();
+         
+         for (RecordBuffer buffer : buffers)
+         {
+            scope = ((scope == -1)
+                     ? buffer.getScopeOpenDepth()
+                     : Math.min(scope, buffer.getScopeOpenDepth()));
+         }
+         
+         if (scope != -1)
+         {
+            registerRecordChangeListeners(Integer.valueOf(scope));
+         }
       }
 
       if (cursor != null)
@@ -1137,16 +1142,45 @@
    }
    
    /**
+    * Check if this type of query should register record change listeners. Use this 
+    * to avoid computing any scope for registration and end up with a no registration
+    * behavior. 
+    * 
+    * @return   {@code true} if we should invest time into computing the scope in order
+    *           to call {@link #registerRecordChangeListeners} or {@code false} if we
+    *           can skip doing any work to honor {@link #registerRecordChangeListeners}.
+    */
+   @Override
+   public boolean shouldRegisterRecordChangeListeners()
+   {
+      return !isLazyMode();
+   }
+   
+   /**
     * Register all <code>RecordChangeListener</code>s associated with this
     * query (if any), with the context-local <code>ChangeBroker</code> at the
     * indicated scope.
     * 
     * @param   scope
     *          Scope at which listeners should be registered with the change
-    *          broker.
+    *          broker. {@code null} means register at the closest scope.
     */
-   public void registerRecordChangeListeners(int scope)
+   @Override
+   public void registerRecordChangeListeners(Integer scope)
    {
+      // Queries over temporary tables have their invalidation dealt with by the
+      // lazy mechanism, so there is no need to register them as listeners.
+      if (isLazyMode())
+      {
+         return;
+      }
+      
+      if (scope == null)
+      {
+         ChangeBroker.get().addListener(this);
+         return;
+      }
+      
       ChangeBroker.get().addListener(this, scope);
       
       // if these were added as global listeners in ChangeBroker remember to remove them
@@ -2237,11 +2271,7 @@
    throws PersistenceException
    {      
       ArrayList<QueryComponent> components = components();
-      // rather check persistence if supports this
-      if (components.size() == 1 && components.get(0).getBuffer().isTemporary() && !isScrolling())
-      {
-         return;
-      }
+
       boolean preselect = isPreselect();
       boolean invalidate = true;
       boolean cacheAll = false;
@@ -3005,8 +3035,7 @@
    throws PersistenceException
    {
       Results results = null;
-      boolean lazyMode = persistence.supportsLazyQueryResultsMode();
-      if (lazyMode && !isScrolling())
+      if (isLazyMode())
       {
          Long templateRowid = getTemplateQueryRowid(args);
          if (templateRowid != null)
@@ -3015,7 +3044,7 @@
             return results;
          }
          
-         return new ScrollingResults(persistence, executeScroll(persistence, fql, args, lazyMode));
+         return new ScrollingResults(persistence, executeScroll(persistence, fql, args, true));
       }
       
       if (cacheOnReval || probablyRequiresResort())
@@ -3457,6 +3486,31 @@
    }
    
    /**
+    * Helper method to understand if this query is going to use lazy query results mode.
+    * Such mode automatically solves invalidation at the database side, so there is no
+    * need to invalidate in the server-side.
+    *  
+    * @return  {@code true} if this is a lazy mode query with implicit invalidation handling
+    */
+   private boolean isLazyMode()
+   {
+      if (lazyMode == null)
+      {
+         ArrayList<QueryComponent> components = components();
+         if (components == null || components.size() != 1)
+         {
+            return false;
+         }
+         RecordBuffer buffer = components.get(0).getBuffer();
+         lazyMode = buffer != null    &&
+                    buffer.isActive() &&
+                    buffer.getPersistence().supportsLazyQueryResultsMode();
+      }
+      
+      return lazyMode;
+   }
+   
+   /**
     * Indicate whether the query results as initially fetched are likely to require a re-sort before they
     * are returned. This decision is based on whether the sort phrase exists and does not match any legacy
     * index of the first query component table.

=== modified file 'src/com/goldencode/p2j/persist/CompoundQuery.java'
--- src/com/goldencode/p2j/persist/CompoundQuery.java	2023-02-21 15:07:12 +0000
+++ src/com/goldencode/p2j/persist/CompoundQuery.java	2023-03-09 12:35:59 +0000
@@ -239,6 +239,8 @@
 **     SVL 20230113          More performance improvements by replacing some "for-each" loops with indexed
 **                           "for" loops.
 ** 082 CA  20230221          Javadoc fixes.
+** 083 AL2 20230309          Optimized record change listener registration to avoid computing scope for queries
+*                            which dont require registration.
 */
 
 /*
@@ -2445,6 +2447,11 @@
       {
          CompoundComponent comp = compoundComponents.get(i);
          Joinable query = comp.getQuery();
+         if (!query.shouldRegisterRecordChangeListeners())
+         {
+            continue;
+         }
+         
          int scope = externalScope;
          RecordBuffer[] buffers = query.getRecordBuffers();
          
@@ -2458,7 +2465,7 @@
          
          if (scope != -1)
          {
-            query.registerRecordChangeListeners(scope);
+            query.registerRecordChangeListeners(Integer.valueOf(scope));
          }
       }
       

=== modified file 'src/com/goldencode/p2j/persist/Joinable.java'
--- src/com/goldencode/p2j/persist/Joinable.java	2022-11-30 14:33:29 +0000
+++ src/com/goldencode/p2j/persist/Joinable.java	2023-03-09 12:13:48 +0000
@@ -48,6 +48,7 @@
 **                           are not the same), to be aware of the external buffers (i.e. added for CAN-FIND
 **                           sub-select clauses).  The translate will be performed before the query is being
 **                           executed.
+** 020 AL2 20230309          Allow registering change listeners at latest scope using null.
 */
 
 /*
@@ -251,15 +252,29 @@
    public Object[] peekLast();
    
    /**
+    * Check if this type of query should register record change listeners. Use this 
+    * to avoid computing any scope for registration and end up with a no registration
+    * behavior. 
+    * 
+    * @return   {@code true} if we should invest time into computing the scope in order
+    *           to call {@link #registerRecordChangeListeners} or {@code false} if we
+    *           can skip doing any work to honor {@link #registerRecordChangeListeners}.
+    */
+   public default boolean shouldRegisterRecordChangeListeners()
+   {
+      return true;
+   }
+   
+   /**
     * Register all <code>RecordChangeListener</code>s associated with this
     * query (if any), with the context-local <code>ChangeBroker</code> at the
     * indicated scope.
     * 
     * @param   scope
     *          Scope at which listeners should be registered with the change
-    *          broker.
+    *          broker. {@code null} means register at the closest scope.
     */
-   public void registerRecordChangeListeners(int scope);
+   public void registerRecordChangeListeners(Integer scope);
    
    /**
     * Create an adaptive query component based on the information in this query (which is

=== modified file 'src/com/goldencode/p2j/persist/PreselectQuery.java'
--- src/com/goldencode/p2j/persist/PreselectQuery.java	2023-02-27 18:30:38 +0000
+++ src/com/goldencode/p2j/persist/PreselectQuery.java	2023-03-09 12:27:04 +0000
@@ -2211,6 +2211,22 @@
       return super.getExternalBuffers();
    }
    
+
+   /**
+    * Check if this type of query should register record change listeners. Use this 
+    * to avoid computing any scope for registration and end up with a no registration
+    * behavior. 
+    * 
+    * @return   {@code true} if we should invest time into computing the scope in order
+    *           to call {@link #registerRecordChangeListeners} or {@code false} if we
+    *           can skip doing any work to honor {@link #registerRecordChangeListeners}.
+    */
+   @Override
+   public boolean shouldRegisterRecordChangeListeners()
+   {
+      return false;
+   }
+   
    /**
     * Register all <code>RecordChangeListener</code>s associated with this
     * query (if any), with the context-local <code>ChangeBroker</code> at the
@@ -2220,9 +2236,10 @@
     * 
     * @param   scope
     *          Scope at which listeners should be registered with the change
-    *          broker.
+    *          broker. {@code null} means register at the closest scope.
     */
-   public void registerRecordChangeListeners(int scope)
+   @Override
+   public void registerRecordChangeListeners(Integer scope)
    {
    }
    

=== modified file 'src/com/goldencode/p2j/persist/RandomAccessQuery.java'
--- src/com/goldencode/p2j/persist/RandomAccessQuery.java	2023-03-07 12:02:16 +0000
+++ src/com/goldencode/p2j/persist/RandomAccessQuery.java	2023-03-09 11:47:41 +0000
@@ -352,6 +352,7 @@
 ** 116 AL2 20230210          Allow direct access for recid/unique index look-ups.
 **         20230227          Added locking if query is find-by-row-id and record is found in cache.
 **         20230307          Honor record nursery before direct access.
+**         20230309          Allow register of record change listeners at latest scope using null.
 */
 
 /*
@@ -2922,20 +2923,37 @@
     * 
     * @param   scope
     *          Scope at which listeners should be registered with the change broker.
+    *          {@code null} means register at the closest scope.
     */
-   public void registerRecordChangeListeners(int scope)
+   @Override
+   public void registerRecordChangeListeners(Integer scope)
    {
       ChangeBroker broker = ChangeBroker.get();
-      broker.addListener(this, scope);
+      if (scope == null)
+      {
+         broker.addListener(this);
+      }
+      else
+      {
+         broker.addListener(this, scope);
+      }
+      
       if (placeholderCleaner != null)
       {
-         broker.addListener(placeholderCleaner, scope);
+         if (scope == null)
+         {
+            broker.addListener(placeholderCleaner);
+         }
+         else
+         {
+            broker.addListener(this, scope);
+         }
       }
       
       // if these were added as global listeners in ChangeBroker remember to remove them
       // when the query is destroyed. Otherwise, the ChangeBroker will drop them automatically
       // when the respective scope is finished.
-      if (scope == 0)
+      if (scope != null && scope == 0)
       {
          unregisterOnCleanup = true;
       }

