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