QueryEvaluationContext.java
/*******************************************************************************
* Copyright (c) 2021 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;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Comparator;
import java.util.Date;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.eclipse.rdf4j.common.annotation.Experimental;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.query.Binding;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.Dataset;
import org.eclipse.rdf4j.query.MutableBindingSet;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet;
/**
* A QueryEvaluationContext stores values and methods that are valid throughout the lifetime of a query execution.
* <p>
* A classic case is the case of NOW() evaluation to the same instant for all invocations of that function in one query
* evaluation.
*/
public interface QueryEvaluationContext {
@Experimental
default Comparator<Value> getComparator() {
return null;
}
class Minimal implements QueryEvaluationContext {
private static final VarHandle NOW;
static {
try {
NOW = MethodHandles
.privateLookupIn(QueryEvaluationContext.Minimal.class, MethodHandles.lookup())
.findVarHandle(QueryEvaluationContext.Minimal.class, "now", Literal.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new UnsupportedOperationException("The nowInLiteral field is missing");
}
}
// The now time as a literal, lazy set if not yet known. We use a VarHandle to access this field. By convention
// this field is volatile, as a precaution in case we call it directly.
private volatile Literal now;
private final Dataset dataset;
private final ValueFactory valueFactory;
private final Comparator<Value> comparator;
/**
* Set the shared now value to a preexisting object
*
* @param now that is shared.
* @param dataset that a query should use to evaluate
* @param comparator to use for ordering
*/
public Minimal(Literal now, Dataset dataset, Comparator<Value> comparator) {
super();
this.now = now;
this.dataset = dataset;
this.valueFactory = SimpleValueFactory.getInstance();
this.comparator = comparator;
}
/**
* Set the shared now value to a preexisting object
*
* @param now that is shared.
* @param dataset that a query should use to evaluate
*/
public Minimal(Literal now, Dataset dataset) {
this(now, dataset, null);
}
/**
* @param dataset that a query should use to evaluate
*/
public Minimal(Dataset dataset) {
this(null, dataset);
}
/**
* @param dataset that a query should use to evaluate
* @param comparator to use for ordering
*/
public Minimal(Dataset dataset, Comparator<Value> comparator) {
this(null, dataset, comparator);
}
/**
* @param dataset that a query should use to the evaluate
* @param valueFactory that a query should use to the evaluate
* @param comparator to use for ordering
*
*/
public Minimal(Dataset dataset, ValueFactory valueFactory, Comparator<Value> comparator) {
this.dataset = dataset;
this.valueFactory = valueFactory;
this.comparator = comparator;
}
/**
* @param dataset that a query should use to the evaluate
* @param valueFactory that a query should use to the evaluate
*/
public Minimal(Dataset dataset, ValueFactory valueFactory) {
this(dataset, valueFactory, null);
}
@Override
public Comparator<Value> getComparator() {
return comparator;
}
@Override
public Literal getNow() {
Literal now = (Literal) NOW.get(this);
// creating a new date is expensive because it uses the XMLGregorianCalendar implementation which is very
// complex.
if (now == null) {
now = valueFactory.createLiteral(new Date());
boolean success = NOW.compareAndSet(this, null, now);
if (!success) {
now = (Literal) NOW.getAcquire(this);
}
}
return now;
}
@Override
public Dataset getDataset() {
return dataset;
}
}
/**
* @return the shared now;
*/
Literal getNow();
/**
* @return The dataset that this query is operation on.
*/
Dataset getDataset();
default MutableBindingSet createBindingSet() {
return new QueryBindingSet();
}
default Predicate<BindingSet> hasBinding(String variableName) {
return bs -> bs.hasBinding(variableName);
}
default Function<BindingSet, Binding> getBinding(String variableName) {
return bs -> bs.getBinding(variableName);
}
default Function<BindingSet, Value> getValue(String variableName) {
Function<BindingSet, Binding> getBinding = getBinding(variableName);
return bs -> {
Binding binding = getBinding.apply(bs);
if (binding == null) {
return null;
} else {
return binding.getValue();
}
};
}
default BiConsumer<Value, MutableBindingSet> setBinding(String variableName) {
return (val, bs) -> bs.setBinding(variableName, val);
}
default BiConsumer<Value, MutableBindingSet> addBinding(String variableName) {
return (val, bs) -> bs.addBinding(variableName, val);
}
default MutableBindingSet createBindingSet(BindingSet bindings) {
return new QueryBindingSet(bindings);
}
}