Skip to content

Commit 14256d0

Browse files
authored
GH-5294 stack overflow sparql update (#5295)
2 parents 782ffd8 + 5aea936 commit 14256d0

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/helpers/collectors/StatementPatternCollector.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
import java.util.List;
1616

1717
import org.eclipse.rdf4j.query.algebra.Filter;
18+
import org.eclipse.rdf4j.query.algebra.Join;
1819
import org.eclipse.rdf4j.query.algebra.QueryModelNode;
1920
import org.eclipse.rdf4j.query.algebra.StatementPattern;
21+
import org.eclipse.rdf4j.query.algebra.TupleExpr;
2022
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
2123

2224
/**
@@ -46,6 +48,37 @@ public void meet(Filter node) {
4648
node.getArg().visit(this);
4749
}
4850

51+
@Override
52+
public void meet(Join node) throws RuntimeException {
53+
TupleExpr leftArg = node.getLeftArg();
54+
TupleExpr rightArg = node.getRightArg();
55+
56+
// INSERT clause is often a deeply nested join. Recursive approach may cause stack overflow. Attempt
57+
// non-recursive (or at least less-recursive) approach first.
58+
while (true) {
59+
if (leftArg instanceof Join && !(rightArg instanceof Join)) {
60+
rightArg.visit(this);
61+
62+
Join join = (Join) leftArg;
63+
leftArg = join.getLeftArg();
64+
rightArg = join.getRightArg();
65+
66+
} else if (rightArg instanceof Join && !(leftArg instanceof Join)) {
67+
leftArg.visit(this);
68+
69+
Join join = (Join) rightArg;
70+
leftArg = join.getLeftArg();
71+
rightArg = join.getRightArg();
72+
73+
} else {
74+
leftArg.visit(this);
75+
rightArg.visit(this);
76+
return;
77+
}
78+
}
79+
80+
}
81+
4982
@Override
5083
public void meet(StatementPattern node) {
5184
statementPatterns.add(node);

core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreConnectionTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,47 @@
1010
*******************************************************************************/
1111
package org.eclipse.rdf4j.sail.memory;
1212

13+
import static org.assertj.core.api.Assertions.assertThat;
14+
1315
import java.io.File;
1416

17+
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
18+
import org.eclipse.rdf4j.model.IRI;
19+
import org.eclipse.rdf4j.model.vocabulary.RDF;
1520
import org.eclipse.rdf4j.repository.Repository;
1621
import org.eclipse.rdf4j.repository.sail.SailRepository;
1722
import org.eclipse.rdf4j.testsuite.repository.RepositoryConnectionTest;
23+
import org.junit.jupiter.api.Test;
1824

1925
public class MemoryStoreConnectionTest extends RepositoryConnectionTest {
2026
@Override
2127
protected Repository createRepository(File dataDir) {
2228
return new SailRepository(new MemoryStore());
2329
}
30+
31+
@Test
32+
public void reallyBigUpdateToTriggerPotentialStackOverflowTest() {
33+
setupTest(IsolationLevels.NONE);
34+
35+
IRI g1 = vf.createIRI("urn:test:g1");
36+
testCon.begin();
37+
testCon.add(vf.createBNode(), RDF.TYPE, g1);
38+
testCon.commit();
39+
40+
testCon.begin();
41+
42+
StringBuilder bigUpdate = new StringBuilder();
43+
bigUpdate.append("DELETE { ?s ?p ?o } INSERT{\n");
44+
45+
for (int i = 0; i < 20000; i++) {
46+
bigUpdate.append(" [] <urn:test:prop> \"").append(i).append("\".\n");
47+
}
48+
bigUpdate.append("\n} WHERE { ?s ?p ?o }");
49+
50+
testCon.prepareUpdate(bigUpdate.toString()).execute();
51+
52+
long size = testCon.size();
53+
assertThat(size).isEqualTo(20000);
54+
testCon.commit();
55+
}
2456
}

0 commit comments

Comments
 (0)