=== modified file 'src/com/goldencode/p2j/persist/AdaptiveQuery.java'
--- old/src/com/goldencode/p2j/persist/AdaptiveQuery.java	2023-01-09 12:28:51 +0000
+++ new/src/com/goldencode/p2j/persist/AdaptiveQuery.java	2023-02-14 11:06:38 +0000
@@ -345,6 +345,8 @@
 **     SVL 20230108          Improved performance by replacing some "for-each" loops with indexed "for" loops.
 **     SVL 20230109          Reflected PreselectQuery.components() now providing direct access to the
 **                           components array in order to improve performance.
+**     RAA 20230213          Added the possibility to execute a query in a lazy manner. Also, when a result
+**                           set is created, ProgressiveResults can no longer be the type returned.
 */
 
 /*
@@ -403,9 +405,11 @@
 package com.goldencode.p2j.persist;
 
 import java.lang.reflect.*;
+import java.sql.*;
 import java.util.*;
 import java.util.function.*;
 import java.util.logging.*;
+
 import com.goldencode.p2j.persist.event.*;
 import com.goldencode.p2j.persist.lock.*;
 import com.goldencode.p2j.persist.orm.*;
@@ -2956,10 +2960,8 @@
    /**
     * Execute the query to create a first level result set.
     * <p>
-    * If we are executing the query after a re-validation, or if the query's sort phrase does not match
-    * any legacy index, fall back to the superclass' implementation. Otherwise, this implementation creates
-    * an instance of {@code ProgressiveResults}, which defers actually executing the query until
-    * the first request to fetch a result.
+    * This implementation can create either a {@code ScrollingResults} or a type of {@code Results}
+    * determined by the execution of the super function.
     *
     * @param   persistence
     *          Object which is used to execute the query.
@@ -2969,8 +2971,7 @@
     *          Substitution parameters to be inserted into placeholders within the FQL query
     *          string.
     *
-    * @return  An instance of {@code ProgressiveResults} which fetches progressively larger
-    *          brackets of query results as the results are accessed.
+    * @return  An instance of {@code ScrollingResults}, possibly fetched in a lazy manner.
     *
     * @throws  PersistenceException
     *          not thrown by this implementation.
@@ -2980,34 +2981,49 @@
    throws PersistenceException
    {
       Results results = null;
-      
-      if (cacheOnReval || probablyRequiresResort())
+      boolean lazyMode = persistence.isTemporary();
+      Long templateRowid = getTemplateQueryRowid(args);
+      if (templateRowid != null)
       {
          results = super.executeQuery(persistence, fql, args);
-      }
-      else
-      {
-         // determine whether this is a (primary key only) projection query
-         boolean projection = !isClientWhere() && !isFullRecords();
-         ArrayList<QueryComponent> components = components();
-         for (int i = 0; projection && i < components.size(); i++)
-         {
-            QueryComponent comp = components.get(i);
-            projection = comp.isIdOnly();
-         }
-         
-         // create the progressive results
-         ProgressiveResults pr = new ProgressiveResults(persistence,
-                                                        fql,
-                                                        args,
-                                                        isScrolling(),
-                                                        getEntities(),
-                                                        projection);
-         pr.addResultsChangeListener(this);
-         results = pr;
+         return results;
       }
       
-      return results;
+      return new ScrollingResults(persistence, executeScroll(persistence, fql, args, lazyMode));
+   }
+   
+   /**
+    * Function responsible with returning a list of {@code ScrollableResults}.
+    * It converts the FQL into SQL and executes the query against the SQL server.
+    * If the query is intended for temporary tables, then the execution could be done in a lazy manner.
+    * 
+    * @param   <T>
+    *          The type of the rows returned in the list.
+    * @param   persistence
+    *          Object which is used to execute the query.
+    * @param   fql
+    *          FQL query string.
+    * @param   args
+    *          Substitution parameters to be inserted into placeholders within the FQL query
+    *          string.
+    * @param   lazyMode
+    *          Flag that indicates whether the query should be executed in a lazy manner.
+    *          Not all queries can be executed with this option, but the checks for that are not done here.
+    *          
+    * @return  A scrollable list of rows of type {@code <T>} from SQL server, as specified by the
+    *          {@code fql} query.
+    *          
+    * @throws  PersistenceException
+    *          when an error occurred while performing the requested operations.
+    */
+   protected <T> ScrollableResults<T> executeScroll(Persistence persistence,
+                                                    String fql,
+                                                    Object[] args,
+                                                    boolean lazyMode)
+   throws PersistenceException
+   {
+      int scrollMode = ResultSet.TYPE_SCROLL_INSENSITIVE;
+      return persistence.scroll(getEntities(), fql, args, 0, 0, scrollMode, lazyMode);
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/persist/Persistence.java'
--- old/src/com/goldencode/p2j/persist/Persistence.java	2023-01-20 12:07:41 +0000
+++ new/src/com/goldencode/p2j/persist/Persistence.java	2023-02-14 11:06:38 +0000
@@ -584,6 +584,7 @@
 **     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
 **     CA  20221109          Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded 
 **                           as 'partial/incomplete'.
+**     RAA 20230213          Added the possibility to create a query defined with lazy. 
 */
 
 /*
@@ -1311,6 +1312,44 @@
                                           int scrollMode)
    throws PersistenceException
    {
+       return scroll(entities, fql, values, maxResults, startOffset, scrollMode, false);
+   }
+   
+   /**
+    * Execute an FQL query and get a scrollable cursor on the result set. Records represented by the result
+    * set are not locked by this method.
+    *
+    * @param   entities
+    *          Names of the DMO entities associated with this query statement.
+    * @param   fql
+    *          FQL query statement to be parsed and executed by ORM framework.
+    * @param   values
+    *          Substitution values for the query.  If none, this should be an empty array.
+    * @param   maxResults
+    *          The maximum number of elements to be returned in the list. If this value is non-positive, no
+    *          upper limit is applied.
+    * @param   startOffset
+    *          The 0-based offset of the first record to retrieve.  If this value is non-positive, an offset
+    *          of 0 is used by default.
+    * @param   scrollMode
+    *          Scroll mode which should apply to the result set.
+    * @param   lazyMode
+    *          Flag that indicates whether the query should be executed in a lazy manner.
+    *
+    * @return  An object which provides access to a scrollable cursor on the result set.
+    *
+    * @throws  PersistenceException
+    *          if there was an error executing the query.
+    */
+   public <T> ScrollableResults<T> scroll(String[] entities,
+                                          String fql,
+                                          Object[] values,
+                                          int maxResults,
+                                          int startOffset,
+                                          int scrollMode,
+                                          boolean lazyMode)
+   throws PersistenceException
+   {
       boolean trace = LOG.isLoggable(Level.FINEST);
       boolean warn = (trace || LOG.isLoggable(Level.WARNING));
       long start = (warn ? System.currentTimeMillis() : 0L);
@@ -1321,7 +1360,7 @@
       
       try
       {
-         Query query = getQuery(local, fql, maxResults, startOffset, values, readOnly);
+         Query query = getQuery(local, fql, maxResults, startOffset, values, readOnly, lazyMode);
          results = query.scroll(local.getSession(), scrollMode);
       }
       catch(UdfException exc)
@@ -3947,7 +3986,44 @@
                           boolean readOnly)
    throws PersistenceException
    {
-      Query query = local.getQuery(fql, maxResults, startOffset, readOnly);
+      return getQuery(local, fql, maxResults, startOffset, values, readOnly, false);
+   }
+   
+   /**
+    * Get or create a query for the given FQL query string. A cached query is used if it exists.
+    *
+    * @param   local
+    *          Context-local state.
+    * @param   fql
+    *          FQL query string.
+    * @param   maxResults
+    *          The maximum number of results to be returned by the query. If this value is
+    *          non-positive, no upper limit is applied.
+    * @param   startOffset
+    *          The 0-based offset of the first record to retrieve.  If this value is non-positive,
+    *          an offset of 0 is used by default.
+    * @param   values
+    *          Substitution values for the query.  If none, this should be an empty array.
+    * @param   readOnly
+    *          {@code true} to prepare the query in read-only mode, else {@code false}.
+    * @param   lazyMode
+    *          Flag that indicates whether the query should be executed in a lazy manner.
+    *
+    * @return  FQL query object.
+    *
+    * @throws  PersistenceException
+    *          if there was an error creating an ORM session.
+    */
+   private Query getQuery(Context local,
+                          String fql,
+                          int maxResults,
+                          int startOffset,
+                          Object[] values,
+                          boolean readOnly,
+                          boolean lazyMode)
+   throws PersistenceException
+   {
+      Query query = local.getQuery(fql, maxResults, startOffset, readOnly, lazyMode);
       
       // Bind parameters, if any.  It is OK to do this with cached queries, because each use is
       // guaranteed to reset the parameters.
@@ -4251,6 +4327,35 @@
       Query getQuery(String fql, int maxResults, int startOffset, boolean readOnly)
       throws PersistenceException
       {
+         return getQuery(fql, maxResults, startOffset, readOnly, false);
+      }
+      
+      /**
+       * Get a cached query for the given combination of FQL query string, maximum result rows,
+       * and starting row offset, creating and caching a new query if no such query already is
+       * cached. The cache is fixed size. When it is full and a new query needs to be added, the
+       * least recently used query is removed.
+       * 
+       * @param   fql
+       *          FQL query string.
+       * @param   maxResults
+       *          The maximum number of results to be returned.
+       * @param   startOffset
+       *          Zero-based offset of first result to be returned (undefined results unless
+       *          {@code fql} includes an {@code order by} phrase).
+       * @param   readOnly
+       *          {@code true} to execute the query in read-only mode, else {@code false}.
+       * @param   lazyMode
+       *          Flag that indicates whether the query should be executed in a lazy manner.
+       * 
+       * @return  A {@code Query} object for the requested {@code fql}.
+       * 
+       * @throws  PersistenceException
+       *          if there was an error creating an ORM session.
+       */
+      Query getQuery(String fql, int maxResults, int startOffset, boolean readOnly, boolean lazyMode)
+      throws PersistenceException
+      {
          Query query = null;
          fql = fql.trim(); // just in case
          
@@ -4281,6 +4386,7 @@
             // an incorrect value from a previous usage
             query.setMaxResults((maxResults > 0) ? maxResults : -1);
             query.setFirstResult(Math.max(startOffset, 0));
+            query.setLazyMode(lazyMode);
          }
          
          query.setReadOnly(readOnly);

=== modified file 'src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java'
--- old/src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java	2023-02-04 14:43:34 +0000
+++ new/src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java	2023-02-14 11:06:38 +0000
@@ -47,6 +47,7 @@
 **     SVL 20230108 Improved performance by replacing some "for-each" loops with indexed "for" loops.
 **         20221031 Collected fields of "select" query.
 **         20221101 Fixed possible NPE.
+**     RAA 20230213 Added the keyword "lazy". This can be appended to the SQL that gets generated.
 */
 
 /*
@@ -192,6 +193,9 @@
    /** The maximum numbers of results in a query. Used for paging results. */
    private int maxResults = -1;
    
+   /** Flag for selecting results in a lazy manner. Used in {@code Select} statements. */
+   private boolean lazyMode = false;
+   
    /** The offset of the record to start a result with. Used for paging results. */
    private int startOffset = -1;
    
@@ -530,7 +534,7 @@
     * @param   fql
     *          A valid {@code FQL} statement.
     * @param   maxResults
-    *          The maximum numbers of results tobe returned.
+    *          The maximum numbers of results to be returned.
     * @param   startOffset
     *          The start offset. Together with {@code maxResults} this parameter helps
     *          implementing paging of a bigger the result set.
@@ -543,6 +547,35 @@
     */
    public String toSQL(String fql, int maxResults, int startOffset, List<RowStructure> rowStructure)
    {
+      return toSQL(fql, maxResults, startOffset, rowStructure, false);
+   }
+   
+   /**
+    * Converts a {@code FQL} statement into its SQL representation.
+    * 
+    * @param   fql
+    *          A valid {@code FQL} statement.
+    * @param   maxResults
+    *          The maximum numbers of results to be returned.
+    * @param   startOffset
+    *          The start offset. Together with {@code maxResults} this parameter helps
+    *          implementing paging of a bigger the result set.
+    * @param   rowStructure
+    *          (Acts as an output parameter). If not {@code null}, at return, the map will contain
+    *          the list of entities returned and the number of fields coded in the generated
+    *          {@code SELECT} statement for each of them.
+    * @param   lazyMode
+    *          Should the returned SQL execute the statement in a lazy manner?
+    *          {@code true} if the answer is yes, {@code false} otherwise.
+    *            
+    * @return  The SQL representation of the {@code fql}. 
+    */
+   public String toSQL(String fql,
+                       int maxResults,
+                       int startOffset,
+                       List<RowStructure> rowStructure,
+                       boolean lazyMode)
+   {
       if (LOG.isLoggable(Level.FINE))
       {
          LOG.log(Level.FINE, 
@@ -578,6 +611,7 @@
       sb = new StringBuilder(fql.length());
       this.maxResults = maxResults;
       this.startOffset = startOffset;
+      this.lazyMode = lazyMode;
       processStatement(root);
       
       String sql = sb.toString();
@@ -1423,6 +1457,11 @@
          {
             generatePaging();
          }
+         
+         if (lazyMode)
+         {
+            generateLazyMode();
+         }
       }
       
       if (aliases.pop() != topLevelAliasMap)
@@ -1564,6 +1603,16 @@
    }
    
    /**
+    * Add the keyword "lazy" to the end of the SQL. This works only when there is a 
+    * {@code Select} statement that is intended to be executed in a lazy manner.
+    */
+   private void generateLazyMode()
+   {
+      sb.append("\n");
+      sb.append("lazy");
+   }
+   
+   /**
     * Generates the {@code ORDER BY} part of the query. The output is directly appended to
     * {@code sb}.
     * 

=== modified file 'src/com/goldencode/p2j/persist/orm/Query.java'
--- old/src/com/goldencode/p2j/persist/orm/Query.java	2023-02-10 09:39:25 +0000
+++ new/src/com/goldencode/p2j/persist/orm/Query.java	2023-02-14 11:06:38 +0000
@@ -26,6 +26,8 @@
 **     SBI 20221023 Added a usage of SQLQuery.uniqueResult(Session session, RowStructure rowStructure).
 **     CA  20221031 Javadoc fixes.
 **     IAS 20230209 Fixed ${TZ} placeholder value
+**     RAA 20230213 Added lazyMode flag. This is useful if the intention is to execute the query in a lazy
+**                  manner.
 */
 
 /*
@@ -127,6 +129,13 @@
    private int maxResults = -1;
    
    /**
+    * Flag accepted by the {@code Select} query. If it is set to {@code true}, then the query will be
+    * executed in a lazy manner.
+    * By default, queries are executed in a preselect manner.
+    */
+   private boolean lazyMode = false;
+   
+   /**
     * The index of the first matching results accepted by the (SELECT) query. Used in combination 
     * with {@code firstResult} to configure result pagination.
     */
@@ -176,6 +185,18 @@
    }
    
    /**
+    * Setter for the {@code lazyMode} flag.
+    * Note: this method should only be called if the {@code Query} executed is a {@code Select}.
+    * 
+    * @param   lazyMode
+    *          If {@code true}, the {@code Query} will be executed in a lazy manner.
+    */
+   public void setLazyMode(boolean lazyMode)
+   {
+      this.lazyMode = lazyMode;
+   }
+   
+   /**
     * Configures the index of the first row to be returned by this query. Depending on the SQL
     * dialect from the {@code session}, the value is injected as the parameter of SQL
     * {@code limit} clause.
@@ -349,7 +370,7 @@
          {
             FqlToSqlConverter fql2sql = FqlToSqlConverter.getInstance(dialect, db, params);
             rowStructure = new ArrayList<>(2); // this should be enough for most cases
-            String sql = fql2sql.toSQL(fql, maxResults, firstResult, rowStructure);
+            String sql = fql2sql.toSQL(fql, maxResults, firstResult, rowStructure, lazyMode);
             paramCount = fql2sql.getLastConversionParamCount();
             wasRewritten = fql2sql.isQueryWasRewritten();
             userTableStatRead = fql2sql.isUserTableStatRead();

