Skip to content

Commit b2c2e30

Browse files
authored
GH-5024 Fix 'nuclear' delete bug in RDF4JTemplate.delete(IRI, List) (#5026)
2 parents 5ed5bad + fc2b9b4 commit b2c2e30

3 files changed

Lines changed: 88 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: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/*******************************************************************************
1+
/**
2+
* *****************************************************************************
23
* Copyright (c) 2021 Eclipse RDF4J contributors.
34
*
45
* All rights reserved. This program and the accompanying materials
@@ -11,6 +12,8 @@
1112

1213
package org.eclipse.rdf4j.spring.support;
1314

15+
import static org.junit.jupiter.api.Assertions.*;
16+
1417
import java.util.List;
1518
import java.util.Set;
1619

@@ -20,13 +23,20 @@
2023
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
2124
import org.eclipse.rdf4j.model.vocabulary.FOAF;
2225
import org.eclipse.rdf4j.model.vocabulary.RDF;
26+
import org.eclipse.rdf4j.query.BindingSet;
2327
import org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPathBuilder;
2428
import org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf;
2529
import org.eclipse.rdf4j.spring.RDF4JSpringTestBase;
30+
import org.eclipse.rdf4j.spring.dao.exception.IncorrectResultSetSizeException;
2631
import org.eclipse.rdf4j.spring.dao.support.opbuilder.UpdateExecutionBuilder;
2732
import org.eclipse.rdf4j.spring.dao.support.sparql.NamedSparqlSupplier;
33+
import org.eclipse.rdf4j.spring.domain.dao.ArtistDao;
34+
import org.eclipse.rdf4j.spring.domain.dao.PaintingDao;
35+
import org.eclipse.rdf4j.spring.domain.model.Artist;
2836
import org.eclipse.rdf4j.spring.domain.model.EX;
37+
import org.eclipse.rdf4j.spring.domain.model.Painting;
2938
import org.eclipse.rdf4j.spring.util.QueryResultUtils;
39+
import org.eclipse.rdf4j.spring.util.TypeMappingUtils;
3040
import org.junit.jupiter.api.Assertions;
3141
import org.junit.jupiter.api.Test;
3242
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,6 +50,13 @@ public class RDF4JTemplateTests extends RDF4JSpringTestBase {
4050
@Autowired
4151
private RDF4JTemplate rdf4JTemplate;
4252

53+
// used for checks
54+
@Autowired
55+
private ArtistDao artistDao;
56+
57+
@Autowired
58+
PaintingDao paintingDao;
59+
4360
@Test
4461
public void testUpdate1() {
4562
UpdateExecutionBuilder updateBuilder = rdf4JTemplate.update(
@@ -427,6 +444,62 @@ public void testAssociate_deleteOutgoing() {
427444

428445
}
429446

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

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

0 commit comments

Comments
 (0)