Skip to content

Commit 2134a1c

Browse files
committed
improvements
1 parent d1db080 commit 2134a1c

13 files changed

Lines changed: 848 additions & 71 deletions

File tree

core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/AbstractQueryModelNode.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public abstract class AbstractQueryModelNode implements QueryModelNode, Variable
6464
private Map<String, Long> longMetricsPlanned = Collections.emptyMap();
6565
private Map<String, Double> doubleMetricsPlanned = Collections.emptyMap();
6666
private Map<String, String> stringMetricsPlanned = Collections.emptyMap();
67+
@JsonIgnore
68+
private transient Map<Object, Object> queryModelMetadata = Collections.emptyMap();
6769

6870
private double cardinality = CARDINALITY_NOT_SET;
6971

@@ -138,6 +140,8 @@ public AbstractQueryModelNode clone() {
138140
: new HashMap<>(doubleMetricsPlanned);
139141
clone.stringMetricsPlanned = stringMetricsPlanned.isEmpty() ? Collections.emptyMap()
140142
: new HashMap<>(stringMetricsPlanned);
143+
clone.queryModelMetadata = queryModelMetadata.isEmpty() ? Collections.emptyMap()
144+
: new HashMap<>(queryModelMetadata);
141145
return clone;
142146
} catch (CloneNotSupportedException e) {
143147
throw new RuntimeException("Query model nodes are required to be cloneable", e);
@@ -448,6 +452,43 @@ public void setRuntimeTelemetryEnabled(boolean runtimeTelemetryEnabled) {
448452
this.runtimeTelemetryEnabled = runtimeTelemetryEnabled;
449453
}
450454

455+
@Override
456+
public Object getQueryModelMetadata(Object key) {
457+
Objects.requireNonNull(key, "key");
458+
return queryModelMetadata.get(key);
459+
}
460+
461+
@Override
462+
public void setQueryModelMetadata(Object key, Object value) {
463+
Objects.requireNonNull(key, "key");
464+
if (value == null) {
465+
removeQueryModelMetadata(key);
466+
return;
467+
}
468+
if (queryModelMetadata.isEmpty()) {
469+
queryModelMetadata = new HashMap<>();
470+
}
471+
queryModelMetadata.put(key, value);
472+
}
473+
474+
@Override
475+
public Object removeQueryModelMetadata(Object key) {
476+
Objects.requireNonNull(key, "key");
477+
if (queryModelMetadata.isEmpty()) {
478+
return null;
479+
}
480+
Object removed = queryModelMetadata.remove(key);
481+
if (queryModelMetadata.isEmpty()) {
482+
queryModelMetadata = Collections.emptyMap();
483+
}
484+
return removed;
485+
}
486+
487+
@Override
488+
public void clearQueryModelMetadata() {
489+
queryModelMetadata = Collections.emptyMap();
490+
}
491+
451492
/**
452493
* @return Human readable number. Eg. 12.1M for 1212213.4 and UNKNOWN for -1.
453494
*/

core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/QueryModelNode.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.io.Serializable;
1515
import java.util.Collections;
1616
import java.util.Map;
17+
import java.util.Objects;
1718

1819
import org.eclipse.rdf4j.common.annotation.Experimental;
1920

@@ -351,4 +352,47 @@ default void setRuntimeTelemetryEnabled(boolean runtimeTelemetryEnabled) {
351352
// no-op
352353
}
353354

355+
/**
356+
* Returns transient optimizer metadata attached to this query model node for the supplied key.
357+
*
358+
* @param key non-null metadata key
359+
* @return the metadata value, or {@code null} if absent
360+
*/
361+
@Experimental
362+
default Object getQueryModelMetadata(Object key) {
363+
Objects.requireNonNull(key, "key");
364+
return null;
365+
}
366+
367+
/**
368+
* Attaches transient optimizer metadata to this query model node. Passing {@code null} as value removes the key.
369+
*
370+
* @param key non-null metadata key
371+
* @param value metadata value, or {@code null} to remove
372+
*/
373+
@Experimental
374+
default void setQueryModelMetadata(Object key, Object value) {
375+
Objects.requireNonNull(key, "key");
376+
}
377+
378+
/**
379+
* Removes transient optimizer metadata from this query model node.
380+
*
381+
* @param key non-null metadata key
382+
* @return the removed value, or {@code null} if absent
383+
*/
384+
@Experimental
385+
default Object removeQueryModelMetadata(Object key) {
386+
Objects.requireNonNull(key, "key");
387+
return null;
388+
}
389+
390+
/**
391+
* Clears all transient optimizer metadata attached to this query model node.
392+
*/
393+
@Experimental
394+
default void clearQueryModelMetadata() {
395+
// no-op
396+
}
397+
354398
}

core/queryalgebra/model/src/test/java/org/eclipse/rdf4j/query/algebra/AbstractQueryModelNodeTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@
88
*
99
* SPDX-License-Identifier: BSD-3-Clause
1010
*******************************************************************************/
11+
// Some portions generated by Codex
1112
package org.eclipse.rdf4j.query.algebra;
1213

1314
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNull;
16+
import static org.junit.jupiter.api.Assertions.assertSame;
17+
18+
import java.lang.reflect.Method;
19+
import java.util.ArrayList;
20+
import java.util.List;
1421

1522
import org.junit.jupiter.api.Test;
1623

@@ -54,4 +61,72 @@ public void getCardinalityString() {
5461
}
5562

5663
}
64+
65+
@Test
66+
public void queryModelMetadataSetGetRemoveAndClear() throws Exception {
67+
StatementPattern statementPattern = new StatementPattern(Var.of("s"), Var.of("p"), Var.of("o"));
68+
Object key = new Object();
69+
Object value = "value";
70+
71+
assertNull(metadata(statementPattern, key));
72+
setMetadata(statementPattern, key, value);
73+
assertSame(value, metadata(statementPattern, key));
74+
assertSame(value, removeMetadata(statementPattern, key));
75+
assertNull(metadata(statementPattern, key));
76+
77+
setMetadata(statementPattern, key, value);
78+
setMetadata(statementPattern, key, null);
79+
assertNull(metadata(statementPattern, key));
80+
81+
setMetadata(statementPattern, key, value);
82+
clearMetadata(statementPattern);
83+
assertNull(metadata(statementPattern, key));
84+
}
85+
86+
@Test
87+
public void queryModelMetadataDoesNotChangeRenderedShape() throws Exception {
88+
StatementPattern statementPattern = new StatementPattern(Var.of("s"), Var.of("p"), Var.of("o"));
89+
String signature = statementPattern.getSignature();
90+
String rendered = statementPattern.toString();
91+
92+
setMetadata(statementPattern, new Object(), "value");
93+
94+
assertEquals(signature, statementPattern.getSignature());
95+
assertEquals(rendered, statementPattern.toString());
96+
}
97+
98+
@Test
99+
public void queryModelMetadataCloneUsesShallowCopy() throws Exception {
100+
StatementPattern statementPattern = new StatementPattern(Var.of("s"), Var.of("p"), Var.of("o"));
101+
Object key = new Object();
102+
List<String> value = new ArrayList<>(List.of("a"));
103+
setMetadata(statementPattern, key, value);
104+
105+
StatementPattern clone = statementPattern.clone();
106+
107+
assertSame(value, metadata(clone, key));
108+
removeMetadata(clone, key);
109+
assertSame(value, metadata(statementPattern, key));
110+
assertNull(metadata(clone, key));
111+
}
112+
113+
private static Object metadata(QueryModelNode node, Object key) throws Exception {
114+
Method method = QueryModelNode.class.getMethod("getQueryModelMetadata", Object.class);
115+
return method.invoke(node, key);
116+
}
117+
118+
private static void setMetadata(QueryModelNode node, Object key, Object value) throws Exception {
119+
Method method = QueryModelNode.class.getMethod("setQueryModelMetadata", Object.class, Object.class);
120+
method.invoke(node, key, value);
121+
}
122+
123+
private static Object removeMetadata(QueryModelNode node, Object key) throws Exception {
124+
Method method = QueryModelNode.class.getMethod("removeQueryModelMetadata", Object.class);
125+
return method.invoke(node, key);
126+
}
127+
128+
private static void clearMetadata(QueryModelNode node) throws Exception {
129+
Method method = QueryModelNode.class.getMethod("clearQueryModelMetadata");
130+
method.invoke(node);
131+
}
57132
}

core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SketchBasedJoinEstimator.java

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ public class SketchBasedJoinEstimator implements QueryOptimizationScopeProvider
177177
private static final long TUPLE_UPDATE_SEED = 9001L;
178178
private static final Component[] COMPONENT_VALUES = Component.values();
179179
private static final int COMPONENT_MASK_COUNT = 1 << COMPONENT_VALUES.length;
180+
private static final Object PATTERN_VARIABLE_EQUALITIES_METADATA_KEY = new Object();
181+
private static final PatternVariableEqualities NO_PATTERN_VARIABLE_EQUALITIES = new PatternVariableEqualities(
182+
new byte[0], new byte[0]);
180183
private static final long BOUND_VAR_MASK_OVERFLOW = Long.MIN_VALUE;
181184
private static final Pair[] PAIR_VALUES = Pair.values();
182185
private static final Set<Component>[] COMPONENT_SETS_BY_MASK = buildComponentSetsByMask();
@@ -6084,12 +6087,17 @@ private Long exactBoundPatternRows(StatementPattern pattern, String sharedVarNam
60846087

60856088
private StatementPattern bindPatternVar(StatementPattern pattern, String sharedVarName, Value sharedValue) {
60866089
StatementPattern boundPattern = pattern.clone();
6090+
boolean mutated = false;
60876091
for (Component component : COMPONENT_VALUES) {
60886092
Var var = varForComponent(boundPattern, component);
60896093
if (var == null || var.hasValue() || var.getName() == null || !sharedVarName.equals(var.getName())) {
60906094
continue;
60916095
}
60926096
var.replaceWith(Var.of(var.getName(), sharedValue, var.isAnonymous(), var.isConstant()));
6097+
mutated = true;
6098+
}
6099+
if (mutated) {
6100+
boundPattern.removeQueryModelMetadata(PATTERN_VARIABLE_EQUALITIES_METADATA_KEY);
60936101
}
60946102
return boundPattern;
60956103
}
@@ -6137,25 +6145,53 @@ private Resource[] exactBoundContexts(Var contextVar) {
61376145
}
61386146

61396147
private boolean statementMatchesPatternVariableEqualities(StatementPattern pattern, Statement statement) {
6140-
Map<String, Value> seen = new HashMap<>();
6148+
PatternVariableEqualities equalities = patternVariableEqualities(pattern);
6149+
if (equalities == NO_PATTERN_VARIABLE_EQUALITIES) {
6150+
return true;
6151+
}
6152+
return equalities.matches(statement);
6153+
}
6154+
6155+
private PatternVariableEqualities patternVariableEqualities(StatementPattern pattern) {
6156+
Object cached = pattern.getQueryModelMetadata(PATTERN_VARIABLE_EQUALITIES_METADATA_KEY);
6157+
if (cached instanceof PatternVariableEqualities) {
6158+
return (PatternVariableEqualities) cached;
6159+
}
6160+
6161+
String[] names = new String[COMPONENT_VALUES.length];
61416162
for (Component component : COMPONENT_VALUES) {
61426163
Var var = varForComponent(pattern, component);
61436164
if (var == null || var.hasValue() || var.getName() == null) {
61446165
continue;
61456166
}
6146-
Value value = statementValue(statement, component);
6147-
if (seen.containsKey(var.getName())) {
6148-
if (!Objects.equals(seen.get(var.getName()), value)) {
6149-
return false;
6167+
names[component.ordinal()] = var.getName();
6168+
}
6169+
6170+
byte[] leftComponents = new byte[6];
6171+
byte[] rightComponents = new byte[6];
6172+
int equalityCount = 0;
6173+
for (int left = 0; left < names.length; left++) {
6174+
String leftName = names[left];
6175+
if (leftName == null) {
6176+
continue;
6177+
}
6178+
for (int right = left + 1; right < names.length; right++) {
6179+
if (leftName.equals(names[right])) {
6180+
leftComponents[equalityCount] = (byte) left;
6181+
rightComponents[equalityCount] = (byte) right;
6182+
equalityCount++;
61506183
}
6151-
} else {
6152-
seen.put(var.getName(), value);
61536184
}
61546185
}
6155-
return true;
6186+
6187+
PatternVariableEqualities equalities = equalityCount == 0 ? NO_PATTERN_VARIABLE_EQUALITIES
6188+
: new PatternVariableEqualities(Arrays.copyOf(leftComponents, equalityCount),
6189+
Arrays.copyOf(rightComponents, equalityCount));
6190+
pattern.setQueryModelMetadata(PATTERN_VARIABLE_EQUALITIES_METADATA_KEY, equalities);
6191+
return equalities;
61566192
}
61576193

6158-
private Value statementValue(Statement statement, Component component) {
6194+
private static Value statementValue(Statement statement, Component component) {
61596195
return switch (component) {
61606196
case S -> statement.getSubject();
61616197
case P -> statement.getPredicate();
@@ -6164,6 +6200,27 @@ private Value statementValue(Statement statement, Component component) {
61646200
};
61656201
}
61666202

6203+
private static final class PatternVariableEqualities {
6204+
private final byte[] leftComponents;
6205+
private final byte[] rightComponents;
6206+
6207+
private PatternVariableEqualities(byte[] leftComponents, byte[] rightComponents) {
6208+
this.leftComponents = leftComponents;
6209+
this.rightComponents = rightComponents;
6210+
}
6211+
6212+
private boolean matches(Statement statement) {
6213+
for (int i = 0; i < leftComponents.length; i++) {
6214+
Value left = statementValue(statement, COMPONENT_VALUES[leftComponents[i]]);
6215+
Value right = statementValue(statement, COMPONENT_VALUES[rightComponents[i]]);
6216+
if (!Objects.equals(left, right)) {
6217+
return false;
6218+
}
6219+
}
6220+
return true;
6221+
}
6222+
}
6223+
61676224
private static final class SharedVarScan {
61686225
private final Map<Value, Long> exactCounts;
61696226
private final List<Value> sampledRows;
@@ -7650,6 +7707,7 @@ private boolean bindVarToConstant(StatementPattern pattern, ValueExpr maybeVarEx
76507707

76517708
private boolean bindPatternVars(StatementPattern pattern, String varName, Value value) {
76527709
boolean found = false;
7710+
boolean mutated = false;
76537711
for (Var patternVar : new Var[] {
76547712
pattern.getSubjectVar(),
76557713
pattern.getPredicateVar(),
@@ -7666,8 +7724,12 @@ private boolean bindPatternVars(StatementPattern pattern, String varName, Value
76667724
if (!patternVar.hasValue()) {
76677725
pattern.replaceChildNode(patternVar,
76687726
Var.of(patternVar.getName(), value, patternVar.isAnonymous(), patternVar.isConstant()));
7727+
mutated = true;
76697728
}
76707729
}
7730+
if (mutated) {
7731+
pattern.removeQueryModelMetadata(PATTERN_VARIABLE_EQUALITIES_METADATA_KEY);
7732+
}
76717733
return found;
76727734
}
76737735

core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDeferredFilterPlacer.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ TupleExpr buildSegmentRoot(Deque<TupleExpr> orderedArgs, List<DeferredFilter> fi
4949

5050
TupleExpr buildSegmentRoot(Deque<TupleExpr> orderedArgs, List<DeferredFilter> filters,
5151
Set<String> boundBeforeSegment, Map<Integer, Integer> filterPlacementSteps) {
52+
return buildSegmentRoot(orderedArgs, filters, boundBeforeSegment, filterPlacementSteps, false);
53+
}
54+
55+
TupleExpr buildSegmentRoot(Deque<TupleExpr> orderedArgs, List<DeferredFilter> filters,
56+
Set<String> boundBeforeSegment, Map<Integer, Integer> filterPlacementSteps, boolean rightDeepJoinTree) {
5257
if (orderedArgs.isEmpty()) {
5358
return null;
5459
}
@@ -87,7 +92,7 @@ TupleExpr buildSegmentRoot(Deque<TupleExpr> orderedArgs, List<DeferredFilter> fi
8792
for (SegmentFactor factor : factors) {
8893
roots.addLast(factor.tupleExpr);
8994
}
90-
TupleExpr root = buildJoinRoot(roots);
95+
TupleExpr root = buildJoinRoot(roots, rightDeepJoinTree);
9196
for (DeferredFilter filter : unresolvedFilters) {
9297
root = filterWrapper.wrap(root, List.of(filter), "root");
9398
}
@@ -664,9 +669,20 @@ private int[] expandWindowToCoverRequiredVars(List<SegmentFactor> factors, int[]
664669
}
665670

666671
private TupleExpr buildJoinRoot(Deque<TupleExpr> orderedArgs) {
672+
return buildJoinRoot(orderedArgs, false);
673+
}
674+
675+
private TupleExpr buildJoinRoot(Deque<TupleExpr> orderedArgs, boolean rightDeepJoinTree) {
667676
if (orderedArgs.isEmpty()) {
668677
return null;
669678
}
679+
if (rightDeepJoinTree) {
680+
TupleExpr root = orderedArgs.removeLast();
681+
while (!orderedArgs.isEmpty()) {
682+
root = joinFactory.create(orderedArgs.removeLast(), root);
683+
}
684+
return root;
685+
}
670686
TupleExpr root = orderedArgs.removeFirst();
671687
while (!orderedArgs.isEmpty()) {
672688
root = joinFactory.create(root, orderedArgs.removeFirst());

0 commit comments

Comments
 (0)