1515import java .util .Set ;
1616import java .util .concurrent .Executor ;
1717import java .util .concurrent .atomic .AtomicBoolean ;
18- import java .util .function .Supplier ;
1918import java .util .stream .Collectors ;
2019
21- import org .eclipse .rdf4j .collection .factory .api .CollectionFactory ;
2220import org .eclipse .rdf4j .common .iteration .CloseableIteration ;
2321import org .eclipse .rdf4j .common .iteration .EmptyIteration ;
2422import org .eclipse .rdf4j .common .iteration .SingletonIteration ;
3937import org .eclipse .rdf4j .federated .algebra .FedXZeroLengthPath ;
4038import org .eclipse .rdf4j .federated .algebra .FederatedDescribeOperator ;
4139import org .eclipse .rdf4j .federated .algebra .FilterExpr ;
40+ import org .eclipse .rdf4j .federated .algebra .FilterTuple ;
4241import org .eclipse .rdf4j .federated .algebra .FilterValueExpr ;
4342import org .eclipse .rdf4j .federated .algebra .HolderNode ;
4443import org .eclipse .rdf4j .federated .algebra .NJoin ;
5352import org .eclipse .rdf4j .federated .endpoint .Endpoint ;
5453import org .eclipse .rdf4j .federated .evaluation .concurrent .ControlledWorkerScheduler ;
5554import org .eclipse .rdf4j .federated .evaluation .concurrent .ParallelServiceExecutor ;
55+ import org .eclipse .rdf4j .federated .evaluation .iterator .BindLeftJoinIteration ;
5656import org .eclipse .rdf4j .federated .evaluation .iterator .FedXPathIteration ;
5757import org .eclipse .rdf4j .federated .evaluation .iterator .FederatedDescribeIteration ;
58+ import org .eclipse .rdf4j .federated .evaluation .iterator .FilteringIteration ;
5859import org .eclipse .rdf4j .federated .evaluation .iterator .SingleBindingSetIteration ;
60+ import org .eclipse .rdf4j .federated .evaluation .join .ControlledWorkerBindJoin ;
5961import org .eclipse .rdf4j .federated .evaluation .join .ControlledWorkerBoundJoin ;
6062import org .eclipse .rdf4j .federated .evaluation .join .ControlledWorkerJoin ;
61- import org .eclipse .rdf4j .federated .evaluation .join .ControlledWorkerLeftJoin ;
6263import org .eclipse .rdf4j .federated .evaluation .join .SynchronousBoundJoin ;
6364import org .eclipse .rdf4j .federated .evaluation .join .SynchronousJoin ;
6465import org .eclipse .rdf4j .federated .evaluation .union .ControlledWorkerUnion ;
6869import org .eclipse .rdf4j .federated .evaluation .union .ParallelUnionOperatorTask ;
6970import org .eclipse .rdf4j .federated .evaluation .union .SynchronousWorkerUnion ;
7071import org .eclipse .rdf4j .federated .evaluation .union .WorkerUnionBase ;
72+ import org .eclipse .rdf4j .federated .exception .ExceptionUtil ;
7173import org .eclipse .rdf4j .federated .exception .FedXRuntimeException ;
7274import org .eclipse .rdf4j .federated .exception .IllegalQueryException ;
7375import org .eclipse .rdf4j .federated .optimizer .DefaultFedXCostModel ;
9799import org .eclipse .rdf4j .query .QueryEvaluationException ;
98100import org .eclipse .rdf4j .query .algebra .DescribeOperator ;
99101import org .eclipse .rdf4j .query .algebra .Join ;
102+ import org .eclipse .rdf4j .query .algebra .LeftJoin ;
100103import org .eclipse .rdf4j .query .algebra .QueryRoot ;
101104import org .eclipse .rdf4j .query .algebra .Service ;
102105import org .eclipse .rdf4j .query .algebra .StatementPattern ;
108111import org .eclipse .rdf4j .query .algebra .evaluation .ValueExprEvaluationException ;
109112import org .eclipse .rdf4j .query .algebra .evaluation .federation .FederatedService ;
110113import org .eclipse .rdf4j .query .algebra .evaluation .federation .ServiceJoinIterator ;
111- import org .eclipse .rdf4j .query .algebra .evaluation .impl .DefaultEvaluationStrategy ;
112114import org .eclipse .rdf4j .query .algebra .evaluation .impl .EvaluationStatistics ;
113115import org .eclipse .rdf4j .query .algebra .evaluation .impl .QueryEvaluationContext ;
114116import org .eclipse .rdf4j .query .algebra .evaluation .impl .StrictEvaluationStrategy ;
115117import org .eclipse .rdf4j .query .algebra .evaluation .iterator .BadlyDesignedLeftJoinIterator ;
116- import org .eclipse .rdf4j .query .algebra .evaluation .iterator .DescribeIteration ;
117118import org .eclipse .rdf4j .query .algebra .evaluation .iterator .HashJoinIteration ;
118119import org .eclipse .rdf4j .query .algebra .evaluation .optimizer .ConstantOptimizer ;
119120import org .eclipse .rdf4j .query .algebra .evaluation .optimizer .DisjunctiveConstraintOptimizer ;
@@ -748,10 +749,7 @@ public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
748749
749750 if (problemVars .containsAll (bindings .getBindingNames ())) {
750751 var leftIter = leftPrepared .evaluate (bindings );
751- ControlledWorkerLeftJoin join = new ControlledWorkerLeftJoin (scheduler , FederationEvalStrategy .this ,
752- leftIter , leftJoin , bindings , leftJoin .getQueryInfo ());
753- executor .execute (join );
754- return join ;
752+ return executeLeftJoin (scheduler , leftIter , leftJoin , bindings , leftJoin .getQueryInfo ());
755753 } else {
756754 Set <String > problemVarsClone = new HashSet <>(problemVars );
757755 problemVarsClone .retainAll (bindings .getBindingNames ());
@@ -815,8 +813,14 @@ public QueryEvaluationStep prepareNaryUnion(NUnion union, QueryEvaluationContext
815813 /**
816814 * Execute the join in a separate thread using some join executor.
817815 *
818- * Join executors are for instance: - {@link SynchronousJoin} - {@link SynchronousBoundJoin} -
819- * {@link ControlledWorkerJoin} - {@link ControlledWorkerBoundJoin}
816+ * Join executors are for instance:
817+ *
818+ * <ul>
819+ * <li>{@link SynchronousJoin}</li>
820+ * <li>{@link SynchronousBoundJoin}</li>
821+ * <li>{@link ControlledWorkerJoin}</li>
822+ * <li>{@link ControlledWorkerBindJoin}</li>
823+ * </ul>
820824 *
821825 * For endpoint federation use controlled worker bound join, for local federation use controlled worker join. The
822826 * other operators are there for completeness.
@@ -836,6 +840,21 @@ protected abstract CloseableIteration<BindingSet> executeJoin(
836840 CloseableIteration <BindingSet > leftIter , TupleExpr rightArg ,
837841 Set <String > joinVariables , BindingSet bindings , QueryInfo queryInfo ) throws QueryEvaluationException ;
838842
843+ /**
844+ * Execute the left join in a separate thread using some join executor.
845+ *
846+ * @param joinScheduler
847+ * @param leftIter
848+ * @param leftJoin
849+ * @param bindings
850+ * @return the result
851+ * @throws QueryEvaluationException
852+ */
853+ protected abstract CloseableIteration <BindingSet > executeLeftJoin (
854+ ControlledWorkerScheduler <BindingSet > joinScheduler ,
855+ CloseableIteration <BindingSet > leftIter , LeftJoin leftJoin ,
856+ BindingSet bindings , QueryInfo queryInfo ) throws QueryEvaluationException ;
857+
839858 public abstract CloseableIteration <BindingSet > evaluateExclusiveGroup (
840859 ExclusiveGroup group , BindingSet bindings )
841860 throws RepositoryException , MalformedQueryException , QueryEvaluationException ;
@@ -920,10 +939,63 @@ public abstract CloseableIteration<BindingSet> evaluateBoundJoinStatementPattern
920939 public abstract CloseableIteration <BindingSet > evaluateGroupedCheck (
921940 CheckStatementPattern stmt , final List <BindingSet > bindings ) throws QueryEvaluationException ;
922941
942+ /**
943+ * Evaluate the left bind join for the given {@link StatementTupleExpr} and bindings at the relevant endpoints.
944+ *
945+ * @param stmt
946+ * @param bindings
947+ * @return the result iteration
948+ * @throws QueryEvaluationException
949+ * @see {@link BindLeftJoinIteration}
950+ */
951+ public CloseableIteration <BindingSet > evaluateLeftBoundJoinStatementPattern (
952+ StatementTupleExpr stmt , final List <BindingSet > bindings ) throws QueryEvaluationException {
953+ // we can omit the bound join handling
954+ if (bindings .size () == 1 ) {
955+ return evaluate (stmt , bindings .get (0 ));
956+ }
957+
958+ FilterValueExpr filterExpr = null ;
959+ if (stmt instanceof FilterTuple ) {
960+ filterExpr = ((FilterTuple ) stmt ).getFilterExpr ();
961+ }
962+
963+ AtomicBoolean isEvaluated = new AtomicBoolean (false );
964+ String preparedQuery = QueryStringUtil .selectQueryStringBoundJoinVALUES ((StatementPattern ) stmt , bindings ,
965+ filterExpr , isEvaluated , stmt .getQueryInfo ().getDataset ());
966+
967+ CloseableIteration <BindingSet > result = null ;
968+ try {
969+ result = evaluateAtStatementSources (preparedQuery , stmt .getStatementSources (), stmt .getQueryInfo ());
970+
971+ // apply filter and/or convert to original bindings
972+ if (filterExpr != null && !isEvaluated .get ()) {
973+ result = new BindLeftJoinIteration (result , bindings ); // apply conversion
974+ result = new FilteringIteration (filterExpr , result , this ); // apply filter
975+ if (!result .hasNext ()) {
976+ result .close ();
977+ return new EmptyIteration <>();
978+ }
979+ } else {
980+ result = new BindLeftJoinIteration (result , bindings );
981+ }
982+
983+ return result ;
984+ } catch (Throwable t ) {
985+ if (result != null ) {
986+ result .close ();
987+ }
988+ if (t instanceof InterruptedException ) {
989+ Thread .currentThread ().interrupt ();
990+ }
991+ throw ExceptionUtil .toQueryEvaluationException (t );
992+ }
993+ }
994+
923995 /**
924996 * Evaluate a SERVICE using vectored evaluation, taking the provided bindings as input.
925997 *
926- * See {@link ControlledWorkerBoundJoin } and {@link FedXConfig#getEnableServiceAsBoundJoin()}
998+ * See {@link ControlledWorkerBindJoin } and {@link FedXConfig#getEnableServiceAsBoundJoin()}
927999 *
9281000 * @param service
9291001 * @param bindings
0 commit comments