=== modified file 'src/com/goldencode/p2j/jmx/FwdJMX.java'
--- old/src/com/goldencode/p2j/jmx/FwdJMX.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/jmx/FwdJMX.java	2023-07-20 13:23:34 +0000
@@ -27,6 +27,7 @@
 **     OM  20230112 Added finer-granulation instrumentation for processing of dynamic queries. 
 ** 004 RAA 20230302 Removed OrmRedo and OrmNoUndoTrace.
 ** 005 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
+** 006 RAA 20230720 Added JMXs for measuring statistics about insert/update statements.
 */ 
 
 /*
@@ -544,6 +545,15 @@
    public static enum Counter 
    implements OptionalCounter
    {
+      /** How many Insert/Update statements are there */
+      InsertUpdateTotal,
+      /**
+       * How many Insert/Update statements have been executed successfully.
+       */
+      InsertUpdateSuccess,
+      /** How many Insert/Update statements were cured after failing the first time. */
+      InsertUpdateCured,
+      
       /** Incomning network traffic */
       NetworkReads,
       /** Outgoing network traffic */

=== modified file 'src/com/goldencode/p2j/persist/Persistence.java'
--- old/src/com/goldencode/p2j/persist/Persistence.java	2023-07-04 08:04:26 +0000
+++ new/src/com/goldencode/p2j/persist/Persistence.java	2023-07-20 13:23:34 +0000
@@ -592,8 +592,6 @@
 **                           Added DATE-RELATION to the 'scroll', 'list', an 'getQuery' parameters
 ** 176 IAS 20230410          Added 'old' methods w/o DataRelation argument.
 ** 177 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
-** 178 RAA 20230601          Added supportsValidateMode() function.
-**     RAA 20230704          Changed name of validateMode to allowDBUniqueCheck.
 */
 
 /*
@@ -3368,17 +3366,6 @@
    }
    
    /**
-    * Check if we can skip the index server-validation.
-    * 
-    * @return   {@code true} If the dialect has support for unique index validation, meaning we can skip 
-    *           this process in the server.
-    */
-   public boolean allowDBUniqueCheck()
-   {
-      return dialect.allowDBUniqueCheck() && temporary;
-   }
-   
-   /**
     * Initialize the helper objects to which this object delegates certain service requests.
     */
    protected void initializeInstance()

=== modified file 'src/com/goldencode/p2j/persist/TempTableHelper.java'
--- old/src/com/goldencode/p2j/persist/TempTableHelper.java	2023-06-19 11:52:59 +0000
+++ new/src/com/goldencode/p2j/persist/TempTableHelper.java	2023-07-20 13:23:34 +0000
@@ -60,8 +60,6 @@
 **     DDF 20230306          Removed usage of computed columns. Added a parameter to getSqlMappedType() 
 **                           (refs: #7108).
 ** 017 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
-** 018 RAA 20230601          The keyword SOFT_UNIQUE can appear when building the "create index" statement.
-**     RAA 20230619          Casted dialect to P2JH2Dialect when calling getCreateIndexString.
 */
 
 /*
@@ -453,7 +451,7 @@
          explicitIndexes.put(new CaseInsensitiveString(index.getName()), sqlIdxName);
          
          StringBuilder sb = new StringBuilder();
-         sb.append(((P2JH2Dialect)dialect).getCreateIndexString(false, index.isUnique() && !isDirty)).append(sqlIdxName)
+         sb.append(dialect.getCreateIndexString(false)).append(sqlIdxName)
            // to enable UNIQUE constraint on temp-table indexes, replace the above line with following:
            // sb.append(dialect.getCreateIndexString(index.isUnique() && !isDirty)).append(sqlIdxName)
            .append(" on ").append(dmo.getSqlTableName()).append(" (");
@@ -514,9 +512,9 @@
          // resource names are unique. The dirty temp-tables indexes do not need the suffix.
          implicitSqlIndexName = "idx_mpid__" + dmo.getSqlTableName() + getAdditionalIndexSuffix();
          implicitIndexName = null;
-         String sb = ((P2JH2Dialect)dialect).getCreateIndexString(false, !isDirty) + implicitSqlIndexName + 
-                     " on " + dmo.getSqlTableName() + " (" + ReservedProperty._MULTIPLEX.column + ", " + 
-                     Session.PK + " )" + suffix;
+         String sb = dialect.getCreateIndexString(false) + implicitSqlIndexName + " on " + dmo.getSqlTableName() +
+                     " (" + ReservedProperty._MULTIPLEX.column + ", " + Session.PK + " )" + 
+                     suffix;
          ddlCreateIndexes.add(sb);
          ddlDropIndexes.add(dialect.getDropIndexString(true, implicitSqlIndexName, dmo.getSqlTableName()) + 
                             createTemporaryTablePostfix);

=== modified file 'src/com/goldencode/p2j/persist/dialect/Dialect.java'
--- old/src/com/goldencode/p2j/persist/dialect/Dialect.java	2023-07-04 08:04:26 +0000
+++ new/src/com/goldencode/p2j/persist/dialect/Dialect.java	2023-07-20 13:23:34 +0000
@@ -109,10 +109,6 @@
 ** 031 DDF 20230330          Changed return value of getComputedColumnPrefix from null to empty string.
 ** 032 DDF 20230406          Removed isAutoRtrimAndCi. Added isAutoRtrim and isAutoCi methods.
 ** 033 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
-** 034 RAA 20230601          Added support for SOFT_UNIQUE keyword.
-**     RAA 20230619          There are two getCreateIndexString functions now, the second one being
-**                           responsible with handling SOFT_UNIQUE indexes.
-**     RAA 20230704          Changed name of validateMode to allowDBUniqueCheck.
 */
 
 /*
@@ -1392,22 +1388,6 @@
    }
    
    /**
-    * Returns the beginning of the DDL statement for creating an index.
-    * 
-    * @param   unique
-    *          {@code true} for unique indexes.
-    * @param   softUnique
-    *          {@code true} for soft-unique indexes.
-    * 
-    * @return  the beginning of the DDL statement for creating an index. 
-    */
-   public String getCreateIndexString(boolean unique, boolean softUnique) 
-   throws PersistenceException
-   {
-      throw new PersistenceException("soft_unique is not accessible from this dialect");
-   }
-   
-   /**
     * Obtain a DDL statement which drops a SQL index.
     *
     * @param   ifExists
@@ -1654,17 +1634,6 @@
    {
       return false;
    }
-   
-   /**
-    * Determine if the {@code Dialect} supports index validation (with the VALIDATE keyword).
-    * 
-    * @return  {@code false}
-    *          By default, VALIDATE is not supported.   
-    */
-   public boolean allowDBUniqueCheck()
-   {
-      return false;
-   }
 
    /**
     * Provides SQL to load sequences.

=== modified file 'src/com/goldencode/p2j/persist/dialect/P2JH2Dialect.java'
--- old/src/com/goldencode/p2j/persist/dialect/P2JH2Dialect.java	2023-07-04 08:04:26 +0000
+++ new/src/com/goldencode/p2j/persist/dialect/P2JH2Dialect.java	2023-07-20 13:23:34 +0000
@@ -120,9 +120,6 @@
 **                           insensitive in isCaseInsensitiveColumn because it is rarely used.
 ** 034 DDF 20230406          Removed isAutoRtrimAndCi. Added isAutoRtrim and isAutoCi methods.
 ** 035 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
-** 036 RAA 20230601          Added supportsValidateMode function.
-**     RAA 20230619          Overrided getCreateIndexString function that deals with SOFT_UNIQUE indexes.
-**     RAA 20230704          Changed name of validateMode to allowDBUniqueCheck.
 */
 
 /*
@@ -1774,44 +1771,6 @@
    }
    
    /**
-    * Determine if the {@code Dialect} supports index validation (with the VALIDATE keyword).
-    * 
-    * @return  {@code true}
-    *          H2 supports this type of validation.   
-    */
-   @Override
-   public boolean allowDBUniqueCheck()
-   {
-      return true;
-   }
-   
-   /**
-    * Returns the beginning of the DDL statement for creating an index.
-    * 
-    * @param   unique
-    *          {@code true} for unique indexes.
-    * @param   softUnique
-    *          {@code true} for soft-unique indexes.
-    * 
-    * @return  the beginning of the DDL statement for creating an index. 
-    */
-   @Override
-   public String getCreateIndexString(boolean unique, boolean softUnique)
-   {
-      if (unique)
-      {
-         return "create unique index ";
-      }
-      
-      if (softUnique)
-      {
-         return "create soft_unique index ";
-      }
-      
-      return "create index ";
-   }
-   
-   /**
     * Get dialect id.
     * 
     * @return  Dialect id.

=== modified file 'src/com/goldencode/p2j/persist/dialect/P2JSQLServer2008Dialect.java'
--- old/src/com/goldencode/p2j/persist/dialect/P2JSQLServer2008Dialect.java	2023-06-19 11:52:59 +0000
+++ new/src/com/goldencode/p2j/persist/dialect/P2JSQLServer2008Dialect.java	2023-07-20 13:23:34 +0000
@@ -46,8 +46,6 @@
 ** 016 RAA 20230208 Added parameter for getCreateTemporaryTablePostfix.
 **     DDF 20230306 Added a parameter to getSqlMappedType() (refs: #7108).
 ** 017 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
-** 018 RAA 20230601 Added support for the SOFT_UNIQUE keyword.
-**     RAA 20230619 Reverted the call of getCreateIndexString so that SOFT_UNIQUE does not get involved.
 */
 
 /*

=== modified file 'src/com/goldencode/p2j/persist/orm/Persister.java'
--- old/src/com/goldencode/p2j/persist/orm/Persister.java	2023-07-20 08:39:50 +0000
+++ new/src/com/goldencode/p2j/persist/orm/Persister.java	2023-07-20 13:23:34 +0000
@@ -22,12 +22,6 @@
 **                  used in insert/update or where predicate.
 **     CA  20230116 Increased updateCache size to 4096.
 ** 003 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
-** 004 RAA 20230601 Added support for the VALIDATE keyword.
-**     RAA 20230619 Split insert and update into two functions each, so dealing with VALIDATE is
-**                  more straight-forward.
-**     RAA 20230623 Instead of appending "validate" to the insert SQL, insertValidateSql is now used.
-**     RAA 20230704 Changed name of validateMode to allowDBUniqueCheck.
-**     RAA 20230719 In case of UNIQUE VIOLATION, the logging is now skipped in this class.
 */
 
 /*
@@ -405,37 +399,7 @@
    <T extends BaseRecord> int insert(T dmo)
    throws PersistenceException
    {
-      return insert(dmo, false);
-   }
-   
-   /**
-    * Insert the data of the given DMO into its corresponding primary table and any secondary
-    * tables in the database. This method must be called within a transaction for the data to be
-    * preserved. Scalar data is inserted first into the primary table, followed by normalized
-    * extent field data, if any, into any secondary tables. Batch execution is used if supported
-    * by the JDBC driver.
-    * 
-    * The Insert operation can be executed normally (having the server validate the index in use), 
-    * or with the help of the {@code validate} and {@code soft_unique} keywords, which, when placed,
-    * notify H2 that validation has not been done and it should do that process itself. This works only
-    * with temporary databases.
-    * 
-    * @param   dmo
-    *          DMO instance whose data is to be inserted into the database.
-    * @param   allowDBUniqueCheck
-    *          Flag (only for temporary databases) that marks if the index server-validation was skipped and
-    *          it should be done by H2.
-    * 
-    * @return  The count of records (primary and secondary) successfully inserted into the
-    *          database.
-    * 
-    * @throws  PersistenceException
-    *          if an error occurs accessing the database.
-    */
-   <T extends BaseRecord> int insert(T dmo, boolean allowDBUniqueCheck)
-   throws PersistenceException
-   {
-      PreparedStatement[] ps = prepareInsertStatements(dmo, allowDBUniqueCheck);
+      PreparedStatement[] ps = prepareInsertStatements(dmo);
       
       try
       {
@@ -564,30 +528,6 @@
    <T extends BaseRecord> void update(T dmo)
    throws PersistenceException
    {
-      update(dmo, false);
-   }
-   
-   /**
-    * Saves changes of the {@code dmo} by executing one or more {@code UPDATE} statements.
-    * 
-    * The Update operation can be executed normally (having the server validate the index in use), 
-    * or with the help of the {@code validate} and {@code soft_unique} keywords, which, when placed,
-    * notify H2 that validation has not been done and it should do that process itself. This works only
-    * with temporary databases.
-    * 
-    * @param   dmo
-    *          The object whose changes will be flushed to database.
-    * @param   <T>
-    *          The type(class) of the Record.
-    * @param   allowDBUniqueCheck
-    *          Flag (only for temporary databases) that marks whether index server-validation was skipped.
-    *
-    * @throws  PersistenceException
-    *          In case of any issue encountered in the process.
-    */
-   <T extends BaseRecord> void update(T dmo, boolean allowDBUniqueCheck)
-   throws PersistenceException
-   {
       BitSet mods = dmo.getDirtyProps();
       if (mods.isEmpty())
       {
@@ -698,11 +638,6 @@
                   sqlBuilder.append(" and list__index=?");
                   sqlPar.add(ReservedProperty.ID_LIST__INDEX);  // extIdx
                }
-               
-               if (allowDBUniqueCheck)
-               {
-                  sqlBuilder.append(" validate");
-               }
             }
             
             Map<Integer, String> sqlStrs = new LinkedHashMap<>();
@@ -802,31 +737,9 @@
          catch (SQLException exc)
          {
             String msg = "Failed to update dmo " + dmo.getClass().getName();
-            
             if (LOG.isLoggable(Level.WARNING))
             {
-               // Skip the logging of UNIQUE VIOLATION - unique_violation exception because this might be just a
-               // failed attempt to update a record with validateMode enabled, meaning that no server-side index
-               // validation took place. If this is not the case, logging will happen later anyway.
-               Throwable cause = exc;
-               while (cause != null                   &&
-                     !(cause instanceof SQLException) &&
-                     cause != cause.getCause())
-               {
-                  cause = cause.getCause();
-               }
-               while (cause != null && cause instanceof SQLException)
-               {
-                  SQLException sqle = (SQLException) cause;
-                  String sqlState = sqle.getSQLState();
-                  if (sqlState != null && !"23505".equals(sqlState)) // UNIQUE VIOLATION - unique_violation
-                  {
-                     // Log this error since it is not a UNIQUE VIOLATION
-                     LOG.log(Level.WARNING, "Error during update", sqle);
-                  }
-                  
-                  cause = sqle.getCause();
-               }
+               LOG.log(Level.WARNING, msg, exc);
             }
             
             throw new PersistenceException(msg, exc);
@@ -1013,28 +926,7 @@
       {
          if (LOG.isLoggable(Level.WARNING))
          {
-            // Skip the logging of UNIQUE VIOLATION - unique_violation exception because this might be just
-            // a failed attempt to insert a record with validateMode enabled, meaning that no server-side index
-            // validation took place. If this is not the case, logging will happen later anyway.
-            Throwable cause = exc;
-            while (cause != null                   &&
-                  !(cause instanceof SQLException) &&
-                  cause != cause.getCause())
-            {
-               cause = cause.getCause();
-            }
-            while (cause != null && cause instanceof SQLException)
-            {
-               SQLException sqle = (SQLException) cause;
-               String sqlState = sqle.getSQLState();
-               if (sqlState != null && !"23505".equals(sqlState)) // UNIQUE VIOLATION - unique_violation
-               {
-                  // Log this error since it is not a UNIQUE VIOLATION
-                  LOG.log(Level.WARNING, "Error during insert", sqle);
-               }
-               
-               cause = sqle.getCause();
-            }
+            LOG.log(Level.WARNING, "Error during insert", exc);
          }
          
          throw new PersistenceException(exc);
@@ -1060,34 +952,8 @@
    private <T extends BaseRecord> PreparedStatement[] prepareInsertStatements(T dmo)
    throws PersistenceException
    {
-      return prepareInsertStatements(dmo, false);
-   }
-   
-   /**
-    * Prepare the insert statements for the table(s) associated with the given DMO. There will
-    * be one statement for the primary table, plus one per secondary table for normalized extent
-    * fields.
-    * 
-    * @param   dmo
-    *          Data model object whose type is used to determine the insert statements to be
-    *          prepared.
-    * @param   allowDBUniqueCheck
-    *          Flag that marks whether index server-validation was skipped.
-    * 
-    * @return  An array of prepared insert statements, one for the primary table and the rest
-    *          (if any) for the secondary, normalized, extent field tables, in ascending order
-    *          of extent size.
-    * 
-    * @throws  PersistenceException
-    *          if there is an error preparing the statements.
-    */
-   private <T extends BaseRecord> PreparedStatement[] prepareInsertStatements(T dmo, 
-                                                                              boolean allowDBUniqueCheck)
-   throws PersistenceException
-   {
       RecordMeta recMeta = dmo._recordMeta();
       String[] insertSql = recMeta.insertSql;
-      String[] insertValidateSql = recMeta.insertValidateSql;
       int tables = insertSql.length;
       PreparedStatement[] ps = new PreparedStatement[tables];
       
@@ -1097,14 +963,7 @@
          
          for (int i = 0; i < tables; i++)
          {
-            if (allowDBUniqueCheck && insertValidateSql[i] != null)
-            {
-               ps[i] = conn.prepareStatement(insertValidateSql[i]);
-            }
-            else
-            {
-               ps[i] = conn.prepareStatement(insertSql[i]);
-            }
+            ps[i] = conn.prepareStatement(insertSql[i]);
          }
       }
       catch (SQLException exc)

=== modified file 'src/com/goldencode/p2j/persist/orm/RecordMeta.java'
--- old/src/com/goldencode/p2j/persist/orm/RecordMeta.java	2023-06-23 13:38:50 +0000
+++ new/src/com/goldencode/p2j/persist/orm/RecordMeta.java	2023-07-20 13:23:34 +0000
@@ -27,7 +27,6 @@
 **     CA  20221114 Added a helper array to keep the index of the normalized extent properties in the DMO
 **                  'props' array.
 ** 003 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
-** 004 RAA 20230623 Added insertValidateSql.
 */
 
 /*
@@ -154,14 +153,6 @@
    /** SQL insert statement(s) which insert a single DMO's data into the database */
    final String[] insertSql;
    
-   /**
-    * SQL insert statement(s) which insert a single DMO's data into the database.
-    * The keyword "validate" is included at the end. Pairing it with "soft_unique" would tell the server
-    * that no index validation should be done because H2 will take care of it.
-    * At the moment, this works only for temporary databases.
-    */
-   final String[] insertValidateSql;
-   
    /** SQL delete statement(s) which delete a single DMO's data from the database */
    final String[] deleteSql;
    
@@ -259,25 +250,6 @@
       insertSql = Persister.composeInsertStatements(tables[0], props, temporary);
       deleteSql = Persister.composeDeleteStatements(tables);
       
-      // compose the insert SQLs, but with the "validate" keyword at the end.
-      // these SQLs are only for temporary databases and, paired with "soft_unique", notify the server
-      // that no index validation is required because H2 will take care of it.
-      if (temporary)
-      {
-         int insertSqlLength = insertSql.length;
-         insertValidateSql = new String[insertSqlLength];
-         for (int i = 0; i < insertSqlLength; i++)
-         {
-            insertValidateSql[i] = insertSql[i] + " validate";
-         }
-      }
-      else
-      {
-         // making it null is fine because this field will be accessed only when we are dealing with a
-         // temporary database.
-         insertValidateSql = null;
-      }
-      
       nonNullableProps = new BitSet(props.length);
       
       // collect index data for record flushing purposes

=== modified file 'src/com/goldencode/p2j/persist/orm/Session.java'
--- old/src/com/goldencode/p2j/persist/orm/Session.java	2023-07-04 08:04:26 +0000
+++ new/src/com/goldencode/p2j/persist/orm/Session.java	2023-07-20 13:23:34 +0000
@@ -43,8 +43,6 @@
 ** 005 SR  20230519 Changed cacheExpired() so now the expired dmo is also deregistered from DmoVersioning.
 **     SR  20230522 Moved the deregister operation above the state check so it will happen regardless of 
 **                  how we exit the method.
-** 006 RAA 20230601 Added support for VALIDATE keyword.
-**     RAA 20230704 Changed name of validateMode to allowDBUniqueCheck.
 */
 
 /*
@@ -707,37 +705,6 @@
    public <T extends BaseRecord> void save(T dmo, boolean updateDmoState)
    throws PersistenceException
    {
-      save(dmo, updateDmoState, false);
-   }
-   
-   /**
-    * Saves a {@code dmo} to database. If so requested, its internal state information is updated.
-    * 
-    * The Save operation can be executed normally (having the server validate the index in use), 
-    * or with the help of the {@code validate} and {@code soft_unique} keywords, which, when placed,
-    * notify H2 that validation has not been done and it should do that process itself. This works only
-    * with temporary databases.
-    * 
-    * @param   dmo
-    *          The record to be saved.
-    * @param   updateDmoState
-    *          If {@code true} its internal state information is updated. This is the standard
-    *          call. When the method is called for validation, use {@code false} as the database
-    *          operation will be rolled back anyway, so the state must not be altered.
-    * @param   allowDBUniqueCheck
-    *          Flag (only for temporary databases) that marks whether index server-validation was skipped.
-    * @param   <T>
-    *          The compile type of the record.
-    *
-    * @throws  PersistenceException
-    *          When an exception is encountered. Must be analyzed as the cause might be expected
-    *          (ex: when validating a record).
-    * @throws  StaleRecordException
-    *          if the record being saved is stale.
-    */
-   public <T extends BaseRecord> void save(T dmo, boolean updateDmoState, boolean allowDBUniqueCheck)
-   throws PersistenceException
-   {
       checkClosed();
       
       checkTx();
@@ -784,12 +751,12 @@
       // decide what to do with the record
       if (wasNew)
       {
-         persister.insert(dmo, allowDBUniqueCheck);
+         persister.insert(dmo);
          wasDirty = true;
       }
       else if (wasDirty)
       {
-         persister.update(dmo, allowDBUniqueCheck);
+         persister.update(dmo);
          wasDirty = true;
       }
       
@@ -1638,7 +1605,7 @@
             // record was not saved since it was added to cache
             try
             {
-               persister.update(dmo, false);
+               persister.update(dmo);
             }
             catch (PersistenceException e)
             {

=== modified file 'src/com/goldencode/p2j/persist/orm/Validation.java'
--- old/src/com/goldencode/p2j/persist/orm/Validation.java	2023-07-04 11:11:35 +0000
+++ new/src/com/goldencode/p2j/persist/orm/Validation.java	2023-07-20 13:29:12 +0000
@@ -35,10 +35,7 @@
 **                  used in insert/update or where predicate.
 **     CA  20230110 getDirtyIndices and getUnvalidatedIndices now return null instead of an empty BitSet, when
 **                  no indices are found.
-** 003 RAA 20230601 Added support for VALIDATE keyword.
-**     RAA 20230619 Split flush function into two for more straight-forward use when dealing with VALIDATE.
-**     RAA 20230704 Changed name of validateMode to allowDBUniqueCheck.
-**     RAA 20230704 In case a flush with VALIDATE fails, but it gets cured, a second flush is now attempted.
+** 003 RAA 20230720 Added JMX for counting cured insert/update statements.    
 */
 
 /*
@@ -100,11 +97,11 @@
 import java.sql.*;
 import java.util.*;
 import com.goldencode.p2j.directory.*;
+import com.goldencode.p2j.jmx.*;
 import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.persist.dialect.*;
 import com.goldencode.p2j.persist.orm.types.*;
 import com.goldencode.p2j.util.*;
-import com.goldencode.p2j.util.logging.CentralLogger;
 
 /**
  * An object which performs validation of the state of a DMO, and optionally flushes the DMO's
@@ -121,9 +118,6 @@
 public class Validation
 implements DmoState
 {
-   /** Logger */
-   private static final CentralLogger LOG = CentralLogger.get(Validation.class);
-   
    /** Record buffer requesting validation */
    private final RecordBuffer buffer;
    
@@ -160,6 +154,18 @@
    /** Flag indicating the record was flushed to the database (either newly inserted or updated) */
    private boolean flushed = false;
    
+   /** Counter for how many Inserts/Updates are executed. */
+   private static final SimpleCounter INSERT_UPDATE_TOTAL = 
+            SimpleCounter.getInstance(FwdJMX.Counter.InsertUpdateTotal);
+   
+   /** Counter for how many Inserts/Updates are successfully executed. */
+   private static final SimpleCounter INSERT_UPDATE_SUCCESS = 
+            SimpleCounter.getInstance(FwdJMX.Counter.InsertUpdateSuccess);
+   
+   /** Counter for how many Inserts/Updates with VALIDATE are executed. */
+   private static final SimpleCounter INSERT_UPDATE_CURED = 
+            SimpleCounter.getInstance(FwdJMX.Counter.InsertUpdateCured);
+   
    /**
     * The maximum permitted size for all combined fields of an index. By default this
     * is equal to Progress version 10.x limit of 1971 bytes.
@@ -438,22 +444,26 @@
       {
          // validate against uncommitted transaction data; no-op for temp-table
          token = uniqueTrackerCtx.lockAndChange(uniqueTracker, dmo, bufferName);
-         boolean willFlush = flush || buffer.isAutoCommit();
-         // allowDBUniqueCheck can be set to true only when dealing with temporary databases
-         // allowDBUniqueCheck is used to determine if H2 will do index validation or should this be done
-         // normally (server-side validation)
-         boolean allowDBUniqueCheck = willFlush && canSkipServerSideUniqueCheck();
          
-         if ((multiplex != null || !flush) && !allowDBUniqueCheck)
+         if (multiplex != null || !flush)
          {
+            boolean countJMX = flush || buffer.isAutoCommit();
+            if (countJMX)
+            {
+               INSERT_UPDATE_TOTAL.update(1);
+            }
             // validate by executing unique index queries
             validateUniqueByQuery(check);
+            if (countJMX)
+            {
+               INSERT_UPDATE_SUCCESS.update(1);
+            }
          }
          
-         if (willFlush)
+         if (flush || buffer.isAutoCommit())
          {
             // validation will be done by the database as part of the flush (for persistent tables)
-            flush(allowDBUniqueCheck);
+            flush();
          }
          
          // validation was performed and succeeded; changes may have been persisted to database;
@@ -508,7 +518,7 @@
    
    /**
     * Flush the record being validated to the database.
-    *          
+    * 
     * @throws  PersistenceException
     *          if there is any non-validation database error.
     * @throws  ValidationException
@@ -522,33 +532,6 @@
           ValidationException,
           StaleRecordException
    {
-      flush(false);
-   }
-   
-   /**
-    * Flush the record being validated to the database.
-    * 
-    * The Flush operation can be executed normally (having the server validate the index in use), 
-    * or with the help of the {@code validate} and {@code soft_unique} keywords, which, when placed,
-    * notify H2 that validation has not been done and it should do that process itself. This works only
-    * with temporary databases.
-    * 
-    * @param   allowDBUniqueCheck
-    *          Flag (only for temporary databases) that marks whether index server-validation was skipped.
-    *          
-    * @throws  PersistenceException
-    *          if there is any non-validation database error.
-    * @throws  ValidationException
-    *          if there is an error validating a unique or not-null constraint.
-    * @throws  StaleRecordException
-    *          if the record being flushed is stale. A stale record should not get this far under normal
-    *          circumstances.
-    */
-   private void flush(boolean allowDBUniqueCheck)
-   throws PersistenceException,
-          ValidationException,
-          StaleRecordException
-   {
       // if not already fired, do it now. When the trigger is fired the oldDMO is reset, preventing double firing
       buffer.maybeFireWriteTrigger();
       
@@ -613,7 +596,50 @@
       
       try
       {
-         flushImpl(session, sp, fullTx, wasNew, allowDBUniqueCheck);
+         // this will perform the appropriate insert or update, based on the state of the DMO
+         session.save(dmo, true);
+         if (wasNew)
+         {
+            buffer.recordInserted();
+         }
+      }
+      catch (PersistenceException exc)
+      {
+         // validation failed, or we had a different error; roll back to savepoint
+         // TODO: is this always safe (or even possible) in the event of a non-unique constraint
+         //       related database error?
+         if (sp != null)
+         {
+            session.rollbackSavepoint(sp);
+         }
+         else if (fullTx)
+         {
+            session.rollback();
+         }
+         
+         // go up the exception chain and look for a SQL exception:
+         Throwable cause = exc.getCause();
+         while (cause != null                   &&
+               !(cause instanceof SQLException) &&
+               cause != cause.getCause())
+         {
+            cause = cause.getCause();
+         }
+         if (cause instanceof SQLException)
+         {
+            SQLException sqle = (SQLException) cause;
+            if ("23505".equals(sqle.getSQLState())) // UNIQUE VIOLATION - unique_violation
+            {
+               // if the right index is found, this method will not return normally, instead it will
+               // throw a specific exception
+               validateUniqueByQuery(null);
+            }
+         }
+         
+         // if the exception could not be identified as a SQL UNIQUE VIOLATION exception or the 
+         // generateUniqueIndexesCondition() method could not identify the right index to compose the
+         // P4GL error message, we let the caller to handle the exception
+         throw new ValidationException(exc.getMessage(), exc);
       }
       finally
       {
@@ -639,102 +665,6 @@
    }
    
    /**
-    * The implementation of the flushing process to the database.
-    * In case a flush with VALIDATE fails, the intention is to see if it can be "cured". If that is the case,
-    * a normal flush will be attempted.
-    * 
-    * @param   session
-    *          The session in use.
-    * @param   sp
-    *          The savepoint used in case of failure.
-    * @param   fullTx
-    *          Flag that marks if this is a full transaction or not.
-    * @param   wasNew
-    *          Is the DMO marked with NEW?
-    * @param   allowDBUniqueCheck
-    *          Flag (only for temporary databases) that marks whether index server-validation was skipped.
-    *          
-    * @throws  PersistenceException
-    *          if there is any non-validation database error.
-    * @throws  ValidationException
-    *          if there is an error validating a unique or not-null constraint.
-    * @throws  StaleRecordException
-    *          if the record being flushed is stale. A stale record should not get this far under normal
-    *          circumstances.
-    */
-   private void flushImpl(Session session, 
-                          Savepoint sp, 
-                          boolean fullTx, 
-                          boolean wasNew, 
-                          boolean allowDBUniqueCheck)
-   throws PersistenceException,
-   ValidationException,
-   StaleRecordException
-   {
-      try
-      {
-         // this will perform the appropriate insert or update, based on the state of the DMO
-         session.save(dmo, true, allowDBUniqueCheck);
-         if (wasNew)
-         {
-            buffer.recordInserted();
-         }
-      }
-      catch (PersistenceException exc)
-      {
-         // validation failed, or we had a different error; roll back to savepoint
-         // TODO: is this always safe (or even possible) in the event of a non-unique constraint
-         //       related database error?
-         if (sp != null)
-         {
-            session.rollbackSavepoint(sp);
-         }
-         else if (fullTx)
-         {
-            session.rollback();
-         }
-         
-         // go up the exception chain and look for a SQL exception:
-         Throwable cause = exc.getCause();
-         while (cause != null                   &&
-               !(cause instanceof SQLException) &&
-               cause != cause.getCause())
-         {
-            cause = cause.getCause();
-         }
-         while (cause instanceof SQLException)
-         {
-            SQLException sqle = (SQLException) cause;
-            if ("23505".equals(sqle.getSQLState())) // UNIQUE VIOLATION - unique_violation
-            {
-               // if the right index is found, this method will not return normally, instead it will
-               // throw a specific exception
-               validateUniqueByQuery(null);
-               if (allowDBUniqueCheck)
-               {
-                  // We were not able to execute the query with VALIDATE, but a "cure" occurred,
-                  // meaning that we can try executing the query normally.
-                  LOG.severe("Flush with VALIDATE failed due to " + sqle);
-                  flushImpl(session, sp, fullTx, wasNew, false);
-                  return;
-               }
-               break;
-            }
-            
-            // if this exception comes from H2, then we need to move to the inner exception (a.k.a. 
-            // JdbcSQLIntegrityConstraintViolationException), because the outer one (SQLException) does not
-            // have the SQLState set accordingly.
-            cause = sqle.getCause();
-         }
-         
-         // if the exception could not be identified as a SQL UNIQUE VIOLATION exception or the 
-         // generateUniqueIndexesCondition() method could not identify the right index to compose the
-         // P4GL error message, we let the caller to handle the exception
-         throw new ValidationException(exc.getMessage(), exc);
-      }
-   }
-   
-   /**
     * Get the state of the {@link #flushed} flag.
     * 
     * @return   See above.
@@ -1188,6 +1118,10 @@
             {
                throw new ValidationException(getFailUniqueIndexMessage(uIndex, bufferName, dmo), 132);
             }
+            else
+            {
+               INSERT_UPDATE_CURED.update(1);
+            }
             
             // if we exit the loop normally, all violations are cured by in-memory changes (which may yet
             // have to be validated and flushed themselves)
@@ -1264,17 +1198,6 @@
    }
    
    /**
-    * Function that checks if VALIDATE keyword can be used with this statement.
-    * 
-    * @return  boolean
-    *          {@code true} if there exists support for the VALIDATE keyword.
-    */
-   private boolean canSkipServerSideUniqueCheck()
-   {
-      return buffer != null && buffer.getPersistence().allowDBUniqueCheck();
-   }
-   
-   /**
     * Verify a unique constraint violation detected by querying the database, by confirming that the record
     * found to be causing the constraint violation does not have unflushed changes associated with it in
     * memory, awaiting its own validation and flush operation. If a modified, unflushed copy of the suspect

