Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,79 +43,9 @@
import org.eclipse.rdf4j.query.Dataset;
import org.eclipse.rdf4j.query.MutableBindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.And;
import org.eclipse.rdf4j.query.algebra.ArbitraryLengthPath;
import org.eclipse.rdf4j.query.algebra.BNodeGenerator;
import org.eclipse.rdf4j.query.algebra.BinaryTupleOperator;
import org.eclipse.rdf4j.query.algebra.BinaryValueOperator;
import org.eclipse.rdf4j.query.algebra.BindingSetAssignment;
import org.eclipse.rdf4j.query.algebra.Bound;
import org.eclipse.rdf4j.query.algebra.Coalesce;
import org.eclipse.rdf4j.query.algebra.Compare;
import org.eclipse.rdf4j.query.algebra.CompareAll;
import org.eclipse.rdf4j.query.algebra.CompareAny;
import org.eclipse.rdf4j.query.algebra.Datatype;
import org.eclipse.rdf4j.query.algebra.DescribeOperator;
import org.eclipse.rdf4j.query.algebra.Difference;
import org.eclipse.rdf4j.query.algebra.Distinct;
import org.eclipse.rdf4j.query.algebra.EmptySet;
import org.eclipse.rdf4j.query.algebra.Exists;
import org.eclipse.rdf4j.query.algebra.Extension;
import org.eclipse.rdf4j.query.algebra.Filter;
import org.eclipse.rdf4j.query.algebra.FunctionCall;
import org.eclipse.rdf4j.query.algebra.Group;
import org.eclipse.rdf4j.query.algebra.HasLang;
import org.eclipse.rdf4j.query.algebra.HasLangDir;
import org.eclipse.rdf4j.query.algebra.IRIFunction;
import org.eclipse.rdf4j.query.algebra.If;
import org.eclipse.rdf4j.query.algebra.In;
import org.eclipse.rdf4j.query.algebra.Intersection;
import org.eclipse.rdf4j.query.algebra.IsBNode;
import org.eclipse.rdf4j.query.algebra.IsLiteral;
import org.eclipse.rdf4j.query.algebra.IsNumeric;
import org.eclipse.rdf4j.query.algebra.IsResource;
import org.eclipse.rdf4j.query.algebra.IsTriple;
import org.eclipse.rdf4j.query.algebra.IsURI;
import org.eclipse.rdf4j.query.algebra.Join;
import org.eclipse.rdf4j.query.algebra.Label;
import org.eclipse.rdf4j.query.algebra.Lang;
import org.eclipse.rdf4j.query.algebra.LangDir;
import org.eclipse.rdf4j.query.algebra.LangMatches;
import org.eclipse.rdf4j.query.algebra.LeftJoin;
import org.eclipse.rdf4j.query.algebra.ListMemberOperator;
import org.eclipse.rdf4j.query.algebra.LocalName;
import org.eclipse.rdf4j.query.algebra.MathExpr;
import org.eclipse.rdf4j.query.algebra.*;
import org.eclipse.rdf4j.query.algebra.MathExpr.MathOp;
import org.eclipse.rdf4j.query.algebra.MultiProjection;
import org.eclipse.rdf4j.query.algebra.Namespace;
import org.eclipse.rdf4j.query.algebra.Not;
import org.eclipse.rdf4j.query.algebra.Or;
import org.eclipse.rdf4j.query.algebra.Order;
import org.eclipse.rdf4j.query.algebra.Projection;
import org.eclipse.rdf4j.query.algebra.QueryModelNode;
import org.eclipse.rdf4j.query.algebra.QueryRoot;
import org.eclipse.rdf4j.query.algebra.Reduced;
import org.eclipse.rdf4j.query.algebra.Regex;
import org.eclipse.rdf4j.query.algebra.SameTerm;
import org.eclipse.rdf4j.query.algebra.Service;
import org.eclipse.rdf4j.query.algebra.SingletonSet;
import org.eclipse.rdf4j.query.algebra.Slice;
import org.eclipse.rdf4j.query.algebra.StatementPattern;
import org.eclipse.rdf4j.query.algebra.StatementPattern.Scope;
import org.eclipse.rdf4j.query.algebra.Str;
import org.eclipse.rdf4j.query.algebra.StrLangDir;
import org.eclipse.rdf4j.query.algebra.TripleComponent;
import org.eclipse.rdf4j.query.algebra.TripleRef;
import org.eclipse.rdf4j.query.algebra.TupleExpr;
import org.eclipse.rdf4j.query.algebra.TupleFunctionCall;
import org.eclipse.rdf4j.query.algebra.UnaryTupleOperator;
import org.eclipse.rdf4j.query.algebra.UnaryValueOperator;
import org.eclipse.rdf4j.query.algebra.Union;
import org.eclipse.rdf4j.query.algebra.ValueConstant;
import org.eclipse.rdf4j.query.algebra.ValueExpr;
import org.eclipse.rdf4j.query.algebra.ValueExprTripleRef;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.ZeroLengthPath;
import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy;
import org.eclipse.rdf4j.query.algebra.evaluation.NativeTripleTermSource;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet;
Expand All @@ -134,21 +64,7 @@
import org.eclipse.rdf4j.query.algebra.evaluation.function.TupleFunction;
import org.eclipse.rdf4j.query.algebra.evaluation.function.TupleFunctionRegistry;
import org.eclipse.rdf4j.query.algebra.evaluation.function.datetime.Now;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.BindingSetAssignmentQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.EncodedTripleTermQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.IntersectionQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.JoinQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.LeftJoinQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.MinusQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.NativeTripleTermQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.OrderQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.ProjectionQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.RegexValueEvaluationStepSupplier;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.ServiceQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.SliceQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.StatementPatternQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.UnionQueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.ZeroLengthPathEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.*;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.values.*;
import org.eclipse.rdf4j.query.algebra.evaluation.iterator.DescribeIteration;
import org.eclipse.rdf4j.query.algebra.evaluation.iterator.ExtensionIterator;
Expand Down Expand Up @@ -382,6 +298,8 @@ public CloseableIteration<BindingSet> evaluate(TupleExpr expr, BindingSet bindin
result = precompile(expr).evaluate(bindings);
} else if (expr instanceof Difference) {
result = precompile(expr).evaluate(bindings);
} else if (expr instanceof Lateral) {
result = precompile(expr).evaluate(bindings);
} else {
throw new QueryEvaluationException(
"Unsupported binary tuple operator type: " + ((BinaryTupleOperator) expr).getClass());
Expand Down Expand Up @@ -879,6 +797,8 @@ protected QueryEvaluationStep prepare(BinaryTupleOperator expr, QueryEvaluationC
return prepare((Intersection) expr, context);
} else if (expr instanceof Difference) {
return prepare((Difference) expr, context);
} else if (expr instanceof Lateral) {
return LateralQueryEvaluationStep.supply(this, (Lateral) expr, context);
} else if (expr == null) {
throw new IllegalArgumentException("expr must not be null");
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2026 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 org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.algebra.Lateral;
import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext;
import org.eclipse.rdf4j.query.algebra.evaluation.iterator.LateralIterator;

public final class LateralQueryEvaluationStep implements QueryEvaluationStep {
private final QueryEvaluationStep left;
private final QueryEvaluationStep right;
private final Lateral lateral;

public static QueryEvaluationStep supply(EvaluationStrategy strategy, Lateral lateral,
QueryEvaluationContext context) {
QueryEvaluationStep left = strategy.precompile(lateral.getLeftArg(), context);
QueryEvaluationStep right = strategy.precompile(lateral.getRightArg(), context);
return new LateralQueryEvaluationStep(left, right, lateral);
}

public LateralQueryEvaluationStep(QueryEvaluationStep left, QueryEvaluationStep right, Lateral lateral) {
this.left = left;
this.right = right;
this.lateral = lateral;
}

@Override
public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
// Evaluate left side
CloseableIteration<BindingSet> leftResults = left.evaluate(bindings);

// For each left result, evaluate right side with injected bindings and union all results
return LateralIterator.getInstance(leftResults, right);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
*
* 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.iterator;

import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.LookAheadIteration;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;

/**
* Lateral join iterator.
* <p>
* This iterator produces results by evaluating the right-hand side for each solution mapping from the left-hand side.
* Note that this is similar to a join but without any join condition.
*
*/
public class LateralIterator extends LookAheadIteration<BindingSet> {

private final CloseableIteration<BindingSet> leftIter;

private CloseableIteration<BindingSet> rightIter;

private final QueryEvaluationStep preparedRight;

public LateralIterator(QueryEvaluationStep leftPrepared,
QueryEvaluationStep preparedRight, BindingSet bindings) throws QueryEvaluationException {
leftIter = leftPrepared.evaluate(bindings);
this.preparedRight = preparedRight;
}

private LateralIterator(CloseableIteration<BindingSet> leftIter, QueryEvaluationStep preparedRight)
throws QueryEvaluationException {
this.leftIter = leftIter;
this.preparedRight = preparedRight;
}

public static CloseableIteration<BindingSet> getInstance(QueryEvaluationStep leftPrepared,
QueryEvaluationStep preparedRight, BindingSet bindings) {
CloseableIteration<BindingSet> leftIter = leftPrepared.evaluate(bindings);
if (leftIter == QueryEvaluationStep.EMPTY_ITERATION) {
return leftIter;
}

return new LateralIterator(leftIter, preparedRight);
}

public static CloseableIteration<BindingSet> getInstance(CloseableIteration<BindingSet> leftIter,
QueryEvaluationStep preparedRight) {
if (leftIter == QueryEvaluationStep.EMPTY_ITERATION) {
return leftIter;
}

return new LateralIterator(leftIter, preparedRight);
}

/*---------*
* Methods *
*---------*/

@Override
protected BindingSet getNextElement() throws QueryEvaluationException {
if (rightIter != null) {
if (rightIter.hasNext()) {
return rightIter.next();
} else {
rightIter.close();
}
}

while (leftIter.hasNext()) {
rightIter = preparedRight.evaluate(leftIter.next());
if (rightIter.hasNext()) {
return rightIter.next();
} else {
rightIter.close();
}
}

return null;
}

@Override
protected void handleClose() throws QueryEvaluationException {
try {
leftIter.close();
} finally {
if (rightIter != null) {
rightIter.close();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (c) 2026 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;

import java.util.LinkedHashSet;
import java.util.Set;

/**
* This operator evaluates the right-hand side for each row from the left-hand side, with variables from the left-hand
* side in scope for the right-hand side evaluation.
*/
public class Lateral extends BinaryTupleOperator {

/*--------------*
* Constructors *
*--------------*/

public Lateral() {
}

public Lateral(TupleExpr leftArg, TupleExpr rightArg) {
super(leftArg, rightArg);
}

/*---------*
* Methods *
*---------*/

@Override
public Set<String> getBindingNames() {
Set<String> bindingNames = new LinkedHashSet<>(16);
bindingNames.addAll(getLeftArg().getBindingNames());
bindingNames.addAll(getRightArg().getBindingNames());
return bindingNames;
}

@Override
public Set<String> getAssuredBindingNames() {
Set<String> bindingNames = new LinkedHashSet<>(16);
bindingNames.addAll(getLeftArg().getAssuredBindingNames());
bindingNames.addAll(getRightArg().getAssuredBindingNames());
return bindingNames;
}

@Override
public <X extends Exception> void visit(QueryModelVisitor<X> visitor) throws X {
visitor.meet(this);
}

@Override
public <X extends Exception> void visitChildren(QueryModelVisitor<X> visitor) throws X {
super.visitChildren(visitor);
}

@Override
public boolean equals(Object other) {
if (other instanceof Lateral && super.equals(other)) {
return true;
}
return false;
}

@Override
public int hashCode() {
return super.hashCode() ^ "Lateral".hashCode();
}

@Override
public Lateral clone() {
return (Lateral) super.clone();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public interface QueryModelVisitor<X extends Exception> {

void meet(Label node) throws X;

void meet(Lateral node) throws X;

void meet(Lang node) throws X;

void meet(LangMatches node) throws X;
Expand Down
Loading
Loading