You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2017/10/03 19:34:04 UTC

[18/65] [abbrv] jena git commit: JENA-1397: Rename java packages

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
new file mode 100644
index 0000000..1e081f3
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver;
+
+import java.util.function.Predicate;
+
+import org.apache.jena.atlas.lib.tuple.Tuple ;
+import org.apache.jena.graph.Graph ;
+import org.apache.jena.sparql.core.BasicPattern ;
+import org.apache.jena.sparql.engine.ExecutionContext ;
+import org.apache.jena.sparql.engine.QueryIterator ;
+import org.apache.jena.sparql.engine.main.StageGenerator ;
+import org.apache.jena.tdb2.store.GraphTDB;
+import org.apache.jena.tdb2.store.NodeId;
+
+/** Execute TDB requests directly -- no reordering
+ *  Using OpExecutor is preferred.
+ */ 
+public class StageGeneratorDirectTDB implements StageGenerator
+{
+    // Using OpExecutor is preferred.
+    StageGenerator above = null ;
+    
+    public StageGeneratorDirectTDB(StageGenerator original)
+    {
+        above = original ;
+    }
+    
+    @Override
+    public QueryIterator execute(BasicPattern pattern, QueryIterator input, ExecutionContext execCxt)
+    {
+        // --- In case this isn't for TDB
+        Graph g = execCxt.getActiveGraph() ;
+        
+        if ( ! ( g instanceof GraphTDB ) )
+            // Not us - bounce up the StageGenerator chain
+            return above.execute(pattern, input, execCxt) ;
+        GraphTDB graph = (GraphTDB)g ;
+        Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()) ;
+        return SolverLib.execute(graph, pattern, input, filter, execCxt) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java
new file mode 100644
index 0000000..99d3d06
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver;
+
+import java.util.Iterator ;
+import java.util.List ;
+import java.util.function.Function ;
+import java.util.function.Predicate ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.iterator.RepeatApplyIterator ;
+import org.apache.jena.atlas.lib.tuple.Tuple ;
+import org.apache.jena.atlas.lib.tuple.TupleFactory ;
+import org.apache.jena.graph.Node ;
+import org.apache.jena.sparql.core.Var ;
+import org.apache.jena.sparql.engine.ExecutionContext ;
+import org.apache.jena.tdb2.store.NodeId;
+import org.apache.jena.tdb2.store.nodetable.NodeTable;
+import org.apache.jena.tdb2.store.nodetupletable.NodeTupleTable;
+
+public class StageMatchTuple extends RepeatApplyIterator<BindingNodeId>
+{
+    private final NodeTupleTable nodeTupleTable ;
+    private final Tuple<Node> patternTuple ;
+
+    private final ExecutionContext execCxt ;
+    private boolean anyGraphs ;
+    private Predicate<Tuple<NodeId>> filter ;
+
+    public StageMatchTuple(NodeTupleTable nodeTupleTable, Iterator<BindingNodeId> input, 
+                            Tuple<Node> tuple, boolean anyGraphs, 
+                            Predicate<Tuple<NodeId>> filter, 
+                            ExecutionContext execCxt)
+    {
+        super(input) ;
+        this.filter = filter ;
+        this.nodeTupleTable = nodeTupleTable ; 
+        this.patternTuple = tuple ;
+        this.execCxt = execCxt ;
+        this.anyGraphs = anyGraphs ; 
+    }
+
+    /** Prepare a pattern (tuple of nodes), and an existing binding of NodeId, into NodeIds and Variables. 
+     *  A variable in the pattern is replaced by its binding or null in the Nodeids.
+     *  A variable that is not bound by the binding is placed in the var array.
+     *  Return false if preparation detechs the pattern can not match. 
+     */
+    public static boolean prepare(NodeTable nodeTable, Tuple<Node> patternTuple, BindingNodeId input, NodeId ids[], Var[] var)
+    {
+        // Process the Node to NodeId conversion ourselves because
+        // we wish to abort if an unknown node is seen.
+        for ( int i = 0 ; i < patternTuple.len() ; i++ )
+        {
+            Node n = patternTuple.get(i) ;
+            // Substitution and turning into NodeIds
+            // Variables unsubstituted are null NodeIds
+            NodeId nId = idFor(nodeTable, input, n) ;
+            if ( NodeId.isDoesNotExist(nId) )
+                return false;
+            ids[i] = nId ;
+            if ( nId == null )
+                var[i] = asVar(n) ;
+        }
+        return true ;
+    }
+    
+    @Override
+    protected Iterator<BindingNodeId> makeNextStage(final BindingNodeId input)
+    {
+        // ---- Convert to NodeIds 
+        NodeId ids[] = new NodeId[patternTuple.len()] ;
+        // Variables for this tuple after subsitution
+        final Var[] var = new Var[patternTuple.len()] ;
+
+        boolean b = prepare(nodeTupleTable.getNodeTable(), patternTuple, input, ids, var) ;
+        if ( !b )
+            // Short cut - known unknown NodeId
+            return Iter.nullIterator(); 
+        
+        Iterator<Tuple<NodeId>> iterMatches = nodeTupleTable.find(TupleFactory.create(ids)) ;  
+        
+        // ** Allow a triple or quad filter here.
+        if ( filter != null )
+            iterMatches = Iter.filter(iterMatches, filter) ;
+        
+        // If we want to reduce to RDF semantics over quads,
+        // we need to reduce the quads to unique triples. 
+        // We do that by having the graph slot as "any", then running
+        // through a distinct-ifier. 
+        // Assumes quads are GSPO - zaps the first slot.
+        // Assumes that tuples are not shared.
+        if ( anyGraphs )
+        {
+            iterMatches = Iter.map(iterMatches, quadsToAnyTriples) ;
+            //Guaranteed 
+            //iterMatches = Iter.distinct(iterMatches) ;
+            
+            // This depends on the way indexes are chosen and
+            // the indexing pattern. It assumes that the index 
+            // chosen ends in G so same triples are adjacent 
+            // in a union query.
+            // 
+            // If any slot is defined, then the index will be X??G.
+            // If no slot is defined, then the index will be ???G.
+            // But the  TupleTable
+            //  See TupleTable.scanAllIndex that ensures the latter.
+            //  No G part way through.
+            iterMatches = Iter.distinctAdjacent(iterMatches) ;
+        }
+        
+        // Map Tuple<NodeId> to BindingNodeId
+        Function<Tuple<NodeId>, BindingNodeId> binder = tuple -> 
+            {
+                BindingNodeId output = new BindingNodeId(input) ;
+                for ( int i = 0 ; i < var.length ; i++ )
+                {
+                    Var v = var[i] ;
+                    if ( v == null )
+                        continue ;
+                    NodeId id = tuple.get(i) ;
+                    if ( reject(output, v, id) )
+                        return null ;
+                    output.put(v, id) ;
+                }
+                return output ;
+        } ;
+        
+        return Iter.iter(iterMatches).map(binder).removeNulls() ;
+    }
+    
+    private static Iterator<Tuple<NodeId>> print(Iterator<Tuple<NodeId>> iter)
+    {
+        if ( ! iter.hasNext() )
+            System.err.println("<empty>") ;
+        else
+        {
+            List<Tuple<NodeId>> r = Iter.toList(iter) ;
+            String str = Iter.asString(r, "\n") ;
+            System.err.println(str) ;
+            // Reset iter
+            iter = Iter.iter(r) ;
+        }
+        return iter ;
+    }
+    
+    private static boolean reject(BindingNodeId output , Var var, NodeId value)
+    {
+        if ( ! output.containsKey(var) )
+            return false ;
+        
+        if ( output.get(var).equals(value) )
+            return false ;
+
+        return true ;
+    }
+    
+    private static Var asVar(Node node)
+    {
+        if ( Var.isVar(node) )
+            return Var.alloc(node) ;
+        return null ;
+    }
+
+    /** Return null for variables, and for nodes, the node id or NodeDoesNotExist */
+    private static NodeId idFor(NodeTable nodeTable, BindingNodeId input, Node node)
+    {
+        if ( Var.isVar(node) )
+        {
+            NodeId n = input.get((Var.alloc(node))) ;
+            // Bound to NodeId or null. 
+            return n ;
+        } 
+        // May return NodeId.NodeDoesNotExist which must not be null. 
+        return nodeTable.getNodeIdForNode(node) ;
+    }
+    
+    private static Function<Tuple<NodeId>, Tuple<NodeId>> quadsToAnyTriples = item -> {
+        return TupleFactory.create4(NodeId.NodeIdAny, item.get(1), item.get(2), item.get(3) ) ;
+    } ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/Stats.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/Stats.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/Stats.java
new file mode 100644
index 0000000..6897847
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/Stats.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver.stats;
+
+import static org.apache.jena.sparql.sse.Item.addPair ;
+import static org.apache.jena.sparql.sse.Item.createTagged ;
+
+import java.io.BufferedOutputStream ;
+import java.io.FileOutputStream ;
+import java.io.IOException ;
+import java.io.OutputStream ;
+import java.util.Iterator ;
+import java.util.Map ;
+import java.util.Map.Entry ;
+
+import org.apache.jena.atlas.lib.DateTimeUtils ;
+import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.graph.Graph ;
+import org.apache.jena.graph.Node ;
+import org.apache.jena.graph.Triple ;
+import org.apache.jena.sparql.engine.optimizer.StatsMatcher ;
+import org.apache.jena.sparql.graph.NodeConst ;
+import org.apache.jena.sparql.sse.Item ;
+import org.apache.jena.sparql.sse.ItemList ;
+import org.apache.jena.sparql.sse.ItemWriter ;
+import org.apache.jena.sparql.util.NodeFactoryExtra ;
+
+public class Stats
+{
+    static Item ZERO = Item.createNode(NodeFactoryExtra.intToNode(0)) ;
+
+    /** Write statistics */
+    static public void write(String filename, StatsResults stats)
+    {
+        write(filename, stats.getPredicates(), stats.getTypes(), stats.getCount()) ;
+    }
+    
+    /** Write statistics */
+    static public void write(OutputStream output, StatsResults stats)
+    {
+        write(output, stats.getPredicates(), stats.getTypes(), stats.getCount()) ;
+    }
+    
+    static private void write(String filename, Map<Node, Integer> predicateStats, Map<Node, Integer> typeStats, long statsTotal)
+    {
+        // Write out the stats
+        try (OutputStream statsOut = new BufferedOutputStream(new FileOutputStream(filename))) {
+            write(statsOut, predicateStats, typeStats, statsTotal) ;
+        } catch (IOException ex)
+        { Log.warn(Stats.class, "Problem when writing stats file", ex) ; }
+    }
+    
+    static private void write(OutputStream output, Map<Node, Integer> predicateStats, Map<Node, Integer> typeStats, long statsTotal)
+    {
+        Item item = format(predicateStats, typeStats, statsTotal) ;
+        ItemWriter.write(output, item) ;
+    }
+    
+
+    /** Gather statistics, any graph */
+    public static StatsCollector gather(Graph graph)
+    {
+        StatsCollector stats = new StatsCollector() ;
+    
+        Iterator<Triple> iter = graph.find(Node.ANY, Node.ANY, Node.ANY) ;
+        for ( ; iter.hasNext() ; )
+        {
+            Triple t = iter.next();
+            stats.record(null, t.getSubject(), t.getPredicate(), t.getObject()) ;
+        }
+        
+        return stats ;
+    }
+
+    public static Item format(StatsResults stats)
+    {
+        return format(stats.getPredicates(), stats.getTypes(), stats.getCount()) ;
+    }
+    
+    private static Item format(Map<Node, Integer> predicates, Map<Node, Integer> types, long count)
+    {
+        Item stats = Item.createList() ;
+        ItemList statsList = stats.getList() ;
+        statsList.add("stats") ;
+
+        Item meta = createTagged(StatsMatcher.META) ;
+        addPair(meta.getList(), "timestamp", NodeFactoryExtra.nowAsDateTime()) ;
+        addPair(meta.getList(), "run@",  DateTimeUtils.nowAsString()) ;
+        if ( count >= 0 )
+            addPair(meta.getList(), StatsMatcher.COUNT, NodeFactoryExtra.intToNode((int)count)) ;
+        statsList.add(meta) ;
+        
+        for ( Entry<Node, Integer> entry : types.entrySet() )
+        {
+            Node type = entry.getKey() ;
+            addTypeTriple(statsList, type, NodeFactoryExtra.intToNode(entry.getValue()) ) ;
+        }
+        
+        for ( Entry<Node, Integer> entry : predicates.entrySet() )
+        {
+            Node node = entry.getKey() ;
+            // Skip these - they just clog things up!
+            if ( node.getURI().startsWith("http://www.w3.org/1999/02/22-rdf-syntax-ns#_") )
+                continue ;
+            addPair(statsList, node, NodeFactoryExtra.intToNode(entry.getValue())) ;
+        }
+        
+        // Add a default rule.
+        addPair(statsList, StatsMatcher.OTHER, ZERO) ;
+        
+        return stats ;
+    }
+
+    private static void addTypeTriple(ItemList statsList, Node type, Node intCount)
+    {
+        ItemList triple = new ItemList() ;
+        triple.add("VAR") ;
+        triple.add(NodeConst.nodeRDFType) ;
+        triple.add(type) ;
+        addPair(statsList, Item.createList(triple), Item.createNode(intCount)) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollector.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollector.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollector.java
new file mode 100644
index 0000000..a751bb8
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollector.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver.stats;
+
+import java.util.Map ;
+
+import org.apache.jena.graph.Node ;
+import org.apache.jena.sparql.graph.NodeConst ;
+
+/** Statistics collector, general purpose, uses Nodes */
+public class StatsCollector extends StatsCollectorBase<Node>
+{
+    public StatsCollector() { super(NodeConst.nodeRDFType) ; }
+
+    @Override
+    protected Map<Node, Integer> convert(Map<Node, Integer> map)
+    {
+        return map ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorBase.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorBase.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorBase.java
new file mode 100644
index 0000000..3de508e
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorBase.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver.stats;
+
+
+import java.util.HashMap ;
+import java.util.Map ;
+
+import org.apache.jena.graph.Node ;
+
+/** Statistics collector, general purpose */
+abstract class StatsCollectorBase<T>
+{
+    private long count = 0 ;
+    private Map<T, Integer> predicates = new HashMap<>(10000) ;
+    private Map<T, Integer> types = new HashMap<>(10000) ;
+    private T typeTrigger ;
+    
+    protected StatsCollectorBase(T typeTrigger)
+    {
+        this.typeTrigger = typeTrigger ;
+    }
+
+    public void record(T g, T s, T p, T o)
+    {
+        count++ ;
+		predicates.put(p, predicates.getOrDefault(p, 0) + 1);
+        if ( typeTrigger != null && typeTrigger.equals(p) )
+        		types.put(o, types.getOrDefault(o, 0) + 1);
+    }
+
+    protected abstract Map<Node, Integer> convert(Map<T, Integer> map) ;
+    
+    public StatsResults results()
+    {
+        return new StatsResults(convert(predicates), convert(types), count) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorNodeId.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorNodeId.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorNodeId.java
new file mode 100644
index 0000000..c4b1b9c
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsCollectorNodeId.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver.stats;
+
+import java.util.HashMap ;
+import java.util.Map ;
+
+import org.apache.jena.graph.Node ;
+import org.apache.jena.sparql.graph.NodeConst ;
+import org.apache.jena.tdb2.store.NodeId;
+import org.apache.jena.tdb2.store.nodetable.NodeTable;
+
+/** Statistics collector, aggregates based on NodeId */
+public class StatsCollectorNodeId extends StatsCollectorBase<NodeId>
+{
+    private NodeTable nodeTable ;
+    
+    public StatsCollectorNodeId(NodeTable nodeTable)
+    {
+        super(findRDFType(nodeTable)) ;
+        this.nodeTable = nodeTable ;
+    }
+    
+    private static NodeId findRDFType(NodeTable nodeTable2)
+    {
+        return nodeTable2.getAllocateNodeId(NodeConst.nodeRDFType) ;
+    }
+
+    @Override
+    protected Map<Node, Integer> convert(Map<NodeId, Integer> stats)
+    {
+        Map<Node, Integer> statsNodes = new HashMap<>(1000) ;
+        for ( NodeId p : stats.keySet() )
+        {
+            Node n = nodeTable.getNodeForNodeId(p) ;
+            statsNodes.put(n, stats.get(p)) ;
+        }
+        return statsNodes ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsResults.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsResults.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsResults.java
new file mode 100644
index 0000000..d386dbe
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/stats/StatsResults.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.solver.stats;
+
+import java.util.Map ;
+
+import org.apache.jena.graph.Node ;
+
+/** Statistics collector */
+public class StatsResults 
+{
+    private final Map<Node, Integer> predicates ;
+    private final Map<Node, Integer> types ;
+    private final long count ;
+
+    StatsResults(Map<Node, Integer> predicates, Map<Node, Integer> types, long count)
+    {
+        this.count = count ;
+        this.predicates = predicates ;
+        this.types = types ;
+    }
+
+    public Map<Node, Integer> getPredicates()
+    {
+        return predicates ;
+    }
+
+    public Map<Node, Integer> getTypes()
+    {
+        return types ;
+    }
+
+    public long getCount()
+    {
+        return count ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphSwitchable.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphSwitchable.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphSwitchable.java
new file mode 100644
index 0000000..a9a2fc0
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphSwitchable.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.jena.atlas.lib.Cache ;
+import org.apache.jena.atlas.lib.CacheFactory ;
+import org.apache.jena.dboe.base.file.Location;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.shared.impl.PrefixMappingImpl;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.DatasetPrefixStorage ;
+
+final
+public class DatasetGraphSwitchable extends DatasetGraphWrapperTxn /* Until ARQ catches up with promote */ 
+{
+    // QueryEngineFactoryWrapper has a QueryEngineFactory that is always loaded that
+    // executes on the unwrapped DSG (recursively). Unwrapping is via getBase, calling
+    // getWrapped() which is implemented with get().
+    
+//    static { 
+//        // QueryEngineRegistry.addFactory(factory());
+//    }
+    
+    private final AtomicReference<DatasetGraph> dsgx = new AtomicReference<>();
+    // Null for in-memory datasets.
+    private final Path basePath;
+    private final Location location ;
+    
+    public DatasetGraphSwitchable(Path base, Location location, DatasetGraph dsg) {
+        // Don't use the slot in datasetGraphWrapper - use the AtomicReference
+        super(null) ;
+        dsgx.set(dsg);
+        this.basePath = base;
+        this.location = location; 
+    }
+
+    /** Is this {@code DatasetGraphSwitchable} just a holder for a {@code DatasetGraph}?
+     *  If so, it does not have a location on disk.
+     */
+    public boolean hasContainerPath() { return basePath != null; } 
+    
+    public Path getContainerPath() { return basePath; }
+    
+    public Location getLocation() { return location; }
+
+    /** The dataset to use for redirection - can be overridden.
+     *  It is also guaranteed that this is called only once per
+     *  delegated call.  Changes to the wrapped object can be
+     *  made based on that contract.
+     */
+    @Override
+    public DatasetGraph get() { return dsgx.get(); }
+
+    /** Set the base {@link DatasetGraph}.
+     * Returns the old value.
+     */ 
+    public DatasetGraph set(DatasetGraph dsg) { 
+        return dsgx.getAndSet(dsg);
+    }
+    
+    /** If and only if the current value is the given old value, set the base {@link DatasetGraph}  
+     * Returns true if a swap happened.
+     */ 
+    public boolean change(DatasetGraph oldDSG, DatasetGraph newDSG) { 
+        // No need to clear. ngCache.clear();
+        return dsgx.compareAndSet(oldDSG, newDSG);
+    }
+
+    private Graph dftGraph = GraphViewSwitchable.createDefaultGraph(this);
+    
+    @Override
+    public Graph getDefaultGraph() {
+        return dftGraph;
+    }
+    
+//    private Cache<Node, Graph> ngCache = CacheFactory.createCache(10);
+    private Cache<Node, Graph> ngCache = CacheFactory.createOneSlotCache();
+    
+    @Override
+    public Graph getGraph(Node gn) {
+        return ngCache.getOrFill(gn, ()->GraphViewSwitchable.createNamedGraph(this, gn));
+    }
+
+    // TDB2 specific.
+    // Does not cope with blank nodes.
+    // A PrefixMapping sending operations via the switchable.
+    private PrefixMapping prefixMapping(Node graphName) {
+        
+        String gn = (graphName == null) ? "" : graphName.getURI(); 
+        
+        return new PrefixMappingImpl() {
+            
+            DatasetPrefixStorage dps() {
+                return ((DatasetGraphTDB)dsgx.get()).getPrefixes();
+            }
+            
+            Graph graph() {
+                DatasetGraphTDB dsg = (DatasetGraphTDB)dsgx.get();
+                if ( gn == null )
+                    return dsg.getDefaultGraph();
+                else
+                    return dsg.getGraph(graphName);
+            }
+            
+            PrefixMapping prefixMapping() {
+                if ( gn == null )
+                    return dps().getPrefixMapping();
+                else
+                    return dps().getPrefixMapping(gn); 
+            }
+
+            @Override
+            protected void set(String prefix, String uri) {
+                dps().insertPrefix(gn, prefix, uri);
+                super.set(prefix, uri);
+            }
+
+            @Override
+            protected String get(String prefix) {
+                return dps().readPrefix(gn, prefix);
+            }
+
+            @Override
+            protected void remove(String prefix) {
+                dps().getPrefixMapping().removeNsPrefix(prefix);
+                super.remove(prefix);
+            }
+            
+            @Override
+            public Map<String, String> getNsPrefixMap() {
+                return prefixMapping().getNsPrefixMap();
+                //return graph().getPrefixMapping().getNsPrefixMap();
+            }
+        };
+    }
+    
+    //static { register() ; }
+    
+    
+//    static QueryEngineFactory factory() {
+//        return new QueryEngineFactory() {
+//            @Override
+//            public boolean accept(Op op, DatasetGraph dataset, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return false;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
+//                return f.accept(op, dataset, context);
+//            }
+//
+//            @Override
+//            public Plan create(Op op, DatasetGraph dataset, Binding inputBinding, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return null;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
+//                return f.create(op, dataset, inputBinding, context);
+//            }
+//
+//            private DatasetGraphSwitchable extract(DatasetGraph dataset) {
+//                if ( dataset instanceof DatasetGraphSwitchable )
+//                    return (DatasetGraphSwitchable)dataset;
+//                return null;
+//            }
+//
+//            @Override
+//            public boolean accept(Query query, DatasetGraph dataset, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return false;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
+//                return f.accept(query, dataset, context);
+//            }
+//
+//            @Override
+//            public Plan create(Query query, DatasetGraph dataset, Binding inputBinding, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return null;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
+//                return f.create(query, dataset, inputBinding, context);
+//            }
+//        };
+//    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java
new file mode 100644
index 0000000..fa2cd47
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import static org.apache.jena.sparql.util.graph.GraphUtils.triples2quads ;
+
+import java.util.Iterator ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.Closeable ;
+import org.apache.jena.atlas.lib.InternalErrorException ;
+import org.apache.jena.atlas.lib.Sync ;
+import org.apache.jena.atlas.lib.tuple.Tuple ;
+import org.apache.jena.dboe.base.file.Location;
+import org.apache.jena.dboe.transaction.TransactionalMonitor;
+import org.apache.jena.dboe.transaction.txn.Transaction;
+import org.apache.jena.dboe.transaction.txn.TransactionException;
+import org.apache.jena.dboe.transaction.txn.TransactionalSystem;
+import org.apache.jena.dboe.transaction.txn.TxnId;
+import org.apache.jena.graph.Graph ;
+import org.apache.jena.graph.GraphUtil ;
+import org.apache.jena.graph.Node ;
+import org.apache.jena.graph.Triple ;
+import org.apache.jena.query.ReadWrite ;
+import org.apache.jena.sparql.core.* ;
+import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
+import org.apache.jena.tdb2.TDBException;
+import org.apache.jena.tdb2.lib.NodeLib;
+import org.apache.jena.tdb2.setup.StoreParams;
+import org.apache.jena.tdb2.store.nodetupletable.NodeTupleTable;
+import org.apache.jena.tdb2.sys.StoreConnection;
+/** This is the class that provides creates a dataset over the storage via
+ *  TripleTable, QuadTable and prefixes.
+ *  This is the coordination of the storage.
+ *  @see DatasetGraphTxn for the Transaction form.
+ */
+final
+public class DatasetGraphTDB extends DatasetGraphTriplesQuads
+                             implements DatasetGraphTxn, Sync, Closeable
+{
+    private StorageTDB storage; 
+//    // SWITCHING.
+//    private TripleTable tripleTable ;
+//    private QuadTable quadTable ;
+//    private DatasetPrefixStorage prefixes ;
+//    private Location location ;
+//    private StoreParams storeParams ;
+//    // SWITCHING.
+    private TransactionalSystem txnSystem ;
+    private final ReorderTransformation transform ;
+    
+    private GraphTDB defaultGraphTDB ;
+    private final boolean checkForChange = false ;
+    private boolean closed = false ;
+
+    /** Application should not create a {@code DatasetGraphTDB} directly */
+    public DatasetGraphTDB(TransactionalSystem txnSystem, 
+                           TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes,
+                           ReorderTransformation transform, Location location, StoreParams params) {
+        reset(txnSystem, tripleTable, quadTable, prefixes, location, params) ;
+        this.transform = transform ;
+        this.defaultGraphTDB = getDefaultGraphTDB() ;
+    }
+
+    public void reset(TransactionalSystem txnSystem,
+                      TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes,
+                      Location location, StoreParams params) {
+//        this.tripleTable = tripleTable ;
+//        this.quadTable = quadTable ;
+//        this.location = location ;
+//        this.prefixes = prefixes ;
+//        this.storeParams = params ;
+        this.txnSystem = txnSystem ;
+        // XXX Threading?
+        // XXX (re)set transaction components in TransactionCoordinator?? 
+        this.storage = new StorageTDB(tripleTable, quadTable, prefixes, location, params);
+        this.defaultGraphTDB = getDefaultGraphTDB();
+    }
+    
+    public QuadTable getQuadTable()         { checkNotClosed(); return storage.quadTable; }
+    public TripleTable getTripleTable()     { checkNotClosed(); return storage.tripleTable; }
+
+    /** Low level manipulation. */
+    public StorageTDB getStorage()              { return storage; }
+    /** Low level manipulation. 
+     * <b>Do not use unless in exclusive mode.</b>
+     */
+    public void setStorage(StorageTDB storage)  { this.storage = storage; }
+    
+    @Override
+    protected Iterator<Quad> findInDftGraph(Node s, Node p, Node o) {
+        checkNotClosed() ;
+        return isolate(triples2quadsDftGraph(getTripleTable().find(s, p, o))) ;
+    }
+
+    @Override
+    protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p, Node o) {
+        checkNotClosed();
+        return isolate(getQuadTable().find(g, s, p, o));
+    }
+
+    @Override
+    protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
+        checkNotClosed();
+        return isolate(getQuadTable().find(Node.ANY, s, p, o));
+    }
+
+    protected Iterator<Quad> triples2quadsDftGraph(Iterator<Triple> iter)
+    { return isolate(triples2quads(Quad.defaultGraphIRI, iter)); }
+ 
+    private static final boolean CHECK_TXN = true; 
+    
+    private <T> Iterator<T> isolate(Iterator<T> iterator) {
+        if ( txnSystem.isInTransaction() ) {
+            TxnId txnId = txnSystem.getThreadTransaction().getTxnId();
+            // Add transaction protection.
+            return new IteratorTxnTracker<>(iterator, txnSystem, txnId);
+        }
+        // Risk the hidden arraylist is copied on growth.
+        return Iter.iterator(iterator);
+    }
+
+    @Override
+    protected void addToDftGraph(Node s, Node p, Node o) { 
+        checkNotClosed() ;
+        requireWriteTxn() ;
+        notifyAdd(null, s, p, o) ;
+        getTripleTable().add(s,p,o) ;
+    }
+
+    @Override
+    protected void addToNamedGraph(Node g, Node s, Node p, Node o) {
+        checkNotClosed() ;
+        requireWriteTxn() ;
+        notifyAdd(g, s, p, o) ;
+        getQuadTable().add(g, s, p, o) ; 
+    }
+
+    @Override
+    protected void deleteFromDftGraph(Node s, Node p, Node o) {
+        checkNotClosed() ;
+        requireWriteTxn() ;
+        notifyDelete(null, s, p, o) ;
+        getTripleTable().delete(s, p, o) ;
+    }
+
+    @Override
+    protected void deleteFromNamedGraph(Node g, Node s, Node p, Node o) {
+        checkNotClosed() ;
+        requireWriteTxn() ;
+        notifyDelete(g, s, p, o) ;
+        getQuadTable().delete(g, s, p, o) ;
+    }
+
+    // Promotion
+    private void requireWriteTxn() {
+        Transaction txn = txnSystem.getThreadTransaction() ;
+        if ( txn == null )
+            throw new TransactionException("Not in a transaction") ;
+            
+        if ( txn.isWriteTxn() )
+            return ;
+        // Transaction.promoteOrException
+        boolean b = txn.promote() ;
+        if ( !b )
+            throw new TransactionException("Can't write") ;
+    }
+
+    // TODO ?? Optimize by integrating with add/delete operations.
+    private final void notifyAdd(Node g, Node s, Node p, Node o) {
+        if ( monitor == null )
+            return ;
+        QuadAction action = QuadAction.ADD ;
+        if ( checkForChange ) {
+            if ( contains(g,s,p,o) )
+                action = QuadAction.NO_ADD ;
+        }
+        monitor.change(action, g, s, p, o);
+    }
+
+    private final void notifyDelete(Node g, Node s, Node p, Node o) {
+        if ( monitor == null )
+            return ;
+        QuadAction action = QuadAction.DELETE ;
+        if ( checkForChange ) {
+            if ( ! contains(g,s,p,o) )
+                action = QuadAction.NO_DELETE ;
+        }
+        monitor.change(action, g, s, p, o);
+    }
+    
+    /** No-op. There is no need to close datasets.
+     *  Use {@link StoreConnection#release(Location)}.
+     *  (Datasets can not be reopened on MS Windows). 
+     */
+    @Override
+    public void close() {
+        if ( closed )
+            return ;
+        closed = true ;
+    }
+    
+    private void checkNotClosed() {
+        if ( closed )
+            throw new TDBException("dataset closed") ;
+    }
+    
+    /** Release resources.
+     *  Do not call directly - this is called from StoreConnection.
+     *  Use {@link StoreConnection#release(Location)}. 
+     */
+    public void shutdown() {
+        close();
+        storage.tripleTable.close() ;
+        storage.quadTable.close() ;
+        storage.prefixes.close();
+        txnSystem.getTxnMgr().shutdown(); 
+    }
+    
+    @Override
+    // Empty graphs don't "exist" 
+    public boolean containsGraph(Node graphNode) {
+        checkNotClosed() ; 
+        if ( Quad.isDefaultGraphExplicit(graphNode) || Quad.isUnionGraph(graphNode)  )
+            return true ;
+        return _containsGraph(graphNode) ; 
+    }
+
+    private boolean _containsGraph(Node graphNode) {
+        // Have to look explicitly, which is a bit of a nuisance.
+        // But does not normally happen for GRAPH <g> because that's rewritten to quads.
+        // Only pattern with complex paths go via GRAPH. 
+        Iterator<Tuple<NodeId>> x = storage.quadTable.getNodeTupleTable().findAsNodeIds(graphNode, null, null, null) ;
+        if ( x == null )
+            return false ; 
+        boolean result = x.hasNext() ;
+        return result ;
+    }
+    
+    @Override
+    public void addGraph(Node graphName, Graph graph) {
+        checkNotClosed() ; 
+        removeGraph(graphName) ;
+        GraphUtil.addInto(getGraph(graphName), graph) ;
+    }
+
+    @Override
+    public final void removeGraph(Node graphName) {
+        checkNotClosed() ; 
+        deleteAny(graphName, Node.ANY, Node.ANY, Node.ANY) ;
+    }
+
+    @Override
+    public Graph getDefaultGraph() {
+        checkNotClosed() ; 
+        return new GraphTDB(this, null) ; 
+    }
+
+    public GraphTDB getDefaultGraphTDB() {
+        checkNotClosed();
+        return (GraphTDB)getDefaultGraph();
+    }
+
+    @Override
+    public Graph getGraph(Node graphNode) {
+        checkNotClosed();
+        return new GraphTDB(this, graphNode);
+    }
+
+    public GraphTDB getGraphTDB(Node graphNode) {
+        checkNotClosed();
+        return (GraphTDB)getGraph(graphNode);
+    }
+
+    public StoreParams getStoreParams() {
+        checkNotClosed();
+        return storage.storeParams;
+    }
+
+    public ReorderTransformation getReorderTransform() {
+        checkNotClosed();
+        return transform;
+    }
+
+    public DatasetPrefixStorage getPrefixes() {
+        checkNotClosed();
+        return storage.prefixes;
+    }
+
+    @Override
+    public Iterator<Node> listGraphNodes() {
+        checkNotClosed();
+        Iterator<Tuple<NodeId>> x = storage.quadTable.getNodeTupleTable().findAll();
+        Iterator<NodeId> z = Iter.iter(x).map(t -> t.get(0)).distinct();
+        return NodeLib.nodes(storage.quadTable.getNodeTupleTable().getNodeTable(), z);
+    }
+
+    @Override
+    public long size() {
+        checkNotClosed();
+        return Iter.count(listGraphNodes());
+    }
+
+    @Override
+    public boolean isEmpty() {
+        checkNotClosed();
+        return getTripleTable().isEmpty() && getQuadTable().isEmpty();
+    }
+
+    @Override
+    public void clear() {
+        checkNotClosed() ; 
+        // Leave the node table alone.
+        getTripleTable().clearTriples() ;
+        getQuadTable().clearQuads() ;
+    }
+    
+    public NodeTupleTable chooseNodeTupleTable(Node graphNode) {
+        checkNotClosed() ; 
+
+        if ( graphNode == null || Quad.isDefaultGraph(graphNode) )
+            return getTripleTable().getNodeTupleTable() ;
+        else
+            // Includes Node.ANY and union graph
+            return getQuadTable().getNodeTupleTable() ;
+    }
+    
+    private static final int sliceSize = 1000 ;
+    
+    @Override
+    public void deleteAny(Node g, Node s, Node p, Node o) {
+        // Delete in batches.
+        // That way, there is no active iterator when a delete
+        // from the indexes happens.
+        checkNotClosed() ;
+        
+        if ( monitor != null ) {
+            // Need to do by nodes because we will log the deletes.
+            super.deleteAny(g, s, p, o); 
+            return ;
+        }
+
+        // Not logging - do by working as NodeIds.
+        NodeTupleTable t = chooseNodeTupleTable(g) ;
+        @SuppressWarnings("unchecked")
+        Tuple<NodeId>[] array = (Tuple<NodeId>[])new Tuple<?>[sliceSize] ;
+
+        while (true) { // Convert/cache s,p,o?
+            // The Node Cache will catch these so don't worry unduely.
+            Iterator<Tuple<NodeId>> iter = null ;
+            if ( g == null )
+                iter = t.findAsNodeIds(s, p, o) ;
+            else
+                iter = t.findAsNodeIds(g, s, p, o) ;
+
+            if ( iter == null || ! iter.hasNext() )
+                return ;
+
+            // Get a slice
+            int len = 0 ;
+            for (; len < sliceSize; len++) {
+                if ( !iter.hasNext() )
+                    break ;
+                array[len] = iter.next() ;
+            }
+            
+            // Delete the NodeId Tuples
+            for (int i = 0; i < len; i++) {
+                t.getTupleTable().delete(array[i]) ;
+                array[i] = null ;
+            }
+            // Finished?
+            if ( len < sliceSize )
+                break ;
+        }
+    }
+    
+    public Location getLocation()       { return storage.location ; }
+
+    @Override
+    public void sync() {
+        checkNotClosed();
+        storage.tripleTable.sync();
+        storage.quadTable.sync();
+        storage.prefixes.sync();
+    }
+    
+    @Override
+    public void setDefaultGraph(Graph g) { 
+        throw new UnsupportedOperationException("Can't set default graph on a TDB-backed dataset") ;
+    }
+
+    @Override
+    public boolean isInTransaction() {
+        return txnSystem.isInTransaction() ;
+    }
+
+    // txnSystem with monitor?
+    @Override
+    public void begin(ReadWrite readWrite) {
+        if ( txnMonitor != null ) txnMonitor.startBegin(readWrite); 
+        txnSystem.begin(readWrite) ;
+        if ( txnMonitor != null ) txnMonitor.finishBegin(readWrite); 
+    }
+
+    @Override
+    public boolean promote() {
+        if ( txnMonitor != null ) txnMonitor.startPromote();
+        try { 
+            return txnSystem.promote() ;
+        } finally { if ( txnMonitor != null ) txnMonitor.finishPromote(); }
+    }
+
+    @Override
+    public void commit() {
+        if ( txnMonitor != null ) txnMonitor.startCommit();
+        txnSystem.commit() ;
+        if ( txnMonitor != null ) txnMonitor.finishCommit();  
+    }
+
+    @Override
+    public void abort() {
+        if ( txnMonitor != null ) txnMonitor.startAbort() ; 
+        txnSystem.abort() ;
+        if ( txnMonitor != null ) txnMonitor.finishAbort() ;  
+    }
+
+    @Override
+    public void end() {
+        if ( txnMonitor != null ) txnMonitor.startEnd(); 
+        txnSystem.end() ;
+        if ( txnMonitor != null ) txnMonitor.finishEnd(); 
+    }
+
+    public TransactionalSystem getTxnSystem() {
+        return txnSystem ;
+    }
+
+    // Watching changes (add, delete, deleteAny) 
+    
+    private DatasetChanges monitor = null ;
+    public void setMonitor(DatasetChanges changes) {
+        monitor = changes ;
+    }
+
+    public void removeMonitor(DatasetChanges changes) {
+        if ( monitor != changes )
+            throw new InternalErrorException() ;
+        monitor = null ;
+    }
+    
+    // Watching Transactional
+    
+    private TransactionalMonitor txnMonitor = null ;
+    public void setTransactionalMonitor(TransactionalMonitor changes) {
+        txnMonitor = changes ;
+    }
+
+    public void removeTransactionalMonitor(TransactionalMonitor changes) {
+        if ( txnMonitor != changes )
+            throw new InternalErrorException() ;
+        txnMonitor = null ;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTxn.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTxn.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTxn.java
new file mode 100644
index 0000000..a1354ea
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTxn.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import org.apache.jena.dboe.transaction.Transactional;
+import org.apache.jena.sparql.core.DatasetGraph ;
+
+/** Transaction representation of a DatasetGraph */ 
+public interface DatasetGraphTxn extends DatasetGraph, Transactional {
+    // Combines org.apache.jena.dboe.transaction.Transactional and org.apache.jena.sparql.core.Transactional.
+    //    o.a.j.dboe.transaction.Transactional add promote()
+    
+    @Override
+    public default boolean supportsTransactions()       { return true ; }
+    
+    @Override
+    public default boolean supportsTransactionAbort()   { return true ; }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphWrapperTxn.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphWrapperTxn.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphWrapperTxn.java
new file mode 100644
index 0000000..8022165
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphWrapperTxn.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import org.apache.jena.sparql.core.DatasetGraphWrapper ;
+
+public class DatasetGraphWrapperTxn extends DatasetGraphWrapper implements DatasetGraphTxn {
+
+    public DatasetGraphWrapperTxn(DatasetGraphTxn dsg) {
+        super(dsg) ;
+    }
+
+    @Override
+    public boolean promote() {
+        return ((DatasetGraphTxn)get()).promote() ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java
new file mode 100644
index 0000000..e1b7e7b
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import java.util.* ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.tuple.Tuple ;
+import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.dboe.base.record.RecordFactory;
+import org.apache.jena.graph.Node ;
+import org.apache.jena.graph.NodeFactory ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.sparql.core.DatasetPrefixStorage ;
+import org.apache.jena.tdb2.store.nodetupletable.NodeTupleTable;
+
+/**
+ * Dataset prefixes; a table of nodes with prefix-centric operations. The table
+ * is G-P-U where G is a graph name ("" is used for the default graph), P is a
+ * string (the prefix) and U is the IRI.
+ */
+public class DatasetPrefixesTDB implements DatasetPrefixStorage
+{
+    // Consider a cache - like PrefixMappingCache was - but needs to respect transactions.
+    // See getPrefixMapping.
+    
+    static final RecordFactory factory = new RecordFactory(3*NodeId.SIZE, 0) ;
+    static final String unamedGraphURI = "" ;
+    
+    private final NodeTupleTable nodeTupleTable ;
+    
+    public DatasetPrefixesTDB(NodeTupleTable nodeTupleTable) {
+        this.nodeTupleTable = nodeTupleTable ;
+    }
+    
+    @Override
+    public void loadPrefixMapping(String graphName, PrefixMapping pmap) {
+        Node g = NodeFactory.createURI(graphName) ; 
+        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, null, null) ;
+        iter.forEachRemaining(tuple->{
+            Node prefix = tuple.get(1)  ;
+            Node uri = tuple.get(2) ;
+            pmap.setNsPrefix(prefix.getLiteralLexicalForm(), uri.getURI()) ;
+        }) ;
+    }
+
+    @Override
+    public synchronized void insertPrefix(String graphName, String prefix, String uri) {
+        Node g = NodeFactory.createURI(graphName) ; 
+        Node p = NodeFactory.createLiteral(prefix) ; 
+        Node u = NodeFactory.createURI(uri) ;
+
+        nodeTupleTable.addRow(g,p,u) ;
+    }
+
+    @Override
+    public Set<String> graphNames() {
+        Iterator<Tuple<Node>> iter = nodeTupleTable.find((Node)null, null, null) ;
+        Set <String> x = new HashSet<>() ;
+        for ( ; iter.hasNext() ; )
+            x.add(iter.next().get(0).getURI()) ;
+        Iter.close(iter) ;
+        return x ;
+    }
+    
+    @Override
+    public synchronized String readPrefix(String graphName, String prefix) {
+        Node g = NodeFactory.createURI(graphName) ; 
+        Node p = NodeFactory.createLiteral(prefix) ; 
+        
+        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, p, null) ;
+        try {
+            if ( ! iter.hasNext() )
+                return null ;
+            Tuple<Node> t = iter.next() ;
+            Node uri = t.get(2) ;
+            return uri.getURI() ;
+        } finally { Iter.close(iter) ; } 
+    }
+
+    @Override
+    public synchronized String readByURI(String graphName, String uriStr) {
+        Node g = NodeFactory.createURI(graphName) ; 
+        Node u = NodeFactory.createURI(uriStr) ; 
+        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, null, u) ;
+        if ( ! iter.hasNext() )
+            return null ;
+        Node prefix = iter.next().get(1) ;
+        Iter.close(iter) ;
+        return prefix.getLiteralLexicalForm()  ;
+    }
+
+    @Override
+    public synchronized Map<String, String> readPrefixMap(String graphName) {
+        Map<String, String> map = new HashMap<>() ;
+        Node g = NodeFactory.createURI(graphName) ;
+        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, null, null) ;
+        for ( ; iter.hasNext() ; ) {
+            try {
+                Tuple<Node> t = iter.next() ;
+                String prefix = t.get(1).getLiteralLexicalForm() ;
+                String uri = t.get(2).getURI() ;
+                map.put(prefix, uri) ;
+            }
+            catch (Exception ex) {
+                Log.warn(this, "Mangled prefix map: graph name=" + graphName, ex) ;
+            }
+        }
+        Iter.close(iter) ;
+        return map ;
+    }
+    
+    @Override
+    public void removeFromPrefixMap(String graphName, String prefix) {
+        Node g = NodeFactory.createURI(graphName) ; 
+        Node p = NodeFactory.createLiteral(prefix) ;
+        removeAll(g, p, null) ;
+    }
+
+    @Override
+    public void removeAllFromPrefixMap(String graphName) {
+        Node g = NodeFactory.createURI(graphName) ; 
+        removeAll(g, null, null) ;
+    }
+
+    /** Remove by pattern */
+    private synchronized void removeAll(Node g, Node p, Node uri) {
+        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, p, uri) ;
+        List<Tuple<Node>> list = Iter.toList(iter) ;    // Materialize.
+        Iter.close(iter) ;
+        for ( Tuple<Node> tuple : list )
+            nodeTupleTable.deleteRow(tuple.get(0), tuple.get(1), tuple.get(2)) ; 
+    }
+    
+    public NodeTupleTable getNodeTupleTable()  { return nodeTupleTable ; }
+    
+    /** Return a PrefixMapping for the unamed graph */
+    @Override
+    public PrefixMapping getPrefixMapping()
+    { return getPrefixMapping(unamedGraphURI) ; }
+
+    /** Return a PrefixMapping for a named graph */
+    @Override
+    public PrefixMapping getPrefixMapping(String graphName) {
+        PrefixMapping pm = new GraphPrefixesProjectionTDB(graphName, this) ;
+        return pm ;
+    }
+    
+    @Override
+    public void close() {
+        nodeTupleTable.close() ;
+    }
+
+    @Override
+    public void sync() {
+        nodeTupleTable.sync() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphPrefixesProjectionTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphPrefixesProjectionTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphPrefixesProjectionTDB.java
new file mode 100644
index 0000000..fc805b0
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphPrefixesProjectionTDB.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import java.util.Map ;
+import java.util.Map.Entry ;
+import java.util.Optional ;
+
+import org.apache.jena.rdf.model.impl.Util ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.sparql.core.DatasetPrefixStorage ;
+
+public class GraphPrefixesProjectionTDB implements PrefixMapping {
+    // Despite the name "TDB" this is general replacement for
+    // PrefixMapping over DatasetPrefixStorage.
+    private final String graphName ;
+    private final DatasetPrefixStorage prefixes ; 
+
+    public GraphPrefixesProjectionTDB(String graphName, DatasetPrefixStorage prefixes)
+    { 
+        this.graphName = graphName ;
+        this.prefixes = prefixes ;
+    }
+
+    @Override
+    public PrefixMapping setNsPrefix(String prefix, String uri) {
+        prefixes.insertPrefix(graphName, prefix, uri); 
+        return this ;
+    }
+
+    @Override
+    public PrefixMapping removeNsPrefix(String prefix) {
+        prefixes.removeFromPrefixMap(graphName, prefix);
+        return this ;
+    }
+
+    @Override
+    public PrefixMapping clearNsPrefixMap() {
+        prefixes.removeAllFromPrefixMap(graphName);
+        return this ;
+    }
+
+    @Override
+    public PrefixMapping setNsPrefixes(PrefixMapping other) {
+        setNsPrefixes(other.getNsPrefixMap()) ;
+        return this ;
+    }
+
+    @Override
+    public PrefixMapping setNsPrefixes(Map<String, String> map) {
+        map.entrySet().forEach(entry->{
+            setNsPrefix(entry.getKey(), entry.getValue()) ;
+        });
+        return this ;
+    }
+
+    @Override
+    public PrefixMapping withDefaultMappings(PrefixMapping other) {
+        other.getNsPrefixMap().entrySet().forEach(entry->{
+            String prefix = entry.getKey() ;
+            String uri = entry.getValue();
+            if (getNsPrefixURI( prefix ) == null && getNsURIPrefix( uri ) == null)
+                setNsPrefix( prefix, uri );
+        }) ;
+        return this ;
+    }
+
+    @Override
+    public String getNsPrefixURI(String prefix) {
+        return prefixes.readPrefix(graphName, prefix) ;
+    }
+
+    @Override
+    public String getNsURIPrefix(String uri) {
+        return prefixes.readByURI(graphName, uri) ;
+    }
+
+    @Override
+    public Map<String, String> getNsPrefixMap() {
+        return prefixes.readPrefixMap(graphName) ;
+    }
+
+    // From PrefixMappingImpl
+    @Override
+    public String expandPrefix(String prefixed) {
+        {
+            int colon = prefixed.indexOf(':') ;
+            if ( colon < 0 )
+                return prefixed ;
+            else {
+                String prefix = prefixed.substring(0, colon) ;
+                String uri = prefixes.readPrefix(graphName, prefix) ;
+                return uri == null ? prefixed : uri + prefixed.substring(colon + 1) ;
+            }
+        }
+    }
+
+    @Override
+    public String qnameFor(String uri) {
+        int split = Util.splitNamespaceXML(uri) ;
+        String ns = uri.substring(0, split); 
+        String local = uri.substring(split) ;
+        if ( local.equals("") )
+            return null ;
+        String prefix = prefixes.readByURI(graphName, ns) ;
+        return prefix == null ? null : prefix + ":" + local ;
+    }
+    
+    @Override
+    public String shortForm(String uri) {
+        Optional<Entry<String, String>> e = findMapping(uri, true) ;
+        if ( ! e.isPresent() )
+            return uri ;
+        return e.get().getKey() + ":" + uri.substring((e.get().getValue()).length()) ;
+    }
+
+    // Do better?
+    
+    @Override
+    public boolean hasNoMappings() { return getNsPrefixMap().isEmpty() ; }
+    
+    @Override
+    public int numPrefixes() {
+        return getNsPrefixMap().size() ;
+    }
+
+    private Optional<Entry<String, String>> findMapping( String uri, boolean partial )
+    {
+        return getNsPrefixMap().entrySet().stream().sequential().filter(e->{
+            String ss = e.getValue();
+            if (uri.startsWith( ss ) && (partial || ss.length() == uri.length())) 
+                return true;
+            return false ;
+        }).findFirst() ;
+    }    
+    
+    @Override
+    public PrefixMapping lock() {
+        return this ;
+    }
+
+    @Override
+    public boolean samePrefixMappingAs(PrefixMapping other) {
+        return this.getNsPrefixMap().equals(other.getNsPrefixMap()) ;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphTDB.java
new file mode 100644
index 0000000..15d2508
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphTDB.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store ;
+
+import java.util.Iterator ;
+import java.util.function.Function;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.Closeable ;
+import org.apache.jena.atlas.lib.Sync ;
+import org.apache.jena.atlas.lib.tuple.Tuple ;
+import org.apache.jena.atlas.lib.tuple.TupleFactory ;
+import org.apache.jena.graph.* ;
+import org.apache.jena.riot.other.GLib ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.sparql.core.GraphView ;
+import org.apache.jena.sparql.core.Quad ;
+import org.apache.jena.tdb2.TDBException;
+import org.apache.jena.tdb2.graph.TransactionHandlerTDB;
+import org.apache.jena.tdb2.store.nodetupletable.NodeTupleTable;
+import org.apache.jena.util.iterator.ExtendedIterator ;
+import org.apache.jena.util.iterator.WrappedIterator ;
+
+/**
+ * General operations for TDB graphs (free-standing graph, default graph and
+ * named graphs)
+ */
+public class GraphTDB extends GraphView implements Closeable, Sync {
+    private final TransactionHandler transactionHandler = new TransactionHandlerTDB(this) ;
+
+    // Switch this to DatasetGraphTransaction
+    private final DatasetGraphTDB    dataset ;
+
+    public GraphTDB(DatasetGraphTDB dataset, Node graphName) {
+        super(dataset, graphName) ;
+        this.dataset = dataset ;
+    }
+
+    /** get the current TDB dataset graph - changes for transactions */
+    public DatasetGraphTDB getDSG() {
+        return dataset ;
+    }
+
+    /** The NodeTupleTable for this graph */
+    public NodeTupleTable getNodeTupleTable() {
+        return getDSG().chooseNodeTupleTable(getGraphName()) ;
+    }
+
+    @Override
+    protected PrefixMapping createPrefixMapping() {
+        if ( isDefaultGraph() )
+            return getDSG().getPrefixes().getPrefixMapping() ;
+        if ( isUnionGraph() )
+            return getDSG().getPrefixes().getPrefixMapping() ;
+        return getDSG().getPrefixes().getPrefixMapping(getGraphName().getURI()) ;
+    }
+
+    @Override
+    public final void sync() {
+        dataset.sync() ;
+    }
+
+    @Override
+    final public void close() {
+        sync() ;
+        // Don't close the GraphBase.
+        //super.close() ;
+    }
+
+    protected static ExtendedIterator<Triple> graphBaseFindDft(DatasetGraphTDB dataset, Triple triple) {
+        Iterator<Quad> iterQuads = dataset.find(Quad.defaultGraphIRI, triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
+        if ( iterQuads == null )
+            return org.apache.jena.util.iterator.NullIterator.instance() ;
+        // Can't be duplicates - fixed graph node..
+        Iterator<Triple> iterTriples = projectQuadsToTriples(Quad.defaultGraphIRI, iterQuads) ;
+        return WrappedIterator.createNoRemove(iterTriples) ;
+    }
+
+    protected static ExtendedIterator<Triple> graphBaseFindNG(DatasetGraphTDB dataset, Node graphNode, Triple m) {
+        Node gn = graphNode ;
+        // Explicitly named union graph.
+        if ( isUnionGraph(gn) )
+            gn = Node.ANY ;
+
+        Iterator<Quad> iter = dataset.getQuadTable().find(gn, m.getMatchSubject(), m.getMatchPredicate(), m.getMatchObject()) ;
+        if ( iter == null )
+            return org.apache.jena.util.iterator.NullIterator.instance() ;
+
+        Iterator<Triple> iterTriples = projectQuadsToTriples((gn == Node.ANY ? null : gn), iter) ;
+
+        if ( gn == Node.ANY )
+            iterTriples = Iter.distinct(iterTriples) ;
+        return WrappedIterator.createNoRemove(iterTriples) ;
+    }
+
+    @Override
+    protected ExtendedIterator<Triple> graphUnionFind(Node s, Node p, Node o) {
+        Node g = Quad.unionGraph ;
+        Iterator<Quad> iterQuads = getDSG().find(g, s, p, o) ;
+        Iterator<Triple> iter = GLib.quads2triples(iterQuads) ;
+        // Suppress duplicates after projecting to triples.
+        // TDB guarantees that duplicates are adjacent.
+        // See SolverLib.
+        iter = Iter.distinctAdjacent(iter) ;
+        return WrappedIterator.createNoRemove(iter) ;
+    }
+
+    @Override
+    protected final int graphBaseSize() {
+        if ( isDefaultGraph() )
+            return (int)getNodeTupleTable().size() ;
+
+        Node gn = getGraphName() ;
+        boolean unionGraph = isUnionGraph(gn) ;
+        gn = unionGraph ? Node.ANY : gn ;
+        Iterator<Tuple<NodeId>> iter = getDSG().getQuadTable().getNodeTupleTable().findAsNodeIds(gn, null, null, null) ;
+        if ( unionGraph ) {
+            iter = Iter.map(iter, project4TupleTo3Tuple) ;
+            iter = Iter.distinctAdjacent(iter) ;
+        }
+        return (int)Iter.count(iter) ;
+    }
+
+	private static Function<Tuple<NodeId>, Tuple<NodeId>> project4TupleTo3Tuple = item -> {
+		if (item.len() != 4)
+			throw new TDBException("Expected a Tuple of 4, got: " + item);
+		return TupleFactory.tuple(item.get(1), item.get(2), item.get(3));
+	};
+	
+	private static Iterator<Triple> projectQuadsToTriples(Node graphNode, Iterator<Quad> iter) {
+	    // Checking.
+        Function<Quad, Triple> f = (q) -> {
+            if ( graphNode != null && !q.getGraph().equals(graphNode) )
+                throw new InternalError("projectQuadsToTriples: Quads from unexpected graph (expected=" + graphNode + ", got=" + q.getGraph() + ")");
+            return q.asTriple();
+        };
+        // Without.
+        //Function<Quad, Triple> f = (q) -> q.asTriple();
+	    return Iter.map(iter, f);
+	}
+
+    @Override
+    public TransactionHandler getTransactionHandler() {
+        return transactionHandler ;
+    }
+
+    @Override
+    public void clear() {
+        dataset.deleteAny(getGraphName(), Node.ANY, Node.ANY, Node.ANY) ;
+        getEventManager().notifyEvent(this, GraphEvents.removeAll) ;
+    }
+
+    @Override
+    public void remove(Node s, Node p, Node o) {
+        if ( getEventManager().listening() ) {
+            // Have to do it the hard way so that triple events happen.
+            super.remove(s, p, o) ;
+            return ;
+        }
+
+        dataset.deleteAny(getGraphName(), s, p, o) ;
+        // We know no one is listening ...
+        // getEventManager().notifyEvent(this, GraphEvents.remove(s, p, o) ) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
new file mode 100644
index 0000000..de4c2c0
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import java.util.Map ;
+
+import org.apache.jena.graph.Graph ;
+import org.apache.jena.graph.Node ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.shared.impl.PrefixMappingImpl ;
+import org.apache.jena.sparql.core.DatasetPrefixStorage ;
+import org.apache.jena.sparql.core.GraphView ;
+import org.apache.jena.sparql.core.Quad ;
+
+/** A GraphView that is sensitive to {@link DatasetGraphSwitchable} switching.
+ */
+public class GraphViewSwitchable extends GraphView {
+    // Fixups for GraphView
+    //   Prefixes.
+    //   Transaction handler.
+    // Long term - ensure that GraphView uses get() always, inc prefixes, transaction handlers
+    
+    // Factory style.
+    public static GraphViewSwitchable createDefaultGraph(DatasetGraphSwitchable dsg)
+    { return new GraphViewSwitchable(dsg, Quad.defaultGraphNodeGenerated) ; }
+    
+    public static GraphView createNamedGraph(DatasetGraphSwitchable dsg, Node graphIRI)
+    { return new GraphViewSwitchable(dsg, graphIRI) ; }
+    
+    public static GraphViewSwitchable createUnionGraph(DatasetGraphSwitchable dsg)
+    { return new GraphViewSwitchable(dsg, Quad.unionGraph) ; }
+    
+    private final DatasetGraphSwitchable dsgx;
+    protected DatasetGraphSwitchable getx() { return dsgx; }
+    
+    protected GraphViewSwitchable(DatasetGraphSwitchable dsg, Node gn) {
+        super(dsg, gn) ;
+        this.dsgx = dsg;
+    }
+
+//    @Override
+//    public TransactionHandler getTransactionHandler() {
+//        // XXX Awiting for promote to be enabled.
+//        return super.getTransactionHandler();
+//    }
+    
+    @Override
+    protected PrefixMapping createPrefixMapping() {
+        Node gn = super.getGraphName();
+        if ( gn == Quad.defaultGraphNodeGenerated )
+            gn = null;
+        if ( Quad.isUnionGraph(gn) ) {
+            // Read-only wrapper would be better that a copy.
+            PrefixMapping pmap = new PrefixMappingImpl();
+            pmap.setNsPrefixes(prefixMapping(null));
+            return pmap; 
+        }
+        return prefixMapping(gn);
+    }
+    
+    /** Return the {@code DatasetGraphSwitchable} we are viewing. */
+    @Override
+    public DatasetGraphSwitchable getDataset() {
+        return getx() ;
+    }
+
+    // TDB2 specific.
+    // Does not cope with blank nodes.
+    // A PrefixMapping sending operations via the switchable.
+    // Long term, rework as PrefixMapping over PrefixMap over DatasetPrefixStorage
+    private PrefixMapping prefixMapping(Node graphName) {
+        
+        String gn = (graphName == null) ? "" : graphName.getURI(); 
+        
+        return new PrefixMappingImpl() {
+            
+            DatasetPrefixStorage dps() {
+                return ((DatasetGraphTDB)(getx().get())).getPrefixes();
+            }
+            
+            Graph graph() {
+                DatasetGraphTDB dsg = (DatasetGraphTDB)getx().get();
+                if ( gn == null )
+                    return dsg.getDefaultGraph();
+                else
+                    return dsg.getGraph(graphName);
+            }
+            
+            PrefixMapping prefixMapping() {
+                if ( gn == null )
+                    return dps().getPrefixMapping();
+                else
+                    return dps().getPrefixMapping(gn); 
+            }
+
+            @Override
+            protected void set(String prefix, String uri) {
+                dps().insertPrefix(gn, prefix, uri);
+                super.set(prefix, uri);
+            }
+
+            @Override
+            protected String get(String prefix) {
+                //Ignore. String x = super.get(prefix);
+                // (Ignore more?)
+                return dps().readPrefix(gn, prefix);
+            }
+
+            @Override
+            protected void remove(String prefix) {
+                dps().getPrefixMapping().removeNsPrefix(prefix);
+                super.remove(prefix);
+            }
+            
+            @Override
+            public Map<String, String> getNsPrefixMap() {
+                return prefixMapping().getNsPrefixMap();
+                //return graph().getPrefixMapping().getNsPrefixMap();
+            }
+        };
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/Hash.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/Hash.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/Hash.java
new file mode 100644
index 0000000..ff06594
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/Hash.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import java.util.Arrays;
+
+import org.apache.jena.atlas.lib.Bytes ;
+
+
+/** Hash values. */ 
+public class Hash
+{
+    private byte [] bytes ;
+    public Hash(int len) { bytes = new byte[len] ; }
+    public int getLen() { return bytes.length ; }
+    public byte [] getBytes() { return bytes ; }
+    
+    @Override
+    public int hashCode()
+    { 
+        return Arrays.hashCode(bytes) ;
+    }
+    
+    @Override
+    public boolean equals(Object other)
+    {
+        if ( this == other ) return true ;
+        if ( ! (other instanceof Hash) )
+            return false ;
+        boolean b = Arrays.equals(bytes, ((Hash)other).bytes) ;
+        return b ;
+    }
+    
+    @Override
+    public String toString()
+    {
+        return "hash:"+Bytes.asHex(bytes) ;
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java
new file mode 100644
index 0000000..1b4d727
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import static java.lang.String.format ;
+
+import java.util.ConcurrentModificationException ;
+import java.util.Iterator ;
+import java.util.NoSuchElementException ;
+import java.util.concurrent.atomic.AtomicLong ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.Closeable ;
+
+/** Wrapper to check an iterator has not been used across some write operation. */ 
+class IteratorCheckNotConcurrent<T> implements Iterator<T>, Closeable
+{
+    private Iterator<T> iter ;
+    private AtomicLong eCount ;  // Needs fixing to be per thera in some way - either from the caller or here. 
+    private boolean finished = false ;
+    private long startEpoch ; 
+
+    IteratorCheckNotConcurrent(Iterator<T> iter, AtomicLong eCount) {
+        // Assumes correct locking to set up, i.e. eCount not changing
+        // (writer on separate thread).
+        this.iter = iter ;
+        this.eCount = eCount ;
+        this.startEpoch = eCount.get() ;
+    }
+
+    private void checkConcurrentModification() {
+        if ( finished )
+            return ;
+
+        long now = eCount.get() ;
+        if ( now != startEpoch ) {
+            policyError(format("Iterator: started at %d, now %d", startEpoch, now)) ;
+        }
+    }
+    
+    private static void policyError(String message) {
+        throw new ConcurrentModificationException(message) ;
+    } 
+
+    @Override
+    public boolean hasNext() {
+        checkConcurrentModification() ;
+        boolean b = iter.hasNext() ;
+        if ( !b )
+            close() ;
+        return b ;
+    }
+
+    @Override
+    public T next() {
+        checkConcurrentModification() ;
+        try { return iter.next() ; }
+        catch (NoSuchElementException ex) {
+            close() ;
+            throw ex ;
+        }
+    }
+
+    @Override
+    public void remove() {
+        checkConcurrentModification() ;
+        iter.remove() ;
+        // Reset the epoch.
+        startEpoch = eCount.get() ;
+    }
+
+    @Override
+    public void close() {
+        finished = true ;
+        Iter.close(iter) ;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorTxnTracker.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorTxnTracker.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorTxnTracker.java
new file mode 100644
index 0000000..59e58c8
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorTxnTracker.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.tdb2.store;
+
+import java.util.Iterator;
+import java.util.Objects;
+
+import org.apache.jena.atlas.iterator.IteratorWrapper;
+import org.apache.jena.dboe.transaction.txn.Transaction;
+import org.apache.jena.dboe.transaction.txn.TransactionException;
+import org.apache.jena.dboe.transaction.txn.TransactionalSystem;
+import org.apache.jena.dboe.transaction.txn.TxnId;
+
+/** Wrapper to check that an iterator is used inside its originating transaction. */
+public class IteratorTxnTracker<T> extends IteratorWrapper<T> {
+    private final TransactionalSystem txnSystem;
+    private final TxnId txnId;
+
+    public IteratorTxnTracker(Iterator<T> iterator, TransactionalSystem txnSystem, TxnId txnId) {
+        super(iterator);
+        this.txnSystem = Objects.requireNonNull(txnSystem, "TransactionalSystem");
+        this.txnId = Objects.requireNonNull(txnId, "TxnId") ;
+    }
+
+    @Override public boolean hasNext()  { check() ; return super.hasNext() ; }
+
+    @Override public T next()           { check() ; return super.next() ; }
+    
+    @Override public void remove()      { check() ; super.remove() ; }
+
+    private void check() {
+        Transaction txn = txnSystem.getThreadTransaction();
+        if ( txn == null )
+            throw new TransactionException("Iterator used outside its original transaction");
+        if ( txn != null && txnId.equals(txn.getTxnId()) )
+            return ;
+        throw new TransactionException("Iterator used inside a different transaction");
+    }
+}
\ No newline at end of file