Skip to content

Commit

Permalink
GH-1574 validate all target nodes for every transaction
Browse files Browse the repository at this point in the history
Signed-off-by: Håvard Ottestad <hmottestad@gmail.com>
  • Loading branch information
hmottestad committed Mar 18, 2020
1 parent d99470f commit e473e34
Show file tree
Hide file tree
Showing 50 changed files with 308 additions and 322 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.vocabulary.SHACL;
import org.eclipse.rdf4j.query.algebra.evaluation.util.ValueComparator;
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -15,6 +16,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;

public class ShaclProperties {
Expand Down Expand Up @@ -48,7 +50,7 @@ public class ShaclProperties {
String flags = "";

Set<Resource> targetClass = new HashSet<>(0);
Set<Value> targetNode = new HashSet<>(0);
TreeSet<Value> targetNode = new TreeSet<>(new ValueComparator());
Set<IRI> targetSubjectsOf = new HashSet<>(0);
Set<IRI> targetObjectsOf = new HashSet<>(0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
import org.eclipse.rdf4j.sail.shacl.planNodes.SetFilterNode;
import org.eclipse.rdf4j.sail.shacl.planNodes.TrimTuple;
import org.eclipse.rdf4j.sail.shacl.planNodes.Unique;
import org.eclipse.rdf4j.sail.shacl.planNodes.ValuesBackedNode;

import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

/**
* sh:targetNode
Expand All @@ -34,9 +35,9 @@
*/
public class TargetNode extends NodeShape {

private final Set<Value> targetNodeSet;
private final TreeSet<Value> targetNodeSet;

TargetNode(Resource id, SailRepositoryConnection connection, boolean deactivated, Set<Value> targetNode) {
TargetNode(Resource id, SailRepositoryConnection connection, boolean deactivated, TreeSet<Value> targetNode) {
super(id, connection, deactivated);
this.targetNodeSet = targetNode;
assert !this.targetNodeSet.isEmpty();
Expand All @@ -56,9 +57,8 @@ public PlanNode getPlan(ConnectionsGroup connectionsGroup, boolean printPlans,
@Override
public PlanNode getPlanAddedStatements(ConnectionsGroup connectionsGroup,
PlaneNodeWrapper planeNodeWrapper) {
PlanNode parent = connectionsGroup.getCachedNodeFor(
new Select(connectionsGroup.getAddedStatements(), getQuery("?a", "?c", null), "?a", "?c"));
return new Unique(new TrimTuple(parent, 0, 1));

return new ValuesBackedNode(targetNodeSet);

}

Expand All @@ -79,29 +79,33 @@ public boolean requiresEvaluation(SailConnection addedStatements, SailConnection
public String getQuery(String subjectVariable, String objectVariable,
RdfsSubClassOfReasoner rdfsSubClassOfReasoner) {

return targetNodeSet.stream()
.map(node -> {
if (node instanceof Resource) {
return "<" + node + ">";
StringBuilder sb = new StringBuilder();
sb.append("VALUES ( ").append(subjectVariable).append(" ) {\n");

targetNodeSet.stream()
.map(value -> {
if (value instanceof Resource) {
return "<" + value + ">";
}
if (node instanceof Literal) {
IRI datatype = ((Literal) node).getDatatype();
if (value instanceof Literal) {
IRI datatype = ((Literal) value).getDatatype();
if (datatype == null) {
return "\"" + node.stringValue() + "\"";
return "\"" + value.stringValue() + "\"";
}
return "\"" + node.stringValue() + "\"^^<" + datatype.stringValue() + ">";
if (((Literal) value).getLanguage().isPresent()) {
return "\"" + value.stringValue() + "\"@" + ((Literal) value).getLanguage().get();
}
return "\"" + value.stringValue() + "\"^^<" + datatype.stringValue() + ">";
}

throw new IllegalStateException(node.getClass().getSimpleName());
throw new IllegalStateException(value.getClass().getSimpleName());

})
.map(r -> "{{ select * where {BIND(" + r + " as " + subjectVariable + "). " + subjectVariable + " ?b1 "
+ objectVariable + " .}}}"
+ "\n UNION \n"
+ "{{ select * where {BIND(" + r + " as " + subjectVariable + "). " + objectVariable + " ?b1 "
+ subjectVariable + " .}}}")
.reduce((a, b) -> a + " UNION " + b)
.get();
.forEach(value -> sb.append("( ").append(value).append(" )\n"));

sb.append("}\n");

return sb.toString();

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.sail.SailException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

Expand Down Expand Up @@ -74,7 +75,8 @@ private void calculateNext() {

}

List<Value> line = Arrays.asList(subject, SimpleValueFactory.getInstance().createLiteral(count));
List<Value> line = new ArrayList<>(
Arrays.asList(subject, SimpleValueFactory.getInstance().createLiteral(count)));

next.line = line;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public Tuple(List<Value> list) {
}

public Tuple(Value... list) {
line = Arrays.asList(list);
line = new ArrayList<>(Arrays.asList(list));
}

public Tuple(List<Value> list, Tuple historyTuple) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*******************************************************************************
* Copyright (c) 2018 Eclipse RDF4J contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*******************************************************************************/

package org.eclipse.rdf4j.sail.shacl.planNodes;

import org.apache.commons.text.StringEscapeUtils;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.sail.SailException;
import org.eclipse.rdf4j.sail.memory.MemoryStoreConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Iterator;

/**
* @author Håvard Ottestad
*/
public class ValuesBackedNode implements PlanNode {

private static final Logger logger = LoggerFactory.getLogger(ValuesBackedNode.class);
private final Collection<Value> collection;
private ValidationExecutionLogger validationExecutionLogger;
boolean printed = false;

public ValuesBackedNode(Collection<Value> collection) {
this.collection = collection;
}

@Override
public CloseableIteration<Tuple, SailException> iterator() {
return new LoggingCloseableIteration(this, validationExecutionLogger) {

Iterator<Value> iterator = collection.iterator();

@Override
public void close() throws SailException {
}

@Override
public boolean localHasNext() throws SailException {
return iterator.hasNext();
}

@Override
public Tuple loggingNext() throws SailException {
return new Tuple(iterator.next());
}

@Override
public void remove() throws SailException {

}
};
}

@Override
public int depth() {
return 0;
}

@Override
public void getPlanAsGraphvizDot(StringBuilder stringBuilder) {
if (printed) {
return;
}
printed = true;
stringBuilder.append(getId() + " [label=\"" + StringEscapeUtils.escapeJava(this.toString()) + "\"];")
.append("\n");

}

@Override
public String getId() {
return System.identityHashCode(this) + "";
}

@Override
public IteratorData getIteratorDataType() {
return IteratorData.tripleBased;
}

@Override
public String toString() {
return "ValuesBackedNode{" +
"collection=" + collection +
", validationExecutionLogger=" + validationExecutionLogger +
", printed=" + printed +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ValuesBackedNode that = (ValuesBackedNode) o;
return collection.equals(that.collection);
}

@Override
public int hashCode() {

return collection.hashCode();

}

@Override
public void receiveLogger(ValidationExecutionLogger validationExecutionLogger) {
this.validationExecutionLogger = validationExecutionLogger;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.eclipse.rdf4j.IsolationLevel;
import org.eclipse.rdf4j.IsolationLevels;
import org.eclipse.rdf4j.common.io.IOUtil;
import org.eclipse.rdf4j.common.iteration.Iterations;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.ValueFactory;
Expand Down Expand Up @@ -91,7 +90,6 @@ abstract public class AbstractShaclTest {
"test-cases/datatype/targetObjectsOf",
"test-cases/datatype/targetSubjectsOf",
"test-cases/datatype/targetSubjectsOfSingle",
"test-cases/datatype/validateTarget",
"test-cases/deactivated/nodeshape",
"test-cases/deactivated/or",
"test-cases/deactivated/propertyshape",
Expand Down Expand Up @@ -125,6 +123,7 @@ abstract public class AbstractShaclTest {
"test-cases/or/classValidateTarget",
"test-cases/or/datatype",
"test-cases/or/datatype2",
"test-cases/or/datatype2",
"test-cases/or/datatypeDifferentPaths",
"test-cases/or/datatypeNodeShape",
"test-cases/or/datatypeTargetNode",
Expand Down Expand Up @@ -245,6 +244,7 @@ static void runTestCase(String shaclPath, String dataPath, ExpectedResult expect

try {
Utils.loadShapeData(shaclRepository, shaclFile);
Utils.loadInitialData(shaclRepository, dataPath + "initialData.ttl");
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand All @@ -258,7 +258,17 @@ static void runTestCase(String shaclPath, String dataPath, ExpectedResult expect
ValueFactory vf = connection.getValueFactory();
connection.add(vf.createBNode(), vf.createIRI("http://example.com/jkhsdfiu3r2y9fjr3u0"),
vf.createLiteral("auto-generated!"), vf.createBNode());
connection.commit();
try {
connection.commit();
} catch (RepositoryException sailException) {
if (!(sailException.getCause() instanceof ShaclSailValidationException)) {
throw sailException;
}
exception = true;
logger.debug(sailException.getMessage());

printResults(sailException);
}
}

}
Expand Down Expand Up @@ -378,6 +388,7 @@ static void runTestCaseSingleTransaction(String shaclPath, String dataPath, Expe
SailRepository shaclRepository = getShaclSail();
try {
Utils.loadShapeData(shaclRepository, shaclPath + "shacl.ttl");
Utils.loadInitialData(shaclRepository, shaclPath + "initialData.ttl");
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -448,6 +459,7 @@ static void runTestCaseRevalidate(String shaclPath, String dataPath, ExpectedRes
SailRepository shaclRepository = getShaclSail();
try {
Utils.loadShapeData(shaclRepository, shaclPath + "shacl.ttl");
Utils.loadInitialData(shaclRepository, shaclPath + "initialData.ttl");
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
/**
* @author Håvard Ottestad
*/
public class TestPlanNodeOrdering {
public class PlanNodeOrderingTest {

@Test
public void testSelect() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

package org.eclipse.rdf4j.sail.shacl;

import org.eclipse.rdf4j.common.iteration.Iterations;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/*******************************************************************************
* Copyright (c) 2019 Eclipse RDF4J contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*******************************************************************************/
package org.eclipse.rdf4j.sail.shacl;

import ch.qos.logback.classic.spi.ILoggingEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import org.eclipse.rdf4j.sail.memory.MemoryStore;

import java.io.IOException;
import java.net.URL;
import java.io.InputStream;
import java.net.URL;
import java.util.UUID;

/**
Expand Down Expand Up @@ -130,6 +130,28 @@ public static SailRepository getSailRepository(URL resourceName) {
return sailRepository;
}

public static void loadInitialData(SailRepository repo, String resourceName) throws IOException {
((ShaclSail) repo.getSail()).disableValidation();

try {
try (InputStream initialData = Utils.class.getClassLoader().getResourceAsStream(resourceName)) {

if (initialData == null) {
return;
}

try (RepositoryConnection conn = repo.getConnection()) {
conn.begin(IsolationLevels.NONE);
conn.add(initialData, "", RDFFormat.TURTLE);
conn.commit();
}
}
} finally {
((ShaclSail) repo.getSail()).enableValidation();
}

}

static class Ex {

public final static String ns = "http://example.com/ns#";
Expand Down
Loading

0 comments on commit e473e34

Please sign in to comment.