]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/DirectQuerySupportImpl.java
Backported b75a6bbc for release/1.35.0
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / DirectQuerySupportImpl.java
1 package fi.vtt.simantics.procore.internal;
2
3 import org.simantics.db.AsyncReadGraph;
4 import org.simantics.db.DirectStatements;
5 import org.simantics.db.ReadGraph;
6 import org.simantics.db.RelationInfo;
7 import org.simantics.db.Resource;
8 import org.simantics.db.common.procedure.wrapper.NoneToAsyncProcedure;
9 import org.simantics.db.common.procedure.wrapper.SyncToAsyncProcedure;
10 import org.simantics.db.exception.AssumptionException;
11 import org.simantics.db.exception.DatabaseException;
12 import org.simantics.db.exception.NoSingleResultException;
13 import org.simantics.db.impl.ClusterI;
14 import org.simantics.db.impl.ClusterI.ClusterTypeEnum;
15 import org.simantics.db.impl.ForEachObjectContextProcedure;
16 import org.simantics.db.impl.ForEachObjectProcedure;
17 import org.simantics.db.impl.ForPossibleRelatedValueContextProcedure;
18 import org.simantics.db.impl.ForPossibleRelatedValueProcedure;
19 import org.simantics.db.impl.ResourceImpl;
20 import org.simantics.db.impl.TransientGraph;
21 import org.simantics.db.impl.graph.ReadGraphImpl;
22 import org.simantics.db.procedure.AsyncContextMultiProcedure;
23 import org.simantics.db.procedure.AsyncContextProcedure;
24 import org.simantics.db.procedure.AsyncMultiProcedure;
25 import org.simantics.db.procedure.AsyncProcedure;
26 import org.simantics.db.procedure.Procedure;
27 import org.simantics.db.procedure.SyncProcedure;
28 import org.simantics.db.procore.cluster.ClusterBig;
29 import org.simantics.db.procore.cluster.ClusterImpl;
30 import org.simantics.db.procore.cluster.ClusterSmall;
31 import org.simantics.db.procore.cluster.ResourceTableSmall;
32 import org.simantics.db.procore.cluster.ValueTableSmall;
33 import org.simantics.db.request.AsyncRead;
34 import org.simantics.db.service.DirectQuerySupport;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Note that the direct value retrieval in this implementation only supports
40  * String-type literals - nothing else!
41  * 
42  * This implementation is mainly intended for optimizing database indexing
43  * performance.
44  */
45 public class DirectQuerySupportImpl implements DirectQuerySupport {
46
47     private static final Logger LOGGER = LoggerFactory.getLogger(DirectQuerySupportImpl.class);
48
49         final private SessionImplSocket session;
50         
51         DirectQuerySupportImpl(SessionImplSocket session) {
52                 this.session = session;
53         }
54
55         @Override
56         final public void forEachDirectPersistentStatement(AsyncReadGraph graph, final Resource subject, final AsyncProcedure<DirectStatements> procedure) {
57                 ReadGraphImpl impl = (ReadGraphImpl)graph;
58                 impl.processor.forEachDirectStatement(impl, subject, procedure, true);
59         }
60
61         @Override
62         final public void forEachDirectStatement(AsyncReadGraph graph, final Resource subject, final AsyncProcedure<DirectStatements> procedure) {
63                 ReadGraphImpl impl = (ReadGraphImpl)graph;
64                 impl.processor.forEachDirectStatement(impl, subject, procedure, false);
65         }
66
67         @Override
68         public void forEachDirectStatement(AsyncReadGraph graph, Resource subject, SyncProcedure<DirectStatements> procedure) {
69                 forEachDirectStatement(graph, subject, new SyncToAsyncProcedure<DirectStatements>(procedure));
70         }
71
72         @Override
73         public void forEachDirectStatement(AsyncReadGraph graph, Resource subject, Procedure<DirectStatements> procedure) {
74                 ReadGraphImpl impl = (ReadGraphImpl)graph;
75                 impl.processor.forEachDirectStatement(impl, subject, procedure);
76         }
77
78         @Override
79         public void forRelationInfo(AsyncReadGraph graph, Resource subject, AsyncProcedure<RelationInfo> procedure) {
80                 ReadGraphImpl impl = (ReadGraphImpl)graph;
81                 impl.processor.forRelationInfo(impl, subject, procedure);
82         }
83
84         @Override
85         public void forRelationInfo(AsyncReadGraph graph, Resource subject, SyncProcedure<RelationInfo> procedure) {
86                 forRelationInfo(graph, subject, new SyncToAsyncProcedure<RelationInfo>(procedure));
87         }
88
89         @Override
90         public void forRelationInfo(AsyncReadGraph graph, Resource subject, Procedure<RelationInfo> procedure) {
91                 forRelationInfo(graph, subject, new NoneToAsyncProcedure<RelationInfo>(procedure));
92         }
93
94         @Override
95         public AsyncMultiProcedure<Resource> compileForEachObject(ReadGraph graph, final Resource relation, AsyncMultiProcedure<Resource> user) {
96                 
97                 try {
98                         RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {
99
100                                 @Override
101                                 public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {
102                                         forRelationInfo(graph, relation, procedure);
103                                 }
104
105                 @Override
106                     public int threadHash() {
107                         return hashCode();
108                     }
109
110                                 @Override
111                                 public int getFlags() {
112                                         return 0;
113                                 }
114
115                         });
116                 final int predicateKey = ((ResourceImpl)relation).id;
117                         return new ForEachObjectProcedure(predicateKey, info, session.queryProvider2, user);
118                 } catch (DatabaseException e) {
119                         return null;
120                 }               
121         
122         }
123
124         @Override
125         public <C> AsyncContextMultiProcedure<C, Resource> compileForEachObject(ReadGraph graph, final Resource relation, AsyncContextMultiProcedure<C, Resource> user) {
126                 
127                 try {
128                         RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {
129
130                                 @Override
131                                 public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {
132                                         forRelationInfo(graph, relation, procedure);
133                                 }
134
135                 @Override
136                     public int threadHash() {
137                         return hashCode();
138                     }
139
140                                 @Override
141                                 public int getFlags() {
142                                         return 0;
143                                 }
144
145                         });
146                 final int predicateKey = ((ResourceImpl)relation).id;
147                         return new ForEachObjectContextProcedure<C>(predicateKey, info, session.queryProvider2, user);
148                 } catch (DatabaseException e) {
149                         return null;
150                 }               
151         
152         }
153         
154         @Override
155         public <T> AsyncProcedure<T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, AsyncProcedure<T> user) {
156                 
157                 try {
158                         RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {
159
160                                 @Override
161                                 public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {
162                                         forRelationInfo(graph, relation, procedure);
163                                 }
164
165                 @Override
166                     public int threadHash() {
167                         return hashCode();
168                     }
169
170                                 @Override
171                                 public int getFlags() {
172                                         return 0;
173                                 }
174
175                         });
176                 final int predicateKey = ((ResourceImpl)relation).id;
177                         return new ForPossibleRelatedValueProcedure<T>(predicateKey, info, user);
178                 } catch (DatabaseException e) {
179                         return null;
180                 }               
181         
182         }
183
184         @Override
185         public <C, T> AsyncContextProcedure<C, T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, AsyncContextProcedure<C, T> user) {
186                 
187                 try {
188                         RelationInfo info = graph.syncRequest(new AsyncRead<RelationInfo>() {
189
190                                 @Override
191                                 public void perform(AsyncReadGraph graph, AsyncProcedure<RelationInfo> procedure) {
192                                         forRelationInfo(graph, relation, procedure);
193                                 }
194
195                 @Override
196                     public int threadHash() {
197                         return hashCode();
198                     }
199
200                                 @Override
201                                 public int getFlags() {
202                                         return 0;
203                                 }
204
205                         });
206                 final int predicateKey = ((ResourceImpl)relation).id;
207                         return new ForPossibleRelatedValueContextProcedure<C, T>(predicateKey, info, user);
208                 } catch (DatabaseException e) {
209                         return null;
210                 }               
211         
212         }
213         
214         @Override
215         public void forEachObjectCompiled(AsyncReadGraph graph, Resource subject, final AsyncMultiProcedure<Resource> procedure) {
216                 
217         assert(subject != null);
218         
219         final ForEachObjectProcedure proc = (ForEachObjectProcedure)procedure;
220 //        final RelationInfo info = proc.info;
221
222         final ReadGraphImpl impl = (ReadGraphImpl)graph;
223         final int subjectId = ((ResourceImpl)subject).id;
224
225 //        int callerThread = impl.callerThread;
226 //        int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;
227         
228 //        impl.inc();
229         
230 //        if(callerThread == suggestSchedule) {
231                 
232 //              if(info.isFunctional) {
233 //                      querySupport.getObjects4(impl, subjectId, proc);
234 //              } else {
235                 session.querySupport.getObjects4(impl, subjectId, proc);
236 //              }
237                 
238 //        } else {
239 //              
240 //              impl.state.barrier.inc();
241 //            impl.state.barrier.dec(callerThread);
242 //              
243 //              queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {
244 //      
245 //                      @Override
246 //                      public void run(int thread) {
247 //                              
248 //                      impl.state.barrier.inc(thread);
249 //                              impl.state.barrier.dec();
250 //
251 //                      if(info.isFunctional) {
252 //                              querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);
253 //                      } else {
254 //                              querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);
255 //                      }
256 //                              
257 //                      }
258 //                      
259 //                      @Override
260 //                      public String toString() {
261 //                              return "gaff8";
262 //                      }
263 //      
264 //              });
265 //              
266 //        }
267                 
268         }
269
270         @Override
271         public <C> void forEachObjectCompiled(AsyncReadGraph graph, Resource subject, C context, final AsyncContextMultiProcedure<C, Resource> procedure) {
272                 
273                 assert(subject != null);
274
275                 final ForEachObjectContextProcedure<C> proc = (ForEachObjectContextProcedure<C>)procedure;
276                 final RelationInfo info = proc.info;
277
278                 final ReadGraphImpl impl = (ReadGraphImpl)graph;
279                 final int subjectId = ((ResourceImpl)subject).id;
280
281 //              int callerThread = impl.callerThread;
282 //              int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;
283
284 //              impl.inc();
285
286                 if(info.isFunctional) {
287                         session.querySupport.getObjects4(impl, subjectId, context, proc);
288                 } else {
289                         session.querySupport.getObjects4(impl, subjectId, context, proc);
290                 }
291                 
292         }
293         
294         @Override
295         public <T> void forPossibleRelatedValueCompiled(AsyncReadGraph graph, Resource subject, final AsyncProcedure<T> procedure) {
296                 
297                 assert(subject != null);
298
299         final ForPossibleRelatedValueProcedure<T> proc = (ForPossibleRelatedValueProcedure<T>)procedure;
300         final RelationInfo info = proc.info;
301         
302         final ReadGraphImpl impl = (ReadGraphImpl)graph;
303         final int subjectId = ((ResourceImpl)subject).id;
304         
305 //        int callerThread = impl.callerThread;
306 //        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;
307         
308 //        impl.inc();
309         
310 //        if(callerThread == suggestSchedule) {
311                 
312                 if(info.isFunctional) {
313                         getRelatedValue4(impl, subjectId, proc);
314                 } else {
315                         getRelatedValue4(impl, subjectId, proc);
316                 }
317                 
318 //        } else {
319 //              
320 //              impl.state.barrier.inc();
321 //            impl.state.barrier.dec(callerThread);
322 //              
323 //              queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {
324 //      
325 //                      @Override
326 //                      public void run(int thread) {
327 //
328 //                      impl.state.barrier.inc(thread);
329 //                      impl.state.barrier.dec();
330 //                              
331 //                      if(info.isFunctional) {
332 //                              getRelatedValue4(impl.newAsync(thread), subjectId, proc);
333 //                      } else {
334 //                              getRelatedValue4(impl.newAsync(thread), subjectId, proc);
335 //                      }
336 //                              
337 //                      }
338 //                      
339 //                      @Override
340 //                      public String toString() {
341 //                              return "gaff11";
342 //                      }
343 //      
344 //              });
345 //              
346 //        }
347                 
348         }
349
350         @Override
351         public <C, T> void forPossibleRelatedValueCompiled(AsyncReadGraph graph, Resource subject, C context, final AsyncContextProcedure<C, T> procedure) {
352                 
353                 assert(subject != null);
354
355         final ForPossibleRelatedValueContextProcedure<C, T> proc = (ForPossibleRelatedValueContextProcedure<C, T>)procedure;
356         final RelationInfo info = proc.info;
357         
358         final ReadGraphImpl impl = (ReadGraphImpl)graph;
359         final int subjectId = ((ResourceImpl)subject).id;
360         
361 //        int callerThread = impl.callerThread;
362 //        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;
363         
364 //        impl.inc();
365                 
366         if(info.isFunctional) {
367                 getRelatedValue4(impl, subjectId, context, proc);
368         } else {
369                 getRelatedValue4(impl, subjectId, context, proc);
370         }
371                 
372         }
373         
374         @Override
375         public <T> void forPossibleType(final AsyncReadGraph graph, Resource subject, final AsyncProcedure<Resource> procedure) {
376                 
377         assert(subject != null);
378
379         final ReadGraphImpl impl = (ReadGraphImpl)graph;
380         final int subjectId = ((ResourceImpl)subject).id;
381
382         try {
383                 
384                 final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);
385                 if(!cluster.isLoaded()) {
386
387 //                      impl.state.inc(0);
388                         
389                         session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {
390                 
391                                         @Override
392                                         public void run() {
393
394                                         try {
395
396                                                         int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
397                                                 procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));
398
399 //                                              impl.state.dec(0);                                              
400                                                 
401                                         } catch (DatabaseException e) {
402                                                 e.printStackTrace();
403                                         }
404                 
405                                         }
406                 
407                                 });
408                                 
409                 } else {
410
411                         int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
412                         procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));
413                         
414                 }
415                 
416         } catch (DatabaseException e) {
417                 e.printStackTrace();
418         }
419         
420         
421                 
422         }
423
424         @Override
425         public <C> void forPossibleDirectType(final AsyncReadGraph graph, Resource subject, final C context, final AsyncContextProcedure<C, Resource> procedure) {
426                 
427         assert(subject != null);
428
429         final ReadGraphImpl impl = (ReadGraphImpl)graph;
430         final int subjectId = ((ResourceImpl)subject).id;
431         
432         try {
433                 
434                 final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);
435                 if(!cluster.isLoaded()) {
436
437 //                      impl.state.inc(0);
438                         
439                         session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {
440                 
441                                         @Override
442                                         public void run() {
443
444                                         try {
445
446                                                 ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);
447                                                 if(ClusterI.CompleteTypeEnum.InstanceOf == type) {
448                                                         int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
449                                                         procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));
450                                                 } else {
451                                                 procedure.execute(graph, context, null);
452                                                 }
453
454 //                                              impl.state.dec(0);
455                                                 
456                                         } catch (DatabaseException e) {
457                                                 e.printStackTrace();
458                                         }
459                 
460                                         }
461                 
462                                 });
463                                 
464                 } else {
465
466                         ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);
467                         if(ClusterI.CompleteTypeEnum.InstanceOf == type) {
468                                 int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
469                                 procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));
470                         } else {
471                         procedure.execute(graph, context, null);
472                         }
473                         
474                 }
475                 
476         } catch (DatabaseException e) {
477                 
478                 procedure.execute(graph, context, null);
479                 
480         } catch (Throwable t) {
481
482                 t.printStackTrace();
483                 procedure.execute(graph, context, null);
484                 
485         }
486                 
487         }
488         
489         
490         private <C, T> void getRelatedValue4(final ReadGraphImpl graph, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {
491                 
492                 int result = 0;
493                 
494                 final int predicate = procedure.predicateKey;
495
496                 if(subject < 0) {
497
498                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
499                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate,
500                                     g -> getRelatedValue4(g, subject, context, procedure)
501                                 );
502                                 return;
503                         }
504                         
505                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
506                 for (int id : g.getObjects(subject, predicate)) {
507                         if(result != 0) {
508                                 procedure.exception(graph, new DatabaseException("Multiple objects"));
509 //                              graph.dec();
510                                 return;
511                         } else {
512                                 result = id;
513                         }
514                 }
515                         }
516                         
517                         if(result == 0) {
518                                 
519                         procedure.exception(graph, new DatabaseException("No objects for " + subject ));
520 //                      graph.dec();
521                         return;
522                         
523                         } else {
524                                 
525                                 getValue4(graph, null, result, context, procedure);
526                                 return;
527                                 
528                         }
529                         
530                 } 
531                 
532         final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);
533         if(!cluster.isLoaded()) {
534                 cluster.load(session.clusterTranslator, () -> getRelatedValue4(graph, subject, context, procedure));
535                 return;
536         }
537         
538         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
539                         
540                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
541                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate,
542                                     g -> getRelatedValue4(g, subject, context, procedure)
543                                 );
544                                 return;
545                         }
546                 
547                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
548                 for (int id : g.getObjects(subject, predicate)) {
549                         if(result != 0) {
550                                 procedure.exception(graph, new DatabaseException("Multiple objects"));
551 //                              graph.dec();
552                                 return;
553                         } else {
554                                 result = id;
555                         }
556                 }
557                         }
558                         
559                         getRelatedDirectValue4(graph, cluster, subject, result, context, procedure);
560                         
561                 } else {
562                         
563                         getRelatedDirectValue4(graph, cluster, subject, 0, context, procedure);
564                         
565                 }
566                 
567         }
568         
569         @SuppressWarnings("unchecked")
570         private <T> void getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {
571                 
572                 Object result = null;
573         
574                 if(subject < 0) {
575
576                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
577                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, 
578                                     g -> getValue4(g, containerCluster, subject, procedure)
579                             );
580                                 return;
581                         }
582                         
583                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
584                                 Object value = g.getValue(subject);
585                                 if(value != null) {
586                                         if(result != null) {
587                                                 procedure.exception(graph, new DatabaseException("Multiple values"));
588 //                                              graph.dec();
589                                                 return;
590                                         } else {
591                                                 result = value;
592                                         }
593                                 }
594                         }
595
596                         procedure.execute(graph, (T)"name");
597 //                      graph.dec();
598                         return;
599
600                 }
601                 
602                 ClusterImpl cluster = containerCluster;
603                 if(!containerCluster.contains(subject)) {
604                         cluster = session.clusterTable.getClusterByResourceKey(subject);
605                         if(!cluster.isLoaded()) {
606                                 cluster.load(session.clusterTranslator, new Runnable() {
607
608                                         @Override
609                                         public void run() {
610                                                 getValue4(graph, containerCluster, subject, procedure);
611                                         }
612
613                                 });
614                                 return;
615                         }
616                 }
617                 
618         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
619
620                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
621                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject,
622                             g -> getValue4(g, containerCluster, subject, procedure)
623                             );
624                                 return;
625                         }
626                 
627                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
628                                 Object value = g.getValue(subject);
629                                 if(value != null) {
630                                         if(result != null) {
631                                                 procedure.exception(graph, new DatabaseException("Multiple values"));
632 //                                              graph.dec();
633                                                 return;
634                                         } else {
635                                                 result = value;
636                                         }
637                                 }
638                         }
639                         
640                         if(result != null) {
641                                 
642                                 procedure.execute(graph, (T)result);
643 //                              graph.state.barrier.dec();
644                                 
645                         } else {
646                                 
647                                 if(ClusterTypeEnum.SMALL == cluster.getType())
648                                         getDirectValue4(graph, (ClusterSmall)cluster, subject, procedure);
649                                 else 
650                                         getDirectValue4(graph, (ClusterBig)cluster, subject, procedure);
651                         }
652
653                 } else {
654
655                         if(ClusterTypeEnum.SMALL == cluster.getType())
656                                 getDirectValue4(graph, (ClusterSmall)cluster, subject, procedure);
657                         else 
658                                 getDirectValue4(graph, (ClusterBig)cluster, subject, procedure);
659
660                 }
661         
662         }
663         
664         @SuppressWarnings("unchecked")
665         private <C, T> void getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {
666                 
667                 Object result = null;
668         
669                 if(subject < 0) {
670
671                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
672                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject,
673                                     g -> getValue4(g, containerCluster, subject, context, procedure)
674                             );
675                                 return;
676                         }
677                         
678                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
679                                 Object value = g.getValue(subject);
680                                 if(value != null) {
681                                         if(result != null) {
682                                                 procedure.exception(graph, new DatabaseException("Multiple values"));
683 //                                              graph.dec();
684                                                 return;
685                                         } else {
686                                                 result = value;
687                                         }
688                                 }
689                         }
690
691                         procedure.execute(graph, context, (T)"name");
692 //                      graph.dec();
693                         return;
694
695                 }
696                 
697                 ClusterImpl cluster = containerCluster;
698                 if(!containerCluster.contains(subject)) {
699                         cluster = session.clusterTable.getClusterByResourceKey(subject);
700                         if(!cluster.isLoaded()) {
701                                 cluster.load(session.clusterTranslator, new Runnable() {
702
703                                         @Override
704                                         public void run() {
705                                                 getValue4(graph, containerCluster, subject, context, procedure);
706                                         }
707
708                                 });
709                                 return;
710                         }
711                 }
712                 
713         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
714
715                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
716                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject,
717                                      g -> getValue4(g, containerCluster, subject, context, procedure)
718                             );
719                                 return;
720                         }
721                 
722                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
723                                 Object value = g.getValue(subject);
724                                 if(value != null) {
725                                         if(result != null) {
726                                                 procedure.exception(graph, new DatabaseException("Multiple values"));
727 //                                              graph.dec();
728                                                 return;
729                                         } else {
730                                                 result = value;
731                                         }
732                                 }
733                         }
734                         
735                         if(result != null) {
736                                 
737                                 procedure.execute(graph, context, (T)result);
738 //                              graph.state.barrier.dec();
739                                 
740                         } else {
741                                 
742                                 if(ClusterTypeEnum.SMALL == cluster.getType())
743                                         getDirectValue4(graph, (ClusterSmall)cluster, subject, context, procedure);
744                                 else 
745                                         getDirectValue4(graph, (ClusterBig)cluster, subject, context, procedure);
746                         }
747
748                 } else {
749
750                         if(ClusterTypeEnum.SMALL == cluster.getType())
751                                 getDirectValue4(graph, (ClusterSmall)cluster, subject, context, procedure);
752                         else 
753                                 getDirectValue4(graph, (ClusterBig)cluster, subject, context, procedure);
754
755                 }
756         
757         }
758
759         private <T> void getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final ForPossibleRelatedValueProcedure<T> procedure) {
760
761                 try {
762
763                         int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);
764                         if(so == 0) {
765                                 if(result == 0) {
766                                         procedure.exception(graph, new DatabaseException("No objects " + subject + " " + procedure.predicateKey));
767 //                                      graph.dec();
768                                 } else {
769                                         getValue4(graph, cluster, result, procedure);
770                                 }
771                         } else {
772                                 if(result == 0) {
773                                         getValue4(graph, cluster, so, procedure);
774                                 } else {
775                                         procedure.exception(graph, new DatabaseException("Multiple objects"));
776 //                                      graph.dec();
777                                 }
778                         }
779
780                 } catch (DatabaseException e) {
781                         e.printStackTrace();
782                 }
783                 
784         }
785
786         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) {
787
788                 try {
789
790                         int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);
791                         if(so == 0) {
792                                 if(result == 0) {
793                                         procedure.exception(graph, new NoSingleResultException("No objects " + subject + " " + procedure.predicateKey, result));
794 //                                      graph.dec();
795                                 } else {
796                                         getValue4(graph, cluster, result, context, procedure);
797                                 }
798                         } else {
799                                 if(result == 0) {
800                                         getValue4(graph, cluster, so, context, procedure);
801                                 } else {
802                                         procedure.exception(graph, new NoSingleResultException("Multiple objects for " + subject + " " + procedure.predicateKey, result));
803 //                                      graph.dec();
804                                 }
805                         }
806
807                 } catch (DatabaseException e) {
808                         LOGGER.error("Could not compute related value for subject {} with predicate {}", subject, procedure.predicateKey);
809                 }
810                 
811         }
812         
813         public <T> void getRelatedValue4(final ReadGraphImpl graph, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {
814                 
815                 int result = 0;
816                 
817                 final int predicate = procedure.predicateKey;
818
819                 if(subject < 0) {
820
821                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
822                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate,
823                                     g -> getRelatedValue4(g, subject, procedure)
824                             );
825                                 return;
826                         }
827                         
828                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
829                 for (int id : g.getObjects(subject, predicate)) {
830                         if(result != 0) {
831                                 procedure.exception(graph, new DatabaseException("Multiple objects"));
832 //                              graph.dec();
833                                 return;
834                         } else {
835                                 result = id;
836                         }
837                 }
838                         }
839                         
840                         if(result == 0) {
841                                 
842                         procedure.exception(graph, new DatabaseException("No objects for " + subject ));
843 //                      graph.dec();
844                         return;
845                         
846                         } else {
847                                 
848                                 getValue4(graph, null, result, procedure);
849                                 return;
850                                 
851                         }
852                         
853                 } 
854                 
855         final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);
856         if(!cluster.isLoaded()) {
857                 cluster.load(session.clusterTranslator, new Runnable() {
858
859                                 @Override
860                                 public void run() {
861                                         getRelatedValue4(graph, subject, procedure);
862                                 }
863                         
864                 });
865                 return;
866         }
867         
868         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
869                         
870                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
871                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate,
872                                     g -> getRelatedValue4(graph, subject, procedure)
873                             );
874                                 return;
875                         }
876                 
877                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
878                 for (int id : g.getObjects(subject, predicate)) {
879                         if(result != 0) {
880                                 procedure.exception(graph, new DatabaseException("Multiple objects"));
881 //                              graph.dec();
882                                 return;
883                         } else {
884                                 result = id;
885                         }
886                 }
887                         }
888                         
889                         getRelatedDirectValue4(graph, cluster, subject, result, procedure);
890                         
891                 } else {
892                         
893                         getRelatedDirectValue4(graph, cluster, subject, 0, procedure);
894                         
895                 }
896                 
897         }
898         
899         private <C, T> void getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {
900                 
901                 try {
902                         byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
903                         @SuppressWarnings("unchecked")
904                         T value = (T)utf(bytes, 0);
905                         procedure.execute(graph, context, value);
906                 } catch (DatabaseException e) {
907                         procedure.execute(graph, context, null);
908                 }
909
910 //              graph.dec();
911                 
912         }
913
914         private <T> void getDirectValue4(final ReadGraphImpl graph, final ClusterBig cluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {
915                 
916                 try {
917                         byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
918                         @SuppressWarnings("unchecked")
919                         T value = (T)utf(bytes, 0);
920                         procedure.execute(graph, value);
921                 } catch (DatabaseException e) {
922                         procedure.execute(graph, null);
923                 }
924
925 //              graph.dec();
926                 
927         }
928
929         private <C, T> void getDirectValue4(final ReadGraphImpl graph, final ClusterBig cluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {
930                 
931                 try {
932                         byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
933                         if(bytes == null) {
934                                 procedure.execute(graph, context, null);
935                         } else {
936                                 @SuppressWarnings("unchecked")
937                                 T value = (T)utf(bytes, 0);
938                                 procedure.execute(graph, context, value);
939                         }
940                 } catch (DatabaseException e) {
941                         procedure.execute(graph, context, null);
942                 }
943
944 //              graph.dec();
945                 
946         }
947         
948         private <T> void getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) {
949                 try {
950                         // Note: this code avoids creating an intermediate byte[]
951                         // to store the encoded string bytes and reads the UTF string
952                         // from the value table byte[] directly into String instead.
953
954                         ResourceTableSmall rt = cluster.resourceTable;
955                         ValueTableSmall vt = cluster.valueTable;
956
957                         byte[] bs = vt.table;
958                         long[] ls = rt.table;
959
960                         int index = ((subject&0xFFF) << 1) - 1 + rt.offset;
961                         int valueIndex = ((int)(ls[index] >>> 24) & 0x3FFFFF) + vt.offset;
962
963                         int size = bs[valueIndex++];
964                         if (size < 0) // two byte size
965                                 size = (int)(((size & 0x7F) << 8) | (bs[valueIndex++] & 0xFF));
966                         if (size <= 0)
967                                 throw new DatabaseException("No value for " + subject); 
968
969                         @SuppressWarnings("unchecked")
970                         T t = (T) utf(bs, valueIndex);
971
972                         procedure.execute(graph, t);
973                 } catch (DatabaseException e) {
974                         procedure.exception(graph, e);
975                 }
976         }
977
978         final private String utf(byte[] bytes, int offset) throws AssumptionException {
979
980                 if(bytes == null) return null;
981
982                 // Read databoard int32 using Length encoding
983                 // https://dev.simantics.org/index.php/Databoard_Specification#Length
984                 int index = offset;
985                 int length = bytes[index++]&0xff; 
986                 if(length >= 0x80) {
987                         if(length >= 0xc0) {
988                                 if(length >= 0xe0) {
989                                         if(length >= 0xf0) {
990                                                 length &= 0x0f;
991                                                 length += ((bytes[index++]&0xff)<<3);
992                                                 length += ((bytes[index++]&0xff)<<11);
993                                                 length += ((bytes[index++]&0xff)<<19);
994                                                 length += 0x10204080;
995                                         }
996                                         else {
997                                                 length &= 0x1f;
998                                                 length += ((bytes[index++]&0xff)<<4);
999                                                 length += ((bytes[index++]&0xff)<<12);
1000                                                 length += ((bytes[index++]&0xff)<<20);
1001                                                 length += 0x204080;
1002                                         }
1003                                 }
1004                                 else {
1005                                         length &= 0x3f;
1006                                         length += ((bytes[index++]&0xff)<<5);
1007                                         length += ((bytes[index++]&0xff)<<13);
1008                                         length += 0x4080;
1009                                 }
1010                         }
1011                         else {
1012                                 length &= 0x7f;
1013                                 length += ((bytes[index++]&0xff)<<6);
1014                                 length += 0x80;
1015                         }
1016                 }
1017
1018                 // Copied from DataInputStream
1019                 int utflen = length;
1020                 char[] chararr = new char[utflen];
1021
1022                 int c, char2, char3;
1023                 int count = index;
1024                 int target = index + length;
1025                 int chararr_count=0;
1026
1027                 while (count < target) {
1028                         c = (int) bytes[count] & 0xff;
1029                         if (c > 127) break;
1030                         count++;
1031                         chararr[chararr_count++]=(char)c;
1032                 }
1033
1034                 while (count < target) {
1035                         c = (int) bytes[count] & 0xff;
1036                         switch (c >> 4) {
1037                         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
1038                                 /* 0xxxxxxx*/
1039                                 count++;
1040                                 chararr[chararr_count++]=(char)c;
1041                                 break;
1042                         case 12: case 13:
1043                                 /* 110x xxxx   10xx xxxx*/
1044                                 count += 2;
1045                                 if (count > target)
1046                                         throw new AssumptionException(
1047                                                         "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
1048                                 char2 = (int) bytes[count-1];
1049                                 if ((char2 & 0xC0) != 0x80)
1050                                         throw new AssumptionException(
1051                                                         "malformed input around byte " + count); 
1052                                 chararr[chararr_count++]=(char)(((c & 0x1F) << 6) | 
1053                                                 (char2 & 0x3F));  
1054                                 break;
1055                         case 14:
1056                                 /* 1110 xxxx  10xx xxxx  10xx xxxx */
1057                                 count += 3;
1058                                 if (count > target)
1059                                         throw new AssumptionException(
1060                                                         "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
1061                                 char2 = (int) bytes[count-2];
1062                                 char3 = (int) bytes[count-1];
1063                                 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
1064                                         throw new AssumptionException(
1065                                                         "malformed input around byte " + (count-1));
1066                                 chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
1067                                                 ((char2 & 0x3F) << 6)  |
1068                                                 ((char3 & 0x3F) << 0));
1069                                 break;
1070                         default:
1071                                 /* 10xx xxxx,  1111 xxxx */
1072                                 throw new AssumptionException(
1073                                                 "malformed input around byte " + count);
1074                         }
1075                 }
1076
1077                 // The number of chars produced may be less than utflen
1078                 return new String(chararr, 0, chararr_count);
1079         }
1080
1081 }