/******************************************************************************* * Copyright (c) 2008 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 * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: Jan 18, 2008 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.datasource.nodecentric.operations; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import org.openanzo.datasource.nodecentric.internal.NodeCentricOperationContext; import org.openanzo.datasource.nodecentric.sql.NamedGraphRdbWrapper; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.exceptions.LogUtils; import org.openanzo.jdbc.container.sql.BaseSQL; import org.openanzo.jdbc.utils.RdbException; import org.openanzo.rdf.Resource; import org.openanzo.rdf.URI; import org.openanzo.rdf.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Set of find operations for both regular finds, as well as find with results. * * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * */ public class Remove { // private static final Logger log = LoggerFactory.getLogger(Remove.class); private static final Logger logger = LoggerFactory.getLogger(Remove.class); private static final String SUBJECT_TEMP = "SUBJECT_IDS_TEMP"; private static final String PREDICATE_TEMP = "PREDICATE_IDS_TEMP"; private static final String OBJECT_TEMP = "OBJECT_IDS_TEMP"; private static final String NAMEDGRAPH_TEMP = "NAMEDGRAPH_IDS_TEMP"; static final String STMT_TMP = "STMT_TMP"; /** * Remove statements in context that match pattern. <li>If graphTable is not null, queries are joined against temporary table containing valid graphs for * this query</li> <li>Else if the namedGraphId is not null, then queries go against only statements that have the specific namedGraphId</li> <li>Finally, * if both the graphTable and namedGraphId are null, query either queries all graphs if metadataGraph==-1, namedGraphs if metadataGraph==0, or * metadataGraphs if metadataGraph==1 * * @param context * source of data * @param tableName * statements table * @param revisioned * include temporal limits in query * @param timestamp * include temporal limit in query, if null and revisioned is true, uses all data including current transaction data * @param subject * Subject resource to match, or wildcard if null * @param predicate * Predicate uri to match, or wildcard if null * @param object * Object value to match, or wildcard if null * @param namedGraphUri * id of namedGraph to query * @return Iterable set of quads containing results of find operation * @throws AnzoException * if there was an error finding statements */ public static long removeQuads(NodeCentricOperationContext context, String tableName, boolean revisioned, Long timestamp, Resource[] subject, URI[] predicate, Value[] object, URI[] namedGraphUri) throws AnzoException { try { ArrayList<Long> subjectIds = null; ArrayList<Long> predicateIds = null; ArrayList<Long> objectIds = null; ArrayList<Long> namedGraphIds = null; try { if (subject != null && subject.length > 0) { if (subject.length > 1 || subject[0] != null) { subjectIds = new ArrayList<Long>(); for (Resource subj : subject) { Long id = context.getNodeLayout().fetchId(subj, context.getConnection()); if (id == null && subject.length == 1) { return 0; // required node is not even in db } else { subjectIds.add(id); } } if (subjectIds.size() == 0) { return 0; // required node is not even in db } if (subjectIds.size() >= 100) { insertIdsToTempTable(context, SUBJECT_TEMP, subjectIds); } } } if (predicate != null && predicate.length > 0) { if (predicate.length > 1 || predicate[0] != null) { predicateIds = new ArrayList<Long>(); for (URI pred : predicate) { Long id = context.getNodeLayout().fetchId(pred, context.getConnection()); if (id == null && predicate.length == 1) { return 0; // required node is not even in db } else { predicateIds.add(id); } } if (predicateIds.size() == 0) { return 0; // required node is not even in db } if (predicateIds.size() >= 100) { insertIdsToTempTable(context, PREDICATE_TEMP, predicateIds); } } } if (object != null && object.length > 0) { if (object.length > 1 || object[0] != null) { objectIds = new ArrayList<Long>(); for (Value obj : object) { Long id = context.getNodeLayout().fetchId(obj, context.getConnection()); if (id == null && object.length == 1) { return 0; // required node is not even in db } else { objectIds.add(id); } } if (objectIds.size() == 0) { return 0; // required node is not even in db } if (objectIds.size() >= 100) { insertIdsToTempTable(context, OBJECT_TEMP, objectIds); } } } if (namedGraphUri != null && namedGraphUri.length > 0) { if (namedGraphUri.length > 1 || namedGraphUri[0] != null) { namedGraphIds = new ArrayList<Long>(); for (URI ngURI : namedGraphUri) { Long id = context.getNodeLayout().fetchId(ngURI, context.getConnection()); if (id == null && namedGraphUri.length == 1) { return 0; // required node is not even in db } else { namedGraphIds.add(id); } } if (namedGraphIds.size() == 0) { return 0; // required node is not even in db } if (namedGraphIds.size() >= 100) { insertIdsToTempTable(context, NAMEDGRAPH_TEMP, namedGraphIds); } } } String sql = generateSQLStatement(context, tableName, revisioned, timestamp, subjectIds, predicateIds, objectIds, namedGraphIds); Statement stmt = context.getConnection().createStatement(); try { return stmt.executeUpdate(sql); } finally { stmt.close(); } } finally { try { if (subjectIds != null && subjectIds.size() >= 100) { BaseSQL.clearTableWithSessionPrefix(context.getStatementProvider(), context.getConnection(), context.getConfiguration().getSessionPrefix(), SUBJECT_TEMP); } if (predicateIds != null && predicateIds.size() >= 100) { BaseSQL.clearTableWithSessionPrefix(context.getStatementProvider(), context.getConnection(), context.getConfiguration().getSessionPrefix(), PREDICATE_TEMP); } if (objectIds != null && objectIds.size() >= 100) { BaseSQL.clearTableWithSessionPrefix(context.getStatementProvider(), context.getConnection(), context.getConfiguration().getSessionPrefix(), OBJECT_TEMP); } if (namedGraphIds != null && namedGraphIds.size() >= 100) { BaseSQL.clearTableWithSessionPrefix(context.getStatementProvider(), context.getConnection(), context.getConfiguration().getSessionPrefix(), NAMEDGRAPH_TEMP); } } catch (RdbException e) { logger.error(LogUtils.RDB_MARKER, "Error clearing temporary tables", e); } } } catch (SQLException e) { throw new AnzoException(ExceptionConstants.RDB.FAILED_EXECUTING_SQL, e); } } static void insertIdsToTempTable(NodeCentricOperationContext context, String tableName, Collection<Long> ids) throws AnzoException { PreparedStatement ps = null; try { ps = context.getStatementProvider().getPreparedSQLStatement(NamedGraphRdbWrapper.insertIdsIntoTempTable, new String[] { context.getConfiguration().getSessionPrefix(), tableName }, context.getConnection()); ps.clearBatch(); for (Long id : ids) { ps.setLong(1, id); ps.addBatch(); } ps.executeBatch(); } catch (SQLException e) { throw new AnzoException(ExceptionConstants.RDB.FAILED_EXECUTING_SQL, e); } finally { try { if (ps != null) ps.close(); } catch (SQLException sqle) { if (logger.isDebugEnabled()) { logger.error(LogUtils.RDB_MARKER, "Error closing prepared statement", sqle); } } } } static final String DELETE = "DELETE FROM "; static final String UPDATE = "UPDATE "; static final String AND = " AND "; static final String WHERE = " WHERE "; static final String REVISIONED = " REND IS NULL "; static final String SET_HEND = " REND="; static final String SUBJECT = " SUBJECT= "; static final String SUBJECT_IN = " SUBJECT IN ("; static final String SUBJECT_IN_TEMP_START = " EXISTS (SELECT ID FROM "; static final String SUBJECT_IN_TEMP_END = " WHERE ID=SUBJECT)"; static final String PREDICATE = " PREDICATE= "; static final String PREDICATE_IN = " PREDICATE IN ("; static final String PREDICATE_IN_TEMP_START = " EXISTS (SELECT ID FROM "; static final String PREDICATE_IN_TEMP_END = " WHERE ID=PREDICATE)"; static final String OBJECT = " OBJECT= "; static final String OBJECT_IN = " OBJECT IN ("; static final String OBJECT_IN_TEMP_START = " EXISTS (SELECT ID FROM "; static final String OBJECT_IN_TEMP_END = " WHERE ID=OBJECT)"; static final String NAMEDGRAPHID = " NAMEDGRAPHID= "; static final String NAMEDGRAPHID_IN = " NAMEDGRAPHID IN ("; static final String NAMEDGRAPHID_IN_TEMP_START = " EXISTS (SELECT ID FROM "; static final String NAMEDGRAPHID_IN_TEMP_END = " WHERE ID=NAMEDGRAPHID)"; static String generateSQLStatement(NodeCentricOperationContext context, String tableName, boolean revisioned, Long timestamp, ArrayList<Long> subjectIds, ArrayList<Long> predicateIds, ArrayList<Long> objectIds, ArrayList<Long> namedGraphIds) { StringBuilder sb = new StringBuilder(); if (revisioned) { sb.append(UPDATE); } else { sb.append(DELETE); } sb.append(tableName); if (timestamp != null) { sb.append(SET_HEND); sb.append(timestamp.toString()); } sb.append(WHERE); boolean first = true; if (subjectIds != null && subjectIds.size() > 0) { if (subjectIds.size() == 1) { sb.append(SUBJECT); sb.append(subjectIds.get(0).toString()); } else if (subjectIds.size() < 100) { sb.append(SUBJECT_IN); for (Iterator<Long> ids = subjectIds.iterator(); ids.hasNext();) { sb.append(ids.next().toString()); if (ids.hasNext()) { sb.append(','); } } sb.append(')'); } else { sb.append(SUBJECT_IN_TEMP_START); sb.append(context.getConfiguration().getSessionPrefix()); sb.append(SUBJECT_TEMP); sb.append(SUBJECT_IN_TEMP_END); } first = false; } if (predicateIds != null && predicateIds.size() > 0) { if (!first) { sb.append(AND); } else { first = false; } if (predicateIds.size() == 1) { sb.append(PREDICATE); sb.append(predicateIds.get(0).toString()); } else if (predicateIds.size() < 100) { sb.append(PREDICATE_IN); for (Iterator<Long> ids = predicateIds.iterator(); ids.hasNext();) { sb.append(ids.next().toString()); if (ids.hasNext()) { sb.append(','); } } sb.append(')'); } else { sb.append(PREDICATE_IN_TEMP_START); sb.append(context.getConfiguration().getSessionPrefix()); sb.append(PREDICATE_TEMP); sb.append(PREDICATE_IN_TEMP_END); } } if (objectIds != null && objectIds.size() > 0) { if (!first) { sb.append(AND); } else { first = false; } if (objectIds.size() == 1) { sb.append(OBJECT); sb.append(objectIds.get(0).toString()); } else if (objectIds.size() < 100) { sb.append(OBJECT_IN); for (Iterator<Long> ids = objectIds.iterator(); ids.hasNext();) { sb.append(ids.next().toString()); if (ids.hasNext()) { sb.append(','); } } sb.append(')'); } else { sb.append(OBJECT_IN_TEMP_START); sb.append(context.getConfiguration().getSessionPrefix()); sb.append(OBJECT_TEMP); sb.append(OBJECT_IN_TEMP_END); } } if (namedGraphIds != null && namedGraphIds.size() > 0) { if (!first) { sb.append(AND); } else { first = false; } if (namedGraphIds.size() == 1) { sb.append(NAMEDGRAPHID); sb.append(namedGraphIds.get(0).toString()); } else if (namedGraphIds.size() < 100) { sb.append(NAMEDGRAPHID_IN); for (Iterator<Long> ids = namedGraphIds.iterator(); ids.hasNext();) { sb.append(ids.next().toString()); if (ids.hasNext()) { sb.append(','); } } sb.append(')'); } else { sb.append(NAMEDGRAPHID_IN_TEMP_START); sb.append(context.getConfiguration().getSessionPrefix()); sb.append(NAMEDGRAPH_TEMP); sb.append(NAMEDGRAPHID_IN_TEMP_END); } } if (revisioned) { if (!first) { sb.append(AND); } else { first = false; } sb.append(REVISIONED); } return sb.toString(); } }