1111package org .eclipse .rdf4j .sail .lmdb ;
1212
1313import static org .eclipse .rdf4j .sail .lmdb .LmdbUtil .E ;
14+ import static org .lwjgl .util .lmdb .LMDB .MDB_GET_BOTH_RANGE ;
1415import static org .lwjgl .util .lmdb .LMDB .MDB_NEXT ;
1516import static org .lwjgl .util .lmdb .LMDB .MDB_NOTFOUND ;
1617import static org .lwjgl .util .lmdb .LMDB .MDB_SET ;
2122import static org .lwjgl .util .lmdb .LMDB .mdb_cursor_get ;
2223import static org .lwjgl .util .lmdb .LMDB .mdb_cursor_open ;
2324import static org .lwjgl .util .lmdb .LMDB .mdb_cursor_renew ;
25+ import static org .lwjgl .util .lmdb .LMDB .mdb_dcmp ;
2426
2527import java .io .IOException ;
2628import java .nio .ByteBuffer ;
@@ -53,6 +55,7 @@ class LmdbRecordIterator implements RecordIterator {
5355 private final long cursor ;
5456
5557 private final MDBVal maxKey ;
58+ private final MDBVal maxValue ;
5659
5760 private final boolean matchValues ;
5861 private GroupMatcher groupMatcher ;
@@ -73,44 +76,60 @@ class LmdbRecordIterator implements RecordIterator {
7376
7477 private ByteBuffer minKeyBuf ;
7578
79+ private ByteBuffer minValueBuf ;
80+
7681 private ByteBuffer maxKeyBuf ;
7782
83+ private ByteBuffer maxValueBuf ;
84+
7885 private int lastResult ;
7986
8087 private final long [] quad ;
81- private final long [] originalQuad ;
88+ private final long [] patternQuad ;
8289
8390 private boolean fetchNext = false ;
8491
8592 private final StampedLongAdderLockManager txnLockManager ;
8693
8794 private final Thread ownerThread = Thread .currentThread ();
8895
89- LmdbRecordIterator (TripleIndex index , boolean rangeSearch , long subj , long pred , long obj ,
96+ private final int indexScore ;
97+
98+ LmdbRecordIterator (TripleIndex index , int indexScore , long subj , long pred , long obj ,
9099 long context , boolean explicit , Txn txnRef ) throws IOException {
91100 this .subj = subj ;
92101 this .pred = pred ;
93102 this .obj = obj ;
94103 this .context = context ;
95- this .originalQuad = new long [] { subj , pred , obj , context };
104+ this .patternQuad = new long [] { subj , pred , obj , context };
96105 this .quad = new long [] { subj , pred , obj , context };
97106 this .pool = Pool .get ();
98107 this .keyData = pool .getVal ();
99108 this .valueData = pool .getVal ();
100109 this .index = index ;
101- if (rangeSearch ) {
110+ this .indexScore = indexScore ;
111+ // prepare min and max keys if index can be used
112+ // otherwise, leave as null to indicate full scan
113+ if (indexScore > 0 ) {
102114 minKeyBuf = pool .getKeyBuffer ();
103- index .getMinKey (minKeyBuf , subj , pred , obj , context );
115+ minValueBuf = pool .getKeyBuffer ();
116+ index .getMinEntry (minKeyBuf , minValueBuf , subj , pred , obj , context );
104117 minKeyBuf .flip ();
118+ minValueBuf .flip ();
105119
106120 this .maxKey = pool .getVal ();
121+ this .maxValue = pool .getVal ();
107122 this .maxKeyBuf = pool .getKeyBuffer ();
108- index .getMaxKey (maxKeyBuf , subj , pred , obj , context );
123+ this .maxValueBuf = pool .getKeyBuffer ();
124+ index .getMaxEntry (maxKeyBuf , maxValueBuf , subj , pred , obj , context );
109125 maxKeyBuf .flip ();
126+ maxValueBuf .flip ();
110127 this .maxKey .mv_data (maxKeyBuf );
128+ this .maxValue .mv_data (maxValueBuf );
111129 } else {
112130 minKeyBuf = null ;
113131 this .maxKey = null ;
132+ this .maxValue = null ;
114133 }
115134
116135 this .matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0 ;
@@ -163,13 +182,15 @@ public long[] next() {
163182 minKeyBuf = pool .getKeyBuffer ();
164183 }
165184 minKeyBuf .clear ();
166- index .toKey (minKeyBuf , quad [0 ], quad [1 ], quad [2 ], quad [3 ]);
185+ index .toEntry (minKeyBuf , minValueBuf , quad [0 ], quad [1 ], quad [2 ], quad [3 ]);
167186 minKeyBuf .flip ();
187+ minValueBuf .flip ();
168188 keyData .mv_data (minKeyBuf );
169- lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_SET );
170- if (lastResult != MDB_SUCCESS ) {
171- // use MDB_SET_RANGE if key was deleted
172- lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_SET_RANGE );
189+ // use set range if entry was deleted
190+ lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_SET_RANGE );
191+ if (lastResult == MDB_SUCCESS ) {
192+ valueData .mv_data (minValueBuf );
193+ lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_GET_BOTH_RANGE );
173194 }
174195 if (lastResult != MDB_SUCCESS ) {
175196 closeInternal (false );
@@ -187,7 +208,12 @@ public long[] next() {
187208 if (minKeyBuf != null ) {
188209 // set cursor to min key
189210 keyData .mv_data (minKeyBuf );
190- lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_SET_RANGE );
211+ lastResult = indexScore >= 2 ? MDB_SUCCESS
212+ : mdb_cursor_get (cursor , keyData , valueData , MDB_SET_RANGE );
213+ if (lastResult == MDB_SUCCESS ) {
214+ valueData .mv_data (minValueBuf );
215+ lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_GET_BOTH_RANGE );
216+ }
191217 } else {
192218 // set cursor to first item
193219 lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_NEXT );
@@ -196,14 +222,17 @@ public long[] next() {
196222
197223 while (lastResult == MDB_SUCCESS ) {
198224 // if (maxKey != null && TripleStore.COMPARATOR.compare(keyData.mv_data(), maxKey.mv_data()) > 0) {
199- if (maxKey != null && mdb_cmp (txn , dbi , keyData , maxKey ) > 0 ) {
225+ int keyDiff ;
226+ if (maxKey != null &&
227+ (keyDiff = mdb_cmp (txn , dbi , keyData , maxKey )) >= 0
228+ && (keyDiff > 0 || mdb_dcmp (txn , dbi , valueData , maxValue ) > 0 )) {
200229 lastResult = MDB_NOTFOUND ;
201- } else if (matches ()) {
230+ } else if (notMatches ()) {
202231 // value doesn't match search key/mask, fetch next value
203232 lastResult = mdb_cursor_get (cursor , keyData , valueData , MDB_NEXT );
204233 } else {
205234 // Matching value found
206- index .keyToQuad (keyData .mv_data (), originalQuad , quad );
235+ index .entryToQuad (keyData .mv_data (), valueData . mv_data (), patternQuad , quad );
207236 // fetch next value
208237 fetchNext = true ;
209238 return quad ;
@@ -216,13 +245,13 @@ public long[] next() {
216245 }
217246 }
218247
219- private boolean matches () {
220-
248+ private boolean notMatches () {
221249 if (groupMatcher != null ) {
222- return !this .groupMatcher .matches (keyData .mv_data ());
250+ return !this .groupMatcher .matches (keyData .mv_data (), valueData . mv_data () );
223251 } else if (matchValues ) {
252+ // lazy init of group matcher
224253 this .groupMatcher = index .createMatcher (subj , pred , obj , context );
225- return !this .groupMatcher .matches (keyData .mv_data ());
254+ return !this .groupMatcher .matches (keyData .mv_data (), valueData . mv_data () );
226255 } else {
227256 return false ;
228257 }
@@ -247,10 +276,13 @@ private void closeInternal(boolean maybeCalledAsync) {
247276 pool .free (valueData );
248277 if (minKeyBuf != null ) {
249278 pool .free (minKeyBuf );
279+ pool .free (minValueBuf );
250280 }
251281 if (maxKey != null ) {
252282 pool .free (maxKeyBuf );
253283 pool .free (maxKey );
284+ pool .free (maxValueBuf );
285+ pool .free (maxValue );
254286 }
255287 }
256288 } finally {
0 commit comments