diff options
Diffstat (limited to 'subprojects/interpreter-rete/src/main/java/tools')
4 files changed, 1139 insertions, 891 deletions
diff --git a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/aggregation/LeftJoinNode.java b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/aggregation/LeftJoinNode.java new file mode 100644 index 00000000..9871e3bc --- /dev/null +++ b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/aggregation/LeftJoinNode.java | |||
@@ -0,0 +1,167 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro | ||
3 | * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.interpreter.rete.aggregation; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.tuple.ITuple; | ||
12 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
13 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
14 | import tools.refinery.interpreter.matchers.tuple.Tuples; | ||
15 | import tools.refinery.interpreter.matchers.util.Direction; | ||
16 | import tools.refinery.interpreter.matchers.util.timeline.Timeline; | ||
17 | import tools.refinery.interpreter.rete.index.DefaultIndexerListener; | ||
18 | import tools.refinery.interpreter.rete.index.Indexer; | ||
19 | import tools.refinery.interpreter.rete.index.ProjectionIndexer; | ||
20 | import tools.refinery.interpreter.rete.index.StandardIndexer; | ||
21 | import tools.refinery.interpreter.rete.network.Node; | ||
22 | import tools.refinery.interpreter.rete.network.ReteContainer; | ||
23 | import tools.refinery.interpreter.rete.network.StandardNode; | ||
24 | import tools.refinery.interpreter.rete.network.communication.Timestamp; | ||
25 | |||
26 | import java.util.Collection; | ||
27 | import java.util.List; | ||
28 | import java.util.Map; | ||
29 | import java.util.Set; | ||
30 | |||
31 | public class LeftJoinNode extends StandardNode { | ||
32 | private final Object defaultValue; | ||
33 | private ProjectionIndexer projectionIndexer; | ||
34 | private TupleMask projectionMask; | ||
35 | private boolean leftInheritanceOutputMask; | ||
36 | private OuterIndexer outerIndexer = null; | ||
37 | |||
38 | public LeftJoinNode(ReteContainer reteContainer, Object defaultValue) { | ||
39 | super(reteContainer); | ||
40 | this.defaultValue = defaultValue; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void networkStructureChanged() { | ||
45 | if (reteContainer.isTimelyEvaluation() && reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
46 | throw new IllegalStateException(this + " cannot be used in recursive differential dataflow evaluation!"); | ||
47 | } | ||
48 | super.networkStructureChanged(); | ||
49 | } | ||
50 | |||
51 | public void initializeWith(ProjectionIndexer projectionIndexer) { | ||
52 | this.projectionIndexer = projectionIndexer; | ||
53 | projectionMask = projectionIndexer.getMask(); | ||
54 | leftInheritanceOutputMask = isLeftInheritanceOutputMask(projectionMask); | ||
55 | projectionIndexer.attachListener(new DefaultIndexerListener(this) { | ||
56 | @Override | ||
57 | public void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, | ||
58 | Timestamp timestamp) { | ||
59 | update(direction, updateElement, signature, change, timestamp); | ||
60 | } | ||
61 | }); | ||
62 | } | ||
63 | |||
64 | private static boolean isLeftInheritanceOutputMask(TupleMask mask) { | ||
65 | int size = mask.getSize(); | ||
66 | int sourceWidth = mask.getSourceWidth(); | ||
67 | if (size != sourceWidth - 1) { | ||
68 | throw new IllegalArgumentException("projectionMask should omit a single index, got " + mask); | ||
69 | } | ||
70 | int[] repetitions = new int[sourceWidth]; | ||
71 | for (int i = 0; i < size; i++) { | ||
72 | int index = mask.indices[i]; | ||
73 | int repetition = repetitions[index] + 1; | ||
74 | if (repetition >= 2) { | ||
75 | throw new IllegalArgumentException("Repeated index %d in projectionMask %s".formatted(index, mask)); | ||
76 | } | ||
77 | repetitions[index] = repetition; | ||
78 | } | ||
79 | for (int i = 0; i < size; i++) { | ||
80 | int index = mask.indices[i]; | ||
81 | if (index != i) { | ||
82 | return false; | ||
83 | } | ||
84 | } | ||
85 | return true; | ||
86 | } | ||
87 | |||
88 | protected void update(Direction direction, Tuple updateElement, Tuple signature, boolean change, | ||
89 | Timestamp timestamp) { | ||
90 | propagateUpdate(direction, updateElement, timestamp); | ||
91 | if (outerIndexer != null) { | ||
92 | outerIndexer.update(direction, updateElement, signature, change, timestamp); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | protected Tuple getDefaultTuple(ITuple key) { | ||
97 | if (leftInheritanceOutputMask) { | ||
98 | return Tuples.staticArityFlatTupleOf(key, defaultValue); | ||
99 | } | ||
100 | var objects = new Object[projectionMask.sourceWidth]; | ||
101 | int targetLength = projectionMask.indices.length; | ||
102 | for (int i = 0; i < targetLength; i++) { | ||
103 | int j = projectionMask.indices[i]; | ||
104 | objects[j] = key.get(j); | ||
105 | } | ||
106 | return Tuples.flatTupleOf(objects); | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
111 | projectionIndexer.getParent().pullInto(collector, flush); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) { | ||
116 | projectionIndexer.getParent().pullIntoWithTimeline(collector, flush); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public Set<Tuple> getPulledContents(boolean flush) { | ||
121 | return projectionIndexer.getParent().getPulledContents(flush); | ||
122 | } | ||
123 | |||
124 | public Indexer getOuterIndexer() { | ||
125 | if (outerIndexer == null) { | ||
126 | outerIndexer = new OuterIndexer(); | ||
127 | getCommunicationTracker().registerDependency(this, outerIndexer); | ||
128 | } | ||
129 | return outerIndexer; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original | ||
134 | * signature. | ||
135 | * | ||
136 | * @author Gabor Bergmann | ||
137 | */ | ||
138 | class OuterIndexer extends StandardIndexer { | ||
139 | public OuterIndexer() { | ||
140 | super(LeftJoinNode.this.reteContainer, LeftJoinNode.this.projectionMask); | ||
141 | this.parent = LeftJoinNode.this; | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public Collection<Tuple> get(Tuple signature) { | ||
146 | var collection = projectionIndexer.get(signature); | ||
147 | if (collection == null || collection.isEmpty()) { | ||
148 | return List.of(getDefaultTuple(signature)); | ||
149 | } | ||
150 | return collection; | ||
151 | } | ||
152 | |||
153 | public void update(Direction direction, Tuple updateElement, Tuple signature, boolean change, | ||
154 | Timestamp timestamp) { | ||
155 | propagate(direction, updateElement, signature, false, timestamp); | ||
156 | if (change) { | ||
157 | var defaultTuple = getDefaultTuple(signature); | ||
158 | propagate(direction.opposite(), defaultTuple, signature, false, timestamp); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | @Override | ||
163 | public Node getActiveNode() { | ||
164 | return projectionIndexer.getActiveNode(); | ||
165 | } | ||
166 | } | ||
167 | } | ||
diff --git a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java index f84eae0a..52a4de41 100644 --- a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java +++ b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java | |||
@@ -10,6 +10,7 @@ | |||
10 | package tools.refinery.interpreter.rete.construction.plancompiler; | 10 | package tools.refinery.interpreter.rete.construction.plancompiler; |
11 | 11 | ||
12 | import org.apache.log4j.Logger; | 12 | import org.apache.log4j.Logger; |
13 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
13 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | 14 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; |
14 | import tools.refinery.interpreter.matchers.backend.CommonQueryHintOptions; | 15 | import tools.refinery.interpreter.matchers.backend.CommonQueryHintOptions; |
15 | import tools.refinery.interpreter.matchers.backend.IQueryBackendHintProvider; | 16 | import tools.refinery.interpreter.matchers.backend.IQueryBackendHintProvider; |
@@ -53,782 +54,843 @@ import java.util.function.Function; | |||
53 | * {@link CompiledSubPlan}. | 54 | * {@link CompiledSubPlan}. |
54 | * | 55 | * |
55 | * @author Bergmann Gabor | 56 | * @author Bergmann Gabor |
56 | * | ||
57 | */ | 57 | */ |
58 | public class ReteRecipeCompiler { | 58 | public class ReteRecipeCompiler { |
59 | 59 | ||
60 | private final IQueryPlannerStrategy plannerStrategy; | 60 | private final IQueryPlannerStrategy plannerStrategy; |
61 | private final IQueryMetaContext metaContext; | 61 | private final IQueryMetaContext metaContext; |
62 | private final IQueryBackendHintProvider hintProvider; | 62 | private final IQueryBackendHintProvider hintProvider; |
63 | private final PDisjunctionRewriter normalizer; | 63 | private final PDisjunctionRewriter normalizer; |
64 | private final QueryAnalyzer queryAnalyzer; | 64 | private final QueryAnalyzer queryAnalyzer; |
65 | private final Logger logger; | 65 | private final Logger logger; |
66 | 66 | ||
67 | /** | 67 | /** |
68 | * @since 2.2 | 68 | * @since 2.2 |
69 | */ | 69 | */ |
70 | protected final boolean deleteAndRederiveEvaluation; | 70 | protected final boolean deleteAndRederiveEvaluation; |
71 | /** | 71 | /** |
72 | * @since 2.4 | 72 | * @since 2.4 |
73 | */ | 73 | */ |
74 | protected final TimelyConfiguration timelyEvaluation; | 74 | protected final TimelyConfiguration timelyEvaluation; |
75 | 75 | ||
76 | /** | 76 | /** |
77 | * @since 1.5 | 77 | * @since 1.5 |
78 | */ | 78 | */ |
79 | public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, | 79 | public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, |
80 | IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer) { | 80 | IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, |
81 | this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null); | 81 | QueryAnalyzer queryAnalyzer) { |
82 | } | 82 | this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null); |
83 | 83 | } | |
84 | /** | 84 | |
85 | * @since 2.4 | 85 | /** |
86 | */ | 86 | * @since 2.4 |
87 | public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, | 87 | */ |
88 | IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer, | 88 | public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, |
89 | boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { | 89 | IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, |
90 | super(); | 90 | QueryAnalyzer queryAnalyzer, |
91 | this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; | 91 | boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { |
92 | this.timelyEvaluation = timelyEvaluation; | 92 | super(); |
93 | this.plannerStrategy = plannerStrategy; | 93 | this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; |
94 | this.logger = logger; | 94 | this.timelyEvaluation = timelyEvaluation; |
95 | this.metaContext = metaContext; | 95 | this.plannerStrategy = plannerStrategy; |
96 | this.queryAnalyzer = queryAnalyzer; | 96 | this.logger = logger; |
97 | this.normalizer = new PDisjunctionRewriterCacher(new SurrogateQueryRewriter(), | 97 | this.metaContext = metaContext; |
98 | new PBodyNormalizer(metaContext) { | 98 | this.queryAnalyzer = queryAnalyzer; |
99 | 99 | this.normalizer = new PDisjunctionRewriterCacher(new SurrogateQueryRewriter(), | |
100 | @Override | 100 | new PBodyNormalizer(metaContext) { |
101 | protected boolean shouldExpandWeakenedAlternatives(PQuery query) { | 101 | |
102 | QueryEvaluationHint hint = ReteRecipeCompiler.this.hintProvider.getQueryEvaluationHint(query); | 102 | @Override |
103 | Boolean expandWeakenedAlternativeConstraints = ReteHintOptions.expandWeakenedAlternativeConstraints | 103 | protected boolean shouldExpandWeakenedAlternatives(PQuery query) { |
104 | .getValueOrDefault(hint); | 104 | QueryEvaluationHint hint = ReteRecipeCompiler.this.hintProvider.getQueryEvaluationHint(query); |
105 | return expandWeakenedAlternativeConstraints; | 105 | Boolean expandWeakenedAlternativeConstraints = |
106 | } | 106 | ReteHintOptions.expandWeakenedAlternativeConstraints |
107 | 107 | .getValueOrDefault(hint); | |
108 | }); | 108 | return expandWeakenedAlternativeConstraints; |
109 | this.hintProvider = hintProvider; | 109 | } |
110 | } | 110 | |
111 | 111 | }); | |
112 | static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; | 112 | this.hintProvider = hintProvider; |
113 | 113 | } | |
114 | // INTERNALLY CACHED | 114 | |
115 | private Map<PBody, SubPlan> plannerCache = new HashMap<PBody, SubPlan>(); | 115 | static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; |
116 | private Set<PBody> planningInProgress = new HashSet<PBody>(); | 116 | |
117 | 117 | // INTERNALLY CACHED | |
118 | private Map<PQuery, CompiledQuery> queryCompilerCache = new HashMap<PQuery, CompiledQuery>(); | 118 | private Map<PBody, SubPlan> plannerCache = new HashMap<PBody, SubPlan>(); |
119 | private Set<PQuery> compilationInProgress = new HashSet<PQuery>(); | 119 | private Set<PBody> planningInProgress = new HashSet<PBody>(); |
120 | private IMultiLookup<PQuery, RecursionCutoffPoint> recursionCutoffPoints = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | 120 | |
121 | private Map<SubPlan, CompiledSubPlan> subPlanCompilerCache = new HashMap<SubPlan, CompiledSubPlan>(); | 121 | private Map<PQuery, CompiledQuery> queryCompilerCache = new HashMap<PQuery, CompiledQuery>(); |
122 | private Map<ReteNodeRecipe, SubPlan> compilerBackTrace = new HashMap<ReteNodeRecipe, SubPlan>(); | 122 | private Set<PQuery> compilationInProgress = new HashSet<PQuery>(); |
123 | 123 | private IMultiLookup<PQuery, RecursionCutoffPoint> recursionCutoffPoints = | |
124 | /** | 124 | CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); |
125 | * Clears internal state | 125 | private Map<SubPlan, CompiledSubPlan> subPlanCompilerCache = new HashMap<SubPlan, CompiledSubPlan>(); |
126 | */ | 126 | private Map<ReteNodeRecipe, SubPlan> compilerBackTrace = new HashMap<ReteNodeRecipe, SubPlan>(); |
127 | public void reset() { | 127 | |
128 | plannerCache.clear(); | 128 | /** |
129 | planningInProgress.clear(); | 129 | * Clears internal state |
130 | queryCompilerCache.clear(); | 130 | */ |
131 | subPlanCompilerCache.clear(); | 131 | public void reset() { |
132 | compilerBackTrace.clear(); | 132 | plannerCache.clear(); |
133 | } | 133 | planningInProgress.clear(); |
134 | 134 | queryCompilerCache.clear(); | |
135 | /** | 135 | subPlanCompilerCache.clear(); |
136 | * Returns a {@link CompiledQuery} compiled from a query | 136 | compilerBackTrace.clear(); |
137 | * @throws InterpreterRuntimeException | 137 | } |
138 | */ | 138 | |
139 | public CompiledQuery getCompiledForm(PQuery query) { | 139 | /** |
140 | CompiledQuery compiled = queryCompilerCache.get(query); | 140 | * Returns a {@link CompiledQuery} compiled from a query |
141 | if (compiled == null) { | 141 | * |
142 | 142 | * @throws InterpreterRuntimeException | |
143 | IRewriterTraceCollector traceCollector = CommonQueryHintOptions.normalizationTraceCollector | 143 | */ |
144 | .getValueOrDefault(hintProvider.getQueryEvaluationHint(query)); | 144 | public CompiledQuery getCompiledForm(PQuery query) { |
145 | if (traceCollector != null) { | 145 | CompiledQuery compiled = queryCompilerCache.get(query); |
146 | traceCollector.addTrace(query, query); | 146 | if (compiled == null) { |
147 | } | 147 | |
148 | 148 | IRewriterTraceCollector traceCollector = CommonQueryHintOptions.normalizationTraceCollector | |
149 | boolean reentrant = !compilationInProgress.add(query); | 149 | .getValueOrDefault(hintProvider.getQueryEvaluationHint(query)); |
150 | if (reentrant) { // oops, recursion into body in progress | 150 | if (traceCollector != null) { |
151 | RecursionCutoffPoint cutoffPoint = new RecursionCutoffPoint(query, getHints(query), metaContext, | 151 | traceCollector.addTrace(query, query); |
152 | deleteAndRederiveEvaluation, timelyEvaluation); | 152 | } |
153 | recursionCutoffPoints.addPair(query, cutoffPoint); | 153 | |
154 | return cutoffPoint.getCompiledQuery(); | 154 | boolean reentrant = !compilationInProgress.add(query); |
155 | } else { // not reentrant, therefore no recursion, do the compilation | 155 | if (reentrant) { // oops, recursion into body in progress |
156 | try { | 156 | RecursionCutoffPoint cutoffPoint = new RecursionCutoffPoint(query, getHints(query), metaContext, |
157 | compiled = compileProduction(query); | 157 | deleteAndRederiveEvaluation, timelyEvaluation); |
158 | queryCompilerCache.put(query, compiled); | 158 | recursionCutoffPoints.addPair(query, cutoffPoint); |
159 | // backTrace.put(compiled.getRecipe(), plan); | 159 | return cutoffPoint.getCompiledQuery(); |
160 | 160 | } else { // not reentrant, therefore no recursion, do the compilation | |
161 | // if this was a recursive query, mend all points where recursion was cut off | 161 | try { |
162 | for (RecursionCutoffPoint cutoffPoint : recursionCutoffPoints.lookupOrEmpty(query)) { | 162 | compiled = compileProduction(query); |
163 | cutoffPoint.mend(compiled); | 163 | queryCompilerCache.put(query, compiled); |
164 | } | 164 | // backTrace.put(compiled.getRecipe(), plan); |
165 | } finally { | 165 | |
166 | compilationInProgress.remove(query); | 166 | // if this was a recursive query, mend all points where recursion was cut off |
167 | } | 167 | for (RecursionCutoffPoint cutoffPoint : recursionCutoffPoints.lookupOrEmpty(query)) { |
168 | } | 168 | cutoffPoint.mend(compiled); |
169 | } | 169 | } |
170 | return compiled; | 170 | } finally { |
171 | } | 171 | compilationInProgress.remove(query); |
172 | 172 | } | |
173 | /** | 173 | } |
174 | * Returns a {@link CompiledSubPlan} compiled from a query plan | 174 | } |
175 | * @throws InterpreterRuntimeException | 175 | return compiled; |
176 | */ | 176 | } |
177 | public CompiledSubPlan getCompiledForm(SubPlan plan) { | 177 | |
178 | CompiledSubPlan compiled = subPlanCompilerCache.get(plan); | 178 | /** |
179 | if (compiled == null) { | 179 | * Returns a {@link CompiledSubPlan} compiled from a query plan |
180 | compiled = doCompileDispatch(plan); | 180 | * |
181 | subPlanCompilerCache.put(plan, compiled); | 181 | * @throws InterpreterRuntimeException |
182 | compilerBackTrace.put(compiled.getRecipe(), plan); | 182 | */ |
183 | } | 183 | public CompiledSubPlan getCompiledForm(SubPlan plan) { |
184 | return compiled; | 184 | CompiledSubPlan compiled = subPlanCompilerCache.get(plan); |
185 | } | 185 | if (compiled == null) { |
186 | 186 | compiled = doCompileDispatch(plan); | |
187 | /** | 187 | subPlanCompilerCache.put(plan, compiled); |
188 | * @throws InterpreterRuntimeException | 188 | compilerBackTrace.put(compiled.getRecipe(), plan); |
189 | */ | 189 | } |
190 | public SubPlan getPlan(PBody pBody) { | 190 | return compiled; |
191 | // if the query is not marked as being compiled, initiate compilation | 191 | } |
192 | // (this is useful in case of recursion if getPlan() is the entry point) | 192 | |
193 | PQuery pQuery = pBody.getPattern(); | 193 | /** |
194 | if (!compilationInProgress.contains(pQuery)) | 194 | * @throws InterpreterRuntimeException |
195 | getCompiledForm(pQuery); | 195 | */ |
196 | 196 | public SubPlan getPlan(PBody pBody) { | |
197 | // Is the plan already cached? | 197 | // if the query is not marked as being compiled, initiate compilation |
198 | SubPlan plan = plannerCache.get(pBody); | 198 | // (this is useful in case of recursion if getPlan() is the entry point) |
199 | if (plan == null) { | 199 | PQuery pQuery = pBody.getPattern(); |
200 | boolean reentrant = !planningInProgress.add(pBody); | 200 | if (!compilationInProgress.contains(pQuery)) |
201 | if (reentrant) { // oops, recursion into body in progress | 201 | getCompiledForm(pQuery); |
202 | throw new IllegalArgumentException( | 202 | |
203 | "Planning-level recursion unsupported: " + pBody.getPattern().getFullyQualifiedName()); | 203 | // Is the plan already cached? |
204 | } else { // not reentrant, therefore no recursion, do the planning | 204 | SubPlan plan = plannerCache.get(pBody); |
205 | try { | 205 | if (plan == null) { |
206 | plan = plannerStrategy.plan(pBody, logger, metaContext); | 206 | boolean reentrant = !planningInProgress.add(pBody); |
207 | plannerCache.put(pBody, plan); | 207 | if (reentrant) { // oops, recursion into body in progress |
208 | } finally { | 208 | throw new IllegalArgumentException( |
209 | planningInProgress.remove(pBody); | 209 | "Planning-level recursion unsupported: " + pBody.getPattern().getFullyQualifiedName()); |
210 | } | 210 | } else { // not reentrant, therefore no recursion, do the planning |
211 | } | 211 | try { |
212 | } | 212 | plan = plannerStrategy.plan(pBody, logger, metaContext); |
213 | return plan; | 213 | plannerCache.put(pBody, plan); |
214 | } | 214 | } finally { |
215 | 215 | planningInProgress.remove(pBody); | |
216 | private CompiledQuery compileProduction(PQuery query) { | 216 | } |
217 | Collection<SubPlan> bodyPlans = new ArrayList<SubPlan>(); | 217 | } |
218 | normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector | 218 | } |
219 | .getValueOrDefault(hintProvider.getQueryEvaluationHint(query))); | 219 | return plan; |
220 | for (PBody pBody : normalizer.rewrite(query).getBodies()) { | 220 | } |
221 | SubPlan bodyPlan = getPlan(pBody); | 221 | |
222 | bodyPlans.add(bodyPlan); | 222 | private CompiledQuery compileProduction(PQuery query) { |
223 | } | 223 | Collection<SubPlan> bodyPlans = new ArrayList<SubPlan>(); |
224 | return doCompileProduction(query, bodyPlans); | 224 | normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector |
225 | } | 225 | .getValueOrDefault(hintProvider.getQueryEvaluationHint(query))); |
226 | 226 | for (PBody pBody : normalizer.rewrite(query).getBodies()) { | |
227 | private CompiledQuery doCompileProduction(PQuery query, Collection<SubPlan> bodies) { | 227 | SubPlan bodyPlan = getPlan(pBody); |
228 | // TODO skip production node if there is just one body and no projection needed? | 228 | bodyPlans.add(bodyPlan); |
229 | Map<PBody, RecipeTraceInfo> bodyFinalTraces = new HashMap<PBody, RecipeTraceInfo>(); | 229 | } |
230 | Collection<ReteNodeRecipe> bodyFinalRecipes = new HashSet<ReteNodeRecipe>(); | 230 | return doCompileProduction(query, bodyPlans); |
231 | 231 | } | |
232 | for (SubPlan bodyFinalPlan : bodies) { | 232 | |
233 | // skip over any projections at the end | 233 | private CompiledQuery doCompileProduction(PQuery query, Collection<SubPlan> bodies) { |
234 | bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); | 234 | // TODO skip production node if there is just one body and no projection needed? |
235 | 235 | Map<PBody, RecipeTraceInfo> bodyFinalTraces = new HashMap<PBody, RecipeTraceInfo>(); | |
236 | // TODO checkAndTrimEqualVariables may introduce superfluous trim, | 236 | Collection<ReteNodeRecipe> bodyFinalRecipes = new HashSet<ReteNodeRecipe>(); |
237 | // but whatever (no uniqueness enforcer needed) | 237 | |
238 | 238 | for (SubPlan bodyFinalPlan : bodies) { | |
239 | // compile body | 239 | // skip over any projections at the end |
240 | final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); | 240 | bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); |
241 | 241 | ||
242 | // project to parameter list | 242 | // TODO checkAndTrimEqualVariables may introduce superfluous trim, |
243 | RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false); | 243 | // but whatever (no uniqueness enforcer needed) |
244 | 244 | ||
245 | bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace); | 245 | // compile body |
246 | bodyFinalRecipes.add(finalTrace.getRecipe()); | 246 | final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); |
247 | } | 247 | |
248 | 248 | // project to parameter list | |
249 | CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes, | 249 | RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false); |
250 | getHints(query), metaContext, deleteAndRederiveEvaluation, timelyEvaluation); | 250 | |
251 | 251 | bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace); | |
252 | return compiled; | 252 | bodyFinalRecipes.add(finalTrace.getRecipe()); |
253 | } | 253 | } |
254 | 254 | ||
255 | private CompiledSubPlan doCompileDispatch(SubPlan plan) { | 255 | CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes, |
256 | final POperation operation = plan.getOperation(); | 256 | getHints(query), metaContext, deleteAndRederiveEvaluation, timelyEvaluation); |
257 | if (operation instanceof PEnumerate) { | 257 | |
258 | return doCompileEnumerate(((PEnumerate) operation).getEnumerablePConstraint(), plan); | 258 | return compiled; |
259 | } else if (operation instanceof PApply) { | 259 | } |
260 | final PConstraint pConstraint = ((PApply) operation).getPConstraint(); | 260 | |
261 | if (pConstraint instanceof EnumerablePConstraint) { | 261 | private CompiledSubPlan doCompileDispatch(SubPlan plan) { |
262 | CompiledSubPlan primaryParent = getCompiledForm(plan.getParentPlans().get(0)); | 262 | final POperation operation = plan.getOperation(); |
263 | PlanningTrace secondaryParent = doEnumerateDispatch(plan, (EnumerablePConstraint) pConstraint); | 263 | if (operation instanceof PEnumerate) { |
264 | return compileToNaturalJoin(plan, primaryParent, secondaryParent); | 264 | return doCompileEnumerate(((PEnumerate) operation).getEnumerablePConstraint(), plan); |
265 | } else if (pConstraint instanceof DeferredPConstraint) { | 265 | } else if (operation instanceof PApply) { |
266 | return doDeferredDispatch((DeferredPConstraint) pConstraint, plan); | 266 | final PConstraint pConstraint = ((PApply) operation).getPConstraint(); |
267 | } else { | 267 | if (pConstraint instanceof EnumerablePConstraint) { |
268 | throw new IllegalArgumentException("Unsupported PConstraint in query plan: " + plan.toShortString()); | 268 | CompiledSubPlan primaryParent = getCompiledForm(plan.getParentPlans().get(0)); |
269 | } | 269 | PlanningTrace secondaryParent = doEnumerateDispatch(plan, (EnumerablePConstraint) pConstraint); |
270 | } else if (operation instanceof PJoin) { | 270 | return compileToNaturalJoin(plan, primaryParent, secondaryParent); |
271 | return doCompileJoin((PJoin) operation, plan); | 271 | } else if (pConstraint instanceof DeferredPConstraint) { |
272 | } else if (operation instanceof PProject) { | 272 | return doDeferredDispatch((DeferredPConstraint) pConstraint, plan); |
273 | return doCompileProject((PProject) operation, plan); | 273 | } else { |
274 | } else if (operation instanceof PStart) { | 274 | throw new IllegalArgumentException("Unsupported PConstraint in query plan: " + plan.toShortString()); |
275 | return doCompileStart((PStart) operation, plan); | 275 | } |
276 | } else { | 276 | } else if (operation instanceof PJoin) { |
277 | throw new IllegalArgumentException("Unsupported POperation in query plan: " + plan.toShortString()); | 277 | return doCompileJoin((PJoin) operation, plan); |
278 | } | 278 | } else if (operation instanceof PProject) { |
279 | } | 279 | return doCompileProject((PProject) operation, plan); |
280 | 280 | } else if (operation instanceof PStart) { | |
281 | private CompiledSubPlan doDeferredDispatch(DeferredPConstraint constraint, SubPlan plan) { | 281 | return doCompileStart((PStart) operation, plan); |
282 | final SubPlan parentPlan = plan.getParentPlans().get(0); | 282 | } else { |
283 | final CompiledSubPlan parentCompiled = getCompiledForm(parentPlan); | 283 | throw new IllegalArgumentException("Unsupported POperation in query plan: " + plan.toShortString()); |
284 | if (constraint instanceof Equality) { | 284 | } |
285 | return compileDeferred((Equality) constraint, plan, parentPlan, parentCompiled); | 285 | } |
286 | } else if (constraint instanceof ExportedParameter) { | 286 | |
287 | return compileDeferred((ExportedParameter) constraint, plan, parentPlan, parentCompiled); | 287 | private CompiledSubPlan doDeferredDispatch(DeferredPConstraint constraint, SubPlan plan) { |
288 | } else if (constraint instanceof Inequality) { | 288 | final SubPlan parentPlan = plan.getParentPlans().get(0); |
289 | return compileDeferred((Inequality) constraint, plan, parentPlan, parentCompiled); | 289 | final CompiledSubPlan parentCompiled = getCompiledForm(parentPlan); |
290 | } else if (constraint instanceof NegativePatternCall) { | 290 | if (constraint instanceof Equality) { |
291 | return compileDeferred((NegativePatternCall) constraint, plan, parentPlan, parentCompiled); | 291 | return compileDeferred((Equality) constraint, plan, parentPlan, parentCompiled); |
292 | } else if (constraint instanceof PatternMatchCounter) { | 292 | } else if (constraint instanceof ExportedParameter) { |
293 | return compileDeferred((PatternMatchCounter) constraint, plan, parentPlan, parentCompiled); | 293 | return compileDeferred((ExportedParameter) constraint, plan, parentPlan, parentCompiled); |
294 | } else if (constraint instanceof AggregatorConstraint) { | 294 | } else if (constraint instanceof Inequality) { |
295 | return compileDeferred((AggregatorConstraint) constraint, plan, parentPlan, parentCompiled); | 295 | return compileDeferred((Inequality) constraint, plan, parentPlan, parentCompiled); |
296 | } else if (constraint instanceof ExpressionEvaluation) { | 296 | } else if (constraint instanceof NegativePatternCall) { |
297 | return compileDeferred((ExpressionEvaluation) constraint, plan, parentPlan, parentCompiled); | 297 | return compileDeferred((NegativePatternCall) constraint, plan, parentPlan, parentCompiled); |
298 | } else if (constraint instanceof TypeFilterConstraint) { | 298 | } else if (constraint instanceof PatternMatchCounter) { |
299 | return compileDeferred((TypeFilterConstraint) constraint, plan, parentPlan, parentCompiled); | 299 | return compileDeferred((PatternMatchCounter) constraint, plan, parentPlan, parentCompiled); |
300 | } | 300 | } else if (constraint instanceof AggregatorConstraint) { |
301 | throw new UnsupportedOperationException("Unknown deferred constraint " + constraint); | 301 | return compileDeferred((AggregatorConstraint) constraint, plan, parentPlan, parentCompiled); |
302 | } | 302 | } else if (constraint instanceof LeftJoinConstraint leftJoinConstraint) { |
303 | 303 | return compileDeferred(leftJoinConstraint, plan, parentCompiled); | |
304 | private CompiledSubPlan compileDeferred(Equality constraint, SubPlan plan, SubPlan parentPlan, | 304 | } else if (constraint instanceof ExpressionEvaluation) { |
305 | CompiledSubPlan parentCompiled) { | 305 | return compileDeferred((ExpressionEvaluation) constraint, plan, parentPlan, parentCompiled); |
306 | if (constraint.isMoot()) | 306 | } else if (constraint instanceof TypeFilterConstraint) { |
307 | return parentCompiled.cloneFor(plan); | 307 | return compileDeferred((TypeFilterConstraint) constraint, plan, parentPlan, parentCompiled); |
308 | 308 | } | |
309 | Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); | 309 | throw new UnsupportedOperationException("Unknown deferred constraint " + constraint); |
310 | Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); | 310 | } |
311 | 311 | ||
312 | if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { | 312 | private CompiledSubPlan compileDeferred(Equality constraint, SubPlan plan, SubPlan parentPlan, |
313 | Integer indexLower = Math.min(index1, index2); | 313 | CompiledSubPlan parentCompiled) { |
314 | Integer indexHigher = Math.max(index1, index2); | 314 | if (constraint.isMoot()) |
315 | 315 | return parentCompiled.cloneFor(plan); | |
316 | EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); | 316 | |
317 | equalityFilterRecipe.setParent(parentCompiled.getRecipe()); | 317 | Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); |
318 | equalityFilterRecipe.getIndices().add(indexLower); | 318 | Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); |
319 | equalityFilterRecipe.getIndices().add(indexHigher); | 319 | |
320 | 320 | if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { | |
321 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), equalityFilterRecipe, parentCompiled); | 321 | Integer indexLower = Math.min(index1, index2); |
322 | } else { | 322 | Integer indexHigher = Math.max(index1, index2); |
323 | throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", | 323 | |
324 | plan.toShortString(), parentCompiled.toString())); | 324 | EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); |
325 | } | 325 | equalityFilterRecipe.setParent(parentCompiled.getRecipe()); |
326 | } | 326 | equalityFilterRecipe.getIndices().add(indexLower); |
327 | 327 | equalityFilterRecipe.getIndices().add(indexHigher); | |
328 | /** | 328 | |
329 | * Precondition: constantTrace must map to a ConstantRecipe, and all of its variables must be contained in | 329 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), equalityFilterRecipe, parentCompiled); |
330 | * toFilterTrace. | 330 | } else { |
331 | */ | 331 | throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", |
332 | private CompiledSubPlan compileConstantFiltering(SubPlan plan, PlanningTrace toFilterTrace, | 332 | plan.toShortString(), parentCompiled.toString())); |
333 | ConstantRecipe constantRecipe, List<PVariable> filteredVariables) { | 333 | } |
334 | PlanningTrace resultTrace = toFilterTrace; | 334 | } |
335 | 335 | ||
336 | int constantVariablesSize = filteredVariables.size(); | 336 | /** |
337 | for (int i = 0; i < constantVariablesSize; ++i) { | 337 | * Precondition: constantTrace must map to a ConstantRecipe, and all of its variables must be contained in |
338 | Object constantValue = constantRecipe.getConstantValues().get(i); | 338 | * toFilterTrace. |
339 | PVariable filteredVariable = filteredVariables.get(i); | 339 | */ |
340 | int filteredColumn = resultTrace.getVariablesTuple().indexOf(filteredVariable); | 340 | private CompiledSubPlan compileConstantFiltering(SubPlan plan, PlanningTrace toFilterTrace, |
341 | 341 | ConstantRecipe constantRecipe, List<PVariable> filteredVariables) { | |
342 | DiscriminatorDispatcherRecipe dispatcherRecipe = FACTORY.createDiscriminatorDispatcherRecipe(); | 342 | PlanningTrace resultTrace = toFilterTrace; |
343 | dispatcherRecipe.setDiscriminationColumnIndex(filteredColumn); | 343 | |
344 | dispatcherRecipe.setParent(resultTrace.getRecipe()); | 344 | int constantVariablesSize = filteredVariables.size(); |
345 | 345 | for (int i = 0; i < constantVariablesSize; ++i) { | |
346 | PlanningTrace dispatcherTrace = new PlanningTrace(plan, resultTrace.getVariablesTuple(), dispatcherRecipe, | 346 | Object constantValue = constantRecipe.getConstantValues().get(i); |
347 | resultTrace); | 347 | PVariable filteredVariable = filteredVariables.get(i); |
348 | 348 | int filteredColumn = resultTrace.getVariablesTuple().indexOf(filteredVariable); | |
349 | DiscriminatorBucketRecipe bucketRecipe = FACTORY.createDiscriminatorBucketRecipe(); | 349 | |
350 | bucketRecipe.setBucketKey(constantValue); | 350 | DiscriminatorDispatcherRecipe dispatcherRecipe = FACTORY.createDiscriminatorDispatcherRecipe(); |
351 | bucketRecipe.setParent(dispatcherRecipe); | 351 | dispatcherRecipe.setDiscriminationColumnIndex(filteredColumn); |
352 | 352 | dispatcherRecipe.setParent(resultTrace.getRecipe()); | |
353 | PlanningTrace bucketTrace = new PlanningTrace(plan, dispatcherTrace.getVariablesTuple(), bucketRecipe, | 353 | |
354 | dispatcherTrace); | 354 | PlanningTrace dispatcherTrace = new PlanningTrace(plan, resultTrace.getVariablesTuple(), dispatcherRecipe, |
355 | 355 | resultTrace); | |
356 | resultTrace = bucketTrace; | 356 | |
357 | } | 357 | DiscriminatorBucketRecipe bucketRecipe = FACTORY.createDiscriminatorBucketRecipe(); |
358 | 358 | bucketRecipe.setBucketKey(constantValue); | |
359 | return resultTrace.cloneFor(plan); | 359 | bucketRecipe.setParent(dispatcherRecipe); |
360 | } | 360 | |
361 | 361 | PlanningTrace bucketTrace = new PlanningTrace(plan, dispatcherTrace.getVariablesTuple(), bucketRecipe, | |
362 | private CompiledSubPlan compileDeferred(ExportedParameter constraint, SubPlan plan, SubPlan parentPlan, | 362 | dispatcherTrace); |
363 | CompiledSubPlan parentCompiled) { | 363 | |
364 | return parentCompiled.cloneFor(plan); | 364 | resultTrace = bucketTrace; |
365 | } | 365 | } |
366 | 366 | ||
367 | private CompiledSubPlan compileDeferred(Inequality constraint, SubPlan plan, SubPlan parentPlan, | 367 | return resultTrace.cloneFor(plan); |
368 | CompiledSubPlan parentCompiled) { | 368 | } |
369 | if (constraint.isEliminable()) | 369 | |
370 | return parentCompiled.cloneFor(plan); | 370 | private CompiledSubPlan compileDeferred(ExportedParameter constraint, SubPlan plan, SubPlan parentPlan, |
371 | 371 | CompiledSubPlan parentCompiled) { | |
372 | Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); | 372 | return parentCompiled.cloneFor(plan); |
373 | Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); | 373 | } |
374 | 374 | ||
375 | if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { | 375 | private CompiledSubPlan compileDeferred(Inequality constraint, SubPlan plan, SubPlan parentPlan, |
376 | Integer indexLower = Math.min(index1, index2); | 376 | CompiledSubPlan parentCompiled) { |
377 | Integer indexHigher = Math.max(index1, index2); | 377 | if (constraint.isEliminable()) |
378 | 378 | return parentCompiled.cloneFor(plan); | |
379 | InequalityFilterRecipe inequalityFilterRecipe = FACTORY.createInequalityFilterRecipe(); | 379 | |
380 | inequalityFilterRecipe.setParent(parentCompiled.getRecipe()); | 380 | Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); |
381 | inequalityFilterRecipe.setSubject(indexLower); | 381 | Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); |
382 | inequalityFilterRecipe.getInequals().add(indexHigher); | 382 | |
383 | 383 | if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { | |
384 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), inequalityFilterRecipe, | 384 | Integer indexLower = Math.min(index1, index2); |
385 | parentCompiled); | 385 | Integer indexHigher = Math.max(index1, index2); |
386 | } else { | 386 | |
387 | throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", | 387 | InequalityFilterRecipe inequalityFilterRecipe = FACTORY.createInequalityFilterRecipe(); |
388 | plan.toShortString(), parentCompiled.toString())); | 388 | inequalityFilterRecipe.setParent(parentCompiled.getRecipe()); |
389 | } | 389 | inequalityFilterRecipe.setSubject(indexLower); |
390 | } | 390 | inequalityFilterRecipe.getInequals().add(indexHigher); |
391 | 391 | ||
392 | private CompiledSubPlan compileDeferred(TypeFilterConstraint constraint, SubPlan plan, SubPlan parentPlan, | 392 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), inequalityFilterRecipe, |
393 | CompiledSubPlan parentCompiled) { | 393 | parentCompiled); |
394 | final IInputKey inputKey = constraint.getInputKey(); | 394 | } else { |
395 | if (!metaContext.isStateless(inputKey)) | 395 | throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", |
396 | throw new UnsupportedOperationException( | 396 | plan.toShortString(), parentCompiled.toString())); |
397 | "Non-enumerable input keys are currently supported in Rete only if they are stateless, unlike " | 397 | } |
398 | + inputKey); | 398 | } |
399 | 399 | ||
400 | final Tuple constraintVariables = constraint.getVariablesTuple(); | 400 | private CompiledSubPlan compileDeferred(TypeFilterConstraint constraint, SubPlan plan, SubPlan parentPlan, |
401 | final List<PVariable> parentVariables = parentCompiled.getVariablesTuple(); | 401 | CompiledSubPlan parentCompiled) { |
402 | 402 | final IInputKey inputKey = constraint.getInputKey(); | |
403 | Mask mask; // select elements of the tuple to check against extensional relation | 403 | if (!metaContext.isStateless(inputKey)) |
404 | if (Tuples.flatTupleOf(parentVariables.toArray()).equals(constraintVariables)) { | 404 | throw new UnsupportedOperationException( |
405 | mask = null; // lucky case, parent signature equals that of input key | 405 | "Non-enumerable input keys are currently supported in Rete only if they are stateless, unlike " |
406 | } else { | 406 | + inputKey); |
407 | List<PVariable> variables = new ArrayList<PVariable>(); | 407 | |
408 | for (Object variable : constraintVariables.getElements()) { | 408 | final Tuple constraintVariables = constraint.getVariablesTuple(); |
409 | variables.add((PVariable) variable); | 409 | final List<PVariable> parentVariables = parentCompiled.getVariablesTuple(); |
410 | } | 410 | |
411 | mask = CompilerHelper.makeProjectionMask(parentCompiled, variables); | 411 | Mask mask; // select elements of the tuple to check against extensional relation |
412 | } | 412 | if (Tuples.flatTupleOf(parentVariables.toArray()).equals(constraintVariables)) { |
413 | InputFilterRecipe inputFilterRecipe = RecipesHelper.inputFilterRecipe(parentCompiled.getRecipe(), inputKey, | 413 | mask = null; // lucky case, parent signature equals that of input key |
414 | inputKey.getStringID(), mask); | 414 | } else { |
415 | return new CompiledSubPlan(plan, parentVariables, inputFilterRecipe, parentCompiled); | 415 | List<PVariable> variables = new ArrayList<PVariable>(); |
416 | } | 416 | for (Object variable : constraintVariables.getElements()) { |
417 | 417 | variables.add((PVariable) variable); | |
418 | private CompiledSubPlan compileDeferred(NegativePatternCall constraint, SubPlan plan, SubPlan parentPlan, | 418 | } |
419 | CompiledSubPlan parentCompiled) { | 419 | mask = CompilerHelper.makeProjectionMask(parentCompiled, variables); |
420 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, | 420 | } |
421 | constraint.getActualParametersTuple()); | 421 | InputFilterRecipe inputFilterRecipe = RecipesHelper.inputFilterRecipe(parentCompiled.getRecipe(), inputKey, |
422 | 422 | inputKey.getStringID(), mask); | |
423 | CompilerHelper.JoinHelper joinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); | 423 | return new CompiledSubPlan(plan, parentVariables, inputFilterRecipe, parentCompiled); |
424 | final RecipeTraceInfo primaryIndexer = joinHelper.getPrimaryIndexer(); | 424 | } |
425 | final RecipeTraceInfo secondaryIndexer = joinHelper.getSecondaryIndexer(); | 425 | |
426 | 426 | private CompiledSubPlan compileDeferred(NegativePatternCall constraint, SubPlan plan, SubPlan parentPlan, | |
427 | AntiJoinRecipe antiJoinRecipe = FACTORY.createAntiJoinRecipe(); | 427 | CompiledSubPlan parentCompiled) { |
428 | antiJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | 428 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, |
429 | antiJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); | 429 | constraint.getActualParametersTuple()); |
430 | 430 | ||
431 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), antiJoinRecipe, primaryIndexer, | 431 | CompilerHelper.JoinHelper joinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); |
432 | secondaryIndexer); | 432 | final RecipeTraceInfo primaryIndexer = joinHelper.getPrimaryIndexer(); |
433 | } | 433 | final RecipeTraceInfo secondaryIndexer = joinHelper.getSecondaryIndexer(); |
434 | 434 | ||
435 | private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan plan, SubPlan parentPlan, | 435 | AntiJoinRecipe antiJoinRecipe = FACTORY.createAntiJoinRecipe(); |
436 | CompiledSubPlan parentCompiled) { | 436 | antiJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); |
437 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, | 437 | antiJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); |
438 | constraint.getActualParametersTuple()); | 438 | |
439 | 439 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), antiJoinRecipe, primaryIndexer, | |
440 | // hack: use some mask computations (+ the indexers) from a fake natural join against the called query | 440 | secondaryIndexer); |
441 | CompilerHelper.JoinHelper fakeJoinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); | 441 | } |
442 | final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); | 442 | |
443 | final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer(); | 443 | private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan plan, SubPlan parentPlan, |
444 | 444 | CompiledSubPlan parentCompiled) { | |
445 | final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>( | 445 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, |
446 | fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple())); | 446 | constraint.getActualParametersTuple()); |
447 | /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); | 447 | |
448 | 448 | // hack: use some mask computations (+ the indexers) from a fake natural join against the called query | |
449 | CountAggregatorRecipe aggregatorRecipe = FACTORY.createCountAggregatorRecipe(); | 449 | CompilerHelper.JoinHelper fakeJoinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); |
450 | aggregatorRecipe.setParent((ProjectionIndexerRecipe) callProjectionIndexer.getRecipe()); | 450 | final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); |
451 | PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, | 451 | final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer(); |
452 | callProjectionIndexer); | 452 | |
453 | 453 | final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>( | |
454 | IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); | 454 | fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple())); |
455 | aggregatorIndexerRecipe.setParent(aggregatorRecipe); | 455 | /* if (!booleanCheck) */ |
456 | // aggregatorIndexerRecipe.setMask(RecipesHelper.mask( | 456 | sideVariablesTuple.add(constraint.getResultVariable()); |
457 | // sideVariablesTuple.size(), | 457 | |
458 | // //use same indices as in the projection indexer | 458 | CountAggregatorRecipe aggregatorRecipe = FACTORY.createCountAggregatorRecipe(); |
459 | // // EVEN if result variable already visible in left parent | 459 | aggregatorRecipe.setParent((ProjectionIndexerRecipe) callProjectionIndexer.getRecipe()); |
460 | // fakeJoinHelper.getSecondaryMask().indices | 460 | PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, |
461 | // )); | 461 | callProjectionIndexer); |
462 | 462 | ||
463 | int aggregatorWidth = sideVariablesTuple.size(); | 463 | IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); |
464 | int aggregateResultIndex = aggregatorWidth - 1; | 464 | aggregatorIndexerRecipe.setParent(aggregatorRecipe); |
465 | 465 | // aggregatorIndexerRecipe.setMask(RecipesHelper.mask( | |
466 | aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( | 466 | // sideVariablesTuple.size(), |
467 | // aggregate according all but the last index | 467 | // //use same indices as in the projection indexer |
468 | aggregateResultIndex, aggregatorWidth))); | 468 | // // EVEN if result variable already visible in left parent |
469 | PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, | 469 | // fakeJoinHelper.getSecondaryMask().indices |
470 | aggregatorTrace); | 470 | // )); |
471 | 471 | ||
472 | JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); | 472 | int aggregatorWidth = sideVariablesTuple.size(); |
473 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | 473 | int aggregateResultIndex = aggregatorWidth - 1; |
474 | naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); | 474 | |
475 | naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, | 475 | aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( |
476 | // extend with last element only - the computation value | 476 | // aggregate according all but the last index |
477 | aggregateResultIndex)); | 477 | aggregateResultIndex, aggregatorWidth))); |
478 | 478 | PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, | |
479 | // what if the new variable already has a value? | 479 | aggregatorTrace); |
480 | // even if already known, we add the new result variable, so that it can be filtered at the end | 480 | |
481 | // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); | 481 | JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); |
482 | 482 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | |
483 | final List<PVariable> aggregatedVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); | 483 | naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); |
484 | aggregatedVariablesTuple.add(constraint.getResultVariable()); | 484 | naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, |
485 | 485 | // extend with last element only - the computation value | |
486 | PlanningTrace joinTrace = new PlanningTrace(plan, aggregatedVariablesTuple, naturalJoinRecipe, primaryIndexer, | 486 | aggregateResultIndex)); |
487 | aggregatorIndexerTrace); | 487 | |
488 | 488 | // what if the new variable already has a value? | |
489 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); | 489 | // even if already known, we add the new result variable, so that it can be filtered at the end |
490 | // if (!alreadyKnown) { | 490 | // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); |
491 | // return joinTrace.cloneFor(plan); | 491 | |
492 | // } else { | 492 | final List<PVariable> aggregatedVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); |
493 | // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); | 493 | aggregatedVariablesTuple.add(constraint.getResultVariable()); |
494 | // } | 494 | |
495 | } | 495 | PlanningTrace joinTrace = new PlanningTrace(plan, aggregatedVariablesTuple, naturalJoinRecipe, primaryIndexer, |
496 | 496 | aggregatorIndexerTrace); | |
497 | private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan plan, SubPlan parentPlan, | 497 | |
498 | CompiledSubPlan parentCompiled) { | 498 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); |
499 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, | 499 | // if (!alreadyKnown) { |
500 | constraint.getActualParametersTuple()); | 500 | // return joinTrace.cloneFor(plan); |
501 | 501 | // } else { | |
502 | // hack: use some mask computations (+ the indexers) from a fake natural join against the called query | 502 | // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); |
503 | CompilerHelper.JoinHelper fakeJoinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); | 503 | // } |
504 | final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); | 504 | } |
505 | TupleMask callGroupMask = fakeJoinHelper.getSecondaryMask(); | 505 | |
506 | 506 | private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan plan, SubPlan parentPlan, | |
507 | final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>( | 507 | CompiledSubPlan parentCompiled) { |
508 | callGroupMask.transform(callTrace.getVariablesTuple())); | 508 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, |
509 | /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); | 509 | constraint.getActualParametersTuple()); |
510 | 510 | ||
511 | IMultisetAggregationOperator<?, ?, ?> operator = constraint.getAggregator().getOperator(); | 511 | // hack: use some mask computations (+ the indexers) from a fake natural join against the called query |
512 | 512 | CompilerHelper.JoinHelper fakeJoinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); | |
513 | SingleColumnAggregatorRecipe columnAggregatorRecipe = FACTORY.createSingleColumnAggregatorRecipe(); | 513 | final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); |
514 | columnAggregatorRecipe.setParent(callTrace.getRecipe()); | 514 | TupleMask callGroupMask = fakeJoinHelper.getSecondaryMask(); |
515 | columnAggregatorRecipe.setMultisetAggregationOperator(operator); | 515 | |
516 | 516 | final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>( | |
517 | int columnIndex = constraint.getAggregatedColumn(); | 517 | callGroupMask.transform(callTrace.getVariablesTuple())); |
518 | IPosetComparator posetComparator = null; | 518 | /* if (!booleanCheck) */ |
519 | Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask); | 519 | sideVariablesTuple.add(constraint.getResultVariable()); |
520 | 520 | ||
521 | // temporary solution to support the deprecated option for now | 521 | IMultisetAggregationOperator<?, ?, ?> operator = constraint.getAggregator().getOperator(); |
522 | final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan)); | 522 | |
523 | 523 | SingleColumnAggregatorRecipe columnAggregatorRecipe = FACTORY.createSingleColumnAggregatorRecipe(); | |
524 | columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); | 524 | columnAggregatorRecipe.setParent(callTrace.getRecipe()); |
525 | if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { | 525 | columnAggregatorRecipe.setMultisetAggregationOperator(operator); |
526 | List<PParameter> parameters = constraint.getReferredQuery().getParameters(); | 526 | |
527 | IInputKey key = parameters.get(columnIndex).getDeclaredUnaryType(); | 527 | int columnIndex = constraint.getAggregatedColumn(); |
528 | if (key != null && metaContext.isPosetKey(key)) { | 528 | IPosetComparator posetComparator = null; |
529 | posetComparator = metaContext.getPosetComparator(Collections.singleton(key)); | 529 | Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask); |
530 | } | 530 | |
531 | } | 531 | // temporary solution to support the deprecated option for now |
532 | 532 | final boolean deleteAndRederiveEvaluationDep = | |
533 | if (posetComparator == null) { | 533 | this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan)); |
534 | columnAggregatorRecipe.setGroupByMask(groupMask); | 534 | |
535 | columnAggregatorRecipe.setAggregableIndex(columnIndex); | 535 | columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); |
536 | } else { | 536 | if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { |
537 | MonotonicityInfo monotonicityInfo = FACTORY.createMonotonicityInfo(); | 537 | List<PParameter> parameters = constraint.getReferredQuery().getParameters(); |
538 | monotonicityInfo.setCoreMask(groupMask); | 538 | IInputKey key = parameters.get(columnIndex).getDeclaredUnaryType(); |
539 | monotonicityInfo.setPosetMask(CompilerHelper.toRecipeMask( | 539 | if (key != null && metaContext.isPosetKey(key)) { |
540 | TupleMask.selectSingle(columnIndex, constraint.getActualParametersTuple().getSize()))); | 540 | posetComparator = metaContext.getPosetComparator(Collections.singleton(key)); |
541 | monotonicityInfo.setPosetComparator(posetComparator); | 541 | } |
542 | columnAggregatorRecipe.setOptionalMonotonicityInfo(monotonicityInfo); | 542 | } |
543 | } | 543 | |
544 | 544 | if (posetComparator == null) { | |
545 | ReteNodeRecipe aggregatorRecipe = columnAggregatorRecipe; | 545 | columnAggregatorRecipe.setGroupByMask(groupMask); |
546 | PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, callTrace); | 546 | columnAggregatorRecipe.setAggregableIndex(columnIndex); |
547 | 547 | } else { | |
548 | IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); | 548 | MonotonicityInfo monotonicityInfo = FACTORY.createMonotonicityInfo(); |
549 | aggregatorIndexerRecipe.setParent(aggregatorRecipe); | 549 | monotonicityInfo.setCoreMask(groupMask); |
550 | 550 | monotonicityInfo.setPosetMask(CompilerHelper.toRecipeMask( | |
551 | int aggregatorWidth = sideVariablesTuple.size(); | 551 | TupleMask.selectSingle(columnIndex, constraint.getActualParametersTuple().getSize()))); |
552 | int aggregateResultIndex = aggregatorWidth - 1; | 552 | monotonicityInfo.setPosetComparator(posetComparator); |
553 | 553 | columnAggregatorRecipe.setOptionalMonotonicityInfo(monotonicityInfo); | |
554 | aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( | 554 | } |
555 | // aggregate according all but the last index | 555 | |
556 | aggregateResultIndex, aggregatorWidth))); | 556 | ReteNodeRecipe aggregatorRecipe = columnAggregatorRecipe; |
557 | PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, | 557 | PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, callTrace); |
558 | aggregatorTrace); | 558 | |
559 | 559 | IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); | |
560 | JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); | 560 | aggregatorIndexerRecipe.setParent(aggregatorRecipe); |
561 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | 561 | |
562 | naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); | 562 | int aggregatorWidth = sideVariablesTuple.size(); |
563 | naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, | 563 | int aggregateResultIndex = aggregatorWidth - 1; |
564 | // extend with last element only - the computation value | 564 | |
565 | aggregateResultIndex)); | 565 | aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( |
566 | 566 | // aggregate according all but the last index | |
567 | // what if the new variable already has a value? | 567 | aggregateResultIndex, aggregatorWidth))); |
568 | // even if already known, we add the new result variable, so that it can be filtered at the end | 568 | PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, |
569 | // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); | 569 | aggregatorTrace); |
570 | 570 | ||
571 | final List<PVariable> finalVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); | 571 | JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); |
572 | finalVariablesTuple.add(constraint.getResultVariable()); | 572 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); |
573 | 573 | naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); | |
574 | PlanningTrace joinTrace = new PlanningTrace(plan, finalVariablesTuple, naturalJoinRecipe, primaryIndexer, | 574 | naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, |
575 | aggregatorIndexerTrace); | 575 | // extend with last element only - the computation value |
576 | 576 | aggregateResultIndex)); | |
577 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); | 577 | |
578 | // if (!alreadyKnown) { | 578 | // what if the new variable already has a value? |
579 | // return joinTrace.cloneFor(plan); | 579 | // even if already known, we add the new result variable, so that it can be filtered at the end |
580 | // } else { | 580 | // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); |
581 | // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); | 581 | |
582 | // } | 582 | final List<PVariable> finalVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); |
583 | } | 583 | finalVariablesTuple.add(constraint.getResultVariable()); |
584 | 584 | ||
585 | private CompiledSubPlan compileDeferred(ExpressionEvaluation constraint, SubPlan plan, SubPlan parentPlan, | 585 | PlanningTrace joinTrace = new PlanningTrace(plan, finalVariablesTuple, naturalJoinRecipe, primaryIndexer, |
586 | CompiledSubPlan parentCompiled) { | 586 | aggregatorIndexerTrace); |
587 | Map<String, Integer> tupleNameMap = new HashMap<String, Integer>(); | 587 | |
588 | for (String name : constraint.getEvaluator().getInputParameterNames()) { | 588 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); |
589 | Map<? extends Object, Integer> index = parentCompiled.getPosMapping(); | 589 | // if (!alreadyKnown) { |
590 | PVariable variable = constraint.getPSystem().getVariableByNameChecked(name); | 590 | // return joinTrace.cloneFor(plan); |
591 | Integer position = index.get(variable); | 591 | // } else { |
592 | tupleNameMap.put(name, position); | 592 | // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); |
593 | } | 593 | // } |
594 | 594 | } | |
595 | final PVariable outputVariable = constraint.getOutputVariable(); | 595 | |
596 | final boolean booleanCheck = outputVariable == null; | 596 | private CompiledSubPlan compileDeferred(LeftJoinConstraint constraint, SubPlan plan, |
597 | 597 | CompiledSubPlan parentCompiled) { | |
598 | // TODO determine whether expression is costly | 598 | var callTrace = referQuery(constraint.getReferredQuery(), plan, constraint.getActualParametersTuple()); |
599 | boolean cacheOutput = ReteHintOptions.cacheOutputOfEvaluatorsByDefault.getValueOrDefault(getHints(plan)); | 599 | var fakeJoinHelper = new CompilerHelper.JoinHelper(plan, parentCompiled, callTrace); |
600 | // for (PAnnotation pAnnotation : | 600 | var primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); |
601 | // plan.getBody().getPattern().getAnnotationsByName(EXPRESSION_EVALUATION_ANNOTATION"")) { | 601 | var secondaryIndexer = fakeJoinHelper.getSecondaryIndexer(); |
602 | // for (Object value : pAnnotation.getAllValues("expensive")) { | 602 | |
603 | // if (value instanceof Boolean) | 603 | var sideVariablesTuple = CompilerHelper.convertVariablesTuple(constraint.getActualParametersTuple()); |
604 | // cacheOutput = (boolean) value; | 604 | var resultVariable = constraint.getResultVariable(); |
605 | // } | 605 | sideVariablesTuple.set(constraint.getOptionalColumn(), resultVariable); |
606 | // } | 606 | |
607 | 607 | var leftNodeRecipe = FACTORY.createOuterJoinNodeRecipe(); | |
608 | ExpressionEnforcerRecipe enforcerRecipe = booleanCheck ? FACTORY.createCheckRecipe() | 608 | var secondaryIndexerRecipe = (ProjectionIndexerRecipe) secondaryIndexer.getRecipe(); |
609 | : FACTORY.createEvalRecipe(); | 609 | leftNodeRecipe.setParent(secondaryIndexerRecipe); |
610 | enforcerRecipe.setParent(parentCompiled.getRecipe()); | 610 | leftNodeRecipe.setDefaultValue(constraint.getDefaultValue()); |
611 | enforcerRecipe.setExpression(RecipesHelper.expressionDefinition(constraint.getEvaluator())); | 611 | var leftNodeTrace = new PlanningTrace(plan, sideVariablesTuple, leftNodeRecipe, secondaryIndexer); |
612 | enforcerRecipe.setCacheOutput(cacheOutput); | 612 | |
613 | if (enforcerRecipe instanceof EvalRecipe) { | 613 | var leftIndexerRecipe = FACTORY.createOuterJoinIndexerRecipe(); |
614 | ((EvalRecipe) enforcerRecipe).setUnwinding(constraint.isUnwinding()); | 614 | leftIndexerRecipe.setParent(leftIndexerRecipe); |
615 | } | 615 | // Must make a copy of the mask here, because we are already using secondaryIndexerRecipe in the plan an mask |
616 | for (Entry<String, Integer> entry : tupleNameMap.entrySet()) { | 616 | // is a containment reference. |
617 | enforcerRecipe.getMappedIndices().put(entry.getKey(), entry.getValue()); | 617 | var copyOfMask = EcoreUtil.copy(secondaryIndexerRecipe.getMask()); |
618 | } | 618 | leftIndexerRecipe.setMask(copyOfMask); |
619 | 619 | var leftIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, leftIndexerRecipe, leftNodeTrace); | |
620 | final List<PVariable> enforcerVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); | 620 | |
621 | if (!booleanCheck) | 621 | var naturalJoinRecipe = FACTORY.createJoinRecipe(); |
622 | enforcerVariablesTuple.add(outputVariable); | 622 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); |
623 | PlanningTrace enforcerTrace = new PlanningTrace(plan, enforcerVariablesTuple, enforcerRecipe, parentCompiled); | 623 | naturalJoinRecipe.setRightParent(leftIndexerRecipe); |
624 | 624 | var complementaryMask = fakeJoinHelper.getNaturalJoinRecipe().getRightParentComplementaryMask(); | |
625 | return CompilerHelper.checkAndTrimEqualVariables(plan, enforcerTrace).cloneFor(plan); | 625 | naturalJoinRecipe.setRightParentComplementaryMask(complementaryMask); |
626 | } | 626 | |
627 | 627 | var primaryVariablesTuple = parentCompiled.getVariablesTuple(); | |
628 | private CompiledSubPlan doCompileJoin(PJoin operation, SubPlan plan) { | 628 | var joinedVariablesTuple = new ArrayList<PVariable>(primaryVariablesTuple.size() + 1); |
629 | final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan); | 629 | joinedVariablesTuple.addAll(primaryVariablesTuple); |
630 | final CompiledSubPlan leftCompiled = compiledParents.get(0); | 630 | joinedVariablesTuple.add(resultVariable); |
631 | final CompiledSubPlan rightCompiled = compiledParents.get(1); | 631 | var joinTrace = new PlanningTrace(plan, joinedVariablesTuple, naturalJoinRecipe, primaryIndexer, |
632 | 632 | leftIndexerTrace); | |
633 | return compileToNaturalJoin(plan, leftCompiled, rightCompiled); | 633 | |
634 | } | 634 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); |
635 | 635 | } | |
636 | private CompiledSubPlan compileToNaturalJoin(SubPlan plan, final PlanningTrace leftCompiled, | 636 | |
637 | final PlanningTrace rightCompiled) { | 637 | private CompiledSubPlan compileDeferred(ExpressionEvaluation constraint, SubPlan plan, SubPlan parentPlan, |
638 | // CHECK IF SPECIAL CASE | 638 | CompiledSubPlan parentCompiled) { |
639 | 639 | Map<String, Integer> tupleNameMap = new HashMap<String, Integer>(); | |
640 | // Is constant filtering applicable? | 640 | for (String name : constraint.getEvaluator().getInputParameterNames()) { |
641 | if (ReteHintOptions.useDiscriminatorDispatchersForConstantFiltering.getValueOrDefault(getHints(plan))) { | 641 | Map<? extends Object, Integer> index = parentCompiled.getPosMapping(); |
642 | if (leftCompiled.getRecipe() instanceof ConstantRecipe | 642 | PVariable variable = constraint.getPSystem().getVariableByNameChecked(name); |
643 | && rightCompiled.getVariablesTuple().containsAll(leftCompiled.getVariablesTuple())) { | 643 | Integer position = index.get(variable); |
644 | return compileConstantFiltering(plan, rightCompiled, (ConstantRecipe) leftCompiled.getRecipe(), | 644 | tupleNameMap.put(name, position); |
645 | leftCompiled.getVariablesTuple()); | 645 | } |
646 | } | 646 | |
647 | if (rightCompiled.getRecipe() instanceof ConstantRecipe | 647 | final PVariable outputVariable = constraint.getOutputVariable(); |
648 | && leftCompiled.getVariablesTuple().containsAll(rightCompiled.getVariablesTuple())) { | 648 | final boolean booleanCheck = outputVariable == null; |
649 | return compileConstantFiltering(plan, leftCompiled, (ConstantRecipe) rightCompiled.getRecipe(), | 649 | |
650 | rightCompiled.getVariablesTuple()); | 650 | // TODO determine whether expression is costly |
651 | } | 651 | boolean cacheOutput = ReteHintOptions.cacheOutputOfEvaluatorsByDefault.getValueOrDefault(getHints(plan)); |
652 | } | 652 | // for (PAnnotation pAnnotation : |
653 | 653 | // plan.getBody().getPattern().getAnnotationsByName(EXPRESSION_EVALUATION_ANNOTATION"")) { | |
654 | // ELSE: ACTUAL JOIN | 654 | // for (Object value : pAnnotation.getAllValues("expensive")) { |
655 | CompilerHelper.JoinHelper joinHelper = new CompilerHelper.JoinHelper(plan, leftCompiled, rightCompiled); | 655 | // if (value instanceof Boolean) |
656 | return new CompiledSubPlan(plan, joinHelper.getNaturalJoinVariablesTuple(), joinHelper.getNaturalJoinRecipe(), | 656 | // cacheOutput = (boolean) value; |
657 | joinHelper.getPrimaryIndexer(), joinHelper.getSecondaryIndexer()); | 657 | // } |
658 | } | 658 | // } |
659 | 659 | ||
660 | private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) { | 660 | ExpressionEnforcerRecipe enforcerRecipe = booleanCheck ? FACTORY.createCheckRecipe() |
661 | final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan); | 661 | : FACTORY.createEvalRecipe(); |
662 | final CompiledSubPlan compiledParent = compiledParents.get(0); | 662 | enforcerRecipe.setParent(parentCompiled.getRecipe()); |
663 | 663 | enforcerRecipe.setExpression(RecipesHelper.expressionDefinition(constraint.getEvaluator())); | |
664 | List<PVariable> projectedVariables = new ArrayList<PVariable>(operation.getToVariables()); | 664 | enforcerRecipe.setCacheOutput(cacheOutput); |
665 | // Determinizing projection: try to keep original order (hopefully facilitates node reuse) | 665 | if (enforcerRecipe instanceof EvalRecipe) { |
666 | Map<PVariable, Integer> parentPosMapping = compiledParent.getPosMapping(); | 666 | ((EvalRecipe) enforcerRecipe).setUnwinding(constraint.isUnwinding()); |
667 | Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get)); | 667 | } |
668 | 668 | for (Entry<String, Integer> entry : tupleNameMap.entrySet()) { | |
669 | return doProjectPlan(compiledParent, projectedVariables, true, | 669 | enforcerRecipe.getMappedIndices().put(entry.getKey(), entry.getValue()); |
670 | parentTrace -> parentTrace.cloneFor(plan), | 670 | } |
671 | (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace), | 671 | |
672 | (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace) | 672 | final List<PVariable> enforcerVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); |
673 | ); | 673 | if (!booleanCheck) |
674 | } | 674 | enforcerVariablesTuple.add(outputVariable); |
675 | 675 | PlanningTrace enforcerTrace = new PlanningTrace(plan, enforcerVariablesTuple, enforcerRecipe, parentCompiled); | |
676 | /** | 676 | |
677 | * Projects a subplan onto the specified variable tuple | 677 | return CompilerHelper.checkAndTrimEqualVariables(plan, enforcerTrace).cloneFor(plan); |
678 | * @param compiledParentPlan the compiled form of the subplan | 678 | } |
679 | * @param targetVariables list of variables to project to | 679 | |
680 | * @param enforceUniqueness whether distinctness shall be enforced after the projection. | 680 | private CompiledSubPlan doCompileJoin(PJoin operation, SubPlan plan) { |
681 | * Specify false only if directly connecting to a production node. | 681 | final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan); |
682 | * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, in case it is sufficient | 682 | final CompiledSubPlan leftCompiled = compiledParents.get(0); |
683 | * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the node and its parent trace | 683 | final CompiledSubPlan rightCompiled = compiledParents.get(1); |
684 | * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the node and its parent trace | 684 | |
685 | * @since 2.1 | 685 | return compileToNaturalJoin(plan, leftCompiled, rightCompiled); |
686 | */ | 686 | } |
687 | <ResultTrace extends RecipeTraceInfo> ResultTrace doProjectPlan( | 687 | |
688 | final CompiledSubPlan compiledParentPlan, | 688 | private CompiledSubPlan compileToNaturalJoin(SubPlan plan, final PlanningTrace leftCompiled, |
689 | final List<PVariable> targetVariables, | 689 | final PlanningTrace rightCompiled) { |
690 | boolean enforceUniqueness, | 690 | // CHECK IF SPECIAL CASE |
691 | Function<CompiledSubPlan, ResultTrace> reinterpretTraceFactory, | 691 | |
692 | BiFunction<ReteNodeRecipe, RecipeTraceInfo, RecipeTraceInfo> intermediateTraceFactory, | 692 | // Is constant filtering applicable? |
693 | BiFunction<ReteNodeRecipe, RecipeTraceInfo, ResultTrace> finalTraceFactory) | 693 | if (ReteHintOptions.useDiscriminatorDispatchersForConstantFiltering.getValueOrDefault(getHints(plan))) { |
694 | { | 694 | if (leftCompiled.getRecipe() instanceof ConstantRecipe |
695 | if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed | 695 | && rightCompiled.getVariablesTuple().containsAll(leftCompiled.getVariablesTuple())) { |
696 | return reinterpretTraceFactory.apply(compiledParentPlan); | 696 | return compileConstantFiltering(plan, rightCompiled, (ConstantRecipe) leftCompiled.getRecipe(), |
697 | 697 | leftCompiled.getVariablesTuple()); | |
698 | // otherwise, we need at least a trimmer | 698 | } |
699 | TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables); | 699 | if (rightCompiled.getRecipe() instanceof ConstantRecipe |
700 | 700 | && leftCompiled.getVariablesTuple().containsAll(rightCompiled.getVariablesTuple())) { | |
701 | // do we need to eliminate duplicates? | 701 | return compileConstantFiltering(plan, leftCompiled, (ConstantRecipe) rightCompiled.getRecipe(), |
702 | SubPlan parentPlan = compiledParentPlan.getSubPlan(); | 702 | rightCompiled.getVariablesTuple()); |
703 | if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined( | 703 | } |
704 | parentPlan, | 704 | } |
705 | targetVariables, | 705 | |
706 | queryAnalyzer, | 706 | // ELSE: ACTUAL JOIN |
707 | true)) | 707 | CompilerHelper.JoinHelper joinHelper = new CompilerHelper.JoinHelper(plan, leftCompiled, rightCompiled); |
708 | { | 708 | return new CompiledSubPlan(plan, joinHelper.getNaturalJoinVariablesTuple(), joinHelper.getNaturalJoinRecipe(), |
709 | // if uniqueness enforcess is unwanted or unneeeded, skip it | 709 | joinHelper.getPrimaryIndexer(), joinHelper.getSecondaryIndexer()); |
710 | return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan); | 710 | } |
711 | } else { | 711 | |
712 | // add a uniqueness enforcer | 712 | private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) { |
713 | UniquenessEnforcerRecipe recipe = FACTORY.createUniquenessEnforcerRecipe(); | 713 | final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan); |
714 | recipe.getParents().add(trimmerRecipe); | 714 | final CompiledSubPlan compiledParent = compiledParents.get(0); |
715 | 715 | ||
716 | // temporary solution to support the deprecated option for now | 716 | List<PVariable> projectedVariables = new ArrayList<PVariable>(operation.getToVariables()); |
717 | final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan)); | 717 | // Determinizing projection: try to keep original order (hopefully facilitates node reuse) |
718 | 718 | Map<PVariable, Integer> parentPosMapping = compiledParent.getPosMapping(); | |
719 | recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); | 719 | Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get)); |
720 | if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { | 720 | |
721 | CompilerHelper.PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), metaContext); | 721 | return doProjectPlan(compiledParent, projectedVariables, true, |
722 | 722 | parentTrace -> parentTrace.cloneFor(plan), | |
723 | if (triplet.comparator != null) { | 723 | (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace), |
724 | MonotonicityInfo info = FACTORY.createMonotonicityInfo(); | 724 | (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace) |
725 | info.setCoreMask(triplet.coreMask); | 725 | ); |
726 | info.setPosetMask(triplet.posetMask); | 726 | } |
727 | info.setPosetComparator(triplet.comparator); | 727 | |
728 | recipe.setOptionalMonotonicityInfo(info); | 728 | /** |
729 | } | 729 | * Projects a subplan onto the specified variable tuple |
730 | } | 730 | * |
731 | 731 | * @param compiledParentPlan the compiled form of the subplan | |
732 | RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan); | 732 | * @param targetVariables list of variables to project to |
733 | return finalTraceFactory.apply(recipe, trimmerTrace); | 733 | * @param enforceUniqueness whether distinctness shall be enforced after the projection. |
734 | } | 734 | * Specify false only if directly connecting to a production node. |
735 | } | 735 | * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, |
736 | 736 | * in case it is sufficient | |
737 | /** | 737 | * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the |
738 | * Projects the final compiled form of a PBody onto the parameter tuple | 738 | * node and its parent trace |
739 | * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to query parameters | 739 | * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the |
740 | * @param enforceUniqueness whether distinctness shall be enforced after the projection. | 740 | * node and its parent trace |
741 | * Specify false only if directly connecting to a production node. | 741 | * @since 2.1 |
742 | * @since 2.1 | 742 | */ |
743 | */ | 743 | <ResultTrace extends RecipeTraceInfo> ResultTrace doProjectPlan( |
744 | RecipeTraceInfo projectBodyFinalToParameters( | 744 | final CompiledSubPlan compiledParentPlan, |
745 | final CompiledSubPlan compiledBody, | 745 | final List<PVariable> targetVariables, |
746 | boolean enforceUniqueness) | 746 | boolean enforceUniqueness, |
747 | { | 747 | Function<CompiledSubPlan, ResultTrace> reinterpretTraceFactory, |
748 | final PBody body = compiledBody.getSubPlan().getBody(); | 748 | BiFunction<ReteNodeRecipe, RecipeTraceInfo, RecipeTraceInfo> intermediateTraceFactory, |
749 | final List<PVariable> parameterList = body.getSymbolicParameterVariables(); | 749 | BiFunction<ReteNodeRecipe, RecipeTraceInfo, ResultTrace> finalTraceFactory) { |
750 | 750 | if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed | |
751 | return doProjectPlan(compiledBody, parameterList, enforceUniqueness, | 751 | return reinterpretTraceFactory.apply(compiledParentPlan); |
752 | parentTrace -> parentTrace, | 752 | |
753 | (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace), | 753 | // otherwise, we need at least a trimmer |
754 | (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace) | 754 | TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables); |
755 | ); | 755 | |
756 | } | 756 | // do we need to eliminate duplicates? |
757 | 757 | SubPlan parentPlan = compiledParentPlan.getSubPlan(); | |
758 | private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) { | 758 | if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined( |
759 | if (!operation.getAPrioriVariables().isEmpty()) { | 759 | parentPlan, |
760 | throw new IllegalArgumentException("Input variables unsupported by Rete: " + plan.toShortString()); | 760 | targetVariables, |
761 | } | 761 | queryAnalyzer, |
762 | final ConstantRecipe recipe = FACTORY.createConstantRecipe(); | 762 | true)) { |
763 | recipe.getConstantValues().clear(); | 763 | // if uniqueness enforcess is unwanted or unneeeded, skip it |
764 | 764 | return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan); | |
765 | return new CompiledSubPlan(plan, new ArrayList<PVariable>(), recipe); | 765 | } else { |
766 | } | 766 | // add a uniqueness enforcer |
767 | 767 | UniquenessEnforcerRecipe recipe = FACTORY.createUniquenessEnforcerRecipe(); | |
768 | private CompiledSubPlan doCompileEnumerate(EnumerablePConstraint constraint, SubPlan plan) { | 768 | recipe.getParents().add(trimmerRecipe); |
769 | final PlanningTrace trimmedTrace = doEnumerateAndDeduplicate(constraint, plan); | 769 | |
770 | 770 | // temporary solution to support the deprecated option for now | |
771 | return trimmedTrace.cloneFor(plan); | 771 | final boolean deleteAndRederiveEvaluationDep = |
772 | } | 772 | this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan)); |
773 | 773 | ||
774 | private PlanningTrace doEnumerateAndDeduplicate(EnumerablePConstraint constraint, SubPlan plan) { | 774 | recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); |
775 | final PlanningTrace coreTrace = doEnumerateDispatch(plan, constraint); | 775 | if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { |
776 | final PlanningTrace trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace); | 776 | CompilerHelper.PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, |
777 | return trimmedTrace; | 777 | parentPlan.getBody(), metaContext); |
778 | } | 778 | |
779 | 779 | if (triplet.comparator != null) { | |
780 | private PlanningTrace doEnumerateDispatch(SubPlan plan, EnumerablePConstraint constraint) { | 780 | MonotonicityInfo info = FACTORY.createMonotonicityInfo(); |
781 | if (constraint instanceof RelationEvaluation) { | 781 | info.setCoreMask(triplet.coreMask); |
782 | return compileEnumerable(plan, (RelationEvaluation) constraint); | 782 | info.setPosetMask(triplet.posetMask); |
783 | } else if (constraint instanceof BinaryTransitiveClosure) { | 783 | info.setPosetComparator(triplet.comparator); |
784 | return compileEnumerable(plan, (BinaryTransitiveClosure) constraint); | 784 | recipe.setOptionalMonotonicityInfo(info); |
785 | } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { | 785 | } |
786 | return compileEnumerable(plan, (BinaryReflexiveTransitiveClosure) constraint); | 786 | } |
787 | |||
788 | RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan); | ||
789 | return finalTraceFactory.apply(recipe, trimmerTrace); | ||
790 | } | ||
791 | } | ||
792 | |||
793 | /** | ||
794 | * Projects the final compiled form of a PBody onto the parameter tuple | ||
795 | * | ||
796 | * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to | ||
797 | * query parameters | ||
798 | * @param enforceUniqueness whether distinctness shall be enforced after the projection. | ||
799 | * Specify false only if directly connecting to a production node. | ||
800 | * @since 2.1 | ||
801 | */ | ||
802 | RecipeTraceInfo projectBodyFinalToParameters( | ||
803 | final CompiledSubPlan compiledBody, | ||
804 | boolean enforceUniqueness) { | ||
805 | final PBody body = compiledBody.getSubPlan().getBody(); | ||
806 | final List<PVariable> parameterList = body.getSymbolicParameterVariables(); | ||
807 | |||
808 | return doProjectPlan(compiledBody, parameterList, enforceUniqueness, | ||
809 | parentTrace -> parentTrace, | ||
810 | (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace), | ||
811 | (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace) | ||
812 | ); | ||
813 | } | ||
814 | |||
815 | private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) { | ||
816 | if (!operation.getAPrioriVariables().isEmpty()) { | ||
817 | throw new IllegalArgumentException("Input variables unsupported by Rete: " + plan.toShortString()); | ||
818 | } | ||
819 | final ConstantRecipe recipe = FACTORY.createConstantRecipe(); | ||
820 | recipe.getConstantValues().clear(); | ||
821 | |||
822 | return new CompiledSubPlan(plan, new ArrayList<PVariable>(), recipe); | ||
823 | } | ||
824 | |||
825 | private CompiledSubPlan doCompileEnumerate(EnumerablePConstraint constraint, SubPlan plan) { | ||
826 | final PlanningTrace trimmedTrace = doEnumerateAndDeduplicate(constraint, plan); | ||
827 | |||
828 | return trimmedTrace.cloneFor(plan); | ||
829 | } | ||
830 | |||
831 | private PlanningTrace doEnumerateAndDeduplicate(EnumerablePConstraint constraint, SubPlan plan) { | ||
832 | final PlanningTrace coreTrace = doEnumerateDispatch(plan, constraint); | ||
833 | final PlanningTrace trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace); | ||
834 | return trimmedTrace; | ||
835 | } | ||
836 | |||
837 | private PlanningTrace doEnumerateDispatch(SubPlan plan, EnumerablePConstraint constraint) { | ||
838 | if (constraint instanceof RelationEvaluation) { | ||
839 | return compileEnumerable(plan, (RelationEvaluation) constraint); | ||
840 | } else if (constraint instanceof BinaryTransitiveClosure) { | ||
841 | return compileEnumerable(plan, (BinaryTransitiveClosure) constraint); | ||
842 | } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { | ||
843 | return compileEnumerable(plan, (BinaryReflexiveTransitiveClosure) constraint); | ||
787 | } else if (constraint instanceof RepresentativeElectionConstraint) { | 844 | } else if (constraint instanceof RepresentativeElectionConstraint) { |
788 | return compileEnumerable(plan, (RepresentativeElectionConstraint) constraint); | 845 | return compileEnumerable(plan, (RepresentativeElectionConstraint) constraint); |
789 | } else if (constraint instanceof ConstantValue) { | 846 | } else if (constraint instanceof ConstantValue) { |
790 | return compileEnumerable(plan, (ConstantValue) constraint); | 847 | return compileEnumerable(plan, (ConstantValue) constraint); |
791 | } else if (constraint instanceof PositivePatternCall) { | 848 | } else if (constraint instanceof PositivePatternCall) { |
792 | return compileEnumerable(plan, (PositivePatternCall) constraint); | 849 | return compileEnumerable(plan, (PositivePatternCall) constraint); |
793 | } else if (constraint instanceof TypeConstraint) { | 850 | } else if (constraint instanceof TypeConstraint) { |
794 | return compileEnumerable(plan, (TypeConstraint) constraint); | 851 | return compileEnumerable(plan, (TypeConstraint) constraint); |
795 | } | 852 | } |
796 | throw new UnsupportedOperationException("Unknown enumerable constraint " + constraint); | 853 | throw new UnsupportedOperationException("Unknown enumerable constraint " + constraint); |
797 | } | 854 | } |
798 | 855 | ||
799 | private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveClosure constraint) { | 856 | private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveClosure constraint) { |
800 | // TODO the implementation would perform better if an inequality check would be used after tcRecipe and | 857 | // TODO the implementation would perform better if an inequality check would be used after tcRecipe and |
801 | // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not available | 858 | // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not |
802 | // in recipe metamodel in VIATRA 2.0 | 859 | // available |
803 | 860 | // in recipe metamodel in VIATRA 2.0 | |
804 | // Find called query | 861 | |
805 | final PQuery referredQuery = constraint.getSupplierKey(); | 862 | // Find called query |
806 | final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); | 863 | final PQuery referredQuery = constraint.getSupplierKey(); |
807 | 864 | final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); | |
808 | // Calculate irreflexive transitive closure | 865 | |
809 | final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe(); | 866 | // Calculate irreflexive transitive closure |
810 | tcRecipe.setParent(callTrace.getRecipe()); | 867 | final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe(); |
811 | final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), tcRecipe, callTrace); | 868 | tcRecipe.setParent(callTrace.getRecipe()); |
812 | 869 | final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), | |
813 | // Enumerate universe type | 870 | tcRecipe, callTrace); |
814 | final IInputKey inputKey = constraint.getUniverseType(); | 871 | |
815 | final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); | 872 | // Enumerate universe type |
816 | final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple( | 873 | final IInputKey inputKey = constraint.getUniverseType(); |
817 | Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe); | 874 | final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), |
818 | 875 | inputKey.getArity()); | |
819 | // Calculate reflexive access by duplicating universe type column | 876 | final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple( |
820 | final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe(); | 877 | Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe); |
821 | reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0)); | 878 | |
822 | reflexiveRecipe.setParent(universeTypeRecipe); | 879 | // Calculate reflexive access by duplicating universe type column |
823 | final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), reflexiveRecipe, universeTypeTrace); | 880 | final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe(); |
824 | 881 | reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0)); | |
825 | // Finally, reduce duplicates after a join | 882 | reflexiveRecipe.setParent(universeTypeRecipe); |
826 | final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe(); | 883 | final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), |
827 | brtcRecipe.getParents().add(tcRecipe); | 884 | reflexiveRecipe, universeTypeTrace); |
828 | brtcRecipe.getParents().add(reflexiveRecipe); | 885 | |
829 | 886 | // Finally, reduce duplicates after a join | |
830 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, tcTrace); | 887 | final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe(); |
831 | } | 888 | brtcRecipe.getParents().add(tcRecipe); |
889 | brtcRecipe.getParents().add(reflexiveRecipe); | ||
890 | |||
891 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, | ||
892 | tcTrace); | ||
893 | } | ||
832 | 894 | ||
833 | private PlanningTrace compileEnumerable(SubPlan plan, RepresentativeElectionConstraint constraint) { | 895 | private PlanningTrace compileEnumerable(SubPlan plan, RepresentativeElectionConstraint constraint) { |
834 | var referredQuery = constraint.getSupplierKey(); | 896 | var referredQuery = constraint.getSupplierKey(); |
@@ -839,109 +901,110 @@ public class ReteRecipeCompiler { | |||
839 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); | 901 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); |
840 | } | 902 | } |
841 | 903 | ||
842 | private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) { | 904 | private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) { |
843 | final PQuery referredQuery = constraint.getSupplierKey(); | 905 | final PQuery referredQuery = constraint.getSupplierKey(); |
844 | final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); | 906 | final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); |
845 | 907 | ||
846 | final TransitiveClosureRecipe recipe = FACTORY.createTransitiveClosureRecipe(); | 908 | final TransitiveClosureRecipe recipe = FACTORY.createTransitiveClosureRecipe(); |
847 | recipe.setParent(callTrace.getRecipe()); | 909 | recipe.setParent(callTrace.getRecipe()); |
848 | 910 | ||
849 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); | 911 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); |
850 | } | 912 | } |
851 | 913 | ||
852 | private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) { | 914 | private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) { |
853 | final List<ReteNodeRecipe> parentRecipes = new ArrayList<ReteNodeRecipe>(); | 915 | final List<ReteNodeRecipe> parentRecipes = new ArrayList<ReteNodeRecipe>(); |
854 | final List<RecipeTraceInfo> parentTraceInfos = new ArrayList<RecipeTraceInfo>(); | 916 | final List<RecipeTraceInfo> parentTraceInfos = new ArrayList<RecipeTraceInfo>(); |
855 | for (final PQuery inputQuery : constraint.getReferredQueries()) { | 917 | for (final PQuery inputQuery : constraint.getReferredQueries()) { |
856 | final CompiledQuery compiledQuery = getCompiledForm(inputQuery); | 918 | final CompiledQuery compiledQuery = getCompiledForm(inputQuery); |
857 | parentRecipes.add(compiledQuery.getRecipe()); | 919 | parentRecipes.add(compiledQuery.getRecipe()); |
858 | parentTraceInfos.add(compiledQuery); | 920 | parentTraceInfos.add(compiledQuery); |
859 | } | 921 | } |
860 | final RelationEvaluationRecipe recipe = FACTORY.createRelationEvaluationRecipe(); | 922 | final RelationEvaluationRecipe recipe = FACTORY.createRelationEvaluationRecipe(); |
861 | recipe.getParents().addAll(parentRecipes); | 923 | recipe.getParents().addAll(parentRecipes); |
862 | recipe.setEvaluator(RecipesHelper.expressionDefinition(constraint.getEvaluator())); | 924 | recipe.setEvaluator(RecipesHelper.expressionDefinition(constraint.getEvaluator())); |
863 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, parentTraceInfos); | 925 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, parentTraceInfos); |
864 | } | 926 | } |
865 | 927 | ||
866 | private PlanningTrace compileEnumerable(SubPlan plan, PositivePatternCall constraint) { | 928 | private PlanningTrace compileEnumerable(SubPlan plan, PositivePatternCall constraint) { |
867 | final PQuery referredQuery = constraint.getReferredQuery(); | 929 | final PQuery referredQuery = constraint.getReferredQuery(); |
868 | return referQuery(referredQuery, plan, constraint.getVariablesTuple()); | 930 | return referQuery(referredQuery, plan, constraint.getVariablesTuple()); |
869 | } | 931 | } |
870 | 932 | ||
871 | private PlanningTrace compileEnumerable(SubPlan plan, TypeConstraint constraint) { | 933 | private PlanningTrace compileEnumerable(SubPlan plan, TypeConstraint constraint) { |
872 | final IInputKey inputKey = constraint.getSupplierKey(); | 934 | final IInputKey inputKey = constraint.getSupplierKey(); |
873 | final InputRecipe recipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); | 935 | final InputRecipe recipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); |
874 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); | 936 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); |
875 | } | 937 | } |
876 | 938 | ||
877 | private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) { | 939 | private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) { |
878 | final ConstantRecipe recipe = FACTORY.createConstantRecipe(); | 940 | final ConstantRecipe recipe = FACTORY.createConstantRecipe(); |
879 | recipe.getConstantValues().add(constraint.getSupplierKey()); | 941 | recipe.getConstantValues().add(constraint.getSupplierKey()); |
880 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); | 942 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); |
881 | } | 943 | } |
882 | 944 | ||
883 | // TODO handle recursion | 945 | // TODO handle recursion |
884 | private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) { | 946 | private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) { |
885 | RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query); | 947 | RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query); |
886 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple), | 948 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple), |
887 | referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning()); | 949 | referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning()); |
888 | } | 950 | } |
889 | 951 | ||
890 | private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) { | 952 | private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) { |
891 | // eliminate superfluous production node? | 953 | // eliminate superfluous production node? |
892 | if (PVisibility.EMBEDDED == query.getVisibility()) { // currently inline patterns only | 954 | if (PVisibility.EMBEDDED == query.getVisibility()) { // currently inline patterns only |
893 | Set<PBody> rewrittenBodies = normalizer.rewrite(query).getBodies(); | 955 | Set<PBody> rewrittenBodies = normalizer.rewrite(query).getBodies(); |
894 | if (1 == rewrittenBodies.size()) { // non-disjunctive | 956 | if (1 == rewrittenBodies.size()) { // non-disjunctive |
895 | // TODO in the future, check if non-recursive - (not currently permitted) | 957 | // TODO in the future, check if non-recursive - (not currently permitted) |
896 | 958 | ||
897 | PBody pBody = rewrittenBodies.iterator().next(); | 959 | PBody pBody = rewrittenBodies.iterator().next(); |
898 | SubPlan bodyFinalPlan = getPlan(pBody); | 960 | SubPlan bodyFinalPlan = getPlan(pBody); |
899 | 961 | ||
900 | // skip over any projections at the end | 962 | // skip over any projections at the end |
901 | bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); | 963 | bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); |
902 | 964 | ||
903 | // TODO checkAndTrimEqualVariables may introduce superfluous trim, | 965 | // TODO checkAndTrimEqualVariables may introduce superfluous trim, |
904 | // but whatever (no uniqueness enforcer needed) | 966 | // but whatever (no uniqueness enforcer needed) |
905 | 967 | ||
906 | // compile body | 968 | // compile body |
907 | final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); | 969 | final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); |
908 | 970 | ||
909 | // project to parameter list, add uniqueness enforcer if necessary | 971 | // project to parameter list, add uniqueness enforcer if necessary |
910 | return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is used */); | 972 | return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is |
911 | } | 973 | used */); |
912 | } | 974 | } |
913 | 975 | } | |
914 | // otherwise, regular reference to recipe realizing the query | 976 | |
915 | return getCompiledForm(query); | 977 | // otherwise, regular reference to recipe realizing the query |
916 | } | 978 | return getCompiledForm(query); |
917 | 979 | } | |
918 | protected List<CompiledSubPlan> getCompiledFormOfParents(SubPlan plan) { | 980 | |
919 | List<CompiledSubPlan> results = new ArrayList<CompiledSubPlan>(); | 981 | protected List<CompiledSubPlan> getCompiledFormOfParents(SubPlan plan) { |
920 | for (SubPlan parentPlan : plan.getParentPlans()) { | 982 | List<CompiledSubPlan> results = new ArrayList<CompiledSubPlan>(); |
921 | results.add(getCompiledForm(parentPlan)); | 983 | for (SubPlan parentPlan : plan.getParentPlans()) { |
922 | } | 984 | results.add(getCompiledForm(parentPlan)); |
923 | return results; | 985 | } |
924 | } | 986 | return results; |
925 | 987 | } | |
926 | /** | 988 | |
927 | * Returns an unmodifiable view of currently cached compiled queries. | 989 | /** |
928 | */ | 990 | * Returns an unmodifiable view of currently cached compiled queries. |
929 | public Map<PQuery, CompiledQuery> getCachedCompiledQueries() { | 991 | */ |
930 | return Collections.unmodifiableMap(queryCompilerCache); | 992 | public Map<PQuery, CompiledQuery> getCachedCompiledQueries() { |
931 | } | 993 | return Collections.unmodifiableMap(queryCompilerCache); |
932 | 994 | } | |
933 | /** | 995 | |
934 | * Returns an unmodifiable view of currently cached query plans. | 996 | /** |
935 | */ | 997 | * Returns an unmodifiable view of currently cached query plans. |
936 | public Map<PBody, SubPlan> getCachedQueryPlans() { | 998 | */ |
937 | return Collections.unmodifiableMap(plannerCache); | 999 | public Map<PBody, SubPlan> getCachedQueryPlans() { |
938 | } | 1000 | return Collections.unmodifiableMap(plannerCache); |
939 | 1001 | } | |
940 | private QueryEvaluationHint getHints(SubPlan plan) { | 1002 | |
941 | return getHints(plan.getBody().getPattern()); | 1003 | private QueryEvaluationHint getHints(SubPlan plan) { |
942 | } | 1004 | return getHints(plan.getBody().getPattern()); |
943 | 1005 | } | |
944 | private QueryEvaluationHint getHints(PQuery pattern) { | 1006 | |
945 | return hintProvider.getQueryEvaluationHint(pattern); | 1007 | private QueryEvaluationHint getHints(PQuery pattern) { |
946 | } | 1008 | return hintProvider.getQueryEvaluationHint(pattern); |
1009 | } | ||
947 | } | 1010 | } |
diff --git a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/ConnectionFactory.java b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/ConnectionFactory.java index c69757b6..fe70cbc3 100644 --- a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/ConnectionFactory.java +++ b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/ConnectionFactory.java | |||
@@ -11,6 +11,7 @@ package tools.refinery.interpreter.rete.network; | |||
11 | 11 | ||
12 | import tools.refinery.interpreter.matchers.tuple.Tuple; | 12 | import tools.refinery.interpreter.matchers.tuple.Tuple; |
13 | import tools.refinery.interpreter.rete.aggregation.IndexerBasedAggregatorNode; | 13 | import tools.refinery.interpreter.rete.aggregation.IndexerBasedAggregatorNode; |
14 | import tools.refinery.interpreter.rete.aggregation.LeftJoinNode; | ||
14 | import tools.refinery.interpreter.rete.boundary.InputConnector; | 15 | import tools.refinery.interpreter.rete.boundary.InputConnector; |
15 | import tools.refinery.interpreter.rete.eval.RelationEvaluatorNode; | 16 | import tools.refinery.interpreter.rete.eval.RelationEvaluatorNode; |
16 | import tools.refinery.interpreter.rete.index.DualInputNode; | 17 | import tools.refinery.interpreter.rete.index.DualInputNode; |
@@ -78,9 +79,12 @@ class ConnectionFactory { | |||
78 | Slots slots = avoidActiveNodeConflict(parentTraces.get(0), parentTraces.get(1)); | 79 | Slots slots = avoidActiveNodeConflict(parentTraces.get(0), parentTraces.get(1)); |
79 | beta.connectToIndexers(slots.primary, slots.secondary); | 80 | beta.connectToIndexers(slots.primary, slots.secondary); |
80 | } else if (recipe instanceof IndexerBasedAggregatorRecipe) { | 81 | } else if (recipe instanceof IndexerBasedAggregatorRecipe) { |
81 | final IndexerBasedAggregatorNode aggregator = (IndexerBasedAggregatorNode) freshNode; | 82 | final IndexerBasedAggregatorNode aggregator = (IndexerBasedAggregatorNode) freshNode; |
82 | final IndexerBasedAggregatorRecipe aggregatorRecipe = (IndexerBasedAggregatorRecipe) recipe; | 83 | final IndexerBasedAggregatorRecipe aggregatorRecipe = (IndexerBasedAggregatorRecipe) recipe; |
83 | aggregator.initializeWith((ProjectionIndexer) resolveIndexer(aggregatorRecipe.getParent())); | 84 | aggregator.initializeWith((ProjectionIndexer) resolveIndexer(aggregatorRecipe.getParent())); |
85 | } else if (recipe instanceof OuterJoinNodeRecipe outerJoinNodeRecipe) { | ||
86 | var leftJoinNode = (LeftJoinNode) freshNode; | ||
87 | leftJoinNode.initializeWith((ProjectionIndexer) resolveIndexer(outerJoinNodeRecipe.getParent())); | ||
84 | } else if (recipe instanceof MultiParentNodeRecipe) { | 88 | } else if (recipe instanceof MultiParentNodeRecipe) { |
85 | final Receiver receiver = (Receiver) freshNode; | 89 | final Receiver receiver = (Receiver) freshNode; |
86 | List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents(); | 90 | List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents(); |
diff --git a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/NodeFactory.java b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/NodeFactory.java index 301b757d..1f6a01ae 100644 --- a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/NodeFactory.java +++ b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/network/NodeFactory.java | |||
@@ -20,6 +20,7 @@ import tools.refinery.interpreter.matchers.tuple.Tuples; | |||
20 | import tools.refinery.interpreter.rete.aggregation.ColumnAggregatorNode; | 20 | import tools.refinery.interpreter.rete.aggregation.ColumnAggregatorNode; |
21 | import tools.refinery.interpreter.rete.aggregation.CountNode; | 21 | import tools.refinery.interpreter.rete.aggregation.CountNode; |
22 | import tools.refinery.interpreter.rete.aggregation.IAggregatorNode; | 22 | import tools.refinery.interpreter.rete.aggregation.IAggregatorNode; |
23 | import tools.refinery.interpreter.rete.aggregation.LeftJoinNode; | ||
23 | import tools.refinery.interpreter.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode; | 24 | import tools.refinery.interpreter.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode; |
24 | import tools.refinery.interpreter.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode; | 25 | import tools.refinery.interpreter.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode; |
25 | import tools.refinery.interpreter.rete.aggregation.timely.FirstOnlyParallelTimelyColumnAggregatorNode; | 26 | import tools.refinery.interpreter.rete.aggregation.timely.FirstOnlyParallelTimelyColumnAggregatorNode; |
@@ -72,18 +73,25 @@ class NodeFactory { | |||
72 | return parentNode.constructIndex(toMask(recipe.getMask()), traces); | 73 | return parentNode.constructIndex(toMask(recipe.getMask()), traces); |
73 | // already traced | 74 | // already traced |
74 | } else if (recipe instanceof AggregatorIndexerRecipe) { | 75 | } else if (recipe instanceof AggregatorIndexerRecipe) { |
75 | int indexOfAggregateResult = recipe.getParent().getArity(); | 76 | int indexOfAggregateResult = recipe.getParent().getArity(); |
76 | int resultPosition = recipe.getMask().getSourceIndices().lastIndexOf(indexOfAggregateResult); | 77 | int resultPosition = recipe.getMask().getSourceIndices().lastIndexOf(indexOfAggregateResult); |
77 | 78 | ||
78 | IAggregatorNode aggregatorNode = (IAggregatorNode) parentNode; | 79 | IAggregatorNode aggregatorNode = (IAggregatorNode) parentNode; |
79 | final Indexer result = (resultPosition == -1) ? aggregatorNode.getAggregatorOuterIndexer() | 80 | final Indexer result = (resultPosition == -1) ? aggregatorNode.getAggregatorOuterIndexer() |
80 | : aggregatorNode.getAggregatorOuterIdentityIndexer(resultPosition); | 81 | : aggregatorNode.getAggregatorOuterIdentityIndexer(resultPosition); |
81 | 82 | ||
82 | for (TraceInfo traceInfo : traces) | 83 | for (TraceInfo traceInfo : traces) |
83 | result.assignTraceInfo(traceInfo); | 84 | result.assignTraceInfo(traceInfo); |
84 | return result; | 85 | return result; |
85 | } else | 86 | } else if (recipe instanceof OuterJoinIndexerRecipe) { |
86 | throw new IllegalArgumentException("Unkown Indexer recipe: " + recipe); | 87 | var leftJoinNode = (LeftJoinNode) parentNode; |
88 | var result = leftJoinNode.getOuterIndexer(); | ||
89 | for (TraceInfo traceInfo : traces) | ||
90 | result.assignTraceInfo(traceInfo); | ||
91 | return result; | ||
92 | } else { | ||
93 | throw new IllegalArgumentException("Unkown Indexer recipe: " + recipe); | ||
94 | } | ||
87 | } | 95 | } |
88 | 96 | ||
89 | /** | 97 | /** |
@@ -134,6 +142,8 @@ class NodeFactory { | |||
134 | return instantiateNode(reteContainer, (CountAggregatorRecipe) recipe); | 142 | return instantiateNode(reteContainer, (CountAggregatorRecipe) recipe); |
135 | if (recipe instanceof SingleColumnAggregatorRecipe) | 143 | if (recipe instanceof SingleColumnAggregatorRecipe) |
136 | return instantiateNode(reteContainer, (SingleColumnAggregatorRecipe) recipe); | 144 | return instantiateNode(reteContainer, (SingleColumnAggregatorRecipe) recipe); |
145 | if (recipe instanceof OuterJoinNodeRecipe outerJoinNodeRecipe) | ||
146 | return instantiateNode(reteContainer, outerJoinNodeRecipe); | ||
137 | if (recipe instanceof DiscriminatorDispatcherRecipe) | 147 | if (recipe instanceof DiscriminatorDispatcherRecipe) |
138 | return instantiateNode(reteContainer, (DiscriminatorDispatcherRecipe) recipe); | 148 | return instantiateNode(reteContainer, (DiscriminatorDispatcherRecipe) recipe); |
139 | if (recipe instanceof DiscriminatorBucketRecipe) | 149 | if (recipe instanceof DiscriminatorBucketRecipe) |
@@ -246,6 +256,10 @@ class NodeFactory { | |||
246 | } | 256 | } |
247 | } | 257 | } |
248 | 258 | ||
259 | private Supplier instantiateNode(ReteContainer reteContainer, OuterJoinNodeRecipe recipe) { | ||
260 | return new LeftJoinNode(reteContainer, recipe.getDefaultValue()); | ||
261 | } | ||
262 | |||
249 | private Supplier instantiateNode(ReteContainer reteContainer, TransitiveClosureRecipe recipe) { | 263 | private Supplier instantiateNode(ReteContainer reteContainer, TransitiveClosureRecipe recipe) { |
250 | return new TransitiveClosureNode(reteContainer); | 264 | return new TransitiveClosureNode(reteContainer); |
251 | } | 265 | } |