/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.jdbc.container;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import org.openanzo.cache.ICacheProvider;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.AnzoRuntimeException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.exceptions.Messages;
import org.openanzo.glitter.Engine;
import org.openanzo.glitter.dataset.DefaultQueryDataset;
import org.openanzo.glitter.exception.GlitterException;
import org.openanzo.glitter.query.QueryResults;
import org.openanzo.glitter.syntax.concrete.ParseException;
import org.openanzo.jdbc.container.query.FindInferred;
import org.openanzo.jdbc.container.query.RdbEngineConfig;
import org.openanzo.jdbc.container.sql.BaseSQL;
import org.openanzo.jdbc.container.sql.GraphSQL;
import org.openanzo.jdbc.layout.CompositeNodeLayout;
import org.openanzo.jdbc.layout.Quad;
import org.openanzo.jdbc.layout.ValueLayoutCacheProxy;
import org.openanzo.jdbc.layout.indexer.LiteralIndexer;
import org.openanzo.jdbc.utils.ClosableIterator;
import org.openanzo.jdbc.utils.IteratorUtils;
import org.openanzo.jdbc.utils.PreparedStatementProvider;
import org.openanzo.jdbc.utils.RdbException;
import org.openanzo.rdf.BaseQuadStore;
import org.openanzo.rdf.BlankNode;
import org.openanzo.rdf.Constants;
import org.openanzo.rdf.PlainLiteral;
import org.openanzo.rdf.Resource;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.TypedLiteral;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Value;
import org.openanzo.rdf.utils.UriGenerator;
import org.openanzo.rdf.vocabulary.Anzo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Node Centric QuadStore backed by a relational database.
*
* Writes are run within database transactions and are locked to prevent concurrent writes from sharing database transactions.
*
* Read operations do not respect transaction isolation.
*
* @author Stephen Evanchik (<a href="mailto:evanchik@us.ibm.com">Stephen Evanchik</a>)
* @author Joe Betz (<a href="mailto:jpbetz@cambridgesemantics.com">jpbetz@cambridgesemantics.com</a>)
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
*
*/
public class RDBQuadStore extends BaseQuadStore implements IRDBQuadStore {
private static final Logger log = LoggerFactory.getLogger(RDBQuadStore.class);
/** ID of named graph's URI */
protected static final String ng = "namedGraphId";
/** ID of statement's subject */
protected static final String subj = "subj";
/** ID of statement's predicate */
protected static final String prop = "prop";
/** ID of statement's object */
protected static final String obj = "obj";
private final Random random = new Random();
protected Connection connection;
private final CompositeNodeLayout nodeLayout;
private String containerName = null;
// Instance of PreparedStatementProvider used by Driver for hard-coded db commands
private PreparedStatementProvider stmtProvider = null;
private final Engine glitter;
private final RDBTransactionManager internalTransactionManager;
private final LiteralIndexer literalIndexer;
/** Is the JDBC connection set to readonly,rw or query */
protected final boolean write;
protected boolean initializing = false;
private boolean returned = false;
private static final String STATEMENT_TABLE_SUFFIX = "_S";
private static final String INITIAL_DATABASE_TABLE = "GLITTERUNIT";
/*
* The filename that contains all of the SQL commands to be loaded in to the PreparedStatementProvider
*/
private String SQL_FILENAME;
/*
* The name of the database table that we use to indicate whether or not the database bas been properly setup for this
* physical layout
*/
private String STATEMENT_TABLE_NAME = null;
/**
* @return the stmtProvider
*/
public PreparedStatementProvider getStmtProvider() {
return stmtProvider;
}
private final class RDBTransactionManager {
private void performCommit() throws AnzoException {
try {
RDBQuadStore.this.connection.commit();
if (!initializing && RDBQuadStore.this.write) {
unlockTable();
}
RDBQuadStore.this.connection.setAutoCommit(true);
} catch (SQLException e) {
log.error(LogUtils.RDB_MARKER, "Exception commiting connection transaction", e);
throw new AnzoException(ExceptionConstants.RDB.OPERATION_ERROR, e, e.getMessage());
}
nodeLayout.commitUncommittedCache();
}
private void performBegin() throws AnzoException {
try {
RDBQuadStore.this.connection.setAutoCommit(false);
if (!initializing && RDBQuadStore.this.write) {
lockTable();
}
} catch (SQLException e) {
log.error(LogUtils.RDB_MARKER, "Exception beginning connection transaction", e);
throw new AnzoException(ExceptionConstants.RDB.OPERATION_ERROR, e, e.getMessage());
}
}
private void performAbort() throws AnzoException {
try {
RDBQuadStore.this.connection.rollback();
if (!initializing && RDBQuadStore.this.write) {
unlockTable();
}
RDBQuadStore.this.connection.setAutoCommit(true);
} catch (SQLException e) {
log.error(LogUtils.RDB_MARKER, "Exception aborting connection transaction", e);
throw new AnzoException(ExceptionConstants.RDB.OPERATION_ERROR, e, e.getMessage());
} finally {
nodeLayout.clearUncommittedCache();
}
}
/** ReentrantLock that makes sure only the same thread that starts a transaction can abort or commit it */
private final ReentrantLock lock = new ReentrantLock();
protected void commit() throws AnzoException {
if (lock.isLocked()) {
if (lock.isHeldByCurrentThread()) {
int count = lock.getHoldCount();
try {
if (count == 1) {
performCommit();
}
} finally {
lock.unlock();
}
} else {
throw new IllegalMonitorStateException(Messages.getString("Messages.CurrentThreadNotOwner"));
}
} else {
throw new IllegalMonitorStateException(Messages.getString("Messages.NotInTransaction"));
}
}
protected void abort() throws AnzoException {
if (lock.isLocked()) {
if (lock.isHeldByCurrentThread()) {
try {
performAbort();
} finally {
lock.unlock();
}
} else {
throw new IllegalMonitorStateException(Messages.getString("Messages.CurrentThreadNotOwner"));
}
} else {
throw new IllegalMonitorStateException(Messages.getString("Messages.NotInTransaction"));
}
}
protected void begin() throws AnzoException {
lock.lock();
if (lock.getHoldCount() == 1) {
performBegin();
}
}
}
final CoreDBConfiguration configuration;
/**
* @return the configuration
*/
public CoreDBConfiguration getConfiguration() {
return configuration;
}
/**
* Create a new RDBQuadStore from the provided properties.
*
* @param configuration
* configuration information
* @param write
* store is for writting
* @return RDBQuadStore for configuration
* @throws AnzoException
*/
public static RDBQuadStore createQuadStore(CoreDBConfiguration configuration, boolean write) throws AnzoException {
RDBQuadStorePool connectionPool = new RDBQuadStorePool(configuration, null);
connectionPool.start();
return connectionPool.acquireRDBQuadStore(write);
}
/**
* Create a new RDBConnection
*
* @param connectionPool
* the connection pool creating this connection
* @param cacheProvider
* the cache provider for of caches of node values to their ids
* @param literalIndexer
* the literal indexer for like text queries
* @param write
* connection is for writting data
* @param configuration
* the configuration data for the connection
* @param connection
* the underlying jdbc connection
*/
protected RDBQuadStore(ICacheProvider cacheProvider, LiteralIndexer literalIndexer, boolean write, CoreDBConfiguration configuration, Connection connection) {
this.connection = connection;
this.write = write;
this.configuration = configuration;
this.connection = connection;
this.literalIndexer = literalIndexer;
// SAE: The call to setContainerName initializes other dependant instance variables
setContainerName(configuration.getContainerName());
setupPreparedStatementProvider();
int maxLength = configuration.getMaxIndexKeyLength();
String optimizationString = configuration.getOptimizationString();
nodeLayout = new CompositeNodeLayout(stmtProvider, configuration.getSupportsSequences(), this.literalIndexer, containerName, cacheProvider.<Long, URI> openCache("URICache", 20000, true), cacheProvider.<URI, Long> openCache("URIIDCache", 20000, true), cacheProvider.<Long, BlankNode> openCache("BlankCache", 20000, true), cacheProvider.<BlankNode, Long> openCache("BlankIDCache", 20000, true), cacheProvider.<Long, PlainLiteral> openCache("LiteralCache", 20000, true), cacheProvider
.<PlainLiteral, Long> openCache("LiteralIdCache", 20000, true), cacheProvider.<Long, TypedLiteral> openCache("TypedCache", 20000, true), cacheProvider.<TypedLiteral, Long> openCache("TypedLiteralIdCache", 20000, true), cacheProvider.<Long, String> openCache("LanguageCache", 20000, true), cacheProvider.<String, Long> openCache("LanguageIdCache", 20000, true), cacheProvider.<Long, String> openCache("DatatypeCache", 20000, true), cacheProvider.<String, Long> openCache("DatatypeIdCache", 20000,
true), maxLength, optimizationString, configuration.getSupportsTempForInsert(), configuration.getSupportsIdentity(), configuration.getSessionPrefix());
RdbEngineConfig config = new RdbEngineConfig();
config.getRdbSolutionGeneratorFactory().setNodeCentricNamedGraphContainer(this);
glitter = new Engine(config);
internalTransactionManager = new RDBTransactionManager();
connect();
}
protected void lockTable() {
if (!initializing && configuration.getSupportsTableLocks()) {
try {
BaseSQL.lockTable(stmtProvider, connection, containerName + "_U", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_S", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_L", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_TL", configuration.getTableLocksExtras());
//BaseSQL.lockTable(getPreparedStatementProvider(), connection, containerName + "_X", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_B", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_LU", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_LL", configuration.getTableLocksExtras());
BaseSQL.lockTable(stmtProvider, connection, containerName + "_LTL", configuration.getTableLocksExtras());
//BaseSQL.lockTable(getPreparedStatementProvider(), connection, containerName + "_XL", configuration.getTableLocksExtras());
} catch (RdbException e) {
try {
abort();
} catch (AnzoException be) {
log.error(LogUtils.RDB_MARKER, "Exception aborting lock tables", be);
}
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_SQLCACHE, e);
}
}
}
protected void unlockTable() {
if (!initializing && configuration.getSupportsTableUnLocks()) {
try {
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_U");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_S");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_L");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_TL");
//BaseSQL.unlockTable(getPreparedStatementProvider(), connection, containerName + "_X");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_B");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_LU");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_LL");
BaseSQL.unlockTable(stmtProvider, connection, containerName + "_LTL");
//BaseSQL.unlockTable(getPreparedStatementProvider(), connection, containerName + "_XL");
} catch (RdbException e) {
try {
abort();
} catch (AnzoException be) {
log.error(LogUtils.RDB_MARKER, "Exception aborting lock tables", be);
}
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_SQLCACHE, e);
}
}
}
private void setupPreparedStatementProvider() {
SQL_FILENAME = configuration.getSqlFilename();
try {
stmtProvider = new PreparedStatementProvider();
stmtProvider.loadSQLFile(findSQLResource(SQL_FILENAME));
} catch (RdbException e) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_SQLCACHE, e);
}
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#connect()
*/
public synchronized void connect() {
if (connection == null) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_CONNECTION);
}
try {
initializing = true;
try {
begin();
// This database layout requires extra entities in order to support
// storing graphs and nodes
if (!hasTable(INITIAL_DATABASE_TABLE)) {
stmtProvider.runSQLGroup("initDBtables", configuration.getInitParams(containerName), connection);
}
if (!hasTable(STATEMENT_TABLE_NAME)) {
stmtProvider.runSQLGroup("initGraphTables", configuration.getInitParams(containerName), connection);
stmtProvider.runSQLGroup("initIndexes", configuration.getInitParams(containerName), connection);
prepopulateLanguageTable();
prepopulateDatatable();
}
//if (configuration.getSupportsTempForInsert() || configuration.getSupportsTempOnFind()) {
if (!hasTempTable("ID_TMP")) {
try {
stmtProvider.runSQLGroup("createTemporaryTables", configuration.getInitParams(containerName), connection);
} catch (SQLException e) {
log.debug(LogUtils.RDB_MARKER, "Unable to create temporary tables:", e);
}
}
//}
commit();
} catch (SQLException e) {
log.error(LogUtils.RDB_MARKER, "Could not initialized database tables ", e);
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_TABLES, e);
} catch (RdbException e) {
log.error(LogUtils.RDB_MARKER, "Could not initialized database tables ", e);
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_TABLES, e);
}
} catch (AnzoException be) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER, be);
} finally {
initializing = false;
}
}
private InputStream findSQLResource(String sqlFilename) throws RdbException {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(sqlFilename);
if (is == null) {
throw new RdbException(ExceptionConstants.RDB.FAILED_LOAD_SQL_FILE, sqlFilename);
}
return is;
}
private void prepopulateLanguageTable() throws RdbException {
ClosableIterator<String> languageIter = null;
try {
languageIter = stmtProvider.listPreloadData(findSQLResource("preloaddata/languages.data"));
ValueLayoutCacheProxy langLayout = nodeLayout.getLanguageLayout();
Collection<String> list = new ArrayList<String>();
org.openanzo.rdf.utils.Collections.addAll(languageIter, list);
langLayout.batchAddAndCache(list, connection);
} finally {
IteratorUtils.close(languageIter);
}
}
private void prepopulateDatatable() throws RdbException {
ClosableIterator<String> datatypeIter = null;
try {
datatypeIter = stmtProvider.listPreloadData(findSQLResource("preloaddata/datatypes.data"));
ValueLayoutCacheProxy datatypeLayout = nodeLayout.getDatatypeLayout();
Collection<String> list = new ArrayList<String>();
org.openanzo.rdf.utils.Collections.addAll(datatypeIter, list);
datatypeLayout.batchAddAndCache(list, connection);
} finally {
IteratorUtils.close(datatypeIter);
}
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#isConnected()
*/
public boolean isConnected() {
if (connection == null) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_CONNECTION);
}
try {
return hasTable(STATEMENT_TABLE_NAME);
} catch (RdbException e) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_INITIALIZE_CONTAINER_CONNECTION, e);
}
}
//private static final Pattern db2TableNamePattern = Pattern.compile("[A-Za-z][A-Za-z0-9_]*");
/**
* Set the URI for this container
*
* @param uri
* for this container
*/
public void setURI(URI uri) {
String containerName = uri.toString();
setContainerName(containerName);
}
/**
* Get the container's name
*
* @return the container's name
*/
public String getContainerName() {
return containerName;
}
/**
* Set the container's name
*
* @param containerName
*/
public void setContainerName(String containerName) {
this.containerName = "G" + Math.abs(containerName.toString().hashCode()); /* makes the table name shorter */
STATEMENT_TABLE_NAME = this.containerName + STATEMENT_TABLE_SUFFIX;
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#close()
*/
public void close() {
if (connection != null) {
try {
connection.close();
} catch (SQLException sqle) {
log.error(LogUtils.RDB_MARKER, "Exception closing connection", sqle);
} finally {
connection = null;
}
}
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#isClosed()
*/
public boolean isClosed() {
try {
return connection == null || connection.isClosed();
} catch (SQLException sqle) {
return true;
}
}
private void clearCaches() {
if (nodeLayout != null)
nodeLayout.clearCache();
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#clearDatabase()
*/
public void clearDatabase() {
try {
deleteTable(STATEMENT_TABLE_NAME);
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + STATEMENT_TABLE_NAME, re);
}
try {
deleteTable(containerName + "_TRANSACTIONS");
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + containerName + "_TRANSACTIONS", re);
}
try {
deleteTable(containerName + "_COMMANDS");
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + containerName + "_COMMANDS", re);
}
try {
deleteTable(containerName + "_CHANGESETS");
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + containerName + "_CHANGESETS", re);
}
try {
deleteTable(containerName + "_ST");
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + containerName + "_ST", re);
}
try {
deleteTable(containerName + "_OBJ_INFER");
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + containerName + "_OBJ_INFER", re);
}
try {
deleteTable(containerName + "_PROP_INFER");
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + containerName + "_PROP_INFER", re);
}
String[] nodeTables = nodeLayout.listTables();
for (int i = 0; i < nodeTables.length; i++) {
String tableName = nodeTables[i];
try {
deleteTable(tableName);
} catch (RdbException re) {
log.debug(LogUtils.RDB_MARKER, "Exception deleting table:" + tableName, re);
}
}
clearCaches();
}
private void deleteTable(String tablename) throws RdbException {
if (!hasTable(tablename))
return;
BaseSQL.dropTable(stmtProvider, connection, tablename);
}
private boolean hasTable(String tablename) throws RdbException {
try {
ResultSet rs = null;
try {
rs = connection.getMetaData().getTables(null, null, configuration.getUsesUppercase() ? tablename.toUpperCase() : tablename.toLowerCase(), new String[] { "TABLE" });
if (!rs.next())
return false;
String tbl = rs.getString(3);
if (!tbl.equalsIgnoreCase(tablename))
return false;
return true;
} finally {
if (rs != null)
rs.close();
}
} catch (SQLException e) {
log.error(LogUtils.RDB_MARKER, "Could not get metadata information from the database table", e);
throw new RdbException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS, e, tablename);
}
}
private boolean hasTempTable(String tablename) throws RdbException {
try {
ResultSet rs = null;
try {
rs = connection.getMetaData().getTables(null, null, configuration.getUsesUppercaseTempTables() ? tablename.toUpperCase() : tablename.toLowerCase(), null);
if (!rs.next()) {
return false;
}
String tbl = rs.getString(3);
if (!tbl.equalsIgnoreCase(tablename)) {
return false;
}
return true;
} finally {
if (rs != null) {
rs.close();
}
}
} catch (SQLException e) {
log.error(LogUtils.RDB_MARKER, "Could not get metadata information from the database table", e);
throw new RdbException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS, e, tablename);
}
}
public void clear() {
try {
try {
begin();
remove(find(null, null, null));
commit();
} catch (RdbException e) {
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CLEAR, e);
} finally {
}
} catch (AnzoException e) {
throw new AnzoRuntimeException(e);
}
}
protected void clear(URI... context) {
try {
try {
begin();
remove(find(null, null, null, context));
} catch (RdbException e) {
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CLEAR, e);
} finally {
commit();
}
} catch (AnzoException e) {
throw new AnzoRuntimeException(e);
}
}
public void add(Statement... statements) {
try {
long tranId = random.nextLong();
if (statements.length > 20 && configuration.getSupportsTempForInsert()) {
long start1 = System.currentTimeMillis();
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
begin();
HashSet<Value> nodes = new HashSet<Value>();
Collection<Statement> stmts = new HashSet<Statement>();
Collections.addAll(stmts, statements);
for (Statement statement : stmts) {
Long gid = nodeLayout.getIfCached(statement.getNamedGraphUri());
if (gid == null)
nodes.add(statement.getNamedGraphUri());
Long sid = nodeLayout.getIfCached(statement.getSubject());
if (sid == null)
nodes.add(statement.getSubject());
Long pid = nodeLayout.getIfCached(statement.getPredicate());
if (pid == null)
nodes.add(statement.getPredicate());
Long oid = nodeLayout.getIfCached(statement.getObject());
if (oid == null)
nodes.add(statement.getObject());
}
Map<Value, Long> alreadyStored = (nodes.size() > 0) ? nodeLayout.resolveStoredNodes(nodes, true, connection, tranId) : new HashMap<Value, Long>();
nodeLayout.commitReferencedIds(connection, tranId);
end = System.currentTimeMillis();
log.debug("[RSN]{}:{}", Integer.toString(alreadyStored.size()), Long.toString(end - start));
start = end;
GraphSQL.BatchInsertStatement stmt = new GraphSQL.BatchInsertStatement(connection, stmtProvider, configuration.getSessionPrefix() + "STMT_TMP");
try {
for (Statement statement : stmts) {
Value[] gspo = { statement.getNamedGraphUri(), statement.getSubject(), statement.getPredicate(), statement.getObject() };
Long[] ids = new Long[4];
for (int i = 0; i < gspo.length; i++) {
if (nodeLayout.isCached(gspo[i])) {
ids[i] = nodeLayout.getIfCached(gspo[i]);
} else {
if (alreadyStored.containsKey(gspo[i])) {
ids[i] = alreadyStored.get(gspo[i]);
} else {
log.error("Could not find node: " + gspo[i]);
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_FIND_NODE, gspo[i].toString());
}
}
}
long ngId = ids[0].longValue();
long subjId = ids[1].longValue();
long predId = ids[2].longValue();
long objId = ids[3].longValue();
stmt.addEntry(UriGenerator.isMetadataGraphUri(statement.getNamedGraphUri()) ? 1 : 0, ngId, subjId, predId, objId);
}
stmt.executeStatement();
end = System.currentTimeMillis();
log.debug(LogUtils.RDB_MARKER, "[BIS]{}", Long.toString(end - start));
start = end;
int count = GraphSQL.purgeInsertStatements(stmtProvider, connection, configuration.getSessionPrefix() + configuration.getUniqueTempName("STMT_TMP", 0), configuration.getSessionPrefix() + configuration.getUniqueTempName("STMT_TMP", 1), STATEMENT_TABLE_NAME);
end = System.currentTimeMillis();
long totalTime = (end - start);
log.debug(LogUtils.RDB_MARKER, "[PIS]{}:{}", Integer.toString(count), Long.toString(end - start));
start = end;
count = GraphSQL.commitInsertStatements(stmtProvider, connection, configuration.getSessionPrefix() + configuration.getUniqueTempName("STMT_TMP", 0), STATEMENT_TABLE_NAME);
end = System.currentTimeMillis();
log.debug(LogUtils.RDB_MARKER, "[CIS]{}:{}", Integer.toString(count), Long.toString(end - start));
totalTime += (end - start);
log.debug(LogUtils.RDB_MARKER, "[PCIS]{}:{}", Integer.toString(count), Long.toString(totalTime));
commit();
} catch (RdbException e) {
log.error(LogUtils.RDB_MARKER, "Failed executing batched insertStatement ", e);
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_INSERT_BATCH, e);
} finally {
stmt.close();
try {
if (configuration.getForceTempTablePurge()) {
BaseSQL.truncateTableWithSessionMayCommit(stmtProvider, connection, configuration.getSessionPrefix(), configuration.getUniqueTempName("STMT_TMP", 0));
}
log.debug(LogUtils.RDB_MARKER, "[STORE]{}:{}:{} Q/mS:{} mS/Q", new Object[] { Integer.toString(statements.length), Long.toString(System.currentTimeMillis() - start1), Long.toString(statements.length / ((System.currentTimeMillis() - start1) + 1)), Long.toString((System.currentTimeMillis() - start1) / (statements.length + 1)) });
} catch (RdbException e) {
log.error(LogUtils.RDB_MARKER, "Failed executing batched insertStatement ", e);
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_INSERT_BATCH, e);
}
}
} else {
begin();
try {
Set<Statement> stmts = new LinkedHashSet<Statement>();
Collections.addAll(stmts, statements);
GraphSQL.BatchInsertStatement stmt = new GraphSQL.BatchInsertStatement(connection, stmtProvider, STATEMENT_TABLE_NAME);
try {
for (Statement statement : stmts) {
long ngId = nodeLayout.store(statement.getNamedGraphUri(), connection, tranId).longValue();
long subjId = nodeLayout.store(statement.getSubject(), connection, tranId).longValue();
long predId = nodeLayout.store(statement.getPredicate(), connection, tranId).longValue();
long objId = nodeLayout.store(statement.getObject(), connection, tranId).longValue();
int exists;
try {
exists = GraphSQL.statementExists(stmtProvider, connection, ngId, subjId, predId, objId, STATEMENT_TABLE_NAME);
} catch (RdbException e) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_STMT_EXISTS, e);
}
if (exists > 0) {
continue;
}
stmt.addEntry(UriGenerator.isMetadataGraphUri(statement.getNamedGraphUri()) ? 1 : 0, ngId, subjId, predId, objId);
}
stmt.executeStatement();
} finally {
stmt.close();
}
commit();
} catch (RdbException e) {
log.error(LogUtils.RDB_MARKER, "Error adding statements", e);
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_ADD_TRIPLES, e);
}
}
} catch (AnzoException be) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_ADD_TRIPLES, be);
}
}
public void remove(Statement... statements) {
try {
try {
begin();
for (Statement statement : statements) {
Long ngId = nodeLayout.fetchId(statement.getNamedGraphUri(), connection);
Long subjId = nodeLayout.fetchId(statement.getSubject(), connection);
Long predId = nodeLayout.fetchId(statement.getPredicate(), connection);
Long objId = nodeLayout.fetchId(statement.getObject(), connection);
if (ngId != null && subjId != null && predId != null && objId != null)
GraphSQL.deleteStatement(stmtProvider, connection, ngId, subjId, predId, objId, STATEMENT_TABLE_NAME);
}
commit();
} catch (RdbException e) {
log.error(LogUtils.RDB_MARKER, "Unable to delete statements: ", e);
abort();
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_DELETE_STATEMENT, e);
}
} catch (AnzoException be) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_DELETE_TRIPLES, be);
}
}
private int rowCount(String tableName) {
try {
return BaseSQL.getRowCount(stmtProvider, connection, tableName);
} catch (RdbException e) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_COUNT_ROWS, e, tableName);
}
}
public Collection<Statement> getStatements() {
return find(null, null, null, (URI[]) null);
}
public Collection<Statement> find(Resource subj, URI prop, Value obj, URI... namedGraphUri) {
try {
//long start=System.currentTimeMillis();
Iterable<Quad> quads = FindInferred.findStatements(this, subj, prop, obj, namedGraphUri);
ArrayList<Statement> stmts = new ArrayList<Statement>();
for (Quad quad : quads) {
URI context = (URI) quad.getNamedGraphTerm().getValue();
Resource s = (Resource) quad.getSubjTerm().getValue();
URI p = (URI) quad.getPredTerm().getValue();
Value o = quad.getObjTerm().getValue();
stmts.add(Constants.valueFactory.createStatement(s, p, o, context));
}
//System.err.println("FindTime("+stmts.size()+")->["+(System.currentTimeMillis()-start)+"]" );
return stmts;
} catch (AnzoException be) {
throw new AnzoRuntimeException(be);
}
}
public int size() {
return rowCount(STATEMENT_TABLE_NAME);
}
public int size(URI... namedGraphUri) {
int size = 0;
for (URI ng : namedGraphUri) {
try {
Long ngId = nodeLayout.fetchId(ng, connection);
if (ngId == null) {
size += 0;
} else {
size += GraphSQL.size(stmtProvider, connection, ngId, STATEMENT_TABLE_NAME);
}
} catch (RdbException e) {
log.error(LogUtils.RDB_MARKER, "Exception getting size of graph", e);
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.FAILED_CONTAINER_COUNT_STATEMENTS, e, Arrays.toString(namedGraphUri));
}
}
return size;
}
public boolean isEmpty() {
return size() == 0;
}
/**
* Tests if a statement is contained in the container.
*
* @param match
* is the statement to be tested
* @return boolean result to indicate if the statement was contained
*/
public boolean contains(Statement match) {
return contains(match.getSubject(), match.getPredicate(), match.getObject(), match.getNamedGraphUri());
}
/**
* Tests if a statement is contained in the container.
*
* @param subj
* Subject resource to match, or wildcard if null
* @param prop
* Predicate uri to match, or wildcard if null
* @param obj
* Object value to match, or wildcard if null
* @param contexts
* Context values to match, or wildcard if null
* @return boolean result to indicate if the statement was contained
*/
public boolean contains(Resource subj, URI prop, Value obj, URI... contexts) {
// FIXME: Optimize this so that the database doesn'q have to work so hard
Collection<Statement> it = null;
it = find(subj, prop, obj, contexts);
return !it.isEmpty();
}
/**
* Execute a SPARQL query against the data within this container
*
* @param defaultNamedGraphsIn
* Set<URI> of URIs for NamedGraphs that will make up the default graph for this query
* @param namedGraphsIn
* Set<URI> of URIs for NamedGraphs that will make up the NamedGraphs for this query
* @param namedDatasets
* Set of URIs for named datasets that will contribute to the query's RDF dataset
* @param query
* SPARQL query string
* @param baseUri
* Base URI against which relative URI references in the SPARQL query are resolved
* @return the Results of running query
* @throws AnzoException
*/
public QueryResults executeQuery(Set<URI> defaultNamedGraphsIn, Set<URI> namedGraphsIn, Set<URI> namedDatasets, String query, URI baseUri) throws AnzoException {
try {
begin();
log.debug(LogUtils.RDB_MARKER, query);
long start = System.currentTimeMillis();
HashSet<URI> defaultNamedGraphs = new HashSet<URI>(defaultNamedGraphsIn);
HashSet<URI> namedGraphs = new HashSet<URI>(namedGraphsIn);
if (namedDatasets != null) {
for (URI uri : namedDatasets) {
for (Statement s : find(uri, Anzo.DEFAULTNAMEDGRAPH, null, uri)) {
defaultNamedGraphs.add((URI) s.getObject());
namedGraphs.add((URI) s.getObject());
}
for (Statement s : find(uri, Anzo.DEFAULTGRAPH, null, uri)) {
defaultNamedGraphs.add((URI) s.getObject());
}
for (Statement s : find(uri, Anzo.NAMEDGRAPH, null, uri)) {
namedGraphs.add((URI) s.getObject());
}
}
}
QueryResults results = glitter.executeQuery(null, query, new DefaultQueryDataset(defaultNamedGraphs, namedGraphs), baseUri);
log.debug(LogUtils.RDB_MARKER, "QueryTime({})->[{}]", Integer.toString(results.getSelectResults().size()), Long.toString(System.currentTimeMillis() - start));
return results;
} catch (ParseException pe) {
abort();
throw new AnzoException(ExceptionConstants.CLIENT.FAILED_CONTAINER_EXEC_QUERY, pe, query);
} catch (GlitterException ge) {
abort();
throw new AnzoException(ExceptionConstants.CLIENT.FAILED_CONTAINER_EXEC_QUERY, ge, query);
} finally {
commit();
}
}
//TODO:Fix this impl
public Collection<URI> getNamedGraphUris() {
return null;
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#begin()
*/
public void begin() throws AnzoException {
internalTransactionManager.begin();
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#abort()
*/
public void abort() throws AnzoException {
internalTransactionManager.abort();
}
/* (non-Javadoc)
* @see org.openanzo.jdbc.container.container.IRDBQuadStore#commit()
*/
public void commit() throws AnzoException {
internalTransactionManager.commit();
}
protected void reset(String id) {
nodeLayout.clearCache();
}
/**
* Get the conection for the quad store
*
* @return the conection for the quad store
*/
public Connection getConnection() {
return connection;
}
/**
* Get the literalIndexer
*
* @return the literalIndexer
*/
public LiteralIndexer getLiteralIndexer() {
return literalIndexer;
}
/**
* Get the preparedStatement provider
*
* @return the preparedStatement provider
*/
public PreparedStatementProvider getPreparedStatementProvider() {
return stmtProvider;
}
/**
* Set if quad store is returned
*
* @param returned
* is returned to pool
*/
public void setReturned(boolean returned) {
this.returned = returned;
}
/**
* Get if quad store is returned to pool
*
* @return if quad store is returned to pool
*/
public boolean getReturned() {
return returned;
}
/**
* @return the nodeLayout
*/
public CompositeNodeLayout getNodeLayout() {
return nodeLayout;
}
}