package eu.fbk.knowledgestore.triplestore;
import java.io.IOException;
import javax.annotation.Nullable;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryEvaluationException;
import info.aduna.iteration.CloseableIteration;
import eu.fbk.knowledgestore.data.Handler;
import eu.fbk.knowledgestore.runtime.DataCorruptedException;
/**
* A triple store transaction.
* <p>
* A {@code TripleTransaction} is a unit of work over the contents of a {@link TripleStore} that
* provides atomicity (i.e., changes are either completely stored or discarded), isolation (i.e.,
* other transaction do not see the modifications of this transaction) and durability (i.e.,
* changes are persisted across different program runs) guarantees.
* </p>
* <p>
* A {@code TripleTransaction} supports the following four main features:
* </p>
* <ul>
* <li><b>Statement retrieval</b>, via method {@link #get(Resource, URI, Value, Resource)};</li>
* <li><b>SPARQL querying</b>, via method {@link #query(SelectQuery, BindingSet)};</li>
* <li><b>Inference materialization</b>, via method {@link #infer(Handler)}</li>
* <li><b>Triple modification</b>, via bulk methods {@link #add(Iterable)} and
* {@link #remove(Iterable)}.</li>
* </ul>
* <p>
* Note that the latter two functionalities are not available for read-only transactions (an
* {@link IllegalStateException} is thrown in that case).
* </p>
* <p>
* Transactions are terminated via {@link #end(boolean)}, whose parameter specifies whether
* changes should be committed. Method {@code end()} always tries to terminate the transaction: if
* it throws an {@code IOException} exception a rollback must be assumed, even if a commit was
* asked; if it throws a {@code DataCorruptedException}, then neither commit or rollback were
* possible and the {@code TripleStore} is left in some unpredictable state with no possibility of
* automatic recovery. In that case, external code may resort to the {@link TripleStore#reset()}
* and re-population procedure to recover the situation. In case the JVM is abruptly shutdown
* during a transaction, the effects of the transaction should be the same as if a rollback was
* performed.
* </p>
* <p>
* {@code TripleTransaction} objects are not required to be thread safe. Access by at most one
* thread at a time can be assumed and must be guaranteed externally, with the only exception of
* method {@link #end(boolean)} that can be called at any moment by any thread in case the
* transaction need to be rolled back. Note also that it must be allowed for operations to be
* issued while streams from previous operations are still open; if a stream is open and a write
* operations is performed that affects one of the statements/query solutions still to be returned
* by the stream (or made a statement/query solution returnable/not returnable by the stream),
* then the behaviour of the stream is undefined.
* </p>
*/
public interface TripleTransaction {
/**
* Returns a Sesame {@code CloseableIteration} over all the RDF statements matching the
* (optional) subject, predicate, object, context supplied. Null values are used as wildcard.
* Implementations are designed to perform high throughput statement extraction (likely more
* efficient than doing a SPARQL query).
*
* @param subject
* the subject to match, null to match any subject
* @param predicate
* the predicate to match, null to match any predicate
* @param object
* the object to match, null to match any object
* @param context
* the context to match, null to match any context
* @return an iteration over matching RDF statements
* @throws IOException
* in case some IO error occurs
* @throws IllegalStateException
* if the {@code TripleTransaction} has been already ended
*/
CloseableIteration<? extends Statement, ? extends Exception> get(@Nullable Resource subject,
@Nullable URI predicate, @Nullable Value object, @Nullable Resource context)
throws IOException, IllegalStateException;
/**
* Evaluates the supplied SPARQL SELECT query, returning a Sesame {@code CloseableIteration}
* over its solutions. The method accepts an optional <tt>BindingSet</tt> object, providing
* bindings for variables in the query; it can be used to instantiate parametric queries,
* similarly to <tt>PreparedStatements</tt> in JDBC.
*
* @param query
* the SPARQL SELECT query to evaluate
* @param bindings
* optional bindings for variables in the query;
* @param timeout
* optional timeout in milliseconds for the query
* @return an iteration over the results of the query
* @throws IOException
* in case some IO error occurs
* @throws UnsupportedOperationException
* in case the query, while being valid according to SPARQL, uses a construct,
* function or feature that is not supported by the triple store implementation
* (refer to the implementation documentation for unsupported features)
* @throws IllegalStateException
* if the {@code TripleTransaction} has been already ended
*/
CloseableIteration<BindingSet, QueryEvaluationException> query(SelectQuery query,
@Nullable BindingSet bindings, @Nullable Long timeout) throws IOException,
UnsupportedOperationException, IllegalStateException;
/**
* Performs inference, materializing the logical closure of the RDF statements contained in
* the triple store. The method accepts an optional {@code Handler} inferred statements can be
* notified to, allowing for their additional processing by external code.
*
* @param handler
* an optional handler inferred RDF statements can be notified to
* @throws IOException
* in case some IO error occurs, with no guarantee that the {@code TripleStore} is
* left in the same state if was when the method was called; note that this
* exception may trigger a rollback on the caller side
* @throws IllegalStateException
* if the {@code TripleTransaction} has been already ended, or if it is read-only
*/
void infer(@Nullable Handler<? super Statement> handler) throws IOException,
IllegalStateException;
/**
* Adds all the RDF statements in the {@code Iterable} specified to the triple store.
* Implementations are designed to perform high throughput insertion. They are also allowed to
* buffer part or all of the operation, successfully returning before specified triples have
* been completely modified; in this case, it must be however guaranteed that subsequent calls
* to {@link #query(SelectQuery, BindingSet)} will 'see' all the triples added.
*
* @param statements
* the statements to add
* @throws IOException
* in case some IO error occurs, with no guarantee that the {@code TripleStore} is
* left in the same state if was when the method was called; note that this
* exception may trigger a rollback on the caller side
* @throws IllegalStateException
* if the {@code TripleTransaction} has been already ended, or if it is read-only
*/
void add(Iterable<? extends Statement> statements) throws IOException, IllegalStateException;
/**
* Removes all the RDF statements in the {@code Iterable} specified from the triple store.
* Implementations are designed to perform high throughput deletion. They are also allowed to
* return before specified triples have been completely removed; in this case, it must be
* however guaranteed that subsequent calls to {@link #query(SelectQuery, BindingSet)} will
* not 'see' any of the triples removed.
*
* @param statements
* the statements to remove
* @throws IOException
* in case some IO error occurs, with no guarantee that the {@code TripleStore} is
* left in the same state if was when the method was called; note that this
* exception may trigger a rollback on the caller side
* @throws IllegalStateException
* if the {@code TripleTransaction} has been already ended, or if it is read-only
*/
void remove(Iterable<? extends Statement> statements) throws IOException,
IllegalStateException;
/**
* Ends the transaction, either committing or rolling back its changes (if any). This method
* always tries to terminate the transaction: if commit is requested but fails, a rollback is
* forced by the method and an {@code IOException} is thrown. If it is not possible either to
* commit or rollback, then the {@code TripleStore} is possibly left in an unknown state and a
* {@code DataCorruptedException} is thrown to signal a data corruption situation that cannot
* be automatically recovered (apart from calling {@link TripleStore#reset()} and repopulating
* the {@code TripleStore}).
*
* @param commit
* true in case changes made by the transaction should be committed
* @throws IOException
* in case some IO error occurs or the commit request cannot be satisfied for any
* reason; it is however guaranteed that a forced rollback has been performed
* @throws DataCorruptedException
* in case it was not possible either to commit or rollback, which implies the
* state of the {@code TripleStore} is unknown and automatic recovery is not
* possible (hence, data is corrupted)
* @throws IllegalStateException
* if the {@code TripleTransaction} has been already ended
*/
void end(boolean commit) throws DataCorruptedException, IOException, IllegalStateException;
}
// IMPLEMENTATION NOTES
//
// - ask, construct and describe queries can be implemented on top of select; a limit clause
// should be generated for ask queries for efficiency; a stream wrapper based on
// MultiProjectionIterator and the code of SailGraphQuery can be used for construct and describe.
// - during query execution, QueryEvaluationException is thrown in case of IO errors or if a
// malformed query is detected after execution began: in the first case an IOException seems more
// appropriate, while in the second case an UnsupportedOperationException seems appropriate
// (it's the client fault, not something expected to be recoverable)
//
// Corruption may result from
// - internal failure of the triple store (e.g. due to bug)
// - bug in the Java code or unsupported rollback that leaves partial modifications in the store