Skip to content

Commit dd47bfc

Browse files
authored
GH-5024 Fix 'nuclear' delete bug in RDF4JTemplate.delete(IRI, List) (#5070)
2 parents 5ed5bad + 8ed93eb commit dd47bfc

3 files changed

Lines changed: 89 additions & 2 deletions

File tree

spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/support/RDF4JTemplate.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,9 @@ public void delete(IRI start, List<PropertyPath> propertyPaths) {
315315
Variable p2 = SparqlBuilder.var("p2_" + i);
316316
Variable s2 = SparqlBuilder.var("s2_" + i);
317317
q.delete(target.has(p1, o1), s2.has(p2, target))
318-
.where(toIri(start).has(p, target).optional(), target.has(p1, o1).optional(),
318+
.where(
319+
toIri(start).has(p, target),
320+
target.has(p1, o1).optional(),
319321
s2.has(p2, target).optional());
320322
}
321323
update(q.getQueryString()).execute();

spring-components/rdf4j-spring/src/test/java/org/eclipse/rdf4j/spring/domain/model/EX.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class EX {
3434
public static final IRI sunflowers = SimpleValueFactory.getInstance().createIRI(base, "sunflowers");
3535
public static final IRI potatoEaters = SimpleValueFactory.getInstance().createIRI(base, "potatoEaters");
3636
public static final IRI guernica = SimpleValueFactory.getInstance().createIRI(base, "guernica");
37+
public static final IRI homeAddress = SimpleValueFactory.getInstance().createIRI(base, "homeAddress");
3738

3839
public static IRI of(String localName) {
3940
return SimpleValueFactory.getInstance().createIRI(base, localName);

spring-components/rdf4j-spring/src/test/java/org/eclipse/rdf4j/spring/support/RDF4JTemplateTests.java

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
* http://www.eclipse.org/org/documents/edl-v10.php.
88
*
99
* SPDX-License-Identifier: BSD-3-Clause
10-
*******************************************************************************/
10+
******************************************************************************/
1111

1212
package org.eclipse.rdf4j.spring.support;
1313

14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNotNull;
16+
import static org.junit.jupiter.api.Assertions.assertThrows;
17+
1418
import java.util.List;
1519
import java.util.Set;
1620

@@ -20,13 +24,20 @@
2024
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
2125
import org.eclipse.rdf4j.model.vocabulary.FOAF;
2226
import org.eclipse.rdf4j.model.vocabulary.RDF;
27+
import org.eclipse.rdf4j.query.BindingSet;
2328
import org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPathBuilder;
2429
import org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf;
2530
import org.eclipse.rdf4j.spring.RDF4JSpringTestBase;
31+
import org.eclipse.rdf4j.spring.dao.exception.IncorrectResultSetSizeException;
2632
import org.eclipse.rdf4j.spring.dao.support.opbuilder.UpdateExecutionBuilder;
2733
import org.eclipse.rdf4j.spring.dao.support.sparql.NamedSparqlSupplier;
34+
import org.eclipse.rdf4j.spring.domain.dao.ArtistDao;
35+
import org.eclipse.rdf4j.spring.domain.dao.PaintingDao;
36+
import org.eclipse.rdf4j.spring.domain.model.Artist;
2837
import org.eclipse.rdf4j.spring.domain.model.EX;
38+
import org.eclipse.rdf4j.spring.domain.model.Painting;
2939
import org.eclipse.rdf4j.spring.util.QueryResultUtils;
40+
import org.eclipse.rdf4j.spring.util.TypeMappingUtils;
3041
import org.junit.jupiter.api.Assertions;
3142
import org.junit.jupiter.api.Test;
3243
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,6 +51,13 @@ public class RDF4JTemplateTests extends RDF4JSpringTestBase {
4051
@Autowired
4152
private RDF4JTemplate rdf4JTemplate;
4253

54+
// used for checks
55+
@Autowired
56+
private ArtistDao artistDao;
57+
58+
@Autowired
59+
PaintingDao paintingDao;
60+
4361
@Test
4462
public void testUpdate1() {
4563
UpdateExecutionBuilder updateBuilder = rdf4JTemplate.update(
@@ -427,6 +445,62 @@ public void testAssociate_deleteOutgoing() {
427445

428446
}
429447

448+
@Test
449+
public void testDeleteWithPropertyPaths() {
450+
int triplesBeforeDelete = countTriples();
451+
Artist picasso = artistDao.getById(EX.Picasso);
452+
assertNotNull(picasso);
453+
Painting guernica = paintingDao.getById(EX.guernica);
454+
assertNotNull(guernica);
455+
rdf4JTemplate.delete(EX.Picasso, List.of(PropertyPathBuilder.of(EX.creatorOf).build()));
456+
assertThrows(IncorrectResultSetSizeException.class, () -> artistDao.getById(EX.Picasso));
457+
assertThrows(IncorrectResultSetSizeException.class, () -> paintingDao.getById(EX.guernica));
458+
assertEquals(triplesBeforeDelete - 8, countTriples());
459+
}
460+
461+
@Test
462+
public void testDeleteWithDisjunctivePropertyPaths() {
463+
int triplesBeforeDelete = countTriples();
464+
Artist picasso = artistDao.getById(EX.Picasso);
465+
assertNotNull(picasso);
466+
Painting guernica = paintingDao.getById(EX.guernica);
467+
assertNotNull(guernica);
468+
rdf4JTemplate.delete(EX.Picasso, List.of(PropertyPathBuilder.of(EX.creatorOf).or(EX.homeAddress).build()));
469+
assertThrows(IncorrectResultSetSizeException.class, () -> artistDao.getById(EX.Picasso));
470+
assertThrows(IncorrectResultSetSizeException.class, () -> paintingDao.getById(EX.guernica));
471+
assertEquals(triplesBeforeDelete - 11, countTriples());
472+
}
473+
474+
@Test
475+
public void testDeleteWithMultiplePropertyPaths() {
476+
int triplesBeforeDelete = countTriples();
477+
Artist picasso = artistDao.getById(EX.Picasso);
478+
assertNotNull(picasso);
479+
Painting guernica = paintingDao.getById(EX.guernica);
480+
assertNotNull(guernica);
481+
rdf4JTemplate.delete(EX.Picasso,
482+
List.of(PropertyPathBuilder.of(EX.creatorOf).build(), PropertyPathBuilder.of(EX.homeAddress).build()));
483+
assertThrows(IncorrectResultSetSizeException.class, () -> artistDao.getById(EX.Picasso));
484+
assertThrows(IncorrectResultSetSizeException.class, () -> paintingDao.getById(EX.guernica));
485+
assertEquals(triplesBeforeDelete - 11, countTriples());
486+
}
487+
488+
@Test
489+
public void testDeleteWithLongerPropertyPaths() {
490+
int triplesBeforeDelete = countTriples();
491+
Artist picasso = artistDao.getById(EX.Picasso);
492+
assertNotNull(picasso);
493+
Painting guernica = paintingDao.getById(EX.guernica);
494+
assertNotNull(guernica);
495+
// deletes guernica and the home address, but not picasso
496+
rdf4JTemplate.delete(EX.guernica,
497+
List.of(PropertyPathBuilder.of(EX.creatorOf).inv().then(EX.homeAddress).build()));
498+
picasso = artistDao.getById(EX.Picasso);
499+
assertNotNull(picasso);
500+
assertThrows(IncorrectResultSetSizeException.class, () -> paintingDao.getById(EX.guernica));
501+
assertEquals(triplesBeforeDelete - 8, countTriples());
502+
}
503+
430504
@Test
431505
public void testAssociate() {
432506
IRI me = EX.of("me");
@@ -464,4 +538,14 @@ public void testAssociate() {
464538
.size());
465539

466540
}
541+
542+
private int countTriples() {
543+
return this.rdf4JTemplate
544+
.tupleQuery("SELECT (count(*) AS ?count) WHERE { ?a ?b ?c }")
545+
.evaluateAndConvert()
546+
.toSingletonOfWholeResult(result -> {
547+
BindingSet bs = result.next();
548+
return TypeMappingUtils.toInt(QueryResultUtils.getValue(bs, "count"));
549+
});
550+
}
467551
}

0 commit comments

Comments
 (0)