]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/DirectQuerySupportImpl.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / DirectQuerySupportImpl.java
index 3e6e852c54f6445f26697dda0549c4d6526dc6ba..fb6a1f7638e6716e283e3d654775ff665092dc36 100644 (file)
-package fi.vtt.simantics.procore.internal;\r
-\r
-import org.simantics.db.AsyncReadGraph;\r
-import org.simantics.db.DirectStatements;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.RelationInfo;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.common.procedure.wrapper.NoneToAsyncProcedure;\r
-import org.simantics.db.common.procedure.wrapper.SyncToAsyncProcedure;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.impl.ClusterI;\r
-import org.simantics.db.impl.ClusterI.ClusterTypeEnum;\r
-import org.simantics.db.impl.ForEachObjectContextProcedure;\r
-import org.simantics.db.impl.ForEachObjectProcedure;\r
-import org.simantics.db.impl.ForPossibleRelatedValueContextProcedure;\r
-import org.simantics.db.impl.ForPossibleRelatedValueProcedure;\r
-import org.simantics.db.impl.ResourceImpl;\r
-import org.simantics.db.impl.TransientGraph;\r
-import org.simantics.db.impl.graph.ReadGraphImpl;\r
-import org.simantics.db.procedure.AsyncContextMultiProcedure;\r
-import org.simantics.db.procedure.AsyncContextProcedure;\r
-import org.simantics.db.procedure.AsyncMultiProcedure;\r
-import org.simantics.db.procedure.AsyncProcedure;\r
-import org.simantics.db.procedure.Procedure;\r
-import org.simantics.db.procedure.SyncProcedure;\r
-import org.simantics.db.procore.cluster.ClusterBig;\r
-import org.simantics.db.procore.cluster.ClusterImpl;\r
-import org.simantics.db.procore.cluster.ClusterSmall;\r
-import org.simantics.db.procore.cluster.ResourceTableSmall;\r
-import org.simantics.db.procore.cluster.ValueTableSmall;\r
-import org.simantics.db.request.AsyncRead;\r
-import org.simantics.db.service.DirectQuerySupport;\r
-import org.simantics.utils.datastructures.Callback;\r
-\r
-public class DirectQuerySupportImpl implements DirectQuerySupport {\r
-\r
-       final private SessionImplSocket session;\r
-       \r
-       DirectQuerySupportImpl(SessionImplSocket session) {\r
-               this.session = session;\r
-       }\r
-\r
-       @Override\r
-       final public void forEachDirectPersistentStatement(AsyncReadGraph graph, final Resource subject, final AsyncProcedure<DirectStatements> procedure) {\r
-               ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               impl.processor.forEachDirectStatement(impl, subject, procedure, true);\r
-       }\r
-\r
-       @Override\r
-       final public void forEachDirectStatement(AsyncReadGraph graph, final Resource subject, final AsyncProcedure<DirectStatements> procedure) {\r
-               ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               impl.processor.forEachDirectStatement(impl, subject, procedure, false);\r
-       }\r
-\r
-       @Override\r
-       public void forEachDirectStatement(AsyncReadGraph graph, Resource subject, SyncProcedure<DirectStatements> procedure) {\r
-               forEachDirectStatement(graph, subject, new SyncToAsyncProcedure<DirectStatements>(procedure));\r
-       }\r
-\r
-       @Override\r
-       public void forEachDirectStatement(AsyncReadGraph graph, Resource subject, Procedure<DirectStatements> procedure) {\r
-               ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               impl.processor.forEachDirectStatement(impl, subject, procedure);\r
-       }\r
-\r
-       @Override\r
-       public void forRelationInfo(AsyncReadGraph graph, Resource subject, AsyncProcedure<RelationInfo> procedure) {\r
-               ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               impl.processor.forRelationInfo(impl, subject, procedure);\r
-       }\r
-\r
-       @Override\r
-       public void forRelationInfo(AsyncReadGraph graph, Resource subject, SyncProcedure<RelationInfo> procedure) {\r
-               forRelationInfo(graph, subject, new SyncToAsyncProcedure<RelationInfo>(procedure));\r
-       }\r
-\r
-       @Override\r
-       public void forRelationInfo(AsyncReadGraph graph, Resource subject, Procedure<RelationInfo> procedure) {\r
-               forRelationInfo(graph, subject, new NoneToAsyncProcedure<RelationInfo>(procedure));\r
-       }\r
-\r
-       @Override\r
-       public AsyncMultiProcedure<Resource> compileForEachObject(ReadGraph graph, final Resource relation, AsyncMultiProcedure<Resource> user) {\r
-               \r
-               try {\r
-                       RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {\r
-\r
-                               @Override\r
-                               public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {\r
-                                       forRelationInfo(graph, relation, procedure);\r
-                               }\r
-\r
-                @Override\r
-                   public int threadHash() {\r
-                       return hashCode();\r
-                   }\r
-\r
-                               @Override\r
-                               public int getFlags() {\r
-                                       return 0;\r
-                               }\r
-\r
-                       });\r
-               final int predicateKey = ((ResourceImpl)relation).id;\r
-                       return new ForEachObjectProcedure(predicateKey, info, session.queryProvider2, user);\r
-               } catch (DatabaseException e) {\r
-                       return null;\r
-               }               \r
-        \r
-       }\r
-\r
-       @Override\r
-       public <C> AsyncContextMultiProcedure<C, Resource> compileForEachObject(ReadGraph graph, final Resource relation, AsyncContextMultiProcedure<C, Resource> user) {\r
-               \r
-               try {\r
-                       RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {\r
-\r
-                               @Override\r
-                               public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {\r
-                                       forRelationInfo(graph, relation, procedure);\r
-                               }\r
-\r
-                @Override\r
-                   public int threadHash() {\r
-                       return hashCode();\r
-                   }\r
-\r
-                               @Override\r
-                               public int getFlags() {\r
-                                       return 0;\r
-                               }\r
-\r
-                       });\r
-               final int predicateKey = ((ResourceImpl)relation).id;\r
-                       return new ForEachObjectContextProcedure<C>(predicateKey, info, session.queryProvider2, user);\r
-               } catch (DatabaseException e) {\r
-                       return null;\r
-               }               \r
-        \r
-       }\r
-       \r
-       @Override\r
-       public <T> AsyncProcedure<T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, AsyncProcedure<T> user) {\r
-               \r
-               try {\r
-                       RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {\r
-\r
-                               @Override\r
-                               public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {\r
-                                       forRelationInfo(graph, relation, procedure);\r
-                               }\r
-\r
-                @Override\r
-                   public int threadHash() {\r
-                       return hashCode();\r
-                   }\r
-\r
-                               @Override\r
-                               public int getFlags() {\r
-                                       return 0;\r
-                               }\r
-\r
-                       });\r
-               final int predicateKey = ((ResourceImpl)relation).id;\r
-                       return new ForPossibleRelatedValueProcedure<T>(predicateKey, info, user);\r
-               } catch (DatabaseException e) {\r
-                       return null;\r
-               }               \r
-        \r
-       }\r
-\r
-       @Override\r
-       public <C, T> AsyncContextProcedure<C, T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, AsyncContextProcedure<C, T> user) {\r
-               \r
-               try {\r
-                       RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {\r
-\r
-                               @Override\r
-                               public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {\r
-                                       forRelationInfo(graph, relation, procedure);\r
-                               }\r
-\r
-                @Override\r
-                   public int threadHash() {\r
-                       return hashCode();\r
-                   }\r
-\r
-                               @Override\r
-                               public int getFlags() {\r
-                                       return 0;\r
-                               }\r
-\r
-                       });\r
-               final int predicateKey = ((ResourceImpl)relation).id;\r
-                       return new ForPossibleRelatedValueContextProcedure<C, T>(predicateKey, info, user);\r
-               } catch (DatabaseException e) {\r
-                       return null;\r
-               }               \r
-        \r
-       }\r
-       \r
-       @Override\r
-       public void forEachObjectCompiled(AsyncReadGraph graph, Resource subject, final AsyncMultiProcedure<Resource> procedure) {\r
-               \r
-        assert(subject != null);\r
-        \r
-        final ForEachObjectProcedure proc = (ForEachObjectProcedure)procedure;\r
-//        final RelationInfo info = proc.info;\r
-\r
-        final ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-        final int subjectId = ((ResourceImpl)subject).id;\r
-\r
-//        int callerThread = impl.callerThread;\r
-//        int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;\r
-        \r
-//        impl.inc();\r
-        \r
-//        if(callerThread == suggestSchedule) {\r
-               \r
-//             if(info.isFunctional) {\r
-//                     querySupport.getObjects4(impl, subjectId, proc);\r
-//             } else {\r
-               session.querySupport.getObjects4(impl, subjectId, proc);\r
-//             }\r
-               \r
-//        } else {\r
-//             \r
-//             impl.state.barrier.inc();\r
-//            impl.state.barrier.dec(callerThread);\r
-//             \r
-//             queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {\r
-//     \r
-//                     @Override\r
-//                     public void run(int thread) {\r
-//                             \r
-//                     impl.state.barrier.inc(thread);\r
-//                             impl.state.barrier.dec();\r
-//\r
-//                     if(info.isFunctional) {\r
-//                             querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);\r
-//                     } else {\r
-//                             querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);\r
-//                     }\r
-//                             \r
-//                     }\r
-//                     \r
-//                     @Override\r
-//                     public String toString() {\r
-//                             return "gaff8";\r
-//                     }\r
-//     \r
-//             });\r
-//             \r
-//        }\r
-               \r
-       }\r
-\r
-       @Override\r
-       public <C> void forEachObjectCompiled(AsyncReadGraph graph, Resource subject, C context, final AsyncContextMultiProcedure<C, Resource> procedure) {\r
-               \r
-               assert(subject != null);\r
-\r
-               final ForEachObjectContextProcedure<C> proc = (ForEachObjectContextProcedure<C>)procedure;\r
-               final RelationInfo info = proc.info;\r
-\r
-               final ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               final int subjectId = ((ResourceImpl)subject).id;\r
-\r
-//             int callerThread = impl.callerThread;\r
-//             int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;\r
-\r
-//             impl.inc();\r
-\r
-               if(info.isFunctional) {\r
-                       session.querySupport.getObjects4(impl, subjectId, context, proc);\r
-               } else {\r
-                       session.querySupport.getObjects4(impl, subjectId, context, proc);\r
-               }\r
-               \r
-       }\r
-       \r
-       @Override\r
-       public <T> void forPossibleRelatedValueCompiled(AsyncReadGraph graph, Resource subject, final AsyncProcedure<T> procedure) {\r
-               \r
-               assert(subject != null);\r
-\r
-        final ForPossibleRelatedValueProcedure<T> proc = (ForPossibleRelatedValueProcedure<T>)procedure;\r
-        final RelationInfo info = proc.info;\r
-        \r
-        final ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-        final int subjectId = ((ResourceImpl)subject).id;\r
-        \r
-//        int callerThread = impl.callerThread;\r
-//        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;\r
-        \r
-//        impl.inc();\r
-        \r
-//        if(callerThread == suggestSchedule) {\r
-               \r
-               if(info.isFunctional) {\r
-                       getRelatedValue4(impl, subjectId, proc);\r
-               } else {\r
-                       getRelatedValue4(impl, subjectId, proc);\r
-               }\r
-               \r
-//        } else {\r
-//             \r
-//             impl.state.barrier.inc();\r
-//            impl.state.barrier.dec(callerThread);\r
-//             \r
-//             queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {\r
-//     \r
-//                     @Override\r
-//                     public void run(int thread) {\r
-//\r
-//                     impl.state.barrier.inc(thread);\r
-//                     impl.state.barrier.dec();\r
-//                             \r
-//                     if(info.isFunctional) {\r
-//                             getRelatedValue4(impl.newAsync(thread), subjectId, proc);\r
-//                     } else {\r
-//                             getRelatedValue4(impl.newAsync(thread), subjectId, proc);\r
-//                     }\r
-//                             \r
-//                     }\r
-//                     \r
-//                     @Override\r
-//                     public String toString() {\r
-//                             return "gaff11";\r
-//                     }\r
-//     \r
-//             });\r
-//             \r
-//        }\r
-               \r
-       }\r
-\r
-       @Override\r
-       public <C, T> void forPossibleRelatedValueCompiled(AsyncReadGraph graph, Resource subject, C context, final AsyncContextProcedure<C, T> procedure) {\r
-               \r
-               assert(subject != null);\r
-\r
-        final ForPossibleRelatedValueContextProcedure<C, T> proc = (ForPossibleRelatedValueContextProcedure<C, T>)procedure;\r
-        final RelationInfo info = proc.info;\r
-        \r
-        final ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-        final int subjectId = ((ResourceImpl)subject).id;\r
-        \r
-//        int callerThread = impl.callerThread;\r
-//        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;\r
-        \r
-//        impl.inc();\r
-               \r
-        if(info.isFunctional) {\r
-               getRelatedValue4(impl, subjectId, context, proc);\r
-        } else {\r
-               getRelatedValue4(impl, subjectId, context, proc);\r
-        }\r
-               \r
-       }\r
-       \r
-       @Override\r
-       public <T> void forPossibleType(final AsyncReadGraph graph, Resource subject, final AsyncProcedure<Resource> procedure) {\r
-               \r
-        assert(subject != null);\r
-\r
-        final ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-        final int subjectId = ((ResourceImpl)subject).id;\r
-\r
-        try {\r
-               \r
-               final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);\r
-               if(!cluster.isLoaded()) {\r
-\r
-//                     impl.state.inc(0);\r
-                       \r
-                       session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {\r
-               \r
-                                       @Override\r
-                                       public void run() {\r
-\r
-                                       try {\r
-\r
-                                                       int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);\r
-                                               procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));\r
-\r
-//                                             impl.state.dec(0);                                              \r
-                                               \r
-                                       } catch (DatabaseException e) {\r
-                                               e.printStackTrace();\r
-                                       }\r
-               \r
-                                       }\r
-               \r
-                               });\r
-                               \r
-               } else {\r
-\r
-                       int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);\r
-                       procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));\r
-                       \r
-               }\r
-               \r
-        } catch (DatabaseException e) {\r
-               e.printStackTrace();\r
-        }\r
-       \r
-       \r
-               \r
-       }\r
-\r
-       @Override\r
-       public <C> void forPossibleDirectType(final AsyncReadGraph graph, Resource subject, final C context, final AsyncContextProcedure<C, Resource> procedure) {\r
-               \r
-        assert(subject != null);\r
-\r
-        final ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-        final int subjectId = ((ResourceImpl)subject).id;\r
-        \r
-        try {\r
-               \r
-               final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);\r
-               if(!cluster.isLoaded()) {\r
-\r
-//                     impl.state.inc(0);\r
-                       \r
-                       session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {\r
-               \r
-                                       @Override\r
-                                       public void run() {\r
-\r
-                                       try {\r
-\r
-                                               ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);\r
-                                               if(ClusterI.CompleteTypeEnum.InstanceOf == type) {\r
-                                                       int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);\r
-                                                       procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));\r
-                                               } else {\r
-                                               procedure.execute(graph, context, null);\r
-                                               }\r
-\r
-//                                             impl.state.dec(0);\r
-                                               \r
-                                       } catch (DatabaseException e) {\r
-                                               e.printStackTrace();\r
-                                       }\r
-               \r
-                                       }\r
-               \r
-                               });\r
-                               \r
-               } else {\r
-\r
-                       ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);\r
-                       if(ClusterI.CompleteTypeEnum.InstanceOf == type) {\r
-                               int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);\r
-                               procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));\r
-                       } else {\r
-                       procedure.execute(graph, context, null);\r
-                       }\r
-                       \r
-               }\r
-               \r
-        } catch (DatabaseException e) {\r
-               \r
-               procedure.execute(graph, context, null);\r
-               \r
-        } catch (Throwable t) {\r
-\r
-               t.printStackTrace();\r
-               procedure.execute(graph, context, null);\r
-               \r
-        }\r
-               \r
-       }\r
-       \r
-       \r
-       private <C, T> void getRelatedValue4(final ReadGraphImpl graph, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {\r
-               \r
-               int result = 0;\r
-               \r
-               final int predicate = procedure.predicateKey;\r
-\r
-               if(subject < 0) {\r
-\r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getRelatedValue4(graph, subject, context, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-                       \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                for (int id : g.getObjects(subject, predicate)) {\r
-                       if(result != 0) {\r
-                               procedure.exception(graph, new DatabaseException("Multiple objects"));\r
-//                             graph.dec();\r
-                               return;\r
-                       } else {\r
-                               result = id;\r
-                       }\r
-                }\r
-                       }\r
-                       \r
-                       if(result == 0) {\r
-                               \r
-                       procedure.exception(graph, new DatabaseException("No objects for " + subject ));\r
-//                     graph.dec();\r
-                       return;\r
-                       \r
-                       } else {\r
-                               \r
-                               getValue4(graph, null, result, context, procedure);\r
-                               return;\r
-                               \r
-                       }\r
-                       \r
-               } \r
-               \r
-        final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);\r
-        if(!cluster.isLoaded()) {\r
-               cluster.load(session.clusterTranslator, new Runnable() {\r
-\r
-                               @Override\r
-                               public void run() {\r
-                                       getRelatedValue4(graph, subject, context, procedure);\r
-                               }\r
-                       \r
-               });\r
-               return;\r
-        }\r
-        \r
-        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {\r
-                       \r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getRelatedValue4(graph, subject, context, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-               \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                for (int id : g.getObjects(subject, predicate)) {\r
-                       if(result != 0) {\r
-                               procedure.exception(graph, new DatabaseException("Multiple objects"));\r
-//                             graph.dec();\r
-                               return;\r
-                       } else {\r
-                               result = id;\r
-                       }\r
-                }\r
-                       }\r
-                       \r
-                       getRelatedDirectValue4(graph, cluster, subject, result, context, procedure);\r
-                       \r
-               } else {\r
-                       \r
-                       getRelatedDirectValue4(graph, cluster, subject, 0, context, procedure);\r
-                       \r
-               }\r
-               \r
-       }\r
-       \r
-       private <T> void getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {\r
-               \r
-               Object result = null;\r
-       \r
-               if(subject < 0) {\r
-\r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getValue4(graph, containerCluster, subject, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-                       \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                               Object value = g.getValue(subject);\r
-                               if(value != null) {\r
-                                       if(result != null) {\r
-                                               procedure.exception(graph, new DatabaseException("Multiple values"));\r
-//                                             graph.dec();\r
-                                               return;\r
-                                       } else {\r
-                                               result = value;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       procedure.execute(graph, (T)"name");\r
-//                     graph.dec();\r
-                       return;\r
-\r
-               }\r
-               \r
-               ClusterImpl cluster = containerCluster;\r
-               if(!containerCluster.contains(subject)) {\r
-                       cluster = session.clusterTable.getClusterByResourceKey(subject);\r
-                       if(!cluster.isLoaded()) {\r
-                               cluster.load(session.clusterTranslator, new Runnable() {\r
-\r
-                                       @Override\r
-                                       public void run() {\r
-                                               getValue4(graph, containerCluster, subject, procedure);\r
-                                       }\r
-\r
-                               });\r
-                               return;\r
-                       }\r
-               }\r
-               \r
-        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {\r
-\r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getValue4(graph, containerCluster, subject, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-               \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                               Object value = g.getValue(subject);\r
-                               if(value != null) {\r
-                                       if(result != null) {\r
-                                               procedure.exception(graph, new DatabaseException("Multiple values"));\r
-//                                             graph.dec();\r
-                                               return;\r
-                                       } else {\r
-                                               result = value;\r
-                                       }\r
-                               }\r
-                       }\r
-                       \r
-                       if(result != null) {\r
-                               \r
-                               procedure.execute(graph, (T)result);\r
-//                             graph.state.barrier.dec();\r
-                               \r
-                       } else {\r
-                               \r
-                               if(ClusterTypeEnum.SMALL == cluster.getType())\r
-                                       getDirectValue4(graph, (ClusterSmall)cluster, subject, procedure);\r
-                               else \r
-                                       getDirectValue4(graph, (ClusterBig)cluster, subject, procedure);\r
-                       }\r
-\r
-               } else {\r
-\r
-                       if(ClusterTypeEnum.SMALL == cluster.getType())\r
-                               getDirectValue4(graph, (ClusterSmall)cluster, subject, procedure);\r
-                       else \r
-                               getDirectValue4(graph, (ClusterBig)cluster, subject, procedure);\r
-\r
-               }\r
-       \r
-       }\r
-       \r
-       private <C, T> void getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {\r
-               \r
-               Object result = null;\r
-       \r
-               if(subject < 0) {\r
-\r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getValue4(graph, containerCluster, subject, context, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-                       \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                               Object value = g.getValue(subject);\r
-                               if(value != null) {\r
-                                       if(result != null) {\r
-                                               procedure.exception(graph, new DatabaseException("Multiple values"));\r
-//                                             graph.dec();\r
-                                               return;\r
-                                       } else {\r
-                                               result = value;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       procedure.execute(graph, context, (T)"name");\r
-//                     graph.dec();\r
-                       return;\r
-\r
-               }\r
-               \r
-               ClusterImpl cluster = containerCluster;\r
-               if(!containerCluster.contains(subject)) {\r
-                       cluster = session.clusterTable.getClusterByResourceKey(subject);\r
-                       if(!cluster.isLoaded()) {\r
-                               cluster.load(session.clusterTranslator, new Runnable() {\r
-\r
-                                       @Override\r
-                                       public void run() {\r
-                                               getValue4(graph, containerCluster, subject, context, procedure);\r
-                                       }\r
-\r
-                               });\r
-                               return;\r
-                       }\r
-               }\r
-               \r
-        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {\r
-\r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getValue4(graph, containerCluster, subject, context, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-               \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                               Object value = g.getValue(subject);\r
-                               if(value != null) {\r
-                                       if(result != null) {\r
-                                               procedure.exception(graph, new DatabaseException("Multiple values"));\r
-//                                             graph.dec();\r
-                                               return;\r
-                                       } else {\r
-                                               result = value;\r
-                                       }\r
-                               }\r
-                       }\r
-                       \r
-                       if(result != null) {\r
-                               \r
-                               procedure.execute(graph, context, (T)result);\r
-//                             graph.state.barrier.dec();\r
-                               \r
-                       } else {\r
-                               \r
-                               if(ClusterTypeEnum.SMALL == cluster.getType())\r
-                                       getDirectValue4(graph, (ClusterSmall)cluster, subject, context, procedure);\r
-                               else \r
-                                       getDirectValue4(graph, (ClusterBig)cluster, subject, context, procedure);\r
-                       }\r
-\r
-               } else {\r
-\r
-                       if(ClusterTypeEnum.SMALL == cluster.getType())\r
-                               getDirectValue4(graph, (ClusterSmall)cluster, subject, context, procedure);\r
-                       else \r
-                               getDirectValue4(graph, (ClusterBig)cluster, subject, context, procedure);\r
-\r
-               }\r
-       \r
-       }\r
-\r
-       private <T> void getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final ForPossibleRelatedValueProcedure<T> procedure) {\r
-\r
-               try {\r
-\r
-                       int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);\r
-                       if(so == 0) {\r
-                               if(result == 0) {\r
-                                       procedure.exception(graph, new DatabaseException("No objects " + subject + " " + procedure.predicateKey));\r
-//                                     graph.dec();\r
-                               } else {\r
-                                       getValue4(graph, cluster, result, procedure);\r
-                               }\r
-                       } else {\r
-                               if(result == 0) {\r
-                                       getValue4(graph, cluster, so, procedure);\r
-                               } else {\r
-                                       procedure.exception(graph, new DatabaseException("Multiple objects"));\r
-//                                     graph.dec();\r
-                               }\r
-                       }\r
-\r
-               } catch (DatabaseException e) {\r
-                       e.printStackTrace();\r
-               }\r
-               \r
-       }\r
-\r
-       private <C, T> void getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {\r
-\r
-               try {\r
-\r
-                       int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);\r
-                       if(so == 0) {\r
-                               if(result == 0) {\r
-                                       procedure.exception(graph, new DatabaseException("No objects " + subject + " " + procedure.predicateKey));\r
-//                                     graph.dec();\r
-                               } else {\r
-                                       getValue4(graph, cluster, result, context, procedure);\r
-                               }\r
-                       } else {\r
-                               if(result == 0) {\r
-                                       getValue4(graph, cluster, so, context, procedure);\r
-                               } else {\r
-                                       procedure.exception(graph, new DatabaseException("Multiple objects"));\r
-//                                     graph.dec();\r
-                               }\r
-                       }\r
-\r
-               } catch (DatabaseException e) {\r
-                       e.printStackTrace();\r
-               }\r
-               \r
-       }\r
-       \r
-       public <T> void getRelatedValue4(final ReadGraphImpl graph, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {\r
-               \r
-               int result = 0;\r
-               \r
-               final int predicate = procedure.predicateKey;\r
-\r
-               if(subject < 0) {\r
-\r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getRelatedValue4(graph, subject, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-                       \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                for (int id : g.getObjects(subject, predicate)) {\r
-                       if(result != 0) {\r
-                               procedure.exception(graph, new DatabaseException("Multiple objects"));\r
-//                             graph.dec();\r
-                               return;\r
-                       } else {\r
-                               result = id;\r
-                       }\r
-                }\r
-                       }\r
-                       \r
-                       if(result == 0) {\r
-                               \r
-                       procedure.exception(graph, new DatabaseException("No objects for " + subject ));\r
-//                     graph.dec();\r
-                       return;\r
-                       \r
-                       } else {\r
-                               \r
-                               getValue4(graph, null, result, procedure);\r
-                               return;\r
-                               \r
-                       }\r
-                       \r
-               } \r
-               \r
-        final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);\r
-        if(!cluster.isLoaded()) {\r
-               cluster.load(session.clusterTranslator, new Runnable() {\r
-\r
-                               @Override\r
-                               public void run() {\r
-                                       getRelatedValue4(graph, subject, procedure);\r
-                               }\r
-                       \r
-               });\r
-               return;\r
-        }\r
-        \r
-        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {\r
-                       \r
-                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {\r
-                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, new Callback<ReadGraphImpl>()  {\r
-\r
-                                       @Override\r
-                                       public void run(ReadGraphImpl graph) {\r
-                                               getRelatedValue4(graph, subject, procedure);\r
-                                       }\r
-                                       \r
-                               });\r
-                               return;\r
-                       }\r
-               \r
-                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {\r
-                for (int id : g.getObjects(subject, predicate)) {\r
-                       if(result != 0) {\r
-                               procedure.exception(graph, new DatabaseException("Multiple objects"));\r
-//                             graph.dec();\r
-                               return;\r
-                       } else {\r
-                               result = id;\r
-                       }\r
-                }\r
-                       }\r
-                       \r
-                       getRelatedDirectValue4(graph, cluster, subject, result, procedure);\r
-                       \r
-               } else {\r
-                       \r
-                       getRelatedDirectValue4(graph, cluster, subject, 0, procedure);\r
-                       \r
-               }\r
-               \r
-       }\r
-       \r
-       private <C, T> void getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {\r
-               \r
-               try {\r
-                       byte[] bytes = cluster.getValue(subject, session.clusterTranslator);\r
-                       T value = (T)utf(bytes);\r
-                       procedure.execute(graph, context, value);\r
-               } catch (DatabaseException e) {\r
-                       procedure.execute(graph, context, null);\r
-               }\r
-\r
-//             graph.dec();\r
-               \r
-       }\r
-\r
-       private <T> void getDirectValue4(final ReadGraphImpl graph, final ClusterBig cluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {\r
-               \r
-               try {\r
-                       byte[] bytes = cluster.getValue(subject, session.clusterTranslator);\r
-                       T value = (T)utf(bytes);\r
-                       procedure.execute(graph, value);\r
-               } catch (DatabaseException e) {\r
-                       procedure.execute(graph, null);\r
-               }\r
-\r
-//             graph.dec();\r
-               \r
-       }\r
-\r
-       private <C, T> void getDirectValue4(final ReadGraphImpl graph, final ClusterBig cluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {\r
-               \r
-               try {\r
-                       byte[] bytes = cluster.getValue(subject, session.clusterTranslator);\r
-                       if(bytes == null) {\r
-                               procedure.execute(graph, context, null);\r
-                       } else {\r
-                               T value = (T)utf(bytes);\r
-                               procedure.execute(graph, context, value);\r
-                       }\r
-               } catch (DatabaseException e) {\r
-                       procedure.execute(graph, context, null);\r
-               }\r
-\r
-//             graph.dec();\r
-               \r
-       }\r
-       \r
-       private final char[] chars = new char[1024];\r
-       \r
-       private <T> void getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {\r
-\r
-               ResourceTableSmall rt = cluster.resourceTable;\r
-               ValueTableSmall vt = cluster.valueTable;\r
-\r
-               byte[] bs = vt.table;\r
-               long[] ls = rt.table;\r
-\r
-               int index = ((subject&0xFFFF) << 1) - 1 + rt.offset;\r
-\r
-               int valueIndex = (int)(ls[index] >>> 24) & 0x3FFFFF + vt.offset;\r
-\r
-               int size = (int)bs[valueIndex++]-1;\r
-               valueIndex++;\r
-               for(int i=0;i<size;i++) {\r
-                       chars[i] = (char)bs[valueIndex++];\r
-               }\r
-\r
-               T value = (T)new String(chars, 0, size);\r
-\r
-               procedure.execute(graph, value);\r
-//             graph.dec();\r
-               \r
-       }\r
-\r
-       final private String utf(byte[] bytes) {\r
-               \r
-               if(bytes == null) return null;\r
-               \r
-               int index = 0;\r
-               int length = bytes[index++]&0xff; \r
-               if(length >= 0x80) {\r
-                       if(length >= 0xc0) {\r
-                               if(length >= 0xe0) {\r
-                                       if(length >= 0xf0) {\r
-                                               length &= 0x0f;\r
-                                               length += ((bytes[index++]&0xff)<<3);\r
-                                               length += ((bytes[index++]&0xff)<<11);\r
-                                               length += ((bytes[index++]&0xff)<<19);\r
-                                               length += 0x10204080;\r
-                                       }\r
-                                       else {\r
-                                               length &= 0x1f;\r
-                                               length += ((bytes[index++]&0xff)<<4);\r
-                                               length += ((bytes[index++]&0xff)<<12);\r
-                                               length += ((bytes[index++]&0xff)<<20);\r
-                                               length += 0x204080;\r
-                                       }\r
-                               }\r
-                               else {\r
-                                       length &= 0x3f;\r
-                                       length += ((bytes[index++]&0xff)<<5);\r
-                                       length += ((bytes[index++]&0xff)<<13);\r
-                                       length += 0x4080;\r
-                               }\r
-                       }\r
-                       else {\r
-                               length &= 0x7f;\r
-                               length += ((bytes[index++]&0xff)<<6);\r
-                               length += 0x80;\r
-                       }\r
-               }\r
-               \r
-               int i = 0;\r
-               int target = length+index;\r
-               while(index < target) {\r
-                       int c = bytes[index++]&0xff;\r
-                       if(c <= 0x7F) {\r
-                               chars[i++] = (char)(c&0x7F);\r
-                       } else if (c > 0x07FF) {\r
-                               int c2 = bytes[index++]&0xff;\r
-                               int c3 = bytes[index++]&0xff;\r
-                               chars[i++] = (char)(((c&0xf)<<12) + ((c2&0x3f)<<6) + (c3&0x3f)); \r
-                       } else {\r
-                               int c2 = bytes[index++]&0xff;\r
-                               chars[i++] = (char)(((c&0x1f)<<6) + (c2&0x3f)); \r
-                       }\r
-                       \r
-                       \r
-//                     if (!((c >= 0x0001) && (c <= 0x007F))) {\r
-//                     } else {\r
-//                     }\r
-//                     \r
-//                             if ((c >= 0x0001) && (c <= 0x007F)) {\r
-//                                     bytearr[byteIndex++] = (byte)( c );\r
-//                             } else if (c > 0x07FF) {\r
-//                                     bytearr[byteIndex++] = (byte)(0xE0 | ((c >> 12) & 0x0F));\r
-//                                     bytearr[byteIndex++] = (byte)(0x80 | ((c >>  6) & 0x3F));\r
-//                                     bytearr[byteIndex++] = (byte)(0x80 | ((c >>  0) & 0x3F));\r
-//                             } else {\r
-//                                     bytearr[byteIndex++] = (byte)(0xC0 | ((c >>  6) & 0x1F));\r
-//                                     bytearr[byteIndex++] = (byte)(0x80 | ((c >>  0) & 0x3F));\r
-//                             }\r
-//                     }\r
-                       \r
-                       \r
-               }\r
-               return new String(chars, 0, i);\r
-       }\r
-       \r
-}\r
+package fi.vtt.simantics.procore.internal;
+
+import org.simantics.db.DirectStatements;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RelationInfo;
+import org.simantics.db.Resource;
+import org.simantics.db.exception.AssumptionException;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.NoSingleResultException;
+import org.simantics.db.impl.ClusterI;
+import org.simantics.db.impl.ClusterI.ClusterTypeEnum;
+import org.simantics.db.impl.ForEachObjectContextProcedure;
+import org.simantics.db.impl.ForEachObjectProcedure;
+import org.simantics.db.impl.ForPossibleRelatedValueContextProcedure;
+import org.simantics.db.impl.ForPossibleRelatedValueProcedure;
+import org.simantics.db.impl.ResourceImpl;
+import org.simantics.db.impl.TransientGraph;
+import org.simantics.db.impl.graph.ReadGraphImpl;
+import org.simantics.db.procedure.SyncContextMultiProcedure;
+import org.simantics.db.procedure.SyncContextProcedure;
+import org.simantics.db.procedure.SyncMultiProcedure;
+import org.simantics.db.procedure.SyncProcedure;
+import org.simantics.db.procore.cluster.ClusterBig;
+import org.simantics.db.procore.cluster.ClusterImpl;
+import org.simantics.db.procore.cluster.ClusterSmall;
+import org.simantics.db.procore.cluster.ResourceTableSmall;
+import org.simantics.db.procore.cluster.ValueTableSmall;
+import org.simantics.db.service.DirectQuerySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Note that the direct value retrieval in this implementation only supports
+ * String-type literals - nothing else!
+ * 
+ * This implementation is mainly intended for optimizing database indexing
+ * performance.
+ */
+public class DirectQuerySupportImpl implements DirectQuerySupport {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DirectQuerySupportImpl.class);
+
+       final private SessionImplSocket session;
+       
+       DirectQuerySupportImpl(SessionImplSocket session) {
+               this.session = session;
+       }
+
+       @Override
+       final public DirectStatements getDirectPersistentStatements(ReadGraph graph, final Resource subject) {
+               ReadGraphImpl impl = (ReadGraphImpl)graph;
+               return impl.processor.getDirectStatements(impl, subject, true);
+       }
+
+       @Override
+       final public DirectStatements getDirectStatements(ReadGraph graph, final Resource subject) {
+               ReadGraphImpl impl = (ReadGraphImpl)graph;
+               return impl.processor.getDirectStatements(impl, subject, false);
+       }
+
+       @Override
+       public RelationInfo getRelationInfo(ReadGraph graph, Resource subject) throws DatabaseException {
+               ReadGraphImpl impl = (ReadGraphImpl)graph;
+               return impl.processor.getRelationInfo(impl, subject);
+       }
+
+       @Override
+       public SyncMultiProcedure<Resource> compileForEachObject(ReadGraph graph, final Resource relation, SyncMultiProcedure<Resource> user) throws DatabaseException {
+
+               RelationInfo info = getRelationInfo(graph, relation);
+               final int predicateKey = ((ResourceImpl)relation).id;
+               return new ForEachObjectProcedure(predicateKey, info, session.queryProvider2, user);
+
+       }
+
+       @Override
+       public <C> SyncContextMultiProcedure<C, Resource> compileForEachObject(ReadGraph graph, final Resource relation, SyncContextMultiProcedure<C, Resource> user) throws DatabaseException {
+
+               RelationInfo info = getRelationInfo(graph, relation);
+               final int predicateKey = ((ResourceImpl)relation).id;
+               return new ForEachObjectContextProcedure<C>(predicateKey, info, session.queryProvider2, user);
+
+       }
+
+       @Override
+       public <T> SyncProcedure<T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, SyncProcedure<T> user) throws DatabaseException {
+
+               RelationInfo info = getRelationInfo(graph, relation);
+               final int predicateKey = ((ResourceImpl)relation).id;
+               return new ForPossibleRelatedValueProcedure<T>(predicateKey, info, user);
+
+       }
+
+       @Override
+       public <C, T> SyncContextProcedure<C, T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, SyncContextProcedure<C, T> user) throws DatabaseException {
+
+               RelationInfo info = getRelationInfo(graph, relation);
+               final int predicateKey = ((ResourceImpl)relation).id;
+               return new ForPossibleRelatedValueContextProcedure<C, T>(predicateKey, info, user);
+
+       }
+
+       @Override
+       public void forEachObjectCompiled(ReadGraph graph, Resource subject, final SyncMultiProcedure<Resource> procedure) {
+               
+        assert(subject != null);
+        
+        final ForEachObjectProcedure proc = (ForEachObjectProcedure)procedure;
+//        final RelationInfo info = proc.info;
+
+        final ReadGraphImpl impl = (ReadGraphImpl)graph;
+        final int subjectId = ((ResourceImpl)subject).id;
+
+//        int callerThread = impl.callerThread;
+//        int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;
+        
+//        impl.inc();
+        
+//        if(callerThread == suggestSchedule) {
+               
+//             if(info.isFunctional) {
+//                     querySupport.getObjects4(impl, subjectId, proc);
+//             } else {
+               session.querySupport.getObjects4(impl, subjectId, proc);
+//             }
+               
+//        } else {
+//             
+//             impl.state.barrier.inc();
+//            impl.state.barrier.dec(callerThread);
+//             
+//             queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {
+//     
+//                     @Override
+//                     public void run(int thread) {
+//                             
+//                     impl.state.barrier.inc(thread);
+//                             impl.state.barrier.dec();
+//
+//                     if(info.isFunctional) {
+//                             querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);
+//                     } else {
+//                             querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);
+//                     }
+//                             
+//                     }
+//                     
+//                     @Override
+//                     public String toString() {
+//                             return "gaff8";
+//                     }
+//     
+//             });
+//             
+//        }
+               
+       }
+
+       @Override
+       public <C> void forEachObjectCompiled(ReadGraph graph, Resource subject, C context, final SyncContextMultiProcedure<C, Resource> procedure) {
+               
+               assert(subject != null);
+
+               final ForEachObjectContextProcedure<C> proc = (ForEachObjectContextProcedure<C>)procedure;
+               final RelationInfo info = proc.info;
+
+               final ReadGraphImpl impl = (ReadGraphImpl)graph;
+               final int subjectId = ((ResourceImpl)subject).id;
+
+//             int callerThread = impl.callerThread;
+//             int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;
+
+//             impl.inc();
+
+               if(info.isFunctional) {
+                       session.querySupport.getObjects4(impl, subjectId, context, proc);
+               } else {
+                       session.querySupport.getObjects4(impl, subjectId, context, proc);
+               }
+               
+       }
+
+       @Override
+       public <T> void forPossibleRelatedValueCompiled(ReadGraph graph, Resource subject, final SyncProcedure<T> procedure) {
+               
+               assert(subject != null);
+
+        final ForPossibleRelatedValueProcedure<T> proc = (ForPossibleRelatedValueProcedure<T>)procedure;
+        
+        final ReadGraphImpl impl = (ReadGraphImpl)graph;
+        final int subjectId = ((ResourceImpl)subject).id;
+        
+//        int callerThread = impl.callerThread;
+//        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;
+        
+//        impl.inc();
+        
+//        if(callerThread == suggestSchedule) {
+               
+//             if(proc.info.isFunctional) {
+        try {
+               T result = getRelatedValue4(impl, subjectId, proc);
+               try {
+                       proc.execute(graph, result);
+               } catch (DatabaseException e2) {
+                       LOGGER.error("Unexpected exception while handling related value", e2);
+               }
+        } catch (DatabaseException e) {
+               try {
+                       proc.exception(graph, e);
+               } catch (DatabaseException e2) {
+                       LOGGER.error("Unexpected exception while handling related value", e2);
+               }
+        }
+//             } else {
+//                     getRelatedValue4(impl, subjectId, proc);
+//             }
+               
+//        } else {
+//             
+//             impl.state.barrier.inc();
+//            impl.state.barrier.dec(callerThread);
+//             
+//             queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {
+//     
+//                     @Override
+//                     public void run(int thread) {
+//
+//                     impl.state.barrier.inc(thread);
+//                     impl.state.barrier.dec();
+//                             
+//                     if(info.isFunctional) {
+//                             getRelatedValue4(impl.newAsync(thread), subjectId, proc);
+//                     } else {
+//                             getRelatedValue4(impl.newAsync(thread), subjectId, proc);
+//                     }
+//                             
+//                     }
+//                     
+//                     @Override
+//                     public String toString() {
+//                             return "gaff11";
+//                     }
+//     
+//             });
+//             
+//        }
+               
+       }
+
+       @Override
+       public <C, T> void forPossibleRelatedValueCompiled(ReadGraph graph, Resource subject, C context, final SyncContextProcedure<C, T> procedure) {
+               
+               assert(subject != null);
+
+        final ForPossibleRelatedValueContextProcedure<C, T> proc = (ForPossibleRelatedValueContextProcedure<C, T>)procedure;
+        
+        final ReadGraphImpl impl = (ReadGraphImpl)graph;
+        final int subjectId = ((ResourceImpl)subject).id;
+        
+//        int callerThread = impl.callerThread;
+//        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;
+        
+//        impl.inc();
+               
+//        if(proc.info.isFunctional) {
+//        } else {
+//             getRelatedValue4(impl, subjectId, context, proc);
+//        }
+
+               try {
+                       T result = getRelatedValue4(impl, subjectId, context, proc);
+               proc.execute(graph, context, result);
+               } catch (DatabaseException e) {
+               proc.execute(graph, context, null);
+               }
+               
+       }
+       
+/*     @Override
+       public <T> void forPossibleType(final AsyncReadGraph graph, Resource subject, final AsyncProcedure<Resource> procedure) {
+               
+        assert(subject != null);
+
+        final ReadGraphImpl impl = (ReadGraphImpl)graph;
+        final int subjectId = ((ResourceImpl)subject).id;
+
+        try {
+               
+               final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);
+               if(!cluster.isLoaded()) {
+
+//                     impl.state.inc(0);
+                       
+                       session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {
+               
+                                       @Override
+                                       public void run() {
+
+                                       try {
+
+                                                       int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
+                                               procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));
+
+//                                             impl.state.dec(0);                                              
+                                               
+                                       } catch (DatabaseException e) {
+                                               e.printStackTrace();
+                                       }
+               
+                                       }
+               
+                               });
+                               
+               } else {
+
+                       int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
+                       procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));
+                       
+               }
+               
+        } catch (DatabaseException e) {
+               e.printStackTrace();
+        }
+       
+       
+               
+       }
+       
+       */
+
+       @Override
+       public <C> void forPossibleDirectType(final ReadGraph graph, Resource subject, final C context, final SyncContextProcedure<C, Resource> procedure) {
+               
+        assert(subject != null);
+
+        final ReadGraphImpl impl = (ReadGraphImpl)graph;
+        final int subjectId = ((ResourceImpl)subject).id;
+        
+        try {
+               
+               final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);
+               if(!cluster.isLoaded()) {
+
+//                     impl.state.inc(0);
+                       
+                       session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {
+               
+                                       @Override
+                                       public void run() {
+
+                                       try {
+
+                                               ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);
+                                               if(ClusterI.CompleteTypeEnum.InstanceOf == type) {
+                                                       int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
+                                                       procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));
+                                               } else {
+                                               procedure.execute(graph, context, null);
+                                               }
+
+//                                             impl.state.dec(0);
+                                               
+                                       } catch (DatabaseException e) {
+                                               LOGGER.error("forPossibleDirectType requestCluster callback failed", e);
+                                       }
+               
+                                       }
+               
+                               });
+                               
+               } else {
+
+                       ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);
+                       if(ClusterI.CompleteTypeEnum.InstanceOf == type) {
+                               int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
+                               procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));
+                       } else {
+                       procedure.execute(graph, context, null);
+                       }
+                       
+               }
+               
+        } catch (DatabaseException e) {
+               
+               procedure.execute(graph, context, null);
+               
+        } catch (Throwable t) {
+
+               LOGGER.error("forPossibleDirectType failed unexpectedly", t);
+               procedure.execute(graph, context, null);
+               
+        }
+               
+       }
+       
+       private <C, T> T getRelatedValue4(final ReadGraphImpl graph, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) throws DatabaseException {
+               
+               int result = 0;
+               
+               final int predicate = procedure.predicateKey;
+
+               if(subject < 0) {
+
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
+                           return getRelatedValue4(graph, subject, context, procedure);
+                       }
+                       
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                for (int id : g.getObjects(subject, predicate)) {
+                       if(result != 0) {
+                               throw new DatabaseException("Multiple objects");
+                       } else {
+                               result = id;
+                       }
+                }
+                       }
+                       
+                       if(result == 0) {
+                               throw new DatabaseException("No objects for " + subject );
+                       } else {
+                               return getValue4(graph, null, result, context, procedure);
+                       }
+                       
+               } 
+               
+        final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);
+        if(!cluster.isLoaded()) {
+               cluster.load();
+               return getRelatedValue4(graph, subject, context, procedure);
+        }
+        
+        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
+                       
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
+                               return getRelatedValue4(graph, subject, context, procedure);
+                       }
+               
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                for (int id : g.getObjects(subject, predicate)) {
+                       if(result != 0) {
+                               throw new DatabaseException("Multiple objects");
+                       } else {
+                               result = id;
+                       }
+                }
+                       }
+                       
+                       return getRelatedDirectValue4(graph, cluster, subject, result, context, procedure);
+                       
+               } else {
+                       
+                       return getRelatedDirectValue4(graph, cluster, subject, 0, context, procedure);
+                       
+               }
+               
+       }
+       
+       @SuppressWarnings("unchecked")
+       private <T> T getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) throws DatabaseException {
+               
+               Object result = null;
+       
+               if(subject < 0) {
+
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
+                           return getValue4(graph, containerCluster, subject, procedure);
+                       }
+                       
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                               Object value = g.getValue(subject);
+                               if(value != null) {
+                                       if(result != null) {
+                                               throw new DatabaseException("Multiple values");
+                                       } else {
+                                               result = value;
+                                       }
+                               }
+                       }
+                       
+                       // FIXME: throw something here instead
+                       return (T)"name";
+                       
+               }
+               
+               ClusterImpl cluster = containerCluster;
+               if(!containerCluster.contains(subject)) {
+                       cluster = session.clusterTable.getClusterByResourceKey(subject);
+                       if(!cluster.isLoaded()) {
+                               cluster.load();
+                               return getValue4(graph, containerCluster, subject, procedure);
+                       }
+               }
+               
+        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
+
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
+                           return getValue4(graph, containerCluster, subject, procedure);
+                       }
+               
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                               Object value = g.getValue(subject);
+                               if(value != null) {
+                                       if(result != null) {
+                                               throw new DatabaseException("Multiple values");
+                                       } else {
+                                               result = value;
+                                       }
+                               }
+                       }
+                       
+                       if(result != null) {
+                               return (T)result;
+                       } else {
+                               if(ClusterTypeEnum.SMALL == cluster.getType())
+                                       return getDirectValue4(graph, (ClusterSmall)cluster, subject);
+                               else 
+                                       return getDirectValue4(graph, (ClusterBig)cluster, subject);
+                       }
+
+               } else {
+
+                       if(ClusterTypeEnum.SMALL == cluster.getType())
+                               return getDirectValue4(graph, (ClusterSmall)cluster, subject);
+                       else 
+                               return getDirectValue4(graph, (ClusterBig)cluster, subject);
+
+               }
+       
+       }
+       
+       @SuppressWarnings("unchecked")
+       private <C, T> T getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) throws DatabaseException {
+               
+               Object result = null;
+       
+               if(subject < 0) {
+
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
+                           return getValue4(graph, containerCluster, subject, context, procedure);
+                       }
+                       
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                               Object value = g.getValue(subject);
+                               if(value != null) {
+                                       if(result != null) {
+                                               throw new DatabaseException("Multiple values");
+                                       } else {
+                                               result = value;
+                                       }
+                               }
+                       }
+
+                       // FIXME: throw something here instead
+                       return (T)"name";
+                       
+               }
+               
+               ClusterImpl cluster = containerCluster;
+               if(!containerCluster.contains(subject)) {
+                       cluster = session.clusterTable.getClusterByResourceKey(subject);
+                       if(!cluster.isLoaded()) {
+                               cluster.load();
+                               return getValue4(graph, containerCluster, subject, context, procedure);
+                       }
+               }
+               
+        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
+
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
+                           return getValue4(graph, containerCluster, subject, context, procedure);
+                       }
+               
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                               Object value = g.getValue(subject);
+                               if(value != null) {
+                                       if(result != null) {
+                                               throw new DatabaseException("Multiple values");
+                                       } else {
+                                               result = value;
+                                       }
+                               }
+                       }
+                       
+                       if(result != null) {
+                               return (T)result;
+                       } else {
+                               if(ClusterTypeEnum.SMALL == cluster.getType())
+                                       return getDirectValue4(graph, (ClusterSmall)cluster, subject);
+                               else 
+                                       return getDirectValue4(graph, (ClusterBig)cluster, subject);
+                       }
+
+               } else {
+
+                       if(ClusterTypeEnum.SMALL == cluster.getType())
+                               return getDirectValue4(graph, (ClusterSmall)cluster, subject);
+                       else 
+                               return getDirectValue4(graph, (ClusterBig)cluster, subject);
+
+               }
+       
+       }
+
+       private <T> T getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final ForPossibleRelatedValueProcedure<T> procedure) throws DatabaseException {
+
+               int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);
+               if(so == 0) {
+                       if(result == 0) {
+                               throw new DatabaseException("No objects " + subject + " " + procedure.predicateKey);
+                       } else {
+                               return getValue4(graph, cluster, result, procedure);
+                       }
+               } else {
+                       if(result == 0) {
+                               return getValue4(graph, cluster, so, procedure);
+                       } else {
+                               throw new DatabaseException("Multiple objects");
+                       }
+               }
+
+       }
+       
+       private <C, T> T getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) throws DatabaseException {
+
+               int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);
+               if(so == 0) {
+                       if(result == 0) {
+                               throw new NoSingleResultException("No objects " + subject + " " + procedure.predicateKey, result);
+                       } else {
+                               return getValue4(graph, cluster, result, context, procedure);
+                       }
+               } else {
+                       if(result == 0) {
+                               return getValue4(graph, cluster, so, context, procedure);
+                       } else {
+                               throw new NoSingleResultException("Multiple objects for " + subject + " " + procedure.predicateKey, result);
+                       }
+               }
+               
+       }
+       
+       public <T> T getRelatedValue4(final ReadGraphImpl graph, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) throws DatabaseException {
+               
+               int result = 0;
+               
+               final int predicate = procedure.predicateKey;
+
+               if(subject < 0) {
+
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
+                           return getRelatedValue4(graph, subject, procedure);
+                       }
+                       
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                for (int id : g.getObjects(subject, predicate)) {
+                       if(result != 0) {
+                               throw new DatabaseException("Multiple objects");
+                       } else {
+                               result = id;
+                       }
+                }
+                       }
+                       
+                       if(result == 0) {
+                               throw new DatabaseException("No objects for " + subject );
+                       } else {
+                               return getValue4(graph, null, result, procedure);
+                       }
+                       
+               } 
+               
+        final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);
+        if(!cluster.isLoaded()) {
+               cluster.load();
+               return getRelatedValue4(graph, subject, procedure);
+        }
+        
+        if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
+                       
+                       if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
+                           SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
+                           return getRelatedValue4(graph, subject, procedure);
+                       }
+               
+                       for(TransientGraph g : session.virtualGraphServerSupport.providers) {
+                for (int id : g.getObjects(subject, predicate)) {
+                       if(result != 0) {
+                               throw new DatabaseException("Multiple objects");
+                       } else {
+                               result = id;
+                       }
+                }
+                       }
+                       
+                       return getRelatedDirectValue4(graph, cluster, subject, result, procedure);
+                       
+               } else {
+                       
+                       return getRelatedDirectValue4(graph, cluster, subject, 0, procedure);
+                       
+               }
+               
+       }
+
+       /*
+       private <C, T> void getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {
+               
+               try {
+                       byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
+                       T value = (T)utf(bytes);
+                       procedure.execute(graph, context, value);
+               } catch (DatabaseException e) {
+                       procedure.execute(graph, context, null);
+               }
+
+//             graph.dec();
+               
+       }
+       */
+
+       @SuppressWarnings("unchecked")
+       private <T> T getDirectValue4(final ReadGraphImpl graph, final ClusterBig cluster, final int subject) throws DatabaseException {
+
+               byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
+               return (T) utf(bytes, 0);
+
+       }
+
+       @SuppressWarnings("unchecked")
+       private <T> T getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject) throws DatabaseException {
+               // Note: this code avoids creating an intermediate byte[]
+               // to store the encoded string bytes and reads the UTF string
+               // from the value table byte[] directly into String instead.
+
+               ResourceTableSmall rt = cluster.resourceTable;
+               ValueTableSmall vt = cluster.valueTable;
+
+               byte[] bs = vt.table;
+               long[] ls = rt.table;
+
+               int index = ((subject&0xFFF) << 1) - 1 + rt.offset;
+               int valueIndex = ((int)(ls[index] >>> 24) & 0x3FFFFF) + vt.offset;
+
+               int size = bs[valueIndex++];
+               if (size < 0) // two byte size
+                       size = (int)(((size & 0x7F) << 8) | (bs[valueIndex++] & 0xFF));
+               if (size <= 0)
+                       throw new DatabaseException("No value for " + subject); 
+
+               return (T) utf(bs, valueIndex);
+       }
+
+       private static final String utf(byte[] bytes, int offset) throws AssumptionException {
+
+               if(bytes == null) return null;
+
+               // Read databoard int32 using Length encoding
+               // https://dev.simantics.org/index.php/Databoard_Specification#Length
+               int index = offset;
+               int length = bytes[index++]&0xff; 
+               if(length >= 0x80) {
+                       if(length >= 0xc0) {
+                               if(length >= 0xe0) {
+                                       if(length >= 0xf0) {
+                                               length &= 0x0f;
+                                               length += ((bytes[index++]&0xff)<<3);
+                                               length += ((bytes[index++]&0xff)<<11);
+                                               length += ((bytes[index++]&0xff)<<19);
+                                               length += 0x10204080;
+                                       }
+                                       else {
+                                               length &= 0x1f;
+                                               length += ((bytes[index++]&0xff)<<4);
+                                               length += ((bytes[index++]&0xff)<<12);
+                                               length += ((bytes[index++]&0xff)<<20);
+                                               length += 0x204080;
+                                       }
+                               }
+                               else {
+                                       length &= 0x3f;
+                                       length += ((bytes[index++]&0xff)<<5);
+                                       length += ((bytes[index++]&0xff)<<13);
+                                       length += 0x4080;
+                               }
+                       }
+                       else {
+                               length &= 0x7f;
+                               length += ((bytes[index++]&0xff)<<6);
+                               length += 0x80;
+                       }
+               }
+
+               // Copied from DataInputStream
+               int utflen = length;
+               char[] chararr = new char[utflen];
+
+               int c, char2, char3;
+               int count = index;
+               int target = index + length;
+               int chararr_count=0;
+
+               while (count < target) {
+                       c = (int) bytes[count] & 0xff;
+                       if (c > 127) break;
+                       count++;
+                       chararr[chararr_count++]=(char)c;
+               }
+
+               while (count < target) {
+                       c = (int) bytes[count] & 0xff;
+                       switch (c >> 4) {
+                       case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+                               /* 0xxxxxxx*/
+                               count++;
+                               chararr[chararr_count++]=(char)c;
+                               break;
+                       case 12: case 13:
+                               /* 110x xxxx   10xx xxxx*/
+                               count += 2;
+                               if (count > target)
+                                       throw new AssumptionException(
+                                                       "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
+                               char2 = (int) bytes[count-1];
+                               if ((char2 & 0xC0) != 0x80)
+                                       throw new AssumptionException(
+                                                       "malformed input around byte " + count); 
+                               chararr[chararr_count++]=(char)(((c & 0x1F) << 6) | 
+                                               (char2 & 0x3F));  
+                               break;
+                       case 14:
+                               /* 1110 xxxx  10xx xxxx  10xx xxxx */
+                               count += 3;
+                               if (count > target)
+                                       throw new AssumptionException(
+                                                       "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
+                               char2 = (int) bytes[count-2];
+                               char3 = (int) bytes[count-1];
+                               if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
+                                       throw new AssumptionException(
+                                                       "malformed input around byte " + (count-1));
+                               chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
+                                               ((char2 & 0x3F) << 6)  |
+                                               ((char3 & 0x3F) << 0));
+                               break;
+                       default:
+                               /* 10xx xxxx,  1111 xxxx */
+                               throw new AssumptionException(
+                                               "malformed input around byte " + count);
+                       }
+               }
+
+               // The number of chars produced may be less than utflen
+               return new String(chararr, 0, chararr_count);
+       }
+
+}