=== modified file 'src/com/goldencode/p2j/persist/ConnectionManager.java'
--- src/com/goldencode/p2j/persist/ConnectionManager.java	2020-09-07 22:11:46 +0000
+++ src/com/goldencode/p2j/persist/ConnectionManager.java	2021-04-02 11:59:31 +0000
@@ -2,7 +2,7 @@
 ** Module   : ConnectionManager.java
 ** Abstract : Provides services for logical database connections and aliases
 **
-** Copyright (c) 2005-2019, Golden Code Development Corporation.
+** Copyright (c) 2005-2021, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
 ** 001 ECF 20051202   @23667 Created initial version. Provides services to
@@ -140,6 +140,7 @@
 ** 055 IAS 20200212          Rework CONNECT and DBPARAM
 ** 056 ECF 20200419          Removed Hibernate dependencies.
 ** 057 IAS 20200819          Added table CRUD ops statistics support
+** 058 IAS 20210401          Fixed UserTableStatUpdater-related logic
 */
 
 /*
@@ -205,6 +206,7 @@
 import java.text.*;
 import java.util.*;
 import java.util.Map.*;
+import java.util.concurrent.*;
 import java.util.function.*;
 import java.util.logging.*;
 import java.util.stream.*;
@@ -382,6 +384,10 @@
       boolean inUse = MetadataManager.inUse(MetadataManager._CONNECT) ||
                       MetadataManager.inUse(MetadataManager._MYCONNECTION);
       metaListeners = inUse ? new HashMap<>() : null;
+      if (MetadataManager.inUse(MetadataManager._USERTABLESTAT))
+      {
+         tableStatUpdaters = new ConcurrentHashMap<>();
+      }
    
 /* Temporary disable _UserTableStat support
       // determine whether we need to initialize a table statistics
@@ -2302,6 +2308,17 @@
    }
    
    /**
+    * Get <code>UserTableStatUpdater</code> object for the database
+    * @param id
+    *        database id
+    * @return <code>UserTableStatUpdater</code> object for the database
+    */
+   public static UserTableStatUpdater tableStatUpdater(String id)
+   {
+      return tableStatUpdaters == null ? null : tableStatUpdaters.get(id);
+   }
+
+   /**
     * Initialize a metaschema database by initializing a connection listener and storing it for
     * later use.
     * <p>

=== modified file 'src/com/goldencode/p2j/persist/P2JIndex.java'
--- src/com/goldencode/p2j/persist/P2JIndex.java	2021-02-20 16:18:22 +0000
+++ src/com/goldencode/p2j/persist/P2JIndex.java	2021-04-08 14:50:32 +0000
@@ -25,6 +25,7 @@
 ** 014 OM  20200925          Separated namespaces for index components.
 ** 015 IAS 20201224          Added word tables support
 **     IAS 20210219          Word tables support for custom extents
+**     IAS 20210408          Added copy c'tor with table name replacement.
 */
 
 /*
@@ -240,6 +241,26 @@
    }
    
    /**
+    * Copy constructor with table name replacement.
+    * 
+    * @param   index
+    *          Instance to copy.
+    * @param   tableName
+    *          table name replacement.
+    */
+   public P2JIndex(P2JIndex index, String tableName)
+   {
+      this(tableName, index.name, index.unique, index.primary, index.word);
+      
+      for (P2JIndexComponent comp : index.components)
+      {
+         components.add(new P2JIndexComponent(comp));
+      }
+      
+      compact();
+   }
+
+   /**
     * Given a list of indexes, identify the minimal set of unique indexes
     * which define the least common denominator, unique constraints for a
     * table. This will be the set of unique indexes which are least specific

=== modified file 'src/com/goldencode/p2j/persist/PreselectQuery.java'
--- src/com/goldencode/p2j/persist/PreselectQuery.java	2021-03-10 12:42:16 +0000
+++ src/com/goldencode/p2j/persist/PreselectQuery.java	2021-04-02 12:01:55 +0000
@@ -565,6 +565,7 @@
 **                           _multiplex sort component matches the index direction.
 **     OM  20201120          Adjusted error messages.
 **     CA  20210310          A dynamic predicate must set the default lock to NONE, instead of SHARE.
+**    IAS  29210401          Update _UserTableStat "read" counter
 */
 
 /*
@@ -630,6 +631,7 @@
 import com.goldencode.p2j.persist.dirty.*;
 import com.goldencode.p2j.persist.event.*;
 import com.goldencode.p2j.persist.lock.*;
+import com.goldencode.p2j.persist.meta.*;
 import com.goldencode.p2j.persist.orm.*;
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.util.LogHelper;
@@ -5760,7 +5762,7 @@
                      try
                      {
                         dmo = p.load(buffer.getDMOImplementationClass(), id, lt, timeout, updateLock);
-                        
+                       
                         break;
                      }
                      catch (LockTimeoutException exc)
@@ -5813,6 +5815,10 @@
                   dmo = p.load(buffer.getDMOImplementationClass(), id, lt, 0L, updateLock);
                }
             }
+            if (dmo != null)
+            {
+               UserTableStatUpdater.retrieve(persistence.getDatabase(), buffer);
+            }
             
             if (LOG_FINE)
             {

=== modified file 'src/com/goldencode/p2j/persist/RandomAccessQuery.java'
--- src/com/goldencode/p2j/persist/RandomAccessQuery.java	2021-03-10 12:42:16 +0000
+++ src/com/goldencode/p2j/persist/RandomAccessQuery.java	2021-04-08 11:47:16 +0000
@@ -330,6 +330,8 @@
 **     CA  20200924          Replaced Method.invoke with ReflectASM.
 **     OM  20201120          Added javadoc note about a possible incorrect logged error.
 **     CA  20210310          A dynamic predicate must set the default lock to NONE, instead of SHARE.
+**    IAS  29210401          Update _UserTableStat "read" counter, flash it before access
+**    IAS  29210402          Do not use <code>ffCache</code> for meta tables
 */
 
 /*
@@ -395,6 +397,8 @@
 import com.goldencode.p2j.persist.dirty.*;
 import com.goldencode.p2j.persist.event.*;
 import com.goldencode.p2j.persist.lock.*;
+import com.goldencode.p2j.persist.meta.*;
+import com.goldencode.p2j.persist.meta.MetadataManager.*;
 import com.goldencode.p2j.persist.orm.*;
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.util.LogHelper;
@@ -1262,6 +1266,10 @@
       buffer.initialize();
       
       DmoMeta dmoMeta = buffer.getDmoInfo();
+      if (dmoMeta.isVST(SystemTable._UserTableStat))
+      {
+         UserTableStatUpdater.persist(buffer.getDatabase().getId());
+      }
       int idxCount = dmoMeta.getIndexCount(true) + dmoMeta.getIndexCount(false);
       
       if (idxCount > 0)
@@ -1329,7 +1337,7 @@
       this.args = args;
       this.dfArgs = args;
       this.indexInfo = indexInfo;
-      if (index != 0)
+      if (!dmoMeta.isMeta() && index != 0)
       {
          this.ffCache = FastFindCache.getInstance(buffer.isTemporary());
       }
@@ -4003,6 +4011,10 @@
          ErrorManager.recordOrThrowError(exc);
       }
       
+      if (dmo != null)
+      {
+         UserTableStatUpdater.retrieve(buffer.getPersistence().getDatabase(), buffer);
+      }
       return dmo;
    }
    
@@ -4652,6 +4664,10 @@
             try
             {
                dmo = persistence.load(buffer.getDMOImplementationClass(), id, lockType, 0L, updateLock);
+               if (dmo != null)
+               {
+                  UserTableStatUpdater.retrieve(persistence.getDatabase(), buffer);
+               }
             }
             catch (MissingRecordException exc)
             {

=== modified file 'src/com/goldencode/p2j/persist/dialect/H2Helper.java'
--- src/com/goldencode/p2j/persist/dialect/H2Helper.java	2021-03-19 12:14:26 +0000
+++ src/com/goldencode/p2j/persist/dialect/H2Helper.java	2021-04-08 11:48:26 +0000
@@ -152,10 +152,12 @@
     *          Database name to be embedded in the connection URL.
     * @param   settings
     *          Object into which to store properties.
-    * 
+    * @param   avoidLogging 
+    *          Flag indicating tables must be NO-UNDO
     * @return  The populated properties object (same object instance as {@code props}).
     */
-   public static Settings setCommonInMemoryProperties(String database, Settings settings, boolean avoidLogging)
+   public static Settings setCommonInMemoryProperties(String database, Settings settings, 
+         boolean avoidLogging)
    {
       boolean inMem = "jdbc:h2:mem:".equals(JDBC_PREFIX);
       StringBuilder url = new StringBuilder();

=== modified file 'src/com/goldencode/p2j/persist/h2/UpdatableMyConnectionView.java'
--- src/com/goldencode/p2j/persist/h2/UpdatableMyConnectionView.java	2021-04-08 11:50:54 +0000
+++ src/com/goldencode/p2j/persist/h2/UpdatableMyConnectionView.java	2021-04-08 14:42:47 +0000
@@ -122,7 +122,7 @@
    public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before,
            int type) throws SQLException {
        prepDelete = conn.prepareStatement(
-             "DELETE FROM meta_myconnection_ WHERE RECID = ? AND myconn_userid = fwdSession()"
+             "DELETE FROM meta_myconnection_ WHERE RECID = ? AND myconn_userid = fwdSession_1()"
        );
        // INSERT and UPDATE triggers should return the FINAL values of the row.
        // Table TEST_TABLE has a generated column, so the FINAL row can be

=== modified file 'src/com/goldencode/p2j/persist/meta/ConnectTableUpdater.java'
--- src/com/goldencode/p2j/persist/meta/ConnectTableUpdater.java	2020-09-24 17:51:18 +0000
+++ src/com/goldencode/p2j/persist/meta/ConnectTableUpdater.java	2021-04-02 14:08:04 +0000
@@ -2,7 +2,7 @@
 ** Module   : ConnectTableUpdater.java
 ** Abstract : Manages metadata connect records for a particular, primary database
 **
-** Copyright (c) 2015-2020, Golden Code Development Corporation.
+** Copyright (c) 2015-2021, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description----------------------------------
 ** 001 ECF 20150111 Created initial version.
@@ -12,6 +12,7 @@
 ** 005 ECF 20200906 New ORM implementation.
 ** 006 IAS 20200819 Added support for additional _Connect table fields.
 ** 007 CA  20200924 Replaced Method.invoke with ReflectASM.
+** 008 IAS 20210401 Do not update MyConnection on server startup.
 */
 
 /*
@@ -221,7 +222,7 @@
     */
    public void databaseConnected(int64 connectID)
    {
-      if (dmoMyconnClass == null || dmoConnectClass == null)
+      if (dmoMyconnClass == null || dmoConnectClass == null || !Persistence.isActive())
       {
          return;
       }

=== modified file 'src/com/goldencode/p2j/persist/meta/MetaTableWrapper.java'
--- src/com/goldencode/p2j/persist/meta/MetaTableWrapper.java	2020-09-24 17:51:18 +0000
+++ src/com/goldencode/p2j/persist/meta/MetaTableWrapper.java	2021-04-02 12:07:49 +0000
@@ -2,11 +2,12 @@
 ** Module   : MetaTableWrapper.java
 ** Abstract : Wraps data for operations with meta table
 **
-** Copyright (c) 2013-2020, Golden Code Development Corporation.
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description----------------------------------
 ** 001 IAS 20200819 Created initial version.
 ** 002 CA  20200924 Replaced Method.invoke with ReflectASM.
+**     IAS 29210401 Re-worked for new ORM
 */
 
 /*
@@ -67,6 +68,10 @@
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.logging.*;
+
+import com.goldencode.p2j.persist.*;
+import com.goldencode.p2j.persist.meta.LockTableUpdater.*;
+import com.goldencode.p2j.persist.meta.UserTableStatUpdater.*;
 import com.goldencode.p2j.persist.orm.*;
 import com.goldencode.p2j.util.*;
 import com.goldencode.proxy.*;
@@ -81,20 +86,20 @@
    private static final Logger LOG = LogHelper.getLogger(MetaTableWrapper.class);
 
    /** DMO class */
-   private final Class<?> dmoClass;
+   private final Class<? extends Record> dmoClass;
 
    /** DMO methods map by name */
    private final Map<Method, Method> methodMap = new HashMap<>();
 
-   /** Name of the DMO Impl class */
-   private final String dmoEntity;
-
    /** DMO Impl object */
    private Object dmoImpl;
    
    /** Meta table DMO proxy */
    private final T metaTableProxy;
    
+   /** Dummy empty class to be used as the super class for proxy. */
+   public static class Empty {}
+
    /** DMO operations <code>InvocationHandler</code> */  
    private final InvocationHandler handler = new InvocationHandler() 
    {
@@ -125,27 +130,31 @@
     * 
     * @param facet
     *        DMO interface class
-    * @param implName
+    * @param ifName
     *        DMO interface class simple name 
     */
-   public MetaTableWrapper(Class<T> facet, String implName)
+   public MetaTableWrapper(Class<T> facet, String ifName)
    {
-      this.dmoEntity = String.format("%s._meta.impl." + implName, DmoMetadataManager.getDmoBasePackage());
+      String dmoName = String.format("%s._meta.%s", 
+            DmoMetadataManager.getDmoBasePackage(), ifName);
+      Class<? extends Record> implementingClass = null;
       try
       {
-         this.dmoClass = Class.forName(dmoEntity);
-         for (Method m : facet.getMethods())
+         DmoMeta dmoInfo = DmoMetadataManager.getDmoInfo(Class.forName(dmoName));
+         implementingClass = dmoInfo.getImplementationClass();
+         this.metaTableProxy = ProxyFactory.getProxy(Empty.class, 
+               new Class<?>[]{facet}, handler);
+         for (Method ifcMeth : MinimalUsertablestat.class.getMethods())
          {
-            Method dmoMethod = dmoClass.getMethod(m.getName(), m.getParameterTypes());
-            methodMap.put(m, dmoMethod);
+            Method dmoMeth = implementingClass.getMethod(ifcMeth.getName(), ifcMeth.getParameterTypes());
+            methodMap.put(ifcMeth, dmoMeth);
          }
-         this.metaTableProxy = ProxyFactory.getProxy(dmoClass, new Class<?>[]{facet}, handler);
       } 
-      catch (ClassNotFoundException | NoSuchMethodException e)
+      catch (ClassNotFoundException | IllegalArgumentException | NoSuchMethodException e)
       {
-         throw new RuntimeException("Error preparing MetaLock implementation class", e);
+         throw new RuntimeException("Error preparing MetaUsertablestat implementation class", e);
       }
-
+      this.dmoClass = implementingClass;
    }
 
    /**
@@ -153,7 +162,7 @@
     * 
     * @return DMO interface class
     */
-   public Class<?> getDmoClass()
+   public Class<? extends Record> getDmoClass()
    {
       return dmoClass;
    }
@@ -168,15 +177,6 @@
    }
 
    /**
-    * Get name of the DMO Impl class 
-    * @return name of the DMO Impl class
-    */
-   public String getDmoEntity()
-   {
-      return dmoEntity;
-   }
-   
-   /**
     * Get meta table DMO proxy 
     * @return meta table DMO proxy
     */

=== modified file 'src/com/goldencode/p2j/persist/meta/UserTableStatUpdater.java'
--- src/com/goldencode/p2j/persist/meta/UserTableStatUpdater.java	2020-09-07 16:23:31 +0000
+++ src/com/goldencode/p2j/persist/meta/UserTableStatUpdater.java	2021-04-08 10:24:53 +0000
@@ -1,3 +1,67 @@
+/*
+** Module   : UserTableStatUpdater.java
+** Abstract : Manages _UserTableStat VST
+**
+** Copyright (c) 2020-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 IAS 20200819 Created initial version.
+** 002 IAS 20210401 Fixed bugs and added new helper methods
+*/
+
+/*
+** 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.meta;
 
 import java.util.*;
@@ -5,13 +69,18 @@
 import java.util.concurrent.atomic.*;
 import java.util.logging.*;
 
+import org.apache.commons.lang3.*;
+
 import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.persist.lock.*;
 import com.goldencode.p2j.persist.meta.TableStats.*;
+import com.goldencode.p2j.persist.orm.*;
+import com.goldencode.p2j.security.SecurityManager;
 import com.goldencode.p2j.util.*;
 
 /**
- * Maintains the CRUD operations' statistics in the <code>_usertablestat</code> VST
+ * Maintains the CRUD operations' statistics in the <code>_usertablestat</code>
+ * VST
  */
 public class UserTableStatUpdater
 {
@@ -26,25 +95,24 @@
 
    /** MetaUsertablestatImpl DMO wrapper */
    private final MetaTableWrapper<MinimalUsertablestat> usertablestatWrapper = new MetaTableWrapper<>(
-         MinimalUsertablestat.class, "MetaUsertablestatImpl");
+         MinimalUsertablestat.class, "MetaUsertablestat");
 
    /** MetaUsertablestatImpl DMO proxy */
    private final MinimalUsertablestat usertablestat = usertablestatWrapper.getMetaTableProxy();
 
-   /** Session Id holder */
-   private final SessionIdHolder sessionIdHolder = new SessionIdHolder();
-
    /** meta database */
    private final Database metaDatabase;
-   
-   /** meta database Persistence */ 
+
+   /** meta database Persistence */
    private final Persistence persistence;
 
+   /** Security manager */
+   private final SecurityManager securityManager;
+
    /** Tables' CRUD operations statistics per connection */
-   private final ConcurrentMap<integer, ConcurrentMap<integer, TableStats>> tableStats = 
-         new ConcurrentSkipListMap<>();
+   private final ConcurrentMap<integer, ConcurrentMap<integer, TableStats>> tableStats = new ConcurrentSkipListMap<>();
 
-   /**  Statistics statistics version */
+   /** Statistics statistics version */
    private final AtomicLong databaseVersion = new AtomicLong(-1);
 
    /** In-memory statistics version */
@@ -54,47 +122,106 @@
    private final AtomicLong nextPrimaryKey = new AtomicLong(1);
 
    /**
+    * Persist statistics in the meta database
+    * 
+    * @param dbId database id
+    */
+   public static void persist(String dbId)
+   {
+      UserTableStatUpdater userTableStatUpdater = ConnectionManager.tableStatUpdater(dbId);
+      if (userTableStatUpdater != null)
+      {
+         try
+         {
+            userTableStatUpdater.persist();
+         } catch (PersistenceException e)
+         {
+            LOG.log(Level.WARNING, "Failed to flush _UserTableStat", e);
+         }
+      }
+   }
+
+   /**
+    * Register retrieve operation
+    * @param db 
+    *        database
+    * @param buffer 
+    *        target buffer
+    */
+   public static void retrieve(Database db, RecordBuffer buffer)
+   {
+      DmoMeta dmoMeta = buffer.getDmoInfo();
+      if (dmoMeta.isMeta())
+      {
+         return;
+      }
+      Class<? extends Record> dmoClass = dmoMeta.getImplementationClass();
+      integer filenum = MetadataManager.getFileNum(db, dmoClass);
+      UserTableStatUpdater userTableStatUpdater = 
+            ConnectionManager.tableStatUpdater(db.getId());
+      if (userTableStatUpdater != null)
+      {
+         TableStats tableStats = userTableStatUpdater.getTableStats(
+               new integer(userTableStatUpdater.getSecurityManager().getSessionId()), 
+               filenum);
+         if (tableStats != null)
+         {
+            tableStats.retrieve();
+         }
+      }
+
+   }
+
+   /**
+    * Get the SecurityManager instance
+    * @return SecurityManager instance
+    */
+   public SecurityManager getSecurityManager()
+   {
+      return securityManager;
+   }
+
+   /**
     * Constructor
     * 
-    * @param metaDatabase
-    *        meta database
+    * @param metaDatabase meta database
     */
    public UserTableStatUpdater(Database metaDatabase)
    {
       this.metaDatabase = metaDatabase;
       this.persistence = PersistenceFactory.getInstance(metaDatabase);
+      this.securityManager = SecurityManager.getInstance();
    }
 
    /**
     * Get table statistics holder for a give connection and table
     * 
-    * @param connectId
-    *        connection id
-    * @param tableNo
-    *        table number
+    * @param connectId connection id
+    * @param tableNo   table number
     * @return table statistics holder
     */
    public TableStats getTableStats(integer connectId, integer tableNo)
    {
-      return tableStats.computeIfAbsent(connectId, k -> new ConcurrentSkipListMap<>()).
-                        computeIfAbsent(tableNo, k -> new TableStats(statisticsVersion));
+      return tableStats.computeIfAbsent(connectId, 
+            k -> new ConcurrentSkipListMap<>()).computeIfAbsent(tableNo,
+                                       k -> new TableStats(statisticsVersion));
    }
-   
-   /** 
+
+   /**
     * Persist statistics in the meta database
     * 
     * @return <code>true</code> if success
-    * @throws PersistenceException 
+    * @throws PersistenceException
     */
-   public boolean persist() 
-   throws PersistenceException
+   public boolean persist() throws PersistenceException
    {
-      long statVersion = statisticsVersion.get(); 
-      if (statVersion <= databaseVersion.get()) 
+      long statVersion = statisticsVersion.get();
+      if (statVersion <= databaseVersion.get())
       {
          return true;
       }
-      try {
+      try
+      {
          boolean commit = persistence.beginTransaction();
          Map<integer, Map<integer, Long>> updates = new HashMap<>();
          new Worker().run(updates);
@@ -105,14 +232,14 @@
          }
          databaseVersion.set(statVersion);
          // update "persisted" value
-         for (Map.Entry<integer, Map<integer, Long>> ce: updates.entrySet())
+         for (Map.Entry<integer, Map<integer, Long>> ce : updates.entrySet())
          {
             ConcurrentMap<integer, TableStats> cs = tableStats.get(ce.getKey());
             if (ce == null)
             {
                continue;
             }
-            for (Map.Entry<integer, Long> te: ce.getValue().entrySet())
+            for (Map.Entry<integer, Long> te : ce.getValue().entrySet())
             {
                TableStats ts = cs.get(te.getKey());
                if (ts != null)
@@ -122,18 +249,17 @@
             }
          }
          return true;
-     } 
-     catch (PersistenceException e) {
+      } catch (PersistenceException e)
+      {
          persistence.rollback();
          return false;
-     }
+      }
    }
-   
+
    /**
     * Drop data for the closed connection
-    *  
-    * @param connectId
-    *        connection id
+    * 
+    * @param connectId connection id
     */
    public void disconnected(int64 connectId)
    {
@@ -142,66 +268,61 @@
       try
       {
          commit = persistence.beginTransaction();
-         persistence.getSingleSQLResult(
-               "DELETE FROM " + USERTABLESTAT_TABLE + " WHERE usertablestat_conn = ?1", 
-               new Object[] {connectId.intValue()}
-         );
+         persistence.executeSQL("DELETE FROM " + USERTABLESTAT_TABLE + " WHERE usertablestat_conn = ?1",
+               new Object[]
+               { connectId.intValue() });
          persistence.flush();
-         
+
          if (commit)
          {
             commit = false;
             persistence.commit();
          }
-         
-      }
-      catch(Exception e)
+
+      } catch (Exception e)
       {
-         LOG.log(Level.WARNING, "Failed to cleanup statistics for the closed connection: " + 
-                  connectId.intValue(), e);
+         LOG.log(Level.WARNING, "Failed to cleanup statistics for the closed connection: " + connectId.intValue(), e);
          try
          {
             if (commit)
             {
                persistence.rollback();
             }
-         }
-         catch (PersistenceException pe)
+         } catch (PersistenceException pe)
          {
             LOG.log(Level.WARNING, "Failed to rollback transaction", e);
          }
       }
    }
-   
+
    /**
-    * Implements incremental update of the _UserTableStat table  
+    * Implements incremental update of the _UserTableStat table
     */
    private class Worker
    {
       /**
        * Perform incremental database update
        * 
-       * @param updates
-       *        version of the updated/inserted records
+       * @param updates version of the updated/inserted records
        * @throws PersistenceException
        */
-      public void run(Map<integer, Map<integer, Long>> updates) 
-      throws PersistenceException
+      public void run(Map<integer, Map<integer, Long>> updates) throws PersistenceException
       {
-         for (Map.Entry<integer, ConcurrentMap<integer, TableStats>> ce: tableStats.entrySet())
+         for (Map.Entry<integer, ConcurrentMap<integer, TableStats>> ce : tableStats.entrySet())
          {
-            for (Map.Entry<integer, TableStats> te: ce.getValue().entrySet())
+            integer connId = ce.getKey();
+            for (Map.Entry<integer, TableStats> te : ce.getValue().entrySet())
             {
                Snapshot data = te.getValue().getStat();
                if (data.version <= data.persisted)
                {
                   continue;
                }
+               integer tblId = te.getKey();
                Record dmo = data.persisted < 0 ? 
-                     createRecord(ce.getKey(), te.getKey(), data) :
-                     updateRecord(ce.getKey(), te.getKey(), data);
-                     updates.computeIfAbsent(ce.getKey(), k -> new HashMap<>()).
-                              put(te.getKey(), data.version);
+                       createRecord(connId, tblId, data):
+                       updateRecord(connId, tblId, data);
+               updates.computeIfAbsent(connId, k -> new HashMap<>()).put(tblId, data.version);
                persistence.save(dmo, dmo.primaryKey());
             }
          }
@@ -209,38 +330,30 @@
 
       /**
        * Update _usertablestat record
-       * @param connId
-       *        connection id
-       * @param tblId
-       *        table id
-       * @param data
-       *        data snapshot
-       * @return
-       *        updated Record object 
+       * 
+       * @param connId connection id
+       * @param tblId  table id
+       * @param data   data snapshot
+       * @return updated Record object
        * @throws PersistenceException
        */
-      private Record updateRecord(integer connId, integer tblId, Snapshot data)
-      throws PersistenceException
+      private Record updateRecord(integer connId, integer tblId, Snapshot data) throws PersistenceException
       {
-         Long id = (Long)persistence.getSingleSQLResult(
-               "SELECT ID FROM " + USERTABLESTAT_TABLE + " WHERE " + 
-                     "USERTABLESTAT_CONN = ?1 AND USERTABLESTAT_NUM = ?2",
-                new Object[] {connId.longValue(), tblId.longValue()});
+         Long id = (Long) persistence.getSingleSQLResult("SELECT ID FROM " + USERTABLESTAT_TABLE + " WHERE "
+               + "USERTABLESTAT_CONN = ?1 AND USERTABLESTAT_NUM = ?2", new Object[]
+         { connId.longValue(), tblId.longValue() });
          Record dmo = null;
-         
+
          if (id != null)
          {
-            RecordIdentifier<String> metaIdent =
-               new RecordIdentifier<>(usertablestatWrapper.getDmoEntity(), id);
+            RecordIdentifier<String> metaIdent = new RecordIdentifier<>(usertablestatWrapper.getDmoClass().getName(),
+                  id);
             dmo = persistence.quickLoad(metaIdent);
          }
          if (dmo == null)
          {
-            throw new PersistenceException(
-                  String.format("No lock record found for connId = %s, tblId = %s",
-                                connId.longValue(), tblId.longValue()
-                  )
-            );
+            throw new PersistenceException(String.format("No lock record found for connId = %s, tblId = %s",
+                  connId.longValue(), tblId.longValue()));
          }
          usertablestatWrapper.setDelegate(dmo);
          MinimalUsertablestat proxy = usertablestatWrapper.getMetaTableProxy();
@@ -250,23 +363,20 @@
 
       /**
        * Created _usertablestat record
-       * @param connId
-       *        connection id
-       * @param tblId
-       *        table id
-       * @param data
-       *        data snapshot
-       * @return
-       *        created new Record object 
+       * 
+       * @param connId connection id
+       * @param tblId  table id
+       * @param data   data snapshot
+       * @return created new Record object
        * @throws PersistenceException
        */
-      private Record createRecord(integer connId, integer tblId, Snapshot data)
-      throws PersistenceException
+      private Record createRecord(integer connId, integer tblId, Snapshot data) throws PersistenceException
       {
          Record dmo = null;
-         try 
+         try
          {
-            dmo = (Record) usertablestatWrapper.getDmoClass().newInstance();
+            dmo = usertablestatWrapper.getDmoClass().newInstance();
+            dmo.initialize(null, true);
             long id = nextPrimaryKey.getAndIncrement();
             dmo.primaryKey(id);
             usertablestatWrapper.setDelegate(dmo);
@@ -275,13 +385,12 @@
             proxy.setUsertablestatConn(connId);
             proxy.setUsertablestatNum(tblId);
             populateStat(data, proxy);
-         }
-         catch (InstantiationException | IllegalAccessException e)
+         } catch (InstantiationException | IllegalAccessException e)
          {
             throw new PersistenceException("Error creating meta usertablestat record", e);
          }
          return dmo;
-     }
+      }
 
       private void populateStat(Snapshot data, MinimalUsertablestat proxy)
       {
@@ -290,8 +399,9 @@
          proxy.setUsertablestatUpdate(new integer(data.update));
          proxy.setUsertablestatDelete(new integer(data.delete));
       }
-      
+
    }
+
    /**
     * Interface which defines the minimally required methods of the
     * <code>MinimalUsertablestat</code> interface. This interface defines the proxy
@@ -305,135 +415,126 @@
       /**
        * Getter: UserTableStat-Id
        * 
-       * @return  UserTableStat-Id
+       * @return UserTableStat-Id
        */
       public int64 getUsertablestatId();
 
       /**
        * Setter: UserTableStat-Id
        * 
-       * @param   usertablestatId
-       *          UserTableStat-Id
+       * @param usertablestatId UserTableStat-Id
        */
       public void setUsertablestatId(NumberType usertablestatId);
 
       /**
        * Getter: Connection
        * 
-       * @return  Connection
+       * @return Connection
        */
       public integer getUsertablestatConn();
 
       /**
        * Setter: Connection
        * 
-       * @param   usertablestatConn
-       *          Connection
+       * @param usertablestatConn Connection
        */
       public void setUsertablestatConn(NumberType usertablestatConn);
 
       /**
        * Getter: Table #
        * 
-       * @return  Table #
+       * @return Table #
        */
       public integer getUsertablestatNum();
 
       /**
        * Setter: Table #
        * 
-       * @param   usertablestatNum
-       *          Table #
+       * @param usertablestatNum Table #
        */
       public void setUsertablestatNum(NumberType usertablestatNum);
 
       /**
        * Getter: read
        * 
-       * @return  read
+       * @return read
        */
       public int64 getUsertablestatRead();
 
       /**
        * Setter: read
        * 
-       * @param   usertablestatRead
-       *          read
+       * @param usertablestatRead read
        */
       public void setUsertablestatRead(NumberType usertablestatRead);
 
       /**
        * Getter: update
        * 
-       * @return  update
+       * @return update
        */
       public int64 getUsertablestatUpdate();
 
       /**
        * Setter: update
        * 
-       * @param   usertablestatUpdate
-       *          update
+       * @param usertablestatUpdate update
        */
       public void setUsertablestatUpdate(NumberType usertablestatUpdate);
 
       /**
        * Getter: create
        * 
-       * @return  create
+       * @return create
        */
       public int64 getUsertablestatCreate();
 
       /**
        * Setter: create
        * 
-       * @param   usertablestatCreate
-       *          create
+       * @param usertablestatCreate create
        */
       public void setUsertablestatCreate(NumberType usertablestatCreate);
 
       /**
        * Getter: delete
        * 
-       * @return  delete
+       * @return delete
        */
       public int64 getUsertablestatDelete();
 
       /**
        * Setter: delete
        * 
-       * @param   usertablestatDelete
-       *          delete
+       * @param usertablestatDelete delete
        */
       public void setUsertablestatDelete(NumberType usertablestatDelete);
 
       /**
        * Getter: Partition Id
        * 
-       * @return  Partition Id
+       * @return Partition Id
        */
       public integer getUsertablestatPartitionid();
 
       /**
        * Setter: Partition Id
        * 
-       * @param   usertablestatPartitionid
-       *          Partition Id
+       * @param usertablestatPartitionid Partition Id
        */
       public void setUsertablestatPartitionid(NumberType usertablestatPartitionid);
 
       /**
        * Getter: Tenant Id
        * 
-       * @return  Tenant Id
+       * @return Tenant Id
        */
       public integer getUsertablestatTenantid();
 
       /**
        * Setter: Tenant Id
        * 
-       * @param   usertablestatTenantid
-       *          Tenant Id
+       * @param usertablestatTenantid Tenant Id
        */
       public void setUsertablestatTenantid(NumberType usertablestatTenantid);
    }

=== modified file 'src/com/goldencode/p2j/persist/orm/DDLGeneratorWorker.java'
--- src/com/goldencode/p2j/persist/orm/DDLGeneratorWorker.java	2021-03-18 11:47:13 +0000
+++ src/com/goldencode/p2j/persist/orm/DDLGeneratorWorker.java	2021-04-08 14:42:24 +0000
@@ -12,6 +12,7 @@
 ** 003 IAS 20201203 Added generation database object for the word tables' support.
 **     IAS 20210219 Word tables support for custom extents
 **     IAS 20210315 Refactored for working with word tables for _temp database
+**     IAS 20210408 _MyConnection VST support
 */
 
 /*
@@ -123,7 +124,19 @@
    
    /** Name of the primary key column. */
    private static final String PK = Configuration.getSchemaConfig().getPrimaryKeyName();
-   
+
+   /** Auxiliary database objects for _MyConnection VST */ 
+   private static final List<String> MY_CONNECTION_AUX = Collections.unmodifiableList(
+         Arrays.asList(
+               "create view meta_myconnection as",
+               "select * from meta_myconnection_",
+               "where myconn_userid = fwdSession_1();",
+               "",
+               "create trigger meta_myconnection_trg instead of insert, update, delete ",
+               "on meta_myconnection ",
+               "for each row call \"com.goldencode.p2j.persist.h2.UpdatableMyConnectionView\";"
+         )
+   );
    /** This represents the surrogate {@code id} column, added to each SQL record. */
    public static final P2JField ID = new P2JField(PK, ParmType.INT64, 0L,
          null, null, null, null, false, null, null, false, null, null, null, null, true, 0);
@@ -1079,7 +1092,8 @@
             System.out.println("Generating INDEX DDL for table " + tableEntry.getKey() + "...");
             
             Table table = tableEntry.getValue();
-            
+            boolean isMeta = MetadataManager.META_SCHEMA.equals(dbName); 
+
             // generate drop indexes:
             for (P2JIndex p2JIndex : table.nonRedundant)
             {
@@ -1091,6 +1105,10 @@
             // emit create statements for the minimum set of unique indexes first
             for (P2JIndex p2JIndex : table.minimalUnique)
             {
+               if (isMeta && "meta_myconnection".equals(p2JIndex.getTable()))
+               {
+                  p2JIndex = new P2JIndex(p2JIndex, "meta_myconnection_");
+               }
                out.print(dialect.getCreateIndexString(p2JIndex, true, true));
                out.print(dialect.getDelimiter());
                out.print(eoln);
@@ -1167,8 +1185,15 @@
             
             Map<Integer, StringBuilder> tableSBs = new HashMap<>();
             
+            String tableName =  table.name;
+            boolean isMyConnectionTable = MetadataManager.META_SCHEMA.equals(dbName) && 
+                                          "meta_myconnection".equals(tableName);
+            if (isMyConnectionTable)
+            {
+               tableName += "_";
+            }
             StringBuilder main = new StringBuilder();
-            main.append(dialect.getCreateTableString()).append(table.name).append(" (").append(eoln);
+            main.append(dialect.getCreateTableString()).append(tableName).append(" (").append(eoln);
             // injecting ID column as the first column
             addFieldImpl(ID, dialect, table, main, eoln);
             tableSBs.put(0, main);
@@ -1233,6 +1258,15 @@
                out.print(eoln);
             }
             
+            if (isMyConnectionTable)
+            {
+               for (String l: MY_CONNECTION_AUX) 
+               {
+                  out.print(l);
+                  out.print(eoln);
+               }
+               out.print(eoln);
+            }
             if (!dialect.useUdf4Contains())
             {
                generateWordTable(dbName, table, out, dialect, eoln);

=== modified file 'src/com/goldencode/p2j/persist/orm/DmoMeta.java'
--- src/com/goldencode/p2j/persist/orm/DmoMeta.java	2021-02-20 16:18:22 +0000
+++ src/com/goldencode/p2j/persist/orm/DmoMeta.java	2021-04-08 10:42:19 +0000
@@ -23,6 +23,7 @@
 **     OM  20201202 Added getPrimaryIndex() method.
 **     IAS 20201210 Added support for word tables.
 **     IAS 20210219 Word tables support for custom extents
+**     IAS 29210401 Added 'isVST' and 'isMeta' methods
 */
 
 /*
@@ -85,8 +86,11 @@
 import com.goldencode.p2j.persist.annotation.*;
 import com.goldencode.p2j.persist.annotation.Table;
 import com.goldencode.p2j.persist.dirty.*;
+import com.goldencode.p2j.persist.meta.MetadataManager.*;
 import com.goldencode.p2j.util.*;
 import com.google.common.collect.*;
+
+import org.apache.commons.lang3.*;
 import org.apache.commons.lang3.tuple.*;
 import java.lang.reflect.*;
 import java.util.*;
@@ -1395,6 +1399,28 @@
       }
       return sb.toString();
    }
+   
+   /**
+    * Check if DMO corresponds to a meta table.
+    * @return <code>true</code> if DMO corresponds to a meta table..
+    */
+   public boolean isMeta()
+   {
+      return !StringUtils.isEmpty(category);
+     
+   }
+
+   /**
+    * Check if DMO corresponds to a specified VST.
+    * @param systemTable
+    *        VST to be checked
+    * @return <code>true</code> if DMO corresponds to a specified VST.
+    */
+   public boolean isVST(SystemTable systemTable)
+   {
+      return Category.VST.name().equals(category) && 
+            systemTable._file_name.equalsIgnoreCase(legacyTable);
+   }
    /**
     * Word index data. 
     */

=== modified file 'src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java'
--- src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java	2021-03-26 20:54:23 +0000
+++ src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java	2021-04-02 12:04:59 +0000
@@ -19,6 +19,7 @@
 **     IAS 20210315 Use Database instead of schema in constructor, re-work implicit ordering
 **     IAS 20210321 Re-worked implicit ordering for word tables. Added implicit sorting for
 **                  _temp and 'dirty' databases.
+**    IAS  29210401 Flash _UserTableStat before access
 */
 
 /*
@@ -95,6 +96,7 @@
 import com.goldencode.p2j.persist.orm.Query.*;
 import com.goldencode.p2j.uast.*;
 import com.goldencode.p2j.persist.meta.*;
+import com.goldencode.p2j.persist.meta.MetadataManager.*;
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.util.ErrorManager;
 import com.goldencode.p2j.util.LogicalExpressionConverter.*;
@@ -2842,6 +2844,10 @@
             DmoMeta dmoInfo = DmoMetadataManager.getDmoInfo(dmoName, schema);
             if (dmoInfo != null)
             {
+               if (dmoInfo.isVST(SystemTable._UserTableStat))
+               {
+                  UserTableStatUpdater.persist(db.getId());
+               }
                if (alias == null || alias.getType() == ALIAS)
                {
                   String aliasName;

=== modified file 'src/com/goldencode/p2j/persist/pl/Functions.java'
--- src/com/goldencode/p2j/persist/pl/Functions.java	2021-02-02 11:53:32 +0000
+++ src/com/goldencode/p2j/persist/pl/Functions.java	2021-04-08 11:45:22 +0000
@@ -3,7 +3,7 @@
 ** Abstract : Dispatcher methods for Java language, server-side database
 **            functions which replace Progress built-in functions
 **
-** Copyright (c) 2004-2020, Golden Code Development Corporation.
+** Copyright (c) 2004-2021, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
 ** 001 ECF 20060811   @28584 Created initial version. Backing methods for
@@ -92,6 +92,7 @@
 ** 040 ECF 20200504          Added missing alias for entryIn(Long, String).
 ** 041 OM  20200212          Unknown value fixes.
 ** 042 IAS 20201121          Added 'words' functions.
+** 043 IAS 20210407          Added 'fwdSession' function.
 */
 
 /*
@@ -152,6 +153,8 @@
 import java.math.*;
 import java.sql.*;
 import com.goldencode.p2j.*;
+import com.goldencode.p2j.net.*;
+import com.goldencode.p2j.security.SecurityManager;
 import com.goldencode.p2j.util.*;
 
 /**
@@ -3577,6 +3580,16 @@
    }
 
    /**
+    * Helper function to return the FWD Session Id.
+    *
+    * @return  FWD Session Id.
+    */
+   @HQLFunction
+   public static int fwdSession()
+   {
+      return SecurityManager.getInstance().getSessionId().intValue();
+   }
+   /**
     * Rewraps an Integer value into a Long.
     * 
     * @param   i

