aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java')
-rw-r--r--subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/construction/plancompiler/ReteRecipeCompiler.java1815
1 files changed, 939 insertions, 876 deletions
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 @@
10package tools.refinery.interpreter.rete.construction.plancompiler; 10package tools.refinery.interpreter.rete.construction.plancompiler;
11 11
12import org.apache.log4j.Logger; 12import org.apache.log4j.Logger;
13import org.eclipse.emf.ecore.util.EcoreUtil;
13import tools.refinery.interpreter.matchers.InterpreterRuntimeException; 14import tools.refinery.interpreter.matchers.InterpreterRuntimeException;
14import tools.refinery.interpreter.matchers.backend.CommonQueryHintOptions; 15import tools.refinery.interpreter.matchers.backend.CommonQueryHintOptions;
15import tools.refinery.interpreter.matchers.backend.IQueryBackendHintProvider; 16import 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 */
58public class ReteRecipeCompiler { 58public 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}