diff options
21 files changed, 261 insertions, 67 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java index ee445a79..efc6146c 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java | |||
@@ -29,7 +29,7 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder { | |||
29 | } | 29 | } |
30 | 30 | ||
31 | @Override | 31 | @Override |
32 | default ViatraModelQueryBuilder queries(Collection<? extends DNF> queries) { | 32 | default ViatraModelQueryBuilder queries(Collection<DNF> queries) { |
33 | ModelQueryBuilder.super.queries(queries); | 33 | ModelQueryBuilder.super.queries(queries); |
34 | return this; | 34 | return this; |
35 | } | 35 | } |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java index 21dcaf15..b4d43ef8 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java | |||
@@ -5,26 +5,25 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | |||
5 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; | 5 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; |
6 | import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; | 6 | import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; |
7 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; | 7 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; |
8 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
8 | import tools.refinery.store.model.Model; | 9 | import tools.refinery.store.model.Model; |
9 | import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext; | 10 | import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext; |
10 | import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener; | ||
11 | import tools.refinery.store.query.view.AnyRelationView; | 11 | import tools.refinery.store.query.view.AnyRelationView; |
12 | 12 | ||
13 | import java.util.Collection; | 13 | import java.util.Map; |
14 | 14 | ||
15 | public class RelationalScope extends QueryScope { | 15 | public class RelationalScope extends QueryScope { |
16 | private final Model model; | 16 | private final Model model; |
17 | private final Map<AnyRelationView, IInputKey> relationViews; | ||
17 | 18 | ||
18 | private final ModelUpdateListener updateListener; | 19 | public RelationalScope(Model model, Map<AnyRelationView, IInputKey> relationViews) { |
19 | |||
20 | public RelationalScope(Model model, Collection<AnyRelationView> relationViews) { | ||
21 | this.model = model; | 20 | this.model = model; |
22 | updateListener = new ModelUpdateListener(model, relationViews); | 21 | this.relationViews = relationViews; |
23 | } | 22 | } |
24 | 23 | ||
25 | @Override | 24 | @Override |
26 | protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, | 25 | protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, |
27 | Logger logger) { | 26 | Logger logger) { |
28 | return new RelationalEngineContext(model, updateListener); | 27 | return new RelationalEngineContext(model, relationViews); |
29 | } | 28 | } |
30 | } | 29 | } |
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 3c276935..810d2c32 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 | |||
@@ -30,7 +30,7 @@ public class ViatraModelQueryAdapterImpl implements ModelQueryAdapter { | |||
30 | ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { | 30 | ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { |
31 | this.model = model; | 31 | this.model = model; |
32 | this.storeAdapter = storeAdapter; | 32 | this.storeAdapter = storeAdapter; |
33 | var scope = new RelationalScope(model, storeAdapter.getRelationViews()); | 33 | var scope = new RelationalScope(model, storeAdapter.getInputKeys()); |
34 | queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope); | 34 | queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope); |
35 | 35 | ||
36 | try { | 36 | try { |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java index 5105c9a7..9f1e55b1 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java | |||
@@ -92,7 +92,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp | |||
92 | throw new IllegalArgumentException( | 92 | throw new IllegalArgumentException( |
93 | "Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name())); | 93 | "Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name())); |
94 | } | 94 | } |
95 | pQuery.setEvaluationHints(queryEvaluationHint); | 95 | pQuery.setEvaluationHints(pQuery.getEvaluationHints().overrideBy(queryEvaluationHint)); |
96 | return this; | 96 | return this; |
97 | } | 97 | } |
98 | 98 | ||
@@ -105,7 +105,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp | |||
105 | 105 | ||
106 | private void validateSymbols(ModelStore store) { | 106 | private void validateSymbols(ModelStore store) { |
107 | var symbols = store.getSymbols(); | 107 | var symbols = store.getSymbols(); |
108 | for (var relationView : dnf2PQuery.getRelationViews()) { | 108 | for (var relationView : dnf2PQuery.getRelationViews().keySet()) { |
109 | var symbol = relationView.getSymbol(); | 109 | var symbol = relationView.getSymbol(); |
110 | if (!symbols.contains(symbol)) { | 110 | if (!symbols.contains(symbol)) { |
111 | throw new IllegalArgumentException("Cannot query relation view %s: symbol %s is not in the model" | 111 | throw new IllegalArgumentException("Cannot query relation view %s: symbol %s is not in the model" |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java index d77b7f4b..69f1f146 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java | |||
@@ -2,6 +2,7 @@ package tools.refinery.store.query.viatra.internal; | |||
2 | 2 | ||
3 | import org.eclipse.viatra.query.runtime.api.IQuerySpecification; | 3 | import org.eclipse.viatra.query.runtime.api.IQuerySpecification; |
4 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; | 4 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; |
5 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
5 | import tools.refinery.store.model.Model; | 6 | import tools.refinery.store.model.Model; |
6 | import tools.refinery.store.model.ModelStore; | 7 | import tools.refinery.store.model.ModelStore; |
7 | import tools.refinery.store.query.DNF; | 8 | import tools.refinery.store.query.DNF; |
@@ -15,15 +16,15 @@ import java.util.Map; | |||
15 | public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAdapter { | 16 | public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAdapter { |
16 | private final ModelStore store; | 17 | private final ModelStore store; |
17 | private final ViatraQueryEngineOptions engineOptions; | 18 | private final ViatraQueryEngineOptions engineOptions; |
18 | private final Collection<AnyRelationView> relationViews; | 19 | private final Map<AnyRelationView, IInputKey> inputKeys; |
19 | private final Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications; | 20 | private final Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications; |
20 | 21 | ||
21 | ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, | 22 | ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, |
22 | Collection<AnyRelationView> relationViews, | 23 | Map<AnyRelationView, IInputKey> inputKeys, |
23 | Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications) { | 24 | Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications) { |
24 | this.store = store; | 25 | this.store = store; |
25 | this.engineOptions = engineOptions; | 26 | this.engineOptions = engineOptions; |
26 | this.relationViews = relationViews; | 27 | this.inputKeys = inputKeys; |
27 | this.querySpecifications = querySpecifications; | 28 | this.querySpecifications = querySpecifications; |
28 | } | 29 | } |
29 | 30 | ||
@@ -32,9 +33,12 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd | |||
32 | return store; | 33 | return store; |
33 | } | 34 | } |
34 | 35 | ||
35 | @Override | ||
36 | public Collection<AnyRelationView> getRelationViews() { | 36 | public Collection<AnyRelationView> getRelationViews() { |
37 | return relationViews; | 37 | return inputKeys.keySet(); |
38 | } | ||
39 | |||
40 | Map<AnyRelationView, IInputKey> getInputKeys() { | ||
41 | return inputKeys; | ||
38 | } | 42 | } |
39 | 43 | ||
40 | @Override | 44 | @Override |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java index 29f0536c..2a24b67c 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java | |||
@@ -12,6 +12,9 @@ import java.util.concurrent.Callable; | |||
12 | * Copied from <code>org.eclipse.viatra.query.runtime.tabular.TabularEngineContext</code> | 12 | * Copied from <code>org.eclipse.viatra.query.runtime.tabular.TabularEngineContext</code> |
13 | */ | 13 | */ |
14 | public class DummyBaseIndexer implements IBaseIndex { | 14 | public class DummyBaseIndexer implements IBaseIndex { |
15 | DummyBaseIndexer() { | ||
16 | } | ||
17 | |||
15 | @Override | 18 | @Override |
16 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | 19 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { |
17 | try { | 20 | try { |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java index 4eb8898b..3bad01b9 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java | |||
@@ -2,17 +2,19 @@ package tools.refinery.store.query.viatra.internal.context; | |||
2 | 2 | ||
3 | import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; | 3 | import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; |
4 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; | 4 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; |
5 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
5 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; | 6 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; |
6 | |||
7 | import tools.refinery.store.model.Model; | 7 | import tools.refinery.store.model.Model; |
8 | import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener; | 8 | import tools.refinery.store.query.view.AnyRelationView; |
9 | |||
10 | import java.util.Map; | ||
9 | 11 | ||
10 | public class RelationalEngineContext implements IEngineContext { | 12 | public class RelationalEngineContext implements IEngineContext { |
11 | private final IBaseIndex baseIndex = new DummyBaseIndexer(); | 13 | private final IBaseIndex baseIndex = new DummyBaseIndexer(); |
12 | private final RelationalRuntimeContext runtimeContext; | 14 | private final RelationalRuntimeContext runtimeContext; |
13 | 15 | ||
14 | public RelationalEngineContext(Model model, ModelUpdateListener updateListener) { | 16 | public RelationalEngineContext(Model model, Map< AnyRelationView, IInputKey> inputKeys) { |
15 | runtimeContext = new RelationalRuntimeContext(model, updateListener); | 17 | runtimeContext = new RelationalRuntimeContext(model, inputKeys); |
16 | } | 18 | } |
17 | 19 | ||
18 | @Override | 20 | @Override |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java index 81046b2a..cba3fa08 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java | |||
@@ -4,24 +4,29 @@ import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContex | |||
4 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | 4 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; |
5 | import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; | 5 | import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; |
6 | import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; | 6 | import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; |
7 | import tools.refinery.store.query.view.AnyRelationView; | ||
7 | 8 | ||
8 | import java.util.Collection; | 9 | import java.util.*; |
9 | import java.util.Map; | ||
10 | import java.util.Set; | ||
11 | 10 | ||
12 | /** | 11 | /** |
13 | * The meta context information for String scopes. | 12 | * The meta context information for String scopes. |
14 | */ | 13 | */ |
15 | public class RelationalQueryMetaContext extends AbstractQueryMetaContext { | 14 | public class RelationalQueryMetaContext extends AbstractQueryMetaContext { |
15 | private final Map<AnyRelationView, IInputKey> inputKeys; | ||
16 | |||
17 | RelationalQueryMetaContext(Map<AnyRelationView, IInputKey> inputKeys) { | ||
18 | this.inputKeys = inputKeys; | ||
19 | } | ||
20 | |||
16 | @Override | 21 | @Override |
17 | public boolean isEnumerable(IInputKey key) { | 22 | public boolean isEnumerable(IInputKey key) { |
18 | ensureValidKey(key); | 23 | checkKey(key); |
19 | return key.isEnumerable(); | 24 | return key.isEnumerable(); |
20 | } | 25 | } |
21 | 26 | ||
22 | @Override | 27 | @Override |
23 | public boolean isStateless(IInputKey key) { | 28 | public boolean isStateless(IInputKey key) { |
24 | ensureValidKey(key); | 29 | checkKey(key); |
25 | return true; | 30 | return true; |
26 | } | 31 | } |
27 | 32 | ||
@@ -32,19 +37,58 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext { | |||
32 | 37 | ||
33 | @Override | 38 | @Override |
34 | public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { | 39 | public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { |
35 | ensureValidKey(implyingKey); | 40 | var relationView = checkKey(implyingKey); |
36 | return Set.of(); | 41 | var relationViewImplications = relationView.getImpliedRelationViews(); |
42 | var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size()); | ||
43 | for (var relationViewImplication : relationViewImplications) { | ||
44 | if (!relationView.equals(relationViewImplication.implyingRelationView())) { | ||
45 | throw new IllegalArgumentException("Relation view %s returned unrelated implication %s".formatted( | ||
46 | relationView, relationViewImplication)); | ||
47 | } | ||
48 | var impliedInputKey = inputKeys.get(relationViewImplication.impliedRelationView()); | ||
49 | // Ignore implications not relevant for any queries included in the model. | ||
50 | if (impliedInputKey != null) { | ||
51 | inputKeyImplications.add(new InputKeyImplication(implyingKey, impliedInputKey, | ||
52 | relationViewImplication.impliedIndices())); | ||
53 | } | ||
54 | } | ||
55 | return inputKeyImplications; | ||
37 | } | 56 | } |
38 | 57 | ||
39 | @Override | 58 | @Override |
40 | public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { | 59 | public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { |
41 | ensureValidKey(key); | 60 | var relationView = checkKey(key); |
42 | return Map.of(); | 61 | var functionalDependencies = relationView.getFunctionalDependencies(); |
62 | var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size()); | ||
63 | for (var functionalDependency : functionalDependencies) { | ||
64 | var forEach = functionalDependency.forEach(); | ||
65 | checkValidIndices(relationView, forEach); | ||
66 | var unique = functionalDependency.unique(); | ||
67 | checkValidIndices(relationView, unique); | ||
68 | var existing = flattened.get(forEach); | ||
69 | if (existing == null) { | ||
70 | flattened.put(forEach, new HashSet<>(unique)); | ||
71 | } else { | ||
72 | existing.addAll(unique); | ||
73 | } | ||
74 | } | ||
75 | return flattened; | ||
76 | } | ||
77 | |||
78 | private static void checkValidIndices(AnyRelationView relationView, Collection<Integer> indices) { | ||
79 | indices.stream().filter(relationView::invalidIndex).findAny().ifPresent(i -> { | ||
80 | throw new IllegalArgumentException("Index %d is invalid for %s".formatted(i, relationView)); | ||
81 | }); | ||
43 | } | 82 | } |
44 | 83 | ||
45 | public void ensureValidKey(IInputKey key) { | 84 | public AnyRelationView checkKey(IInputKey key) { |
46 | if (!(key instanceof RelationViewWrapper)) { | 85 | if (!(key instanceof RelationViewWrapper wrapper)) { |
47 | throw new IllegalArgumentException("The input key %s is not a valid input key".formatted(key)); | 86 | throw new IllegalArgumentException("The input key %s is not a valid input key".formatted(key)); |
48 | } | 87 | } |
88 | var relationView = wrapper.getWrappedKey(); | ||
89 | if (!inputKeys.containsKey(relationView)) { | ||
90 | throw new IllegalArgumentException("The input key %s is not present in the model".formatted(key)); | ||
91 | } | ||
92 | return relationView; | ||
49 | } | 93 | } |
50 | } | 94 | } |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java index 9c1d966c..71ab5cb4 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java | |||
@@ -13,6 +13,7 @@ import tools.refinery.store.query.view.AnyRelationView; | |||
13 | 13 | ||
14 | import java.lang.reflect.InvocationTargetException; | 14 | import java.lang.reflect.InvocationTargetException; |
15 | import java.util.Iterator; | 15 | import java.util.Iterator; |
16 | import java.util.Map; | ||
16 | import java.util.Optional; | 17 | import java.util.Optional; |
17 | import java.util.concurrent.Callable; | 18 | import java.util.concurrent.Callable; |
18 | 19 | ||
@@ -20,15 +21,16 @@ import static tools.refinery.store.util.CollectionsUtil.filter; | |||
20 | import static tools.refinery.store.util.CollectionsUtil.map; | 21 | import static tools.refinery.store.util.CollectionsUtil.map; |
21 | 22 | ||
22 | public class RelationalRuntimeContext implements IQueryRuntimeContext { | 23 | public class RelationalRuntimeContext implements IQueryRuntimeContext { |
23 | private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext(); | 24 | private final RelationalQueryMetaContext metaContext; |
24 | 25 | ||
25 | private final ModelUpdateListener modelUpdateListener; | 26 | private final ModelUpdateListener modelUpdateListener; |
26 | 27 | ||
27 | private final Model model; | 28 | private final Model model; |
28 | 29 | ||
29 | public RelationalRuntimeContext(Model model, ModelUpdateListener relationUpdateListener) { | 30 | RelationalRuntimeContext(Model model, Map<AnyRelationView, IInputKey> inputKeys) { |
30 | this.model = model; | 31 | this.model = model; |
31 | this.modelUpdateListener = relationUpdateListener; | 32 | metaContext = new RelationalQueryMetaContext(inputKeys); |
33 | modelUpdateListener = new ModelUpdateListener(model, inputKeys.keySet()); | ||
32 | } | 34 | } |
33 | 35 | ||
34 | @Override | 36 | @Override |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java index f2cb8d8d..60f1bcae 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java | |||
@@ -1,8 +1,10 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.pquery; | 1 | package tools.refinery.store.query.viatra.internal.pquery; |
2 | 2 | ||
3 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; | 3 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; |
4 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
4 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | 5 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; |
5 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; | 6 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; |
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; | ||
6 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; | 8 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; |
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; | 9 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; |
8 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality; | 10 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality; |
@@ -35,6 +37,8 @@ public class DNF2PQuery { | |||
35 | 37 | ||
36 | private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>(); | 38 | private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>(); |
37 | 39 | ||
40 | private final Map<AnyRelationView, RawPQuery> view2EmbeddedMap = new HashMap<>(); | ||
41 | |||
38 | private Function<DNF, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null, | 42 | private Function<DNF, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null, |
39 | QueryEvaluationHint.BackendRequirement.UNSPECIFIED); | 43 | QueryEvaluationHint.BackendRequirement.UNSPECIFIED); |
40 | 44 | ||
@@ -63,8 +67,8 @@ public class DNF2PQuery { | |||
63 | return pQuery; | 67 | return pQuery; |
64 | } | 68 | } |
65 | 69 | ||
66 | public Collection<AnyRelationView> getRelationViews() { | 70 | public Map<AnyRelationView, IInputKey> getRelationViews() { |
67 | return Collections.unmodifiableCollection(view2WrapperMap.keySet()); | 71 | return Collections.unmodifiableMap(view2WrapperMap); |
68 | } | 72 | } |
69 | 73 | ||
70 | public RawPQuery getAlreadyTranslated(DNF dnfQuery) { | 74 | public RawPQuery getAlreadyTranslated(DNF dnfQuery) { |
@@ -86,6 +90,17 @@ public class DNF2PQuery { | |||
86 | } | 90 | } |
87 | pQuery.setParameters(parameterList); | 91 | pQuery.setParameters(parameterList); |
88 | 92 | ||
93 | for (var functionalDependency : dnfQuery.getFunctionalDependencies()) { | ||
94 | var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency"); | ||
95 | for (var forEachVariable : functionalDependency.forEach()) { | ||
96 | functionalDependencyAnnotation.addAttribute("forEach", forEachVariable.getUniqueName()); | ||
97 | } | ||
98 | for (var uniqueVariable : functionalDependency.unique()) { | ||
99 | functionalDependencyAnnotation.addAttribute("unique", uniqueVariable.getUniqueName()); | ||
100 | } | ||
101 | pQuery.addAnnotation(functionalDependencyAnnotation); | ||
102 | } | ||
103 | |||
89 | // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates | 104 | // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates |
90 | // global static state (<code>nextID</code>) without locking. Therefore, we need to synchronize before creating | 105 | // global static state (<code>nextID</code>) without locking. Therefore, we need to synchronize before creating |
91 | // any query constraints to avoid a data race. | 106 | // any query constraints to avoid a data race. |
@@ -159,6 +174,10 @@ public class DNF2PQuery { | |||
159 | } | 174 | } |
160 | 175 | ||
161 | private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) { | 176 | private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) { |
177 | return view2EmbeddedMap.computeIfAbsent(relationView, this::doTranslateEmbeddedRelationViewPQuery); | ||
178 | } | ||
179 | |||
180 | private RawPQuery doTranslateEmbeddedRelationViewPQuery(AnyRelationView relationView) { | ||
162 | var embeddedPQuery = new RawPQuery(DNFUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED); | 181 | var embeddedPQuery = new RawPQuery(DNFUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED); |
163 | var body = new PBody(embeddedPQuery); | 182 | var body = new PBody(embeddedPQuery); |
164 | int arity = relationView.arity(); | 183 | int arity = relationView.arity(); |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java index 830495b2..71b74396 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java | |||
@@ -4,6 +4,7 @@ import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; | |||
4 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | 4 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; |
5 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; | 5 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; |
6 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | 6 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; |
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; | ||
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery; | 8 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery; |
8 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; | 9 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; |
9 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; | 10 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; |
@@ -37,6 +38,11 @@ public class RawPQuery extends BasePQuery { | |||
37 | } | 38 | } |
38 | 39 | ||
39 | @Override | 40 | @Override |
41 | public void addAnnotation(PAnnotation annotation) { | ||
42 | super.addAnnotation(annotation); | ||
43 | } | ||
44 | |||
45 | @Override | ||
40 | public List<PParameter> getParameters() { | 46 | public List<PParameter> getParameters() { |
41 | return parameters; | 47 | return parameters; |
42 | } | 48 | } |
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java index 69491fda..c529117e 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java | |||
@@ -6,6 +6,7 @@ import org.junit.jupiter.params.provider.MethodSource; | |||
6 | import tools.refinery.store.representation.cardinality.UpperCardinalities; | 6 | import tools.refinery.store.representation.cardinality.UpperCardinalities; |
7 | import tools.refinery.store.representation.cardinality.UpperCardinality; | 7 | import tools.refinery.store.representation.cardinality.UpperCardinality; |
8 | 8 | ||
9 | import java.util.List; | ||
9 | import java.util.stream.Stream; | 10 | import java.util.stream.Stream; |
10 | 11 | ||
11 | import static org.hamcrest.MatcherAssert.assertThat; | 12 | import static org.hamcrest.MatcherAssert.assertThat; |
@@ -14,32 +15,32 @@ import static org.hamcrest.Matchers.equalTo; | |||
14 | class UpperCardinalitySumAggregationOperatorStreamTest { | 15 | class UpperCardinalitySumAggregationOperatorStreamTest { |
15 | @ParameterizedTest | 16 | @ParameterizedTest |
16 | @MethodSource | 17 | @MethodSource |
17 | void testStream(Stream<UpperCardinality> stream, UpperCardinality expected) { | 18 | void testStream(List<UpperCardinality> list, UpperCardinality expected) { |
18 | var result = UpperCardinalitySumAggregationOperator.INSTANCE.aggregateStream(stream); | 19 | var result = UpperCardinalitySumAggregationOperator.INSTANCE.aggregateStream(list.stream()); |
19 | assertThat(result, equalTo(expected)); | 20 | assertThat(result, equalTo(expected)); |
20 | } | 21 | } |
21 | 22 | ||
22 | static Stream<Arguments> testStream() { | 23 | static Stream<Arguments> testStream() { |
23 | return Stream.of( | 24 | return Stream.of( |
24 | Arguments.of(Stream.of(), UpperCardinalities.ZERO), | 25 | Arguments.of(List.of(), UpperCardinalities.ZERO), |
25 | Arguments.of(Stream.of(UpperCardinality.of(3)), UpperCardinality.of(3)), | 26 | Arguments.of(List.of(UpperCardinality.of(3)), UpperCardinality.of(3)), |
26 | Arguments.of( | 27 | Arguments.of( |
27 | Stream.of( | 28 | List.of( |
28 | UpperCardinality.of(2), | 29 | UpperCardinality.of(2), |
29 | UpperCardinality.of(3) | 30 | UpperCardinality.of(3) |
30 | ), | 31 | ), |
31 | UpperCardinality.of(5) | 32 | UpperCardinality.of(5) |
32 | ), | 33 | ), |
33 | Arguments.of(Stream.of(UpperCardinalities.UNBOUNDED), UpperCardinalities.UNBOUNDED), | 34 | Arguments.of(List.of(UpperCardinalities.UNBOUNDED), UpperCardinalities.UNBOUNDED), |
34 | Arguments.of( | 35 | Arguments.of( |
35 | Stream.of( | 36 | List.of( |
36 | UpperCardinalities.UNBOUNDED, | 37 | UpperCardinalities.UNBOUNDED, |
37 | UpperCardinalities.UNBOUNDED | 38 | UpperCardinalities.UNBOUNDED |
38 | ), | 39 | ), |
39 | UpperCardinalities.UNBOUNDED | 40 | UpperCardinalities.UNBOUNDED |
40 | ), | 41 | ), |
41 | Arguments.of( | 42 | Arguments.of( |
42 | Stream.of( | 43 | List.of( |
43 | UpperCardinalities.UNBOUNDED, | 44 | UpperCardinalities.UNBOUNDED, |
44 | UpperCardinality.of(3) | 45 | UpperCardinality.of(3) |
45 | ), | 46 | ), |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java b/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java index 336c25a1..3ce4eed9 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java | |||
@@ -5,22 +5,48 @@ import tools.refinery.store.query.atom.DNFAtom; | |||
5 | 5 | ||
6 | import java.util.*; | 6 | import java.util.*; |
7 | 7 | ||
8 | public class DNF implements SymbolLike { | 8 | public final class DNF implements SymbolLike { |
9 | private final String name; | 9 | private final String name; |
10 | 10 | ||
11 | private final String uniqueName; | 11 | private final String uniqueName; |
12 | 12 | ||
13 | private final List<Variable> parameters; | 13 | private final List<Variable> parameters; |
14 | 14 | ||
15 | private final List<FunctionalDependency<Variable>> functionalDependencies; | ||
16 | |||
15 | private final List<DNFAnd> clauses; | 17 | private final List<DNFAnd> clauses; |
16 | 18 | ||
17 | private DNF(String name, List<Variable> parameters, List<DNFAnd> clauses) { | 19 | private DNF(String name, List<Variable> parameters, List<FunctionalDependency<Variable>> functionalDependencies, |
20 | List<DNFAnd> clauses) { | ||
21 | validateFunctionalDependencies(parameters, functionalDependencies); | ||
18 | this.name = name; | 22 | this.name = name; |
19 | this.uniqueName = DNFUtils.generateUniqueName(name); | 23 | this.uniqueName = DNFUtils.generateUniqueName(name); |
20 | this.parameters = parameters; | 24 | this.parameters = parameters; |
25 | this.functionalDependencies = functionalDependencies; | ||
21 | this.clauses = clauses; | 26 | this.clauses = clauses; |
22 | } | 27 | } |
23 | 28 | ||
29 | private static void validateFunctionalDependencies( | ||
30 | Collection<Variable> parameters, Collection<FunctionalDependency<Variable>> functionalDependencies) { | ||
31 | var parameterSet = new HashSet<>(parameters); | ||
32 | for (var functionalDependency : functionalDependencies) { | ||
33 | validateParameters(parameters, parameterSet, functionalDependency.forEach(), functionalDependency); | ||
34 | validateParameters(parameters, parameterSet, functionalDependency.unique(), functionalDependency); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | private static void validateParameters(Collection<Variable> parameters, Set<Variable> parameterSet, | ||
39 | Collection<Variable> toValidate, | ||
40 | FunctionalDependency<Variable> functionalDependency) { | ||
41 | for (var variable : toValidate) { | ||
42 | if (!parameterSet.contains(variable)) { | ||
43 | throw new IllegalArgumentException( | ||
44 | "Variable %s of functional dependency %s does not appear in the parameter list %s" | ||
45 | .formatted(variable, functionalDependency, parameters)); | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
24 | @Override | 50 | @Override |
25 | public String name() { | 51 | public String name() { |
26 | return name; | 52 | return name; |
@@ -34,6 +60,10 @@ public class DNF implements SymbolLike { | |||
34 | return parameters; | 60 | return parameters; |
35 | } | 61 | } |
36 | 62 | ||
63 | public List<FunctionalDependency<Variable>> getFunctionalDependencies() { | ||
64 | return functionalDependencies; | ||
65 | } | ||
66 | |||
37 | @Override | 67 | @Override |
38 | public int arity() { | 68 | public int arity() { |
39 | return parameters.size(); | 69 | return parameters.size(); |
@@ -57,6 +87,8 @@ public class DNF implements SymbolLike { | |||
57 | 87 | ||
58 | private final List<Variable> parameters = new ArrayList<>(); | 88 | private final List<Variable> parameters = new ArrayList<>(); |
59 | 89 | ||
90 | private final List<FunctionalDependency<Variable>> functionalDependencies = new ArrayList<>(); | ||
91 | |||
60 | private final List<List<DNFAtom>> clauses = new ArrayList<>(); | 92 | private final List<List<DNFAtom>> clauses = new ArrayList<>(); |
61 | 93 | ||
62 | private Builder(String name) { | 94 | private Builder(String name) { |
@@ -64,7 +96,7 @@ public class DNF implements SymbolLike { | |||
64 | } | 96 | } |
65 | 97 | ||
66 | public Builder parameter(Variable variable) { | 98 | public Builder parameter(Variable variable) { |
67 | this.parameters.add(variable); | 99 | parameters.add(variable); |
68 | return this; | 100 | return this; |
69 | } | 101 | } |
70 | 102 | ||
@@ -73,10 +105,24 @@ public class DNF implements SymbolLike { | |||
73 | } | 105 | } |
74 | 106 | ||
75 | public Builder parameters(Collection<Variable> variables) { | 107 | public Builder parameters(Collection<Variable> variables) { |
76 | this.parameters.addAll(variables); | 108 | parameters.addAll(variables); |
77 | return this; | 109 | return this; |
78 | } | 110 | } |
79 | 111 | ||
112 | public Builder functionalDependencies(Collection<FunctionalDependency<Variable>> functionalDependencies) { | ||
113 | this.functionalDependencies.addAll(functionalDependencies); | ||
114 | return this; | ||
115 | } | ||
116 | |||
117 | public Builder functionalDependency(FunctionalDependency<Variable> functionalDependency) { | ||
118 | functionalDependencies.add(functionalDependency); | ||
119 | return this; | ||
120 | } | ||
121 | |||
122 | public Builder functionalDependency(Set<Variable> forEach, Set<Variable> unique) { | ||
123 | return functionalDependency(new FunctionalDependency<>(forEach, unique)); | ||
124 | } | ||
125 | |||
80 | public Builder clause(DNFAtom... atoms) { | 126 | public Builder clause(DNFAtom... atoms) { |
81 | clauses.add(List.of(atoms)); | 127 | clauses.add(List.of(atoms)); |
82 | return this; | 128 | return this; |
@@ -113,9 +159,12 @@ public class DNF implements SymbolLike { | |||
113 | constraint.collectAllVariables(variables); | 159 | constraint.collectAllVariables(variables); |
114 | } | 160 | } |
115 | parameters.forEach(variables::remove); | 161 | parameters.forEach(variables::remove); |
116 | postProcessedClauses.add(new DNFAnd(variables, constraints)); | 162 | postProcessedClauses.add(new DNFAnd(Collections.unmodifiableSet(variables), |
163 | Collections.unmodifiableList(constraints))); | ||
117 | } | 164 | } |
118 | return new DNF(name, List.copyOf(parameters), postProcessedClauses); | 165 | return new DNF(name, Collections.unmodifiableList(parameters), |
166 | Collections.unmodifiableList(functionalDependencies), | ||
167 | Collections.unmodifiableList(postProcessedClauses)); | ||
119 | } | 168 | } |
120 | } | 169 | } |
121 | } | 170 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java b/subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java new file mode 100644 index 00000000..63a81713 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java | |||
@@ -0,0 +1,15 @@ | |||
1 | package tools.refinery.store.query; | ||
2 | |||
3 | import java.util.HashSet; | ||
4 | import java.util.Set; | ||
5 | |||
6 | public record FunctionalDependency<T>(Set<T> forEach, Set<T> unique) { | ||
7 | public FunctionalDependency { | ||
8 | var uniqueForEach = new HashSet<>(unique); | ||
9 | uniqueForEach.retainAll(forEach); | ||
10 | if (!uniqueForEach.isEmpty()) { | ||
11 | throw new IllegalArgumentException("Variables %s appear on both sides of the functional dependency" | ||
12 | .formatted(uniqueForEach)); | ||
13 | } | ||
14 | } | ||
15 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java index 853a1796..4364d844 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java | |||
@@ -11,7 +11,7 @@ public interface ModelQueryBuilder extends ModelAdapterBuilder { | |||
11 | return queries(List.of(queries)); | 11 | return queries(List.of(queries)); |
12 | } | 12 | } |
13 | 13 | ||
14 | default ModelQueryBuilder queries(Collection<? extends DNF> queries) { | 14 | default ModelQueryBuilder queries(Collection<DNF> queries) { |
15 | queries.forEach(this::query); | 15 | queries.forEach(this::query); |
16 | return this; | 16 | return this; |
17 | } | 17 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java index 9df1038b..eb64b589 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java | |||
@@ -1,12 +1,23 @@ | |||
1 | package tools.refinery.store.query.view; | 1 | package tools.refinery.store.query.view; |
2 | 2 | ||
3 | import tools.refinery.store.model.Model; | 3 | import tools.refinery.store.model.Model; |
4 | import tools.refinery.store.representation.SymbolLike; | 4 | import tools.refinery.store.query.FunctionalDependency; |
5 | import tools.refinery.store.representation.AnySymbol; | 5 | import tools.refinery.store.representation.AnySymbol; |
6 | import tools.refinery.store.representation.SymbolLike; | ||
7 | |||
8 | import java.util.Set; | ||
6 | 9 | ||
7 | public sealed interface AnyRelationView extends SymbolLike permits RelationView { | 10 | public sealed interface AnyRelationView extends SymbolLike permits RelationView { |
8 | AnySymbol getSymbol(); | 11 | AnySymbol getSymbol(); |
9 | 12 | ||
13 | default Set<FunctionalDependency<Integer>> getFunctionalDependencies() { | ||
14 | return Set.of(); | ||
15 | } | ||
16 | |||
17 | default Set<RelationViewImplication> getImpliedRelationViews() { | ||
18 | return Set.of(); | ||
19 | } | ||
20 | |||
10 | boolean get(Model model, Object[] tuple); | 21 | boolean get(Model model, Object[] tuple); |
11 | 22 | ||
12 | Iterable<Object[]> getAll(Model model); | 23 | Iterable<Object[]> getAll(Model model); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java index d263679a..3d278a8b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java | |||
@@ -1,11 +1,16 @@ | |||
1 | package tools.refinery.store.query.view; | 1 | package tools.refinery.store.query.view; |
2 | 2 | ||
3 | import tools.refinery.store.model.Model; | 3 | import tools.refinery.store.model.Model; |
4 | import tools.refinery.store.query.FunctionalDependency; | ||
5 | import tools.refinery.store.representation.Symbol; | ||
4 | import tools.refinery.store.tuple.Tuple; | 6 | import tools.refinery.store.tuple.Tuple; |
5 | import tools.refinery.store.tuple.Tuple1; | 7 | import tools.refinery.store.tuple.Tuple1; |
6 | import tools.refinery.store.representation.Symbol; | ||
7 | 8 | ||
8 | public class FunctionalRelationView<T> extends RelationView<T> { | 9 | import java.util.Set; |
10 | import java.util.stream.Collectors; | ||
11 | import java.util.stream.IntStream; | ||
12 | |||
13 | public final class FunctionalRelationView<T> extends RelationView<T> { | ||
9 | public FunctionalRelationView(Symbol<T> symbol, String name) { | 14 | public FunctionalRelationView(Symbol<T> symbol, String name) { |
10 | super(symbol, name); | 15 | super(symbol, name); |
11 | } | 16 | } |
@@ -15,13 +20,35 @@ public class FunctionalRelationView<T> extends RelationView<T> { | |||
15 | } | 20 | } |
16 | 21 | ||
17 | @Override | 22 | @Override |
23 | public Set<FunctionalDependency<Integer>> getFunctionalDependencies() { | ||
24 | var arity = getSymbol().arity(); | ||
25 | var forEach = IntStream.range(0, arity).boxed().collect(Collectors.toUnmodifiableSet()); | ||
26 | var unique = Set.of(arity); | ||
27 | return Set.of(new FunctionalDependency<>(forEach, unique)); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Set<RelationViewImplication> getImpliedRelationViews() { | ||
32 | var symbol = getSymbol(); | ||
33 | var impliedIndices = IntStream.range(0, symbol.arity()).boxed().toList(); | ||
34 | var keyOnlyRelationView = new KeyOnlyRelationView<>(symbol); | ||
35 | return Set.of(new RelationViewImplication(this, keyOnlyRelationView, impliedIndices)); | ||
36 | } | ||
37 | |||
38 | @Override | ||
18 | public boolean filter(Tuple key, T value) { | 39 | public boolean filter(Tuple key, T value) { |
19 | return true; | 40 | return true; |
20 | } | 41 | } |
21 | 42 | ||
22 | @Override | 43 | @Override |
23 | public Object[] forwardMap(Tuple key, T value) { | 44 | public Object[] forwardMap(Tuple key, T value) { |
24 | return toTuple1ArrayPlusValue(key, value); | 45 | int size = key.getSize(); |
46 | Object[] result = new Object[size + 1]; | ||
47 | for (int i = 0; i < size; i++) { | ||
48 | result[i] = Tuple.of(key.get(i)); | ||
49 | } | ||
50 | result[key.getSize()] = value; | ||
51 | return result; | ||
25 | } | 52 | } |
26 | 53 | ||
27 | @Override | 54 | @Override |
@@ -41,13 +68,4 @@ public class FunctionalRelationView<T> extends RelationView<T> { | |||
41 | public int arity() { | 68 | public int arity() { |
42 | return getSymbol().arity() + 1; | 69 | return getSymbol().arity() + 1; |
43 | } | 70 | } |
44 | |||
45 | private static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) { | ||
46 | Object[] result = new Object[t.getSize() + 1]; | ||
47 | for (int i = 0; i < t.getSize(); i++) { | ||
48 | result[i] = Tuple.of(t.get(i)); | ||
49 | } | ||
50 | result[t.getSize()] = value; | ||
51 | return result; | ||
52 | } | ||
53 | } | 71 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java index fdfcdf28..e1b2e45b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java | |||
@@ -5,7 +5,7 @@ import tools.refinery.store.tuple.Tuple; | |||
5 | 5 | ||
6 | import java.util.Objects; | 6 | import java.util.Objects; |
7 | 7 | ||
8 | public class KeyOnlyRelationView<T> extends TuplePreservingRelationView<T> { | 8 | public final class KeyOnlyRelationView<T> extends TuplePreservingRelationView<T> { |
9 | public static final String VIEW_NAME = "key"; | 9 | public static final String VIEW_NAME = "key"; |
10 | 10 | ||
11 | private final T defaultValue; | 11 | private final T defaultValue; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java new file mode 100644 index 00000000..2ba1fcc4 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java | |||
@@ -0,0 +1,19 @@ | |||
1 | package tools.refinery.store.query.view; | ||
2 | |||
3 | import java.util.List; | ||
4 | |||
5 | public record RelationViewImplication(AnyRelationView implyingRelationView, AnyRelationView impliedRelationView, | ||
6 | List<Integer> impliedIndices) { | ||
7 | public RelationViewImplication { | ||
8 | if (impliedIndices.size() != impliedRelationView.arity()) { | ||
9 | throw new IllegalArgumentException("Expected %d implied indices for %s, but %d are provided" | ||
10 | .formatted(impliedRelationView.arity(), impliedRelationView, impliedIndices.size())); | ||
11 | } | ||
12 | for (var index : impliedIndices) { | ||
13 | if (impliedRelationView.invalidIndex(index)) { | ||
14 | throw new IllegalArgumentException("%d is not a valid index for %s".formatted(index, | ||
15 | implyingRelationView)); | ||
16 | } | ||
17 | } | ||
18 | } | ||
19 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java b/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java index 404b5ddb..77fae5f4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java | |||
@@ -2,8 +2,6 @@ package tools.refinery.store.representation; | |||
2 | 2 | ||
3 | import java.util.Objects; | 3 | import java.util.Objects; |
4 | 4 | ||
5 | // Deliberately not a record, because we want equality by reference. | ||
6 | @SuppressWarnings({"squid:S6206", "ClassCanBeRecord"}) | ||
7 | public final class Symbol<T> implements AnySymbol { | 5 | public final class Symbol<T> implements AnySymbol { |
8 | private final String name; | 6 | private final String name; |
9 | private final int arity; | 7 | private final int arity; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java b/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java index 8c84500c..30a892ae 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java | |||
@@ -4,4 +4,8 @@ public interface SymbolLike { | |||
4 | String name(); | 4 | String name(); |
5 | 5 | ||
6 | int arity(); | 6 | int arity(); |
7 | |||
8 | default boolean invalidIndex(int i) { | ||
9 | return i < 0 || i >= arity(); | ||
10 | } | ||
7 | } | 11 | } |