=== modified file 'src/com/goldencode/p2j/jmx/FwdJMX.java'
--- old/src/com/goldencode/p2j/jmx/FwdJMX.java	2023-01-13 19:02:33 +0000
+++ new/src/com/goldencode/p2j/jmx/FwdJMX.java	2023-02-09 11:37:06 +0000
@@ -25,6 +25,7 @@
 **     AL2 20230104 Added OrmRedo.
 **                  Added OrmNoUndoTrace.
 **     OM  20230112 Added finer-granulation instrumentation for processing of dynamic queries. 
+**     DDF 20230209 Added counters for psCache (prepared statement cache) hit/miss. (refs: #7035)
 */ 
 
 /*
@@ -551,7 +552,11 @@
       /** Outgoing network traffic */
       NetworkWrites,
       /** Widget attributes flush count */
-      WidgetAttrFlushes;
+      WidgetAttrFlushes,
+      /** psCache hit counter */
+      PSCacheHits,
+      /** psCache miss counter */
+      PSCacheMisses;
       
       /** Counter instance */
       private final SimpleLongCounter counter = new SimpleLongCounter();

=== modified file 'src/com/goldencode/p2j/persist/FQLBundle.java'
--- old/src/com/goldencode/p2j/persist/FQLBundle.java	2022-11-03 00:24:27 +0000
+++ new/src/com/goldencode/p2j/persist/FQLBundle.java	2023-02-09 11:38:33 +0000
@@ -19,6 +19,7 @@
 ** 008 EVL 20160223          Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
 ** 009 ECF 20200906          New ORM implementation.
 ** 010 OM  20221103          Renamed class to FQLBundle.
+** 011 DDF 20230209          Refactored ParameterIndices into ParameterDiff. (refs: #7035)
 */
 
 /*
@@ -153,8 +154,8 @@
    /** Parallel set of FQL statements for dirty database use */
    private List<String> dirtyStatements = null;
    
-   /** Mapping of indices in the array of query substitution parameters */
-   private ParameterIndices paramIndices = null;
+   /** Mapping of substitution parameters functions */
+   private ParameterDiff paramDiff = null;
    
    /**
     * Default constructor.
@@ -347,27 +348,27 @@
    }
    
    /**
-    * Get the <code>ParameterIndices</code> object associated with this
+    * Get the <code>ParameterDiff</code> object associated with this
     * bundle.  This object maintains an array of zero-based indices into the
-    * array of query substitution arguments for the base (i.e., unaugmented)
+    * array of query substitution parameters lambdas for the base (i.e., unaugmented)
     * where clause, as it exists <i>after</i> preprocessing is complete.  This
     * additional level of indirection is necessary because the query
     * substitution placeholders may have been reordered within the FQL where
     * clause expression during the FQL preprocessing rewrite step, and some
     * parameters may have been inlined into the where clause.
     *
-    * @return  An object which provides a mapping of indices into the query
-    *          substitution parameter list.
+    * @return  An object which provides a mapping of query substitution parameter
+    *          functions.
     */
-   ParameterIndices getParameterIndices()
+   ParameterDiff getParameterDiff()
    {
-      return paramIndices;
+      return paramDiff;
    }
    
    /**
-    * Set the <code>ParameterIndices</code> object associated with this
+    * Set the <code>ParameterDiff</code> object associated with this
     * bundle.  This object maintains an array of zero-based indices into the
-    * array of query substitution arguments for the base (i.e., unaugmented)
+    * array of query substitution parameters lambdas for the base (i.e., unaugmented)
     * where clause, as it exists <i>after</i> preprocessing is complete.  This
     * additional level of indirection is necessary because the query
     * substitution placeholders may have been reordered within the FQL where
@@ -375,12 +376,12 @@
     * parameters may have been inlined into the where clause.
     *
     * @param   paramIndices
-    *          An object which provides a mapping of indices into the query
-    *          substitution parameter list.
+    *          An object which provides a mapping of query substitution parameter
+    *          functions.
     */
-   void setParameterIndices(ParameterIndices paramIndices)
+   void setParameterDiff(ParameterDiff paramDiff)
    {
-      this.paramIndices = paramIndices;
+      this.paramDiff = paramDiff;
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/persist/FQLHelper.java'
--- old/src/com/goldencode/p2j/persist/FQLHelper.java	2022-11-03 00:24:27 +0000
+++ new/src/com/goldencode/p2j/persist/FQLHelper.java	2023-02-09 11:38:58 +0000
@@ -130,6 +130,8 @@
 **                           prevent NPE in proxy case.
 **     CA  20221006          Added JMX instrumentation for FQL prepare (in 'obtain'). Refs #6814
 **     OM  20221103          Class renamed to FQLHelper.
+**     DDF 20230209          Method call changed due to method refactoring of FQLBundle and 
+**                           FQLPreprocessor. (refs: #7035)
 */
 
 /*
@@ -971,14 +973,14 @@
          }
       }
       
-      // Store query substitution parameter indices.
+      // Store query substitution parameter lambdas.
       // TODO:  currently, these are shared, and not dialect specific, but
       // only because we do not inline parameters from this class.  Were this
-      // to change, we would need to maintain separate ParameterIndices
+      // to change, we would need to maintain separate ParameterDiff
       // objects for the primary and dirty database statements.
       if (primary)
       {
-         bundle.setParameterIndices(fqlPreprocessor.getParameterIndices());
+         bundle.setParameterDiff(fqlPreprocessor.getParameterDiff());
       }
    }
    

=== modified file 'src/com/goldencode/p2j/persist/FQLPreprocessor.java'
--- old/src/com/goldencode/p2j/persist/FQLPreprocessor.java	2022-11-30 14:33:29 +0000
+++ new/src/com/goldencode/p2j/persist/FQLPreprocessor.java	2023-02-09 11:40:23 +0000
@@ -282,6 +282,10 @@
 **                           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.
+**     DDF 20230209          Extracted query parameters from the where clause and refactored ParameterIndices
+**                           into ParameterDiff to store a mapping of substitution parameter lambdas for all
+**                           parameters (parameters given at query initialization and the ones extracted 
+**                           from the AST). (refs: #7035)
 */
 
 /*
@@ -467,7 +471,7 @@
  * substitution parameters in the proper order:
  * <ul>
  *   <li>{@link #getFQL()}
- *   <li>{@link #getParameterIndices()}
+ *   <li>{@link #getParameterDiff()}
  *   <li>{@link #ansiJoins()}
  * </ul>
  * <p>
@@ -615,8 +619,8 @@
     */
    private boolean inlinedTernary = false;
    
-   /** Mapping of index positions for substitution parameters after rewrite */
-   private ParameterIndices paramIndices = null;
+   /** Mapping of substitution parameters functions after rewrite */
+   private ParameterDiff paramDiff = null;
    
    /** Map of properties used by this where clause, keyed by DMO entity */
    private Map<String, Set<String>> restrictionProperties = null;
@@ -1289,9 +1293,9 @@
    }
    
    /**
-    * Get the <code>ParameterIndices</code> object associated with this query.
+    * Get the <code>ParameterDiff</code> object associated with this query.
     * This object maintains an array of zero-based indices into the array of
-    * query substitution arguments for the where clause, as it exists
+    * query substitution parameter lambdas for the where clause, as it exists
     * <i>after</i> preprocessing is complete.  This additional level of
     * indirection is necessary because the query substitution placeholders may
     * have been reordered within the FQL where clause expression during the
@@ -1299,11 +1303,11 @@
     * inlined into the where clause.
     *
     * @return  An object which provides a mapping of indices into the query
-    *          substitution parameter list.
+    *          substitution parameter lambdas list.
     */
-   ParameterIndices getParameterIndices()
+   ParameterDiff getParameterDiff()
    {
-      return paramIndices;
+      return paramDiff;
    }
    
    /**
@@ -1554,6 +1558,9 @@
          
          this.findByRowid = checkFindByRowid(root);
          
+         // parameters need to be created before fql is emitted
+         this.paramDiff = createParameterDiffs(root, parameters);
+         
          // Emit the preprocessed FQL expression text.
          this.fql = emit(root, false);
          
@@ -1563,8 +1570,6 @@
             LOG.log(Level.FINEST,
                     "[" + database + "] Preprocessed FQL:  " + fql + sep + root.dumpTree());
          }
-         
-         this.paramIndices = createParameterIndices(root, parameters);
       }
    }
    
@@ -5849,16 +5854,18 @@
    }
    
    /**
-    * Create the mapping of substitution parameters to their corresponding
+    * Create the mapping of substitution parameters functions to their corresponding
     * substitution placeholders in the rewritten FQL where clause.  These
     * placeholders (<code>?</code>) may have been reordered or removed (due to
     * inlining) during the rewrite process.
     * <p>
     * During parsing of the FQL clause, an index annotation was added to each
     * AST of type <code>SUBST</code> (token type for the query substitution
-    * parameter placeholder), denoting that placeholder's position in the
-    * original where clause.  We now walk the rewritten AST, extracting this
-    * annotation and storing it in a new list.  The resulting array defines
+    * parameter placeholder), and each <code>NUM_LITERAL</code>, <code>DEC_LITERAL</code>,
+    * <code>STRING</code> which is substituted to <code>SUBST</code> 
+    * denoting that placeholder's lambda in the original where clause.  
+    * We now walk the rewritten AST, extracting this annotation
+    * and storing it in a new list.  The resulting array defines
     * the new order of the substitution parameters.  For parameters which
     * were dropped, due to inlining, a null placeholder will appear in the new
     * list.
@@ -5871,41 +5878,96 @@
     * @return  Object which maps new query substitution parameter positions to their positions in the original
     *          parameters array. These indices are zero-based.
     */
-   private ParameterIndices createParameterIndices(HQLAst root, Object[] parameters)
+   private ParameterDiff createParameterDiffs(HQLAst root, Object[] parameters)
    {
-      if (parameters == null)
-      {
-         return (new ParameterIndices(null));
-      }
+      int parameterCounter = parameters != null ? parameters.length : 0;
+      FqlType parameterType = null;
       
-      List<Integer> indices = new ArrayList<>(parameters.length);
+      List<Function<Object[], Object>> retrieveParameterFunctions = new ArrayList<>();
       
       // Extract the "index" annotation from each SUBST node, in order of
       // the tree walk, which will correspond to the order of the placeholders
       // in the final, infix expression.
+      
+      // There are two types of parameters, parameters that are substituted from the beginning,
+      // and those that are substituted in the mainWalk() method.
+      // In the case of the "extracted-value" annotation being present, create a function 
+      // that returns the value after being casted to a BaseDataType.
+      // For the substituted parameters that already existed, the function returns
+      // the index positioned value in the params given.
       Iterator<Aast> iter = root.iterator();
       while (iter.hasNext())
       {
-         Long index = null;
          HQLAst next = (HQLAst) iter.next();
          switch (next.getType())
-         {
-            case SUBST:
-               index = (Long) next.getAnnotation("index");
-               indices.add(index.intValue());
-               break;
-               
+         {            
+            // The BOOL_TRUE and BOOL_FALSE cases are not handled
             case NUM_LITERAL:
+               if (parameterType == null)
+               {
+                  parameterType = FqlType.INTEGER;
+               }
             case DEC_LITERAL:
+               if (parameterType == null)
+               {
+                  parameterType = FqlType.DECIMAL;
+               }
             case STRING:
-            case IS_NULL:
-            case NOT_NULL:
-            case BOOL_FALSE:
+               if (parameterType == null)
+               {
+                  parameterType = FqlType.TEXT;
+               }
+               
                if (inline && next.isAnnotation("inlined"))
                {
-                  // Allow null to remain at this position in the array.
-                  indices.add(null);
-               }
+                  break;
+               }
+               
+               // Add an index and datatype annotation to the current node.
+               // The extracted-value annotation stores the value of the node.
+               next.putAnnotation("index", (long) parameterCounter++);
+               next.putAnnotation("datatype", parameterType.toString());
+               next.putAnnotation("extracted-value", next.getText());
+               parameterType = null;
+               
+               // Change the node type and value
+               next.setType(SUBST);
+               next.setText("?");
+            case SUBST:
+               Function<Object[], Object> rp = null;
+               
+               if (next.isAnnotation("extracted-value"))
+               {
+                  rp = (Object[] params) -> 
+                  {
+                     String value = (String) next.getAnnotation("extracted-value");
+                     FqlType fqlType = Enum.valueOf(FqlType.class, (String) next.getAnnotation("datatype"));
+                     switch (fqlType)
+                     {
+                        case INTEGER:
+                           return new integer(value);
+                        case DECIMAL:
+                           return new decimal(value);
+                        case TEXT:
+                           assert (value.charAt(0) == '\'');
+                           assert (value.charAt(value.length() - 1) == '\'');
+                           value = value.substring(1, value.length() - 1);
+                           return new character(value);
+                        default:
+                           throw new IllegalStateException("No matching datatype for the extracted value.");
+                     }
+                  };
+               }
+               else 
+               {
+                  rp = (Object[] params) -> 
+                  {
+                     Long index = (Long) next.getAnnotation("index");
+                     return params[index.intValue()];
+                  };
+               }
+               
+               retrieveParameterFunctions.add(rp);
                break;
                
             default:
@@ -5913,9 +5975,15 @@
          }
       }
       
-      Integer[] idx = indices.toArray(new Integer[indices.size()]);
-      
-      return (new ParameterIndices(idx));
+      if (retrieveParameterFunctions.size() == 0)
+      {
+         return new ParameterDiff(null);
+      }
+      
+      @SuppressWarnings("unchecked")
+      Function<Object[], Object>[] rpf = retrieveParameterFunctions.toArray(new Function[] {});
+      
+      return new ParameterDiff(rpf);
    }
    
    /**

=== added file 'src/com/goldencode/p2j/persist/ParameterDiff.java'
--- old/src/com/goldencode/p2j/persist/ParameterDiff.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/ParameterDiff.java	2023-02-07 13:27:23 +0000
@@ -0,0 +1,208 @@
+/*
+** Module   : ParameterDiff.java
+** Abstract : Mapping of query substitution parameters functions in an array, after where clause preprocessing.
+**
+** Copyright (c) 2008-2023, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- -JPRM- ------------------------------------Description------------------------------------
+** 001 ECF 20081009 @40080 Created initial version. A mapping of query substitution parameters in an array,
+**                         after where clause preprocessing.
+** 002 OM  20221103        New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
+** 003 DDF 20230201        Refactored ParameterIndices into ParameterDiff by replacing the Integer indices
+**                         array with an array of lambdas. Instead of storing the index of the parameters
+**                         from the AST node, we store a lambda that takes an array of arguments and returns 
+**                         either an extracted value from the AST, or an existing value from the given array.
+*/
+
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+
+package com.goldencode.p2j.persist;
+
+import java.util.function.Function;
+
+/**
+ * A mapping of the active indices in an array of query substitution
+ * parameters, after an HQL where clause has been preprocessed.  The HQL
+ * preprocessor may inline or re-order query substitution parameters.  Thus,
+ * given an array of query substitution parameters, it is necessary to provide
+ * a means for a query object to organize the remaining parameters when
+ * submitting a query for execution.
+ * <p>
+ * A query object will have the original array of parameters provided by
+ * business logic, as well as an instance of {@link FQLPreprocessor}.  That
+ * instance will provide an instance of this class via its {@link
+ * FQLPreprocessor#getParameterDiff()} method.  To obtain the correct list
+ * of parameters which corresponds with the preprocessed where clause, the
+ * query iterates its original array of parameters.  For each index in that
+ * array, it calls {@link #getIndex(int)} to obtain the remapped position of
+ * the parameter originally at the current index.  If <code>null</code> is
+ * returned, it means the parameter originally at the current index has been
+ * inlined into the where clause.  A non-<code>null</code> value represents
+ * the zero-based index at which the parameter can be found in the original
+ * array.
+ */
+final class ParameterDiff
+{
+   /** Count of substitution parameters functions in use */
+   private final int count;
+   
+   /** Array of functions for substitution parameters */
+   private final Function<Object[], Object>[] substParamFunctions;
+   
+   /**
+    * Constructor which sets the array of substitution parameters 
+    * functions of query for the associated where clause.
+    * 
+    * @param   substFunctions
+    *          Array of substitution parameter functions. If
+    *          <code>null</code>, the associated query string has no query
+    *          substitution parameters.
+    */
+   ParameterDiff(Function<Object[], Object>[] substFunctions)
+   {
+      this.substParamFunctions = substFunctions;
+      int counter = 0;
+      if (substParamFunctions != null)
+      {
+         for (int i = 0; i < substParamFunctions.length; i++)
+         {
+            if (substParamFunctions[i] == null)
+            {
+               continue;
+            }
+            counter++;
+         }
+      }
+      this.count = counter;
+   }
+   
+   /**
+    * Get the count of query substitution parameters functions which were created for
+    * the where clause preprocessing step. This is the count of the passed 
+    * lambda array items to this object's constructor.
+    * 
+    * @return  Count of query substitution parameters functions.
+    */
+   int getCount()
+   {
+      return count;
+   }
+   
+   /**
+    * Given a position of a query substitution parameter function in the original 
+    * array of lambdas (before the where clause preprocessing step), get the
+    * lambda of that parameter in the original array.  Note that the
+    * original array is not changed, only the mapping of lambdas may have
+    * changed.  If the index returned matches the <code>position</code>
+    * supplied, this indicates the parameter's placeholder (<code>?</code>) is
+    * located at the same relative position in the where clause as before
+    * preprocessing.
+    * 
+    * @param   position
+    *          Zero-based index of the target substitution parameter function
+    *          in the original array.
+    * 
+    * @return  New, zero-based index of the target substitution parameter function 
+    *          in the original array, or <code>null</code> if the parameter was
+    *          inlined during preprocessing of the where clause.
+    */
+   Function<Object[], Object> getIndex(int position)
+   {
+      return (substParamFunctions == null ? null : substParamFunctions[position]);
+   }
+   
+   /**
+    * Method for retrieving the substitution parameter functions.
+    * 
+    * @return  The substitution parameter functions, or null if
+    *          no functions were assigned.
+    */
+   Function<Object[], Object>[] getSubstParamFunctions()
+   {
+      return substParamFunctions;
+   }
+   
+   /**
+    * Apply the substitution parameters lambdas based on the params given. The params 
+    * value is used just for already substituded parameters from the where clause,
+    * while the value of the subtituted parameters from the preprocessing are replaced
+    * by the value of the node annotation <code>extracted-value</code>. 
+    * 
+    * @param   params
+    *          Parameter array for lambda which selects the parameter value
+    *          for substituted parameters that were not created during
+    *          the preprocessing phase.
+    *          The parameter array is still required for the other lambdas,
+    *          but not used.
+    * 
+    * @return  Object representing the value of the argument.
+    */
+   Object[] getSubstParameters(Object[] params)
+   {
+      if (substParamFunctions == null)
+      {
+         return null;
+      }
+      
+      Object[] args = new Object[count];
+      for (int position = 0; position < substParamFunctions.length; position++)
+      {
+         if (substParamFunctions[position] != null)
+         {
+            args[position] = substParamFunctions[position].apply(params);
+         }
+      }
+      return args;
+   }
+}
\ No newline at end of file

=== modified file 'src/com/goldencode/p2j/persist/QueryComponent.java'
--- old/src/com/goldencode/p2j/persist/QueryComponent.java	2022-11-30 14:33:29 +0000
+++ new/src/com/goldencode/p2j/persist/QueryComponent.java	2023-02-09 11:40:53 +0000
@@ -32,6 +32,8 @@
 **     CA  20221130 Refactored the WHERE clause translation (when the bound and definition buffers 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.
+**     DDF 20230209 Refactored ParameterIndices into ParameterDiff. Extracted arguments also need to be
+**                  resolved. (refs: #7035)
 */
 
 /*
@@ -490,7 +492,7 @@
    protected Object[] getArgs()
    throws PersistenceException
    {
-      if (dfArgs != null && currentArgs == null)
+      if (currentArgs == null)
       {
          currentArgs = filterArgs(getHQLPreprocessor());
       }
@@ -594,29 +596,28 @@
       // in this case the processDynamicFilters() is not required to be called before calling
       // resolveArgs() because it was called before constructing the preprocessor
       Object[] intermediate = resolveArgs();
-      if (intermediate == null)
-      {
-         return null;
-      }
-      
-      ParameterIndices pi = preproc.getParameterIndices();
-      int len = pi.getCount();
-      Object[] filteredArgs = new Object[len];
-      for (int i = 0, count = 0; count < len; i++)
-      {
-         Integer nextIndex = pi.getIndex(i);
-         if (nextIndex == null)
-         {
-            // original substitution parameter was inlined into HQL by preprocessor;  skip it
-            continue;
-         }
-         
-         // Substitution placeholders may have been reordered if this
-         // statement was rewritten by the HQL preprocessor. Make sure
-         // we access the correct substitution parameter.
-         Object next = intermediate[nextIndex];
-         
-         filteredArgs[count++] = next;
+      ParameterDiff pd = preproc.getParameterDiff();
+      if (intermediate == null && pd == null)
+      {
+         return null;
+      }
+      
+      if (pd == null)
+      {
+         return intermediate;
+      }
+
+      Object[] filteredArgs = pd.getSubstParameters(intermediate);
+      
+      if (filteredArgs == null)
+      {
+         return null;
+      }
+      
+      for (int i = 0; i < filteredArgs.length; i++)
+      {
+         // Substitution parameters returned also need to be resolved.
+         filteredArgs[i] = resolveArg(filteredArgs[i]);
       }
       
       return filteredArgs;

=== modified file 'src/com/goldencode/p2j/persist/RandomAccessQuery.java'
--- old/src/com/goldencode/p2j/persist/RandomAccessQuery.java	2022-11-30 14:33:29 +0000
+++ new/src/com/goldencode/p2j/persist/RandomAccessQuery.java	2023-02-09 11:41:19 +0000
@@ -347,6 +347,8 @@
 **                           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.
+**     DDF 20230209          Refactoring ParameterIndices into ParameterDiff and added method for retrieving 
+**                           parameter values. (refs: #7035)
 */
 
 /*
@@ -4251,7 +4253,7 @@
       boolean debug = LOG.isLoggable(Level.FINE);
       
       DirtyShareContext dirtyContext = buffer.getDirtyContext();
-      ParameterIndices pi = activeBundle.getParameterIndices();
+      ParameterDiff pi = activeBundle.getParameterDiff();
       int origArgCount = (values == null ? 0 : values.length);
       int baseArgCount = (pi == null ? origArgCount : pi.getCount());
       
@@ -4338,26 +4340,12 @@
             // resolve these parameters.
             for (int j = 0, i = startIndex; i < baseArgCount; j++)
             {
-               int k = -1;
-               if (pi == null)
-               {
-                  k = j;
-               }
-               else
-               {
-                  Integer idx = pi.getIndex(j);
-                  if (idx == null)
-                  {
-                     // original substitution parameter was inlined into HQL by preprocessor;  skip it
-                     continue;
-                  }
-                  k = idx;
-               }
                
                // Substitution placeholders may have been reordered if this
                // statement was rewritten by the HQL preprocessor. Make sure
                // we access the correct substitution parameter.
-               Object next = values[k];
+               Function<Object[], Object> currentParameterFunction = pi.getIndex(j);
+               Object next = currentParameterFunction.apply(values);
                
                // store parameters in the base args array, resolving them first if necessary
                if (next instanceof Resolvable)

=== modified file 'src/com/goldencode/p2j/persist/RecordBuffer.java'
--- old/src/com/goldencode/p2j/persist/RecordBuffer.java	2023-01-23 18:18:57 +0000
+++ new/src/com/goldencode/p2j/persist/RecordBuffer.java	2023-02-09 11:41:45 +0000
@@ -1265,6 +1265,8 @@
 **                           runtime.
 **     CA  20230123          isPropertyIndexed must have protected access, because is accessed from static
 **                           methods. 
+**     DDF 20230209          Refactoring ParameterIndices into ParameterDiff and added method for retrieving 
+**                           parameter values. (refs: #7035)
 */
 
 /*
@@ -10743,28 +10745,9 @@
             
             if (args != null && args.length > 0)
             {
-               ParameterIndices pi = preproc.getParameterIndices();
-               int len = pi.getCount();
-               List<Object> argList = new ArrayList<>(len);
-               for (int i = 0, count = 0; count < len; i++)
-               {
-                  Integer nextIndex = pi.getIndex(i);
-                  if (nextIndex == null)
-                  {
-                     // original substitution parameter was inlined into HQL by preprocessor;
-                     // skip it.
-                     continue;
-                  }
-                  
-                  // substitution placeholders may have been reordered if this statement was
-                  // rewritten by the HQL preprocessor; make sure we access the correct
-                  // substitution parameter
-                  Object next = args[nextIndex];
-                  argList.add(next);
-                  
-                  count++;
-               }
-               args = argList.toArray();
+               ParameterDiff pd = preproc.getParameterDiff();
+               Object[] filteredArgs = pd.getSubstParameters(args);
+               args = filteredArgs;
             }
          }
          

=== modified file 'src/com/goldencode/p2j/persist/TemporaryBuffer.java'
--- old/src/com/goldencode/p2j/persist/TemporaryBuffer.java	2023-02-03 19:01:50 +0000
+++ new/src/com/goldencode/p2j/persist/TemporaryBuffer.java	2023-02-09 11:42:37 +0000
@@ -645,6 +645,8 @@
 **                           runtime.
 **     HC  20230118          Eliminated some of the uses of String.toUpperCase and/or
 **                           String.toLowerCase for performance.
+**     DDF 20230209          Refactoring ParameterIndices into ParameterDiff and added method for retrieving 
+**                           parameter values. (refs: #7035)
 */
 
 /*
@@ -7856,28 +7858,9 @@
          
          if (args != null && args.length > 0)
          {
-            ParameterIndices pi = preproc.getParameterIndices();
-            int len = pi.getCount();
-            List<Object> argList = new ArrayList<>(len);
-            for (int i = 0, count = 0; count < len; i++)
-            {
-               Integer nextIndex = pi.getIndex(i);
-               if (nextIndex == null)
-               {
-                  // original substitution parameter was inlined into HQL by preprocessor;
-                  // skip it.
-                  continue;
-               }
-               
-               // substitution placeholders may have been reordered if this statement was
-               // rewritten by the HQL preprocessor; make sure we access the correct
-               // substitution parameter
-               Object next = args[nextIndex];
-               argList.add(next);
-               
-               count++;
-            }
-            args = argList.toArray();
+            ParameterDiff pd = preproc.getParameterDiff();
+            Object[] filteredArgs = pd.getSubstParameters(args);
+            args = filteredArgs;
          }
       }
       String delStmt = query.toFinalExpression();

=== modified file 'src/com/goldencode/p2j/persist/orm/TempTableDataSourceProvider.java'
--- old/src/com/goldencode/p2j/persist/orm/TempTableDataSourceProvider.java	2022-10-11 15:08:04 +0000
+++ new/src/com/goldencode/p2j/persist/orm/TempTableDataSourceProvider.java	2023-02-09 11:24:29 +0000
@@ -79,6 +79,7 @@
 import java.util.logging.*;
 import javax.sql.DataSource;
 import com.goldencode.cache.*;
+import com.goldencode.p2j.jmx.*;
 import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.persist.dialect.*;
 import com.goldencode.p2j.persist.orm.types.*;
@@ -120,6 +121,14 @@
       /** Logger */
       private static final Logger LOG = LogHelper.getLogger(DataSourceImpl.class.getName());
       
+      private static final SimpleCounter PSCacheHits = SimpleCounter.getInstance(
+               FwdJMX.Counter.PSCacheHits
+      );
+      
+      private static final SimpleCounter PSCacheMisses = SimpleCounter.getInstance(
+               FwdJMX.Counter.PSCacheMisses
+      );
+      
       /**
        * Attempts to establish a connection with the data source of the {@code _temp} database.
        * Uses the credentials from the {@code DatabaseManager} configuration.
@@ -231,6 +240,7 @@
                      PreparedStatement raw = ctx.physicalConn.prepareStatement(sql, resultSetType, resultSetConcurrency);
                      ps = new UnclosablePreparedStatement(raw, false);
                      ctx.psCache.put(key, ps);
+                     PSCacheMisses.update(1L);
                   }
                   else if (ps.isCheckedOut())
                   {
@@ -243,6 +253,11 @@
                      {
                         LOG.log(Level.FINE, "Multiply-cached PreparedStatement: " + sql);
                      }
+                     PSCacheMisses.update(1L);
+                  }
+                  else 
+                  {
+                     PSCacheHits.update(1L);
                   }
                   
                   ps.checkOut();

