/* * 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.tdb.store.tupletable; import static java.lang.String.format; import static org.apache.jena.tdb.sys.SystemTDB.SizeOfNodeId ; import java.util.Iterator; import org.apache.jena.atlas.iterator.* ; import org.apache.jena.atlas.lib.Bytes ; import org.apache.jena.atlas.lib.tuple.Tuple ; import org.apache.jena.tdb.TDBException ; import org.apache.jena.tdb.base.record.Record ; import org.apache.jena.tdb.base.record.RecordFactory ; import org.apache.jena.tdb.index.RangeIndex ; import org.apache.jena.tdb.lib.ColumnMap ; import org.apache.jena.tdb.lib.TupleLib ; import org.apache.jena.tdb.store.NodeId ; public class TupleIndexRecord extends TupleIndexBase { private static final boolean Check = false ; private RangeIndex index ; private RecordFactory factory ; public TupleIndexRecord(int N, ColumnMap colMapping, String name, RecordFactory factory, RangeIndex index) { super(N, colMapping, name); this.factory = factory; this.index = index; if ( factory.keyLength() != N * SizeOfNodeId ) throw new TDBException(format("Mismatch: TupleIndex of length %d is not comparative with a factory for key length %d", N, factory.keyLength())); } /** * Insert a tuple - return true if it was really added, false if it was a * duplicate */ @Override protected boolean performAdd(Tuple<NodeId> tuple) { Record r = TupleLib.record(factory, tuple, colMap); return index.add(r); } /** * Delete a tuple - return true if it was deleted, false if it didn't exist */ @Override protected boolean performDelete(Tuple<NodeId> tuple) { Record r = TupleLib.record(factory, tuple, colMap); return index.delete(r); } /** * Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means * match any. Input pattern in natural order, not index order. */ @Override protected Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> pattern) { return findOrScan(pattern); } // Package visibility for testing. final Iterator<Tuple<NodeId>> findOrScan(Tuple<NodeId> pattern) { return findWorker(pattern, true, true); } final Iterator<Tuple<NodeId>> findOrPartialScan(Tuple<NodeId> pattern) { return findWorker(pattern, true, false); } final Iterator<Tuple<NodeId>> findByIndex(Tuple<NodeId> pattern) { return findWorker(pattern, false, false); } private Iterator<Tuple<NodeId>> findWorker(Tuple<NodeId> patternNaturalOrder, boolean partialScanAllowed, boolean fullScanAllowed) { if ( Check ) { if ( tupleLength != patternNaturalOrder.len() ) throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", patternNaturalOrder.len(), tupleLength)); } // Convert to index order. Tuple<NodeId> pattern = colMap.map(patternNaturalOrder); // Canonical form. int numSlots = 0; int leadingIdx = -2; // Index of last leading pattern NodeId. Start less // than numSlots-1 boolean leading = true; // Records. Record minRec = factory.createKeyOnly(); Record maxRec = factory.createKeyOnly(); // Set the prefixes. for ( int i = 0 ; i < pattern.len() ; i++ ) { NodeId X = pattern.get(i); if ( NodeId.isAny(X) ) { X = null; // No longer seting leading key slots. leading = false; continue; } numSlots++; if ( leading ) { leadingIdx = i; Bytes.setLong(X.getId(), minRec.getKey(), i * SizeOfNodeId); Bytes.setLong(X.getId(), maxRec.getKey(), i * SizeOfNodeId); } } // Is it a simple existence test? if ( numSlots == pattern.len() ) { if ( index.contains(minRec) ) return new SingletonIterator<>(pattern); else return new NullIterator<>(); } Iterator<Record> iter = null; if ( leadingIdx < 0 ) { if ( !fullScanAllowed ) return null; // System.out.println("Full scan") ; // Full scan necessary iter = index.iterator(); } else { // Adjust the maxRec. NodeId X = pattern.get(leadingIdx); // Set the max Record to the leading NodeIds, +1. // Example, SP? inclusive to S(P+1)? exclusive where ? is zero. Bytes.setLong(X.getId() + 1, maxRec.getKey(), leadingIdx * SizeOfNodeId); iter = index.iterator(minRec, maxRec); } Iterator<Tuple<NodeId>> tuples = Iter.map(iter, item -> TupleLib.tuple(item, colMap)); if ( leadingIdx < numSlots - 1 ) { if ( !partialScanAllowed ) return null; // Didn't match all defined slots in request. // Partial or full scan needed. // pattern.unmap(colMap) ; tuples = TupleIndex.scan(tuples, patternNaturalOrder); } return tuples; } @Override public Iterator<Tuple<NodeId>> all() { Iterator<Record> iter = index.iterator(); return Iter.map(iter, item -> TupleLib.tuple(item, colMap)); } @Override public void close() { index.close(); } @Override public void sync() { index.sync(); } public final RangeIndex getRangeIndex() { return index; } // protected final RecordFactory getRecordFactory() { return factory ; } @Override public boolean isEmpty() { return index.isEmpty(); } @Override public void clear() { index.clear(); } @Override public long size() { return index.size(); } }