StatementPatternQueryEvaluationStepTest.java
/*******************************************************************************
* Copyright (c) 2025 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.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.Dataset;
import org.eclipse.rdf4j.query.MutableBindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.StatementPattern;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.StatementPatternQueryEvaluationStep;
import org.junit.jupiter.api.Test;
class StatementPatternQueryEvaluationStepTest {
@Test
void convertIterationSkipsBindingChecks() {
InstrumentedQueryEvaluationContext context = new InstrumentedQueryEvaluationContext();
SingleStatementTripleSource tripleSource = new SingleStatementTripleSource();
StatementPattern statementPattern = new StatementPattern(new Var("s"), new Var("p"), new Var("o"));
StatementPatternQueryEvaluationStep evaluationStep = new StatementPatternQueryEvaluationStep(
statementPattern,
context,
tripleSource);
try (CloseableIteration<BindingSet> iteration = evaluationStep.evaluate(context.createBindingSet())) {
assertThat(iteration.hasNext()).isTrue();
BindingSet converted = iteration.next();
assertThat(converted).isInstanceOf(InstrumentedBindingSet.class);
InstrumentedBindingSet bindingSet = (InstrumentedBindingSet) converted;
assertThat(bindingSet.wasIsEmptyInvoked()).isFalse();
assertThat(bindingSet.getBindingNames())
.containsExactlyInAnyOrder("s", "p", "o");
assertThat(bindingSet.getValue("s")).isEqualTo(tripleSource.statement.getSubject());
assertThat(bindingSet.getValue("p")).isEqualTo(tripleSource.statement.getPredicate());
assertThat(bindingSet.getValue("o")).isEqualTo(tripleSource.statement.getObject());
assertThat(iteration.hasNext()).isFalse();
}
assertThat(context.bindingChecks.get()).isZero();
}
private static final class InstrumentedQueryEvaluationContext implements QueryEvaluationContext {
private final AtomicInteger bindingChecks = new AtomicInteger();
@Override
public Predicate<BindingSet> hasBinding(String variableName) {
return bindings -> {
bindingChecks.incrementAndGet();
return bindings.hasBinding(variableName);
};
}
@Override
public MutableBindingSet createBindingSet() {
return new InstrumentedBindingSet();
}
@Override
public Dataset getDataset() {
return null;
}
@Override
public org.eclipse.rdf4j.model.Literal getNow() {
return null;
}
}
private static final class InstrumentedBindingSet
extends org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet {
private boolean isEmptyInvoked = false;
private InstrumentedBindingSet() {
}
@Override
public boolean isEmpty() {
isEmptyInvoked = true;
return super.isEmpty();
}
private boolean wasIsEmptyInvoked() {
return isEmptyInvoked;
}
}
private static final class SingleStatementTripleSource implements TripleSource {
private final ValueFactory valueFactory = SimpleValueFactory.getInstance();
private final Statement statement = valueFactory.createStatement(
valueFactory.createIRI("urn:subj"),
valueFactory.createIRI("urn:pred"),
valueFactory.createLiteral("obj"));
@Override
public CloseableIteration<? extends Statement> getStatements(Resource subj, IRI pred, Value obj,
Resource... contexts) throws QueryEvaluationException {
return new CloseableIteratorIteration<>(List.of(statement).iterator());
}
@Override
public ValueFactory getValueFactory() {
return valueFactory;
}
}
}