Skip to content

Commit 442a573

Browse files
authored
GH-5167 rsx:targetShape bug (#5173)
2 parents b7dccf4 + a82a08a commit 442a573

104 files changed

Lines changed: 1820 additions & 64 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/StatementMatcher.java

Lines changed: 125 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
package org.eclipse.rdf4j.sail.shacl.ast;
1212

1313
import java.util.ArrayList;
14+
import java.util.Collections;
1415
import java.util.HashSet;
16+
import java.util.LinkedHashSet;
1517
import java.util.List;
1618
import java.util.Objects;
1719
import java.util.Set;
@@ -31,10 +33,10 @@ public class StatementMatcher {
3133
private final Variable<IRI> predicate;
3234
private final Variable<? extends Value> object;
3335

34-
// private final Set<String> varNames;
36+
// private final Set<String> varNames;
3537
private final Targetable origin;
3638

37-
private final Set<String> inheritedVarNames;
39+
private Set<String> inheritedVarNames;
3840

3941
private List<StatementMatcher> subset = List.of();
4042

@@ -171,7 +173,7 @@ private void addSubset(StatementMatcher s) {
171173

172174
public static List<StatementMatcher> swap(List<StatementMatcher> statementMatchers, Variable<?> existingVariable,
173175
Variable<?> newVariable) {
174-
if (statementMatchers.size() == 0) {
176+
if (statementMatchers.isEmpty()) {
175177
return List.of();
176178
}
177179
if (statementMatchers.size() == 1) {
@@ -216,37 +218,47 @@ private static String formatForToString(String field, String name, Value value)
216218

217219
private StatementMatcher swap(Variable<?> existingVariable, Variable<?> newVariable) {
218220
String subjectName = getSubjectName();
221+
String subjectBasename = getSubjectBasename();
219222
Resource subjectValue = getSubjectValue();
223+
220224
String predicateName = getPredicateName();
225+
String predicateBasename = getPredicateBasename();
221226
IRI predicateValue = getPredicateValue();
227+
222228
String objectName = getObjectName();
229+
String objectBasename = getObjectBasename();
223230
Value objectValue = getObjectValue();
231+
224232
boolean changed = false;
225233

226234
if (Objects.equals(existingVariable.name, subjectName)
227235
&& Objects.equals(existingVariable.value, subjectValue)) {
228236
changed = true;
229237
subjectName = newVariable.name;
230238
subjectValue = (Resource) newVariable.value;
239+
subjectBasename = newVariable.baseName;
231240
}
232241

233242
if (Objects.equals(existingVariable.name, predicateName)
234243
&& Objects.equals(existingVariable.value, predicateValue)) {
235244
changed = true;
236245
predicateName = newVariable.name;
237246
predicateValue = (IRI) newVariable.value;
247+
predicateBasename = newVariable.baseName;
238248
}
239249

240250
if (Objects.equals(existingVariable.name, objectName) && Objects.equals(existingVariable.value, objectValue)) {
241251
changed = true;
242252
objectName = newVariable.name;
243253
objectValue = newVariable.value;
254+
objectBasename = newVariable.baseName;
244255
}
245256

246257
if (changed) {
247258
assert subset.isEmpty();
248-
return new StatementMatcher(new Variable<>(subjectName, subjectValue),
249-
new Variable<>(predicateName, predicateValue), new Variable<>(objectName, objectValue), origin,
259+
return new StatementMatcher(new Variable<>(subjectName, subjectValue, subjectBasename),
260+
new Variable<>(predicateName, predicateValue, predicateBasename),
261+
new Variable<>(objectName, objectValue, objectBasename), origin,
250262
inheritedVarNames);
251263
}
252264
return this;
@@ -268,6 +280,10 @@ public String getSubjectName() {
268280
return subject.name;
269281
}
270282

283+
public String getSubjectBasename() {
284+
return subject.baseName;
285+
}
286+
271287
public Resource getSubjectValue() {
272288
return subject.value;
273289
}
@@ -280,6 +296,10 @@ public String getPredicateName() {
280296
return predicate.name;
281297
}
282298

299+
public String getPredicateBasename() {
300+
return predicate.baseName;
301+
}
302+
283303
public IRI getPredicateValue() {
284304
return predicate.value;
285305
}
@@ -292,6 +312,10 @@ public String getObjectName() {
292312
return object.name;
293313
}
294314

315+
public String getObjectBasename() {
316+
return object.baseName;
317+
}
318+
295319
public Value getObjectValue() {
296320
return object.value;
297321
}
@@ -324,6 +348,7 @@ public int hashCode() {
324348

325349
public String getSparqlValuesDecl(Set<String> varNamesRestriction, boolean addInheritedVarNames,
326350
Set<String> varNamesInQueryFragment) {
351+
327352
StringBuilder sb = new StringBuilder("VALUES ( ");
328353
if (subject.name != null && varNamesRestriction.contains(subject.name) ||
329354
subject.baseName != null && varNamesRestriction.contains(subject.baseName)) {
@@ -362,13 +387,13 @@ public String getSparqlValuesDecl(Set<String> varNamesRestriction, boolean addIn
362387
return sb.toString();
363388
}
364389

365-
public Set<String> getVarNames(Set<String> varNamesRestriction, boolean addInheritedVarNames,
390+
public LinkedHashSet<String> getVarNames(Set<String> varNamesRestriction, boolean addInheritedVarNames,
366391
Set<String> varNamesInQueryFragment) {
367392
if (varNamesRestriction.isEmpty()) {
368-
return Set.of();
393+
return new LinkedHashSet<>();
369394
}
370395

371-
HashSet<String> ret = new HashSet<>();
396+
LinkedHashSet<String> ret = new LinkedHashSet<>();
372397
if (subject.name != null && varNamesRestriction.contains(subject.name)
373398
&& varNamesInQueryFragment.contains(subject.name)) {
374399
ret.add(subject.name);
@@ -448,6 +473,26 @@ public boolean hasObject(Variable<Value> variable) {
448473
return variable.name.equals(object.name);
449474
}
450475

476+
public Set<String> getInheritedVarNames() {
477+
return Set.copyOf(inheritedVarNames);
478+
}
479+
480+
public Set<String> getVarNames() {
481+
Set<String> varNames = new HashSet<>();
482+
483+
if (subject.name != null) {
484+
varNames.add(subject.name);
485+
}
486+
if (predicate.name != null) {
487+
varNames.add(predicate.name);
488+
}
489+
if (object.name != null) {
490+
varNames.add(object.name);
491+
}
492+
493+
return Collections.unmodifiableSet(varNames);
494+
}
495+
451496
public static class StableRandomVariableProvider {
452497

453498
// We just need a random base that isn't used elsewhere in the ShaclSail, but we don't want it to be stable so
@@ -471,9 +516,12 @@ public StableRandomVariableProvider(String prefix) {
471516
* increments of one.
472517
*
473518
* @param inputQuery the query string that should be normalized
519+
* @param union
474520
* @return a normalized query string
475521
*/
476-
public static String normalize(String inputQuery) {
522+
public static String normalize(String inputQuery, List<? extends Variable> protectedVars,
523+
List<StatementMatcher> union) {
524+
477525
if (!inputQuery.contains(BASE)) {
478526
return inputQuery;
479527
}
@@ -499,18 +547,30 @@ public static String normalize(String inputQuery) {
499547
if (lowest == 0 && incrementsOfOne) {
500548
return inputQuery;
501549
}
550+
String joinedProtectedVars = protectedVars.stream()
551+
.map(Variable::getName)
552+
.filter(Objects::nonNull)
553+
.filter(s -> s.contains(BASE))
554+
.collect(Collectors.joining());
502555

503-
return normalizeRange(inputQuery, lowest, highest);
556+
return normalizeRange(inputQuery, lowest, highest, joinedProtectedVars, union);
504557
}
505558

506-
private static String normalizeRange(String inputQuery, int lowest, int highest) {
559+
private static String normalizeRange(String inputQuery, int lowest, int highest, String joinedProtectedVars,
560+
List<StatementMatcher> union) {
507561

508562
String normalizedQuery = inputQuery;
509563
for (int i = 0; i <= highest; i++) {
510-
if (!normalizedQuery.contains(BASE + i + "_")) {
564+
String replacement = BASE + i + "_";
565+
if (!normalizedQuery.contains(replacement)) {
511566
for (int j = Math.max(i + 1, lowest); j <= highest; j++) {
512-
if (normalizedQuery.contains(BASE + j + "_")) {
513-
normalizedQuery = normalizedQuery.replace(BASE + j + "_", BASE + i + "_");
567+
String original = BASE + j + "_";
568+
if (normalizedQuery.contains(original)) {
569+
if (joinedProtectedVars.contains(original)) {
570+
continue;
571+
}
572+
normalizedQuery = normalizedQuery.replace(original, replacement);
573+
replaceInStatementMatcher(union, original, replacement);
514574
break;
515575
}
516576
}
@@ -520,6 +580,13 @@ private static String normalizeRange(String inputQuery, int lowest, int highest)
520580
return normalizedQuery;
521581
}
522582

583+
private static void replaceInStatementMatcher(List<StatementMatcher> statementMatchers, String original,
584+
String replacement) {
585+
for (StatementMatcher statementMatcher : statementMatchers) {
586+
statementMatcher.replaceVariableName(original, replacement);
587+
}
588+
}
589+
523590
public Variable<Value> next() {
524591
counter++;
525592

@@ -538,6 +605,44 @@ public Variable<Value> current() {
538605
}
539606
}
540607

608+
private void replaceVariableName(String original, String replacement) {
609+
610+
if (subject.name != null && subject.name.contains(original)) {
611+
subject.name = subject.name.replace(original, replacement);
612+
}
613+
if (subject.baseName != null && subject.baseName.contains(original)) {
614+
subject.baseName = subject.baseName.replace(original, replacement);
615+
}
616+
if (predicate.name != null && predicate.name.contains(original)) {
617+
predicate.name = predicate.name.replace(original, replacement);
618+
}
619+
if (predicate.baseName != null && predicate.baseName.contains(original)) {
620+
predicate.baseName = predicate.baseName.replace(original, replacement);
621+
}
622+
if (object.name != null && object.name.contains(original)) {
623+
object.name = object.name.replace(original, replacement);
624+
}
625+
if (object.baseName != null && object.baseName.contains(original)) {
626+
object.baseName = object.baseName.replace(original, replacement);
627+
}
628+
629+
boolean contains = false;
630+
for (String inheritedVarName : inheritedVarNames) {
631+
if (inheritedVarName.contains(original)) {
632+
contains = true;
633+
break;
634+
}
635+
}
636+
if (contains) {
637+
HashSet<String> newInheritedVarNames = new HashSet<>();
638+
for (String inheritedVarName : inheritedVarNames) {
639+
newInheritedVarNames.add(inheritedVarName.replace(original, replacement));
640+
}
641+
inheritedVarNames = newInheritedVarNames;
642+
}
643+
644+
}
645+
541646
public static class Variable<T extends Value> {
542647
public static final Variable<Value> VALUE = new Variable<>("value");
543648
public static final Variable<Value> THIS = new Variable<>("this");
@@ -562,6 +667,12 @@ public Variable(Variable<?> baseVariable, String name) {
562667
this.baseName = baseVariable.name;
563668
}
564669

670+
public Variable(String name, T value, String baseName) {
671+
this.name = name;
672+
this.value = value;
673+
this.baseName = baseName;
674+
}
675+
565676
public Variable(T value) {
566677
this.value = value;
567678
}

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BindSelect.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public BindSelect(SailConnection connection, Resource[] dataGraph, SparqlFragmen
9191
throw new IllegalStateException();
9292
}
9393

94-
this.query = StatementMatcher.StableRandomVariableProvider.normalize(query.getFragment());
94+
this.query = StatementMatcher.StableRandomVariableProvider.normalize(query.getFragment(), vars, List.of());
9595
this.prefixes = query.getNamespacesForSparql();
9696
this.direction = direction;
9797
this.includePropertyShapeValues = includePropertyShapeValues;

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalInnerJoin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package org.eclipse.rdf4j.sail.shacl.ast.planNodes;
1313

1414
import java.util.ArrayDeque;
15+
import java.util.List;
1516
import java.util.Objects;
1617
import java.util.function.Function;
1718

@@ -64,7 +65,7 @@ public BulkedExternalInnerJoin(PlanNode leftNode, SailConnection connection, Res
6465

6566
this.leftNode = PlanNodeHelper.handleSorting(this, leftNode);
6667
this.query = query.getNamespacesForSparql() + StatementMatcher.StableRandomVariableProvider
67-
.normalize(query.getFragment());
68+
.normalize(query.getFragment(), List.of(), List.of());
6869
this.connection = connection;
6970
assert this.connection != null;
7071
this.skipBasedOnPreviousConnection = skipBasedOnPreviousConnection;

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalLeftOuterJoin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package org.eclipse.rdf4j.sail.shacl.ast.planNodes;
1313

1414
import java.util.ArrayDeque;
15+
import java.util.List;
1516
import java.util.Objects;
1617
import java.util.function.Function;
1718

@@ -49,7 +50,7 @@ public BulkedExternalLeftOuterJoin(PlanNode leftNode, SailConnection connection,
4950
leftNode = PlanNodeHelper.handleSorting(this, leftNode);
5051
this.leftNode = leftNode;
5152
this.query = query.getNamespacesForSparql()
52-
+ StatementMatcher.StableRandomVariableProvider.normalize(query.getFragment());
53+
+ StatementMatcher.StableRandomVariableProvider.normalize(query.getFragment(), List.of(), List.of());
5354
this.connection = connection;
5455
assert this.connection != null;
5556
this.mapper = mapper;

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/ExternalFilterByQuery.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package org.eclipse.rdf4j.sail.shacl.ast.planNodes;
1313

14+
import java.util.List;
1415
import java.util.Objects;
1516
import java.util.function.Function;
1617

@@ -53,7 +54,7 @@ public ExternalFilterByQuery(SailConnection connection, Resource[] dataGraph, Pl
5354

5455
this.queryString = queryFragment.getNamespacesForSparql()
5556
+ StatementMatcher.StableRandomVariableProvider.normalize("SELECT " + queryVariable.asSparqlVariable()
56-
+ " WHERE {\n" + queryFragment.getFragment() + "\n}");
57+
+ " WHERE {\n" + queryFragment.getFragment() + "\n}", List.of(queryVariable), List.of());
5758
try {
5859
this.query = SparqlQueryParserCache.get(queryString);
5960
} catch (MalformedQueryException e) {

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/PatternFilter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class PatternFilter extends FilterPlanNode {
2525

2626
public PatternFilter(PlanNode parent, String pattern, String flags) {
2727
super(parent);
28-
if (flags != null && flags.length() > 0) {
28+
if (flags != null && !flags.isEmpty()) {
2929

3030
int flag = 0b0;
3131

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/Select.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package org.eclipse.rdf4j.sail.shacl.ast.planNodes;
1313

14+
import java.util.List;
1415
import java.util.Objects;
1516
import java.util.function.Function;
1617

@@ -67,10 +68,11 @@ public Select(SailConnection connection, SparqlFragment queryFragment, String or
6768

6869
if (!sorted && fragment.trim().startsWith("select ")) {
6970
this.query = queryFragment.getNamespacesForSparql() + "\n"
70-
+ StatementMatcher.StableRandomVariableProvider.normalize(fragment);
71+
+ StatementMatcher.StableRandomVariableProvider.normalize(fragment, List.of(), List.of());
7172
} else {
7273
this.query = queryFragment.getNamespacesForSparql() + "\n" + StatementMatcher.StableRandomVariableProvider
73-
.normalize("select * where {\n" + fragment + "\n}" + (sorted ? " order by " + orderBy : ""));
74+
.normalize("select * where {\n" + fragment + "\n}" + (sorted ? " order by " + orderBy : ""),
75+
List.of(), List.of());
7476
}
7577

7678
dataset = PlanNodeHelper.asDefaultGraphDataset(dataGraph);
@@ -87,7 +89,7 @@ public Select(SailConnection connection, String query, Function<BindingSet, Vali
8789
this.connection = connection;
8890
assert this.connection != null;
8991
this.mapper = mapper;
90-
this.query = StatementMatcher.StableRandomVariableProvider.normalize(query);
92+
this.query = StatementMatcher.StableRandomVariableProvider.normalize(query, List.of(), List.of());
9193
this.dataset = PlanNodeHelper.asDefaultGraphDataset(dataGraph);
9294

9395
this.sorted = false;

0 commit comments

Comments
 (0)