/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Sep 16, 2009 */ package com.bigdata.rdf.sail; import java.util.Properties; import org.apache.log4j.Logger; import org.openrdf.model.Literal; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.query.BindingSet; import org.openrdf.query.QueryLanguage; import org.openrdf.query.TupleQueryResult; import org.openrdf.repository.RepositoryResult; import com.bigdata.rdf.axioms.NoAxioms; import com.bigdata.rdf.changesets.IChangeRecord; import com.bigdata.rdf.internal.XSD; import com.bigdata.rdf.model.BigdataBNode; import com.bigdata.rdf.model.BigdataStatement; import com.bigdata.rdf.model.BigdataValueFactory; import com.bigdata.rdf.model.StatementEnum; import com.bigdata.rdf.store.AbstractTripleStore; /** * Test suite {@link RDRHistory}. */ public class TestRDRHistory extends ProxyBigdataSailTestCase { private static final Logger log = Logger.getLogger(TestRDRHistory.class); @Override public Properties getProperties() { return getProperties(RDRHistory.class); } public Properties getProperties(final Class<? extends RDRHistory> cls) { final Properties props = super.getProperties(); // no inference props.setProperty(BigdataSail.Options.TRUTH_MAINTENANCE, "false"); props.setProperty(BigdataSail.Options.AXIOMS_CLASS, NoAxioms.class.getName()); props.setProperty(BigdataSail.Options.JUSTIFY, "false"); props.setProperty(BigdataSail.Options.TEXT_INDEX, "false"); // turn on RDR history props.setProperty(AbstractTripleStore.Options.RDR_HISTORY_CLASS, cls.getName()); return props; } /** * */ public TestRDRHistory() { } /** * @param arg0 */ public TestRDRHistory(String arg0) { super(arg0); } /** * Test basic add/remove. */ public void testAddAndRemove() throws Exception { BigdataSailRepositoryConnection cxn = null; final BigdataSail sail = getSail(getProperties()); try { sail.initialize(); final BigdataSailRepository repo = new BigdataSailRepository(sail); cxn = (BigdataSailRepositoryConnection) repo.getConnection(); final BigdataValueFactory vf = (BigdataValueFactory) sail .getValueFactory(); final URI s = vf.createURI(":s"); final URI p = vf.createURI(":p"); final URI o = vf.createURI(":o"); final BigdataStatement stmt = vf.createStatement(s, p, o); // Add statement (first time added). cxn.add(stmt); cxn.commit(); if (log.isInfoEnabled()) { log.info(cxn.getTripleStore().dumpStore().insert(0,'\n')); } // Now 2 statements. // 1. ground statement (Explicit) // 2. <<s,p,o>> blaze:history:added timestamp1 assertEquals(2, cxn.size()); // Verify ground statement exists (Explicit). { final RepositoryResult<Statement> stmts = cxn.getStatements( s, p, o, true); try { assertTrue(stmts.hasNext()); final BigdataStatement tmp = (BigdataStatement) stmts.next(); assertEquals(StatementEnum.Explicit, tmp.getStatementType()); assertFalse(stmts.hasNext()); // Should be no more statements. } finally { stmts.close(); } } { final BigdataBNode sid = vf.createBNode(stmt); final RepositoryResult<Statement> stmts = cxn.getStatements( sid, RDRHistory.Vocab.ADDED, null, true); try { assertTrue(stmts.hasNext()); final BigdataStatement tmp = (BigdataStatement) stmts.next(); final Literal l = (Literal) tmp.getObject(); assertEquals(XSD.DATETIME, l.getDatatype()); assertFalse(stmts.hasNext()); // Should be no more statements. } finally { stmts.close(); } } // Remove statement (first time removed). cxn.remove(stmt); cxn.commit(); if (log.isInfoEnabled()) { log.info(cxn.getTripleStore().dumpStore().insert(0,'\n')); } // Now 3 statements // 1. ground statement (was Explicit, now History) // 2. <<s,p,o>> blaze:history:added timestamp1 // 3. <<s,p,o>> blaze:history:removed timestamp2 assertEquals(3, cxn.size()); // Verify ground statement no longer found (is History mode now). { final RepositoryResult<Statement> stmts = cxn.getStatements( s, p, o, true); try { assertFalse(stmts.hasNext()); } finally { stmts.close(); } } // Verify blaze:history:removed now found. { final BigdataBNode sid = vf.createBNode(stmt); final RepositoryResult<Statement> stmts = cxn.getStatements( sid, RDRHistory.Vocab.REMOVED, null, true); try { assertTrue(stmts.hasNext()); final BigdataStatement tmp = (BigdataStatement) stmts.next(); final Literal l = (Literal) tmp.getObject(); assertEquals(XSD.DATETIME, l.getDatatype()); } finally { stmts.close(); } } // Add same statement again (2nd time). cxn.add(stmt); cxn.commit(); if (log.isInfoEnabled()) { log.info(cxn.getTripleStore().dumpStore().insert(0,'\n')); } // Now 4 statements // 1. ground statement (was History, now Explicit) // 2. <<s,p,o>> blaze:history:added timestamp1 // 3. <<s,p,o>> blaze:history:removed timestamp2 // 4. <<s,p,o>> blaze:history:added timestamp3 assertEquals(4, cxn.size()); // Ground statement is found again. { final RepositoryResult<Statement> stmts = cxn.getStatements( s, p, o, true); try { assertTrue(stmts.hasNext()); } finally { stmts.close(); } } { final BigdataBNode sid = vf.createBNode(stmt); final RepositoryResult<Statement> stmts = cxn.getStatements( sid, null, null, true); try { int adds = 0; int removes = 0; while (stmts.hasNext()) { final Statement result = stmts.next(); final Literal l = (Literal) result.getObject(); assertTrue(l.getDatatype().equals(XSD.DATETIME)); final URI action = result.getPredicate(); if (action.equals(RDRHistory.Vocab.ADDED)) { adds++; } else if (action.equals(RDRHistory.Vocab.REMOVED)) { removes++; } else { fail(); } } assertEquals(2, adds); assertEquals(1, removes); } finally { stmts.close(); } } } finally { if (cxn != null) cxn.close(); sail.__tearDownUnitTest(); } } /** * Test custom history handler. */ public void testCustomHistory() throws Exception { BigdataSailRepositoryConnection cxn = null; final BigdataSail sail = getSail(getProperties(CustomRDRHistory.class)); try { sail.initialize(); final BigdataSailRepository repo = new BigdataSailRepository(sail); cxn = (BigdataSailRepositoryConnection) repo.getConnection(); final BigdataValueFactory vf = (BigdataValueFactory) sail .getValueFactory(); final URI s = vf.createURI(":s"); final URI p = vf.createURI(":p"); final URI o = vf.createURI(":o"); final Literal l = vf.createLiteral("o"); BigdataStatement stmt1 = vf.createStatement(s, p, o); BigdataStatement stmt2 = vf.createStatement(s, p, l); cxn.add(stmt1); cxn.add(stmt2); cxn.commit(); if (log.isInfoEnabled()) { log.info(cxn.getTripleStore().dumpStore().insert(0,'\n')); } assertEquals(3, cxn.size()); { final RepositoryResult<Statement> stmts = cxn.getStatements( s, p, o, true); try { assertTrue(stmts.hasNext()); } finally { stmts.close(); } } { final RepositoryResult<Statement> stmts = cxn.getStatements( s, p, l, true); try { assertTrue(stmts.hasNext()); } finally { stmts.close(); } } { final BigdataBNode sid = vf.createBNode(stmt1); final RepositoryResult<Statement> stmts = cxn.getStatements( sid, RDRHistory.Vocab.ADDED, null, true); try { assertFalse(stmts.hasNext()); } finally { stmts.close(); } } { final BigdataBNode sid = vf.createBNode(stmt2); final RepositoryResult<Statement> stmts = cxn.getStatements( sid, RDRHistory.Vocab.ADDED, null, true); try { assertTrue(stmts.hasNext()); final Literal l2 = (Literal) stmts.next().getObject(); assertTrue(l2.getDatatype().equals(XSD.DATETIME)); } finally { stmts.close(); } } } finally { if (cxn != null) cxn.close(); sail.__tearDownUnitTest(); } } /** * Test the SPARQL integration. */ public void testSparqlIntegration() throws Exception { BigdataSailRepositoryConnection cxn = null; final BigdataSail sail = getSail(getProperties()); try { sail.initialize(); final BigdataSailRepository repo = new BigdataSailRepository(sail); { final BigdataSailRepositoryConnection read = repo.getReadOnlyConnection(); try { read.prepareTupleQuery(QueryLanguage.SPARQL, "select * { ?s ?p ?o }").evaluate(); } finally { read.close(); } } cxn = (BigdataSailRepositoryConnection) repo.getConnection(); { final String sparql = "insert { " + " ?s <:p> \"foo\" . " + "} where { " + " values (?s) { " + " (<:s1>) " + " (<:s2>) " + " } " + "}"; cxn.prepareUpdate(QueryLanguage.SPARQL, sparql).execute(); cxn.commit(); } { final String sparql = "delete { " + " ?s <:p> ?o . " + "} insert { " + " ?s <:p> \"bar\" . " + "} where { " + " ?s <:p> ?o . " + // " values (?s) { " + // " (<:s1>) " + // " } " + "}"; cxn.prepareUpdate(QueryLanguage.SPARQL, sparql).execute(); cxn.commit(); } if (log.isInfoEnabled()) { log.info(cxn.getTripleStore().dumpStore().insert(0,'\n')); } assertEquals(10, cxn.size()); { final String sparql = "select ?s ?p ?o ?action ?time \n" + "where { \n" + // " service bd:history { \n" + // " << ?s ?p ?o >> ?action ?time . \n" + // " } \n" + " bind(<< ?s ?p ?o >> as ?sid) . \n" + " hint:Prior hint:history true . \n" + " ?sid ?action ?time . \n" + " values (?s) { \n" + " (<:s1>) \n" + " } \n" + "}"; final TupleQueryResult result = cxn.prepareTupleQuery(QueryLanguage.SPARQL, sparql).evaluate(); try { int i = 0; while (result.hasNext()) { final BindingSet bs = result.next(); i++; if (log.isDebugEnabled()) { log.debug(bs); } } assertEquals(3, i); } finally { result.close(); } } { final String sparql = "select ?s ?p ?o ?action ?time \n" + "where { \n" + // " service bd:history { \n" + // " << ?s ?p ?o >> ?action ?time . \n" + // " } \n" + " bind(<< ?s ?p ?o >> as ?sid) . \n" + " hint:Prior hint:history true . \n" + " ?sid ?action ?time . \n" + "}"; final TupleQueryResult result = cxn.prepareTupleQuery(QueryLanguage.SPARQL, sparql).evaluate(); try { int i = 0; while (result.hasNext()) { final BindingSet bs = result.next(); i++; if (log.isDebugEnabled()) { log.debug(bs); } } assertEquals(6, i); } finally { result.close(); } } { final String sparql = "select ?s ?p ?o ?action ?time \n" + "where { \n" + // " service bd:history { \n" + // " << ?s <:p> ?o >> <:removed> ?time . \n" + // " } \n" + " bind(<< ?s <:p> ?o >> as ?sid) . \n" + " hint:Prior hint:history true . \n" + " ?sid <blaze:history:removed> ?time . \n" + "}"; final TupleQueryResult result = cxn.prepareTupleQuery(QueryLanguage.SPARQL, sparql).evaluate(); try { int i = 0; while (result.hasNext()) { final BindingSet bs = result.next(); i++; if (log.isDebugEnabled()) { log.debug(bs); } } assertEquals(2, i); } finally { result.close(); } } } finally { if (cxn != null) cxn.close(); sail.__tearDownUnitTest(); } } /** * Test whether the RDRHistory can handle statements that are added * and removed in the same commit. */ public void testFullyRedundantEvents() throws Exception { BigdataSailRepositoryConnection cxn = null; final BigdataSail sail = getSail(getProperties()); try { sail.initialize(); final BigdataSailRepository repo = new BigdataSailRepository(sail); cxn = (BigdataSailRepositoryConnection) repo.getConnection(); final BigdataValueFactory vf = (BigdataValueFactory) sail .getValueFactory(); final URI s = vf.createURI(":s"); final URI p = vf.createURI(":p"); final Literal o = vf.createLiteral("foo"); final BigdataStatement stmt = vf.createStatement(s, p, o); final BigdataBNode sid = vf.createBNode(stmt); cxn.add(stmt); cxn.commit(); assertEquals(1, cxn.getTripleStore().getAccessPath(sid, null, null).rangeCount(false)); cxn.remove(stmt); cxn.add(stmt); cxn.commit(); assertEquals(1, cxn.getTripleStore().getAccessPath(sid, null, null).rangeCount(false)); } finally { if (cxn != null) cxn.close(); sail.__tearDownUnitTest(); } } /** * Test whether the RDRHistory can handle statements that are added * and removed in the same commit. */ public void testPartiallyRedundantEvents() throws Exception { BigdataSailRepositoryConnection cxn = null; final BigdataSail sail = getSail(getProperties()); try { sail.initialize(); final BigdataSailRepository repo = new BigdataSailRepository(sail); cxn = (BigdataSailRepositoryConnection) repo.getConnection(); final BigdataValueFactory vf = (BigdataValueFactory) sail .getValueFactory(); final URI s = vf.createURI(":s"); final URI p = vf.createURI(":p"); final Literal o = vf.createLiteral("foo"); final Literal bar = vf.createLiteral("bar"); final BigdataStatement stmt = vf.createStatement(s, p, o); final BigdataStatement stmt2 = vf.createStatement(s, p, bar); final BigdataBNode sid = vf.createBNode(stmt); final BigdataBNode sid2 = vf.createBNode(stmt2); cxn.add(stmt); cxn.commit(); assertEquals(1, cxn.getTripleStore().getAccessPath(sid, null, null).rangeCount(false)); cxn.remove(stmt); cxn.add(stmt); cxn.add(stmt2); cxn.commit(); assertEquals(1, cxn.getTripleStore().getAccessPath(sid, null, null).rangeCount(false)); assertEquals(1, cxn.getTripleStore().getAccessPath(sid2, null, null).rangeCount(false)); } finally { if (cxn != null) cxn.close(); sail.__tearDownUnitTest(); } } public static class CustomRDRHistory extends RDRHistory { public CustomRDRHistory(AbstractTripleStore database) { super(database); } /** * Only accept stmts where isLiteral(stmt.o) */ @Override protected boolean accept(final IChangeRecord record) { return record.getStatement().o().isLiteral(); } } }