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