diff options
Diffstat (limited to 'subprojects/store-query-viatra/src')
4 files changed, 144 insertions, 67 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java index e0341598..37700413 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java | |||
@@ -21,32 +21,37 @@ import java.util.Map; | |||
21 | 21 | ||
22 | public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { | 22 | public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { |
23 | private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery"; | 23 | private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery"; |
24 | private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE; | ||
24 | private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends"; | 25 | private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends"; |
26 | private static final MethodHandle GET_QUERY_BACKENDS_HANDLE; | ||
25 | 27 | ||
26 | private final Model model; | 28 | private final Model model; |
27 | private final ViatraModelQueryStoreAdapterImpl storeAdapter; | 29 | private final ViatraModelQueryStoreAdapterImpl storeAdapter; |
28 | private final ViatraQueryEngineImpl queryEngine; | 30 | private final ViatraQueryEngineImpl queryEngine; |
29 | private final MethodHandle setUpdatePropagationDelayedHandle; | 31 | |
30 | private final MethodHandle getQueryBackendsHandle; | ||
31 | private final Map<Dnf, ResultSet> resultSets; | 32 | private final Map<Dnf, ResultSet> resultSets; |
32 | private boolean pendingChanges; | 33 | private boolean pendingChanges; |
33 | 34 | ||
34 | ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { | 35 | static { |
35 | this.model = model; | ||
36 | this.storeAdapter = storeAdapter; | ||
37 | var scope = new RelationalScope(this); | ||
38 | queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope); | ||
39 | |||
40 | try { | 36 | try { |
41 | var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup()); | 37 | var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup()); |
42 | setUpdatePropagationDelayedHandle = lookup.findSetter(ViatraQueryEngineImpl.class, | 38 | SET_UPDATE_PROPAGATION_DELAYED_HANDLE = lookup.findSetter(ViatraQueryEngineImpl.class, |
43 | DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE); | 39 | DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE); |
44 | getQueryBackendsHandle = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME, | 40 | GET_QUERY_BACKENDS_HANDLE = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME, |
45 | Map.class); | 41 | Map.class); |
46 | } catch (IllegalAccessException | NoSuchFieldException e) { | 42 | } catch (IllegalAccessException | NoSuchFieldException e) { |
47 | throw new IllegalStateException("Cannot access private members of %s" | 43 | throw new IllegalStateException("Cannot access private members of %s" |
48 | .formatted(ViatraQueryEngineImpl.class.getName()), e); | 44 | .formatted(ViatraQueryEngineImpl.class.getName()), e); |
49 | } | 45 | } |
46 | } | ||
47 | |||
48 | ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { | ||
49 | this.model = model; | ||
50 | this.storeAdapter = storeAdapter; | ||
51 | var scope = new RelationalScope(this); | ||
52 | queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope); | ||
53 | |||
54 | |||
50 | 55 | ||
51 | var querySpecifications = storeAdapter.getQuerySpecifications(); | 56 | var querySpecifications = storeAdapter.getQuerySpecifications(); |
52 | GenericQueryGroup.of( | 57 | GenericQueryGroup.of( |
@@ -67,7 +72,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { | |||
67 | 72 | ||
68 | private void setUpdatePropagationDelayed(boolean value) { | 73 | private void setUpdatePropagationDelayed(boolean value) { |
69 | try { | 74 | try { |
70 | setUpdatePropagationDelayedHandle.invokeExact(queryEngine, value); | 75 | SET_UPDATE_PROPAGATION_DELAYED_HANDLE.invokeExact(queryEngine, value); |
71 | } catch (Error e) { | 76 | } catch (Error e) { |
72 | // Fatal JVM errors should not be wrapped. | 77 | // Fatal JVM errors should not be wrapped. |
73 | throw e; | 78 | throw e; |
@@ -79,7 +84,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { | |||
79 | private Collection<IQueryBackend> getQueryBackends() { | 84 | private Collection<IQueryBackend> getQueryBackends() { |
80 | try { | 85 | try { |
81 | @SuppressWarnings("unchecked") | 86 | @SuppressWarnings("unchecked") |
82 | var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) getQueryBackendsHandle.invokeExact(queryEngine); | 87 | var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) GET_QUERY_BACKENDS_HANDLE.invokeExact(queryEngine); |
83 | return backendMap.values(); | 88 | return backendMap.values(); |
84 | } catch (Error e) { | 89 | } catch (Error e) { |
85 | // Fatal JVM errors should not be wrapped. | 90 | // Fatal JVM errors should not be wrapped. |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java new file mode 100644 index 00000000..75588b81 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java | |||
@@ -0,0 +1,48 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.pquery; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; | ||
4 | import org.eclipse.viatra.query.runtime.rete.index.Indexer; | ||
5 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine; | ||
6 | import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher; | ||
7 | import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo; | ||
8 | |||
9 | import java.lang.invoke.MethodHandle; | ||
10 | import java.lang.invoke.MethodHandles; | ||
11 | import java.lang.invoke.MethodType; | ||
12 | |||
13 | final class IndexerUtils { | ||
14 | private static final MethodHandle GET_ENGINE_HANDLE; | ||
15 | private static final MethodHandle GET_PRODUCTION_NODE_TRACE_HANDLE; | ||
16 | private static final MethodHandle ACCESS_PROJECTION_HANDLE; | ||
17 | |||
18 | static { | ||
19 | try { | ||
20 | var lookup = MethodHandles.privateLookupIn(RetePatternMatcher.class, MethodHandles.lookup()); | ||
21 | GET_ENGINE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "engine", ReteEngine.class); | ||
22 | GET_PRODUCTION_NODE_TRACE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "productionNodeTrace", | ||
23 | RecipeTraceInfo.class); | ||
24 | ACCESS_PROJECTION_HANDLE = lookup.findVirtual(ReteEngine.class, "accessProjection", | ||
25 | MethodType.methodType(Indexer.class, RecipeTraceInfo.class, TupleMask.class)); | ||
26 | } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) { | ||
27 | throw new IllegalStateException("Cannot access private members of %s" | ||
28 | .formatted(RetePatternMatcher.class.getPackageName()), e); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | private IndexerUtils() { | ||
33 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | ||
34 | } | ||
35 | |||
36 | public static Indexer getIndexer(RetePatternMatcher backend, TupleMask mask) { | ||
37 | try { | ||
38 | var engine = (ReteEngine) GET_ENGINE_HANDLE.invokeExact(backend); | ||
39 | var trace = (RecipeTraceInfo) GET_PRODUCTION_NODE_TRACE_HANDLE.invokeExact(backend); | ||
40 | return (Indexer) ACCESS_PROJECTION_HANDLE.invokeExact(engine, trace, mask); | ||
41 | } catch (Error e) { | ||
42 | // Fatal JVM errors should not be wrapped. | ||
43 | throw e; | ||
44 | } catch (Throwable e) { | ||
45 | throw new IllegalStateException("Cannot access matcher for mask " + mask, e); | ||
46 | } | ||
47 | } | ||
48 | } | ||
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java index e944e873..8f56586e 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java | |||
@@ -2,71 +2,92 @@ package tools.refinery.store.query.viatra.internal.pquery; | |||
2 | 2 | ||
3 | import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher; | 3 | import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher; |
4 | import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; | 4 | import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; |
5 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | ||
6 | import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability; | ||
7 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider; | ||
8 | import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
10 | import org.eclipse.viatra.query.runtime.rete.index.Indexer; | ||
11 | import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher; | ||
5 | import tools.refinery.store.query.ResultSet; | 12 | import tools.refinery.store.query.ResultSet; |
6 | import tools.refinery.store.query.viatra.ViatraTupleLike; | 13 | import tools.refinery.store.query.viatra.ViatraTupleLike; |
7 | import tools.refinery.store.tuple.Tuple; | 14 | import tools.refinery.store.tuple.Tuple; |
8 | import tools.refinery.store.tuple.TupleLike; | 15 | import tools.refinery.store.tuple.TupleLike; |
9 | 16 | ||
10 | import java.util.Optional; | ||
11 | import java.util.stream.Stream; | 17 | import java.util.stream.Stream; |
12 | 18 | ||
19 | /** | ||
20 | * Directly access the tuples inside a VIATRA pattern matcher.<p> | ||
21 | * This class neglects calling | ||
22 | * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)} | ||
23 | * and | ||
24 | * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}, | ||
25 | * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial | ||
26 | * implementation for these methods. | ||
27 | * Using this class with any other runtime context may lead to undefined behavior. | ||
28 | */ | ||
13 | public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet { | 29 | public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet { |
14 | protected final Object[] empty; | 30 | private final Object[] empty; |
31 | private final TupleMask identityMask; | ||
32 | private Indexer emptyMaskIndexer; | ||
15 | 33 | ||
16 | public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { | 34 | public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { |
17 | super(specification); | 35 | super(specification); |
18 | empty = new Object[specification.getParameterNames().size()]; | 36 | var arity = specification.getParameterNames().size(); |
19 | } | 37 | empty = new Object[arity]; |
38 | identityMask = TupleMask.identity(arity); | ||
39 | } | ||
20 | 40 | ||
21 | @Override | 41 | @Override |
22 | public boolean hasResult() { | 42 | protected void setBackend(ViatraQueryEngine engine, IQueryResultProvider resultProvider, |
23 | return backend.hasMatch(empty); | 43 | IMatcherCapability capabilities) { |
24 | } | 44 | super.setBackend(engine, resultProvider, capabilities); |
45 | if (resultProvider instanceof RetePatternMatcher reteBackend) { | ||
46 | emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, TupleMask.empty(identityMask.sourceWidth)); | ||
47 | } | ||
48 | } | ||
25 | 49 | ||
26 | @Override | 50 | @Override |
27 | public boolean hasResult(Tuple parameters) { | 51 | public boolean hasResult(TupleLike parameters) { |
28 | return backend.hasMatch(toParametersArray(parameters)); | 52 | org.eclipse.viatra.query.runtime.matchers.tuple.Tuple tuple; |
29 | } | 53 | if (parameters instanceof ViatraTupleLike viatraTupleLike) { |
54 | tuple = viatraTupleLike.wrappedTuple().toImmutable(); | ||
55 | } else { | ||
56 | var parametersArray = toParametersArray(parameters); | ||
57 | tuple = Tuples.flatTupleOf(parametersArray); | ||
58 | } | ||
59 | if (emptyMaskIndexer == null) { | ||
60 | return backend.hasMatch(identityMask, tuple); | ||
61 | } | ||
62 | var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf()); | ||
63 | return matches != null && matches.contains(tuple); | ||
64 | } | ||
30 | 65 | ||
31 | @Override | 66 | @Override |
32 | public Optional<TupleLike> oneResult() { | 67 | public Stream<TupleLike> allResults() { |
33 | return backend.getOneArbitraryMatch(empty).map(ViatraTupleLike::new); | 68 | if (emptyMaskIndexer == null) { |
34 | } | 69 | return backend.getAllMatches(empty).map(ViatraTupleLike::new); |
70 | } | ||
71 | var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf()); | ||
72 | return matches == null ? Stream.of() : matches.stream().map(ViatraTupleLike::new); | ||
73 | } | ||
35 | 74 | ||
36 | @Override | 75 | @Override |
37 | public Optional<TupleLike> oneResult(Tuple parameters) { | 76 | public int countResults() { |
38 | return backend.getOneArbitraryMatch(toParametersArray(parameters)).map(ViatraTupleLike::new); | 77 | if (emptyMaskIndexer == null) { |
39 | } | 78 | return backend.countMatches(empty); |
79 | } | ||
80 | var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf()); | ||
81 | return matches == null ? 0 : matches.size(); | ||
82 | } | ||
40 | 83 | ||
41 | @Override | 84 | private Object[] toParametersArray(TupleLike tuple) { |
42 | public Stream<TupleLike> allResults() { | 85 | int size = tuple.getSize(); |
43 | return backend.getAllMatches(empty).map(ViatraTupleLike::new); | 86 | var array = new Object[size]; |
44 | } | 87 | for (int i = 0; i < size; i++) { |
45 | 88 | var value = tuple.get(i); | |
46 | @Override | 89 | array[i] = Tuple.of(value); |
47 | public Stream<TupleLike> allResults(Tuple parameters) { | 90 | } |
48 | return backend.getAllMatches(toParametersArray(parameters)).map(ViatraTupleLike::new); | 91 | return array; |
49 | } | 92 | } |
50 | |||
51 | @Override | ||
52 | public int countResults() { | ||
53 | return backend.countMatches(empty); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public int countResults(Tuple parameters) { | ||
58 | return backend.countMatches(toParametersArray(parameters)); | ||
59 | } | ||
60 | |||
61 | private Object[] toParametersArray(Tuple tuple) { | ||
62 | int size = tuple.getSize(); | ||
63 | var array = new Object[tuple.getSize()]; | ||
64 | for (int i = 0; i < size; i++) { | ||
65 | var value = tuple.get(i); | ||
66 | if (value >= 0) { | ||
67 | array[i] = Tuple.of(value); | ||
68 | } | ||
69 | } | ||
70 | return array; | ||
71 | } | ||
72 | } | 93 | } |
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java index 54ae70c3..3dd517c4 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java | |||
@@ -16,8 +16,7 @@ import java.util.HashSet; | |||
16 | import java.util.Set; | 16 | import java.util.Set; |
17 | import java.util.stream.Stream; | 17 | import java.util.stream.Stream; |
18 | 18 | ||
19 | import static org.junit.jupiter.api.Assertions.assertEquals; | 19 | import static org.junit.jupiter.api.Assertions.*; |
20 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
21 | import static tools.refinery.store.query.literal.Literals.not; | 20 | import static tools.refinery.store.query.literal.Literals.not; |
22 | 21 | ||
23 | class QueryTest { | 22 | class QueryTest { |
@@ -97,10 +96,14 @@ class QueryTest { | |||
97 | friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); | 96 | friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); |
98 | 97 | ||
99 | assertEquals(0, predicateResultSet.countResults()); | 98 | assertEquals(0, predicateResultSet.countResults()); |
99 | assertFalse(predicateResultSet.hasResult(Tuple.of(0, 1))); | ||
100 | assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2))); | ||
100 | 101 | ||
101 | queryEngine.flushChanges(); | 102 | queryEngine.flushChanges(); |
102 | assertEquals(3, predicateResultSet.countResults()); | 103 | assertEquals(3, predicateResultSet.countResults()); |
103 | compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2))); | 104 | compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2))); |
105 | assertTrue(predicateResultSet.hasResult(Tuple.of(0, 1))); | ||
106 | assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2))); | ||
104 | } | 107 | } |
105 | 108 | ||
106 | @Test | 109 | @Test |