=== modified file 'src/com/goldencode/p2j/persist/AdaptiveQuery.java'
--- old/src/com/goldencode/p2j/persist/AdaptiveQuery.java	2022-10-05 08:54:36 +0000
+++ new/src/com/goldencode/p2j/persist/AdaptiveQuery.java	2022-10-31 09:25:05 +0000
@@ -3276,6 +3276,11 @@
    protected void fetch(boolean available, LockType lockType, boolean errorIfNull)
    throws MissingRecordException
    {
+      if (available && getResults().isAutoFetch())
+      {
+         return;
+      }
+
       super.fetch(available, lockType, errorIfNull);
       
       if (!isScrolling() && !foundFirst && available && offEnd == OffEnd.NONE)
@@ -3790,20 +3795,52 @@
       }
       
       /**
+       * Is the result delegate responsible for fetching? This is usually true when using
+       * query delegates which handle the fetching process already.
+       * 
+       * @return   {@code true} for complex results which also do the fetching
+       * 
+       */
+      @Override
+      public boolean isAutoFetch()
+      {
+         return true;
+      }
+      
+      /**
        * Get the array of objects at the current result row.
        *
        * @return  Object array or <code>null</code>.
        */
       public Object[] get()
       {
-         int len = buffers.length;
-         Object[] row = new Object[len];
-         for (int i = 0; i < len; i++)
+         return get(null);
+      }
+      
+      /**
+       * Get the array of objects at the current result row if lazy fetching allows it.
+       *
+       * @param   fetching
+       *          The instance responsible with lazy fetching.
+       * @return  Object array or <code>null</code>.
+       */
+      public Object[] get(LazyFetching fetching)
+      {
+         // if lazy fetching is not intended at all or
+         // if lazy fetching is intended and the record is OK to be used
+         if (fetching == null || fetching.isFullRecordOk())
          {
-            row[i] = get(i);
+            int len = buffers.length;
+            Object[] row = new Object[len];
+            for (int i = 0; i < len; i++)
+            {
+               row[i] = get(i);
+            }
+            
+            return row;
          }
          
-         return row;
+         throw new IllegalStateException("This kind of Results does not support lazy fetching");
       }
       
       /**

=== modified file 'src/com/goldencode/p2j/persist/PreselectQuery.java'
--- old/src/com/goldencode/p2j/persist/PreselectQuery.java	2022-10-13 18:29:04 +0000
+++ new/src/com/goldencode/p2j/persist/PreselectQuery.java	2022-10-27 13:02:52 +0000
@@ -585,6 +585,8 @@
 **     OM  20220530          Fixed flush of nursery (only the affected index).
 **     OM  20220701          Flatten calls to initialize().
 **     CA  20221006          Added JMX instrumentation for query assemble. Refs #6814
+**     RAA 20221010          Added class PreselectLazyFetching which enables the possibility of lazy fetching
+**                           for Preselect queries on temporary no-undo tables.
 **     CA  20221013          In 'repositionByID', if the row number is after the last row in the ResultSet,
 **                           assume an error (query off-end) and do not try to fetch the record.
 */
@@ -762,6 +764,9 @@
    /** Has query been registered for resource cleanup? */
    private boolean regCleaner = false;
    
+   /** Instance of lazy fetching. */
+   private LazyFetching fetching = new PreselectLazyFetching();
+   
    /** Cleaner executed when this query's block is exit. */
    private final Finalizable cleaner = new Finalizable()
    {
@@ -5669,8 +5674,11 @@
          }
          else
          {
-            Object[] data = available ? results.get() : null;
-            coreFetch(data, lockType, errorIfNull, false);
+            if (!results.isAutoFetch())
+            {
+               Object[] data = available ? results.get(fetching) : null;
+               coreFetch(data, lockType, errorIfNull, false);
+            }   
             
             if (available)
             {
@@ -5732,10 +5740,19 @@
    {
       int recFetched = 0;
       Iterator<QueryComponent> iter = components();
+      boolean isTemporary = persistence.isTemporary();
+      
       for (int i = 0; iter.hasNext(); i++)
       {
          QueryComponent comp = iter.next();
          RecordBuffer buffer = comp.getBuffer();
+           
+         if (isTemporary && !((TemporaryBuffer)buffer).isUndoable() && data != null && data[i] == null)
+         {
+            recFetched++;
+            continue;
+         }
+         
          boolean dirtyCopy = false;
          Record dmo = null;
          LockType lt = lockType;
@@ -6669,7 +6686,30 @@
       @Override
       public Object[] get()
       {
-         return (cursor == 0) ? new Object[] { templateRowid } : null; 
+         return get(null);
+      }
+      
+      /**
+       * Get the array of objects at the current result row if lazy fetching allows it.
+       *
+       * @param   fetching
+       *          The instance responsible with lazy fetching.
+       * @return  Object array or {@code null}.
+       */
+      @Override
+      public Object[] get(LazyFetching fetching)
+      {
+         if (cursor != 0)
+            return null;
+         
+         // if lazy fetching is not intended at all or
+         // if lazy fetching is intended and the record is OK to be used
+         if (fetching == null || fetching.isFullRecordOk())
+         {
+            return new Object[] { templateRowid };
+         }
+         
+         throw new IllegalStateException("This kind of Results does not support lazy fetching");
       }
       
       /**
@@ -6791,5 +6831,67 @@
          // else cursor is -1 (before), 0 (on single record), 1 (after)
       }
    }
+   
+   /**
+    * Class that implements the concept of {@link LazyFetching} for {@link PreselectQuery}.
+    * It provides a quick response as to whether a {@link Record} should be lazy fetched or not.
+    */
+   class PreselectLazyFetching
+   implements LazyFetching
+   {
+
+      /**
+       * Method to check if a {@link Record} should be fully/partially fetched or not.
+       * Fully fetched means that a {@code Record} should be retrieved from the database,
+       * partially fetched means that a {@code Record} should be retrieved from the cache.
+       * 
+       * @param   comp
+       *          The number of the {@link QueryComponent} for which we check it's {@link RecordBuffer}.
+       * @param   id
+       *          The id of the {@code Record} that is about to be put in the {@code RecordBuffer}.
+       * @return  {@code true} if a {@code Record} should be fully/partially fetched, 
+       *          {@code false} otherwise.
+       */
+      @Override
+      public boolean shouldFetch(Integer comp, Long id)
+      {
+         QueryComponent component = components.get(comp);
+         RecordBuffer buffer = component.getBuffer();
+         if (!buffer.isTemporary() || ((TemporaryBuffer)buffer).isUndoable())
+         {
+            return true;
+         }
+         return component.checkFetching(id);
+         
+      }
+
+      /**
+       * Setter for the {@code oldId}, which is responsible with knowing the id of the
+       * last {@code Record} that was inside the {@link RecordBuffer}.
+       * 
+       * @param   comp
+       *          The number of the {@link QueryComponent} for which we check it's {@code RecordBuffer}.
+       * @param   id
+       *          The id of the old {@code Record}.
+       */
+      @Override
+      public void setOldId(Integer comp, Long id)
+      {
+         components.get(comp).setOldId(id);
+      }
+
+      /**
+       * Method for checking if the {@code Record} is hydrated or is affected by lazy fetching.
+       * 
+       * @return  {@code true} if the {@code Record} is hydrated and is OK to be used, 
+       *          {@code false} otherwise.  
+       */
+      @Override
+      public boolean isFullRecordOk()
+      {
+         return true;
+      }
+      
+   }
 }
 

=== modified file 'src/com/goldencode/p2j/persist/PresortCompoundQuery.java'
--- old/src/com/goldencode/p2j/persist/PresortCompoundQuery.java	2020-09-06 23:15:41 +0000
+++ new/src/com/goldencode/p2j/persist/PresortCompoundQuery.java	2022-10-27 12:15:52 +0000
@@ -90,6 +90,7 @@
 import java.io.*;
 import java.util.*;
 import com.goldencode.p2j.persist.lock.*;
+import com.goldencode.p2j.persist.orm.LazyFetching;
 import com.goldencode.p2j.util.*;
 
 /**
@@ -651,7 +652,26 @@
        */
       public Object[] get()
       {
-         return row;
+         return get(null);
+      }
+      
+      /**
+       * Get the array of objects at the current result row if lazy fetching allows it.
+       *
+       * @param   fetching
+       *          The instance responsible with lazy fetching.
+       * @return  Object array or <code>null</code>.
+       */
+      public Object[] get(LazyFetching fetching)
+      {
+         // if lazy fetching is not intended at all or
+         // if lazy fetching is intended and the record is OK to be used
+         if (fetching == null || fetching.isFullRecordOk())
+         {
+            return row;
+         }
+         
+         throw new IllegalStateException("This kind of Results does not support lazy fetching");   
       }
       
       /**

=== modified file 'src/com/goldencode/p2j/persist/Presorter.java'
--- old/src/com/goldencode/p2j/persist/Presorter.java	2021-09-23 13:39:02 +0000
+++ new/src/com/goldencode/p2j/persist/Presorter.java	2022-10-27 10:39:27 +0000
@@ -96,6 +96,7 @@
 import java.util.function.*;
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.persist.event.SessionListener;
+import com.goldencode.p2j.persist.orm.LazyFetching;
 
 /**
  * This helper class performs client-side (i.e., relative to the database
@@ -934,15 +935,33 @@
        */
       public Object[] get()
       {
+         return get(null);
+      }
+      
+      /**
+       * Get the array of objects at the current result row if lazy fetching allows it.
+       *
+       * @param   fetching
+       *          The instance responsible with lazy fetching.
+       * @return  Object array or <code>null</code>.
+       */
+      public Object[] get(LazyFetching fetching)
+      {
          if (scrolling)
          {
-            return super.get();
+            return super.get(fetching);
          }
          else
          {
-            Object[] datum = (Object[]) currentRow;
-            
-            return datum;
+            // if lazy fetching is not intended at all or
+            // if lazy fetching is intended and the record is OK to be used
+            if (fetching == null || fetching.isFullRecordOk())
+            {
+               Object[] datum = (Object[]) currentRow;
+               return datum;
+            }
+
+            throw new IllegalStateException("Record is not hydrated and should be, in order to be used");
          }
       }
       

=== modified file 'src/com/goldencode/p2j/persist/ProgressiveResults.java'
--- old/src/com/goldencode/p2j/persist/ProgressiveResults.java	2022-08-08 13:03:07 +0000
+++ new/src/com/goldencode/p2j/persist/ProgressiveResults.java	2022-10-31 09:28:01 +0000
@@ -199,6 +199,7 @@
 import java.util.*;
 import java.util.logging.*;
 import com.goldencode.p2j.persist.event.*;
+import com.goldencode.p2j.persist.orm.LazyFetching;
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.util.ErrorManager;
 
@@ -459,13 +460,29 @@
     */
    public Object[] get()
    {
+      return get(null);
+   }
+   
+   /**
+    * Get the array of objects at the current result row.
+    *
+    * @param   fetching
+    *          The instance responsible with lazy fetching. 
+    * @return  Object array or <code>null</code>.
+    */
+   public Object[] get(LazyFetching fetching)
+   {
       if (position < 0)
       {
          return null;
       }
       
+      // we can use cache if lazy fetching is not intended at all or
+      // if lazy fetching is intended and the record is OK to be used
+      boolean canUseCache = fetching == null || fetching.isFullRecordOk();
+      
       // If we have a cache and it contains the requested record, retrieve from the cache.
-      if (cache != null && position < cache.size())
+      if (canUseCache && cache != null && position < cache.size())
       {
          return cache.get(position);
       }
@@ -476,7 +493,7 @@
          Results results = getResults(bracket);
          if (results != null)
          {
-            return results.get();
+            return results.get(fetching);
          }
       }
       catch (Exception exc)

=== modified file 'src/com/goldencode/p2j/persist/QueryComponent.java'
--- old/src/com/goldencode/p2j/persist/QueryComponent.java	2021-08-22 21:31:39 +0000
+++ new/src/com/goldencode/p2j/persist/QueryComponent.java	2022-10-31 09:28:50 +0000
@@ -164,6 +164,9 @@
    /** Index information string as it is returned by INDEX-INFORMATION. */
    private String indexInfo = null;
    
+   /** The id of the old Record that was in the buffer. */
+   private Long oldId = null;
+   
    /**
     * The list of dynamic filters. These are filters added dynamically, at runtime and can also be
     * removed dynamically.
@@ -828,6 +831,41 @@
    }
    
    /**
+    * Method used for checking if fetching is necessary for this component.
+    * 
+    * @param   id
+    *          The id of the {@code Record} that will potentially be fetched.
+    * @return  true if the buffer needs to fetch this record, or false otherwise.
+    */
+   public boolean checkFetching(Long id)
+   {
+      Record record = buffer.getCurrentRecord();
+      if (record == null)
+      {
+         return true;
+      }
+      
+      Long bufferId = record.getId();
+      if (bufferId != null && id.equals(bufferId) && id.equals(oldId))
+      {
+         return false;
+      }
+     
+      return true;
+   }
+   
+   /**
+    * Setter for the old record's id.
+    * 
+    * @param   id
+    *          The id of the record that was just fetched.
+    */
+   public void setOldId(Long id)
+   {
+      oldId = id;
+   }
+   
+   /**
     * Set the record buffer for this component's query.
     * 
     * @param    buffer

=== modified file 'src/com/goldencode/p2j/persist/Results.java'
--- old/src/com/goldencode/p2j/persist/Results.java	2020-09-06 23:15:41 +0000
+++ new/src/com/goldencode/p2j/persist/Results.java	2022-10-31 09:29:16 +0000
@@ -71,6 +71,8 @@
 
 package com.goldencode.p2j.persist;
 
+import com.goldencode.p2j.persist.orm.LazyFetching;
+
 /**
  * An abstraction layer between a joinable query implementation and its results.  This interface
  * defines methods for manipulating a results set using a scrollable cursor paradigm.  Each row
@@ -122,6 +124,17 @@
    public boolean isLast();
    
    /**
+    * Method to implement if lazy fetching is intended. By default, this is not the case.
+    * 
+    * @return  {@code false} by default. If lazy fetching is intended, then this method needs
+    *          to be implemented to return {@code true}. 
+    */
+   default public boolean isAutoFetch() 
+   {
+      return false;
+   }
+   
+   /**
     * Get the array of objects at the current result row.
     *
     * @return  Object array or {@code null}.
@@ -129,6 +142,24 @@
    public Object[] get();
    
    /**
+    * Get the array of objects at the current result row. The default method involves no lazy fetching.
+    * If lazy fetching is desired, then this method needs to be implemented.
+    * 
+    * @param   fetching
+    *          The instance responsible with lazy fetching.
+    * @return  Array of {@code Object}s and {@code null}s.
+    */
+   default public Object[] get(LazyFetching fetching) 
+   {
+      if (fetching != null)
+      {
+         throw new IllegalStateException(this.getClass().getName() + 
+                                         " does not implement get() with onlyDifferences.");
+      }
+      return get();
+   }
+   
+   /**
     * Get the object at the current result row, at the specified column.
     *
     * @param   column
@@ -241,4 +272,4 @@
    {
       throw new IllegalStateException("Cannot add a row for this type of results");
    }
-}
\ No newline at end of file
+}

=== modified file 'src/com/goldencode/p2j/persist/ResultsAdapter.java'
--- old/src/com/goldencode/p2j/persist/ResultsAdapter.java	2020-09-06 23:15:41 +0000
+++ new/src/com/goldencode/p2j/persist/ResultsAdapter.java	2022-10-31 09:30:10 +0000
@@ -83,6 +83,8 @@
 
 import java.io.*;
 
+import com.goldencode.p2j.persist.orm.LazyFetching;
+
 /**
  * An implementation of the {@code Results} interface which delegates all work to another {@code
  * Results} implementation.  A worker can be connected at any time using {@link #setResults}, or
@@ -203,6 +205,19 @@
    }
    
    /**
+    * Is the result delegate responsible for fetching? This is usually true when using
+    * query delegates which handle the fetching process already.
+    * 
+    * @return   {@code true} for complex results which also do the fetching
+    * 
+    */
+   @Override
+   public boolean isAutoFetch()
+   {
+      return results.isAutoFetch();
+   }
+   
+   /**
     * Get the array of objects at the current result row.
     *
     * @return  Object array or {@code null}.
@@ -211,6 +226,18 @@
    {
       return results.get();
    }
+
+   /**
+    * Get the array of objects at the current result row if lazy fetching allows it.
+    * 
+    * @param   fetching
+    *          The instance responsible with lazy fetching.
+    * @return  Object array or {@code null}.
+    */
+   public Object[] get(LazyFetching fetching)
+   {
+      return results.get(fetching);
+   }
    
    /**
     * Get the object at the current result row, at the specified column.
@@ -405,4 +432,4 @@
          provider.provideNewResults(this);
       }
    }
-}
\ No newline at end of file
+}

=== modified file 'src/com/goldencode/p2j/persist/ScrollingResults.java'
--- old/src/com/goldencode/p2j/persist/ScrollingResults.java	2020-09-07 16:23:31 +0000
+++ new/src/com/goldencode/p2j/persist/ScrollingResults.java	2022-10-31 09:30:33 +0000
@@ -197,6 +197,18 @@
    }
    
    /**
+    * Get an array of objects or nulls at the current result row if lazy fetching allows it.
+    * 
+    * @param   fetching
+    *          The instance responsible with lazy fetching.  
+    * @return  Object array or <code>null</code>.
+    */
+   public Object[] get(LazyFetching fetching)
+   {
+      return results.get(fetching);
+   }
+   
+   /**
     * Get the object at the current result row, at the specified column.
     *
     * @param   column

=== modified file 'src/com/goldencode/p2j/persist/SimpleResults.java'
--- old/src/com/goldencode/p2j/persist/SimpleResults.java	2020-09-06 23:15:41 +0000
+++ new/src/com/goldencode/p2j/persist/SimpleResults.java	2022-10-31 09:30:52 +0000
@@ -82,6 +82,8 @@
 
 import java.util.*;
 
+import com.goldencode.p2j.persist.orm.LazyFetching;
+
 /**
  * A simple implementation of {@code Results} which stores row data in a list provided to the
  * constructor. The row data consists of a list of array objects. Each object in the array is
@@ -209,21 +211,40 @@
     */
    public Object[] get()
    {
+      return get(null);
+   }
+   
+   /**
+    * Get the array of objects at the current result row if lazy fetching allows it.
+    *
+    * @param   fetching
+    *          The instance responsible with lazy fetching.
+    * @return  Object array or {@code null}.
+    */
+   public Object[] get(LazyFetching fetching)
+   {
       if (position < 0 || position > getNumberOfLoadedRows() - 1)
       {
          return null;
       }
       
-      Object res = rows.get(position);
-      if (res instanceof Object[])
-      {
-         return (Object[]) res;
-      }
-      else
-      {
-          return new Object[] { res };
-      }
-   }
+      // if lazy fetching is not intended at all or
+      // if lazy fetching is intended and the record is OK to be used
+      if (fetching == null || fetching.isFullRecordOk())
+      {
+         Object res = rows.get(position);
+         if (res instanceof Object[])
+         {
+            return (Object[]) res;
+         }
+         else
+         {
+             return new Object[] { res };
+         }
+      }
+      
+      throw new IllegalStateException("This kind of Results does not support lazy fetching");
+   } 
    
    /**
     * Get the object at the current result row, at the specified column.

=== modified file 'src/com/goldencode/p2j/persist/orm/BaseRecord.java'
--- old/src/com/goldencode/p2j/persist/orm/BaseRecord.java	2022-10-08 21:58:19 +0000
+++ new/src/com/goldencode/p2j/persist/orm/BaseRecord.java	2022-10-31 09:31:18 +0000
@@ -703,6 +703,17 @@
    }
    
    /**
+    * Getter for the id of the current record.
+    * 
+    * @return  id
+    *          The id that was requested.
+    */
+   public Long getId()
+   {
+      return id;
+   }
+   
+   /**
     * Indicate whether any of this record's validatable properties are dirty and have not been validated.
     * 
     * @return  {@code true} if record needs validation, else {@code false}.

=== modified file 'src/com/goldencode/p2j/persist/orm/ScrollableResults.java'
--- old/src/com/goldencode/p2j/persist/orm/ScrollableResults.java	2022-08-11 15:12:46 +0000
+++ new/src/com/goldencode/p2j/persist/orm/ScrollableResults.java	2022-10-31 09:32:44 +0000
@@ -107,7 +107,10 @@
     * @param   session
     *          The {@code Session} to be used.
     */
-   public ScrollableResults(Statement stmt, ResultSet rs, List<RowStructure> rowStructure, Session session)
+   public ScrollableResults(Statement stmt, 
+                            ResultSet rs, 
+                            List<RowStructure> rowStructure, 
+                            Session session)
    {
       this.stmt = stmt;
       this.rs = rs;
@@ -192,16 +195,21 @@
    }
    
    /**
-    * Obtain the full set of values on current row. An array of primary keys or full {@code Record} will be
-    * returned.
+    * Obtain the full set of values on current row. An array of primary keys or full {@code Record}s,  
+    * or {@code null}s will be returned. First two cases correspond to the need of fetching,
+    * while {@code null} means that no fetching is necessary for that component.
     * 
+    * @param   fetching
+    *          The instance responsible with lazy fetching.
+    *          
     * @return  An array of {@code Object} s if the cursor is on a valid position. Otherwise {@code null} is
     *          returned. The type of returned elements may be both {@code Long} when an array of PKs (recids)
     *          is queried or an array of full {@code Record}s. The caller is responsible for handling both
     *          cases (including hydration, if needed).
     */
-   public Object[] get()
+   public Object[] get(LazyFetching fetching)
    {
+      
       return execute(() ->
       {
          int rowSize = (rowStructure == null) ? 0 : rowStructure.size();
@@ -227,13 +235,33 @@
             ret = new Object[rowSize];
             int rsOffset = 1;
             int recCnt = 0;
+            
             for (RowStructure rowStruct : rowStructure)
             {
                int expColumnCount = rowStruct.count;
+               Long object = rs.getLong(rsOffset);
+ 
+               //if fetching is not null, lazy fetching is intended
+               if (fetching != null)
+               {
+                  if (object != null && !fetching.shouldFetch(recCnt, object))
+                  {
+                     ret[recCnt] = null;
+                     // position on next record
+                     rsOffset += expColumnCount;
+                     ++recCnt;
+                     continue;
+                  } 
+               }
+               
                try
                {
                   ret[recCnt] =
                      SQLQuery.hydrateRecord(rs, rowStruct.dmoClass, rsOffset, expColumnCount, session);
+                  if (fetching != null)
+                  {
+                     fetching.setOldId(recCnt, object);
+                  }
                }
                catch (PersistenceException e)
                {
@@ -250,6 +278,20 @@
    }
    
    /**
+    * Obtain the full set of values on current row. An array of primary keys or full {@code Record} will be
+    * returned.
+    * 
+    * @return  An array of {@code Object} s if the cursor is on a valid position. Otherwise {@code null} is
+    *          returned. The type of returned elements may be both {@code Long} when an array of PKs (recids)
+    *          is queried or an array of full {@code Record}s. The caller is responsible for handling both
+    *          cases (including hydration, if needed).
+    */
+   public Object[] get()
+   {
+      return get(null);
+   }
+   
+   /**
     * Obtain the selection of values on current row.
     * 
     * @param    dmoInfo

