View Javadoc
1   /*
2   Copyright (c) 2007 Health Market Science, Inc.
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.NoSuchElementException;
26  import java.util.TreeSet;
27  
28  import static com.healthmarketscience.jackcess.Database.*;
29  import com.healthmarketscience.jackcess.impl.ColumnImpl;
30  import com.healthmarketscience.jackcess.impl.JetFormatTest;
31  import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
32  import com.healthmarketscience.jackcess.impl.RowIdImpl;
33  import com.healthmarketscience.jackcess.impl.TableImpl;
34  import com.healthmarketscience.jackcess.util.CaseInsensitiveColumnMatcher;
35  import com.healthmarketscience.jackcess.util.ColumnMatcher;
36  import com.healthmarketscience.jackcess.util.RowFilterTest;
37  import com.healthmarketscience.jackcess.util.SimpleColumnMatcher;
38  import junit.framework.TestCase;
39  import static com.healthmarketscience.jackcess.TestUtil.*;
40  
41  /**
42   * @author James Ahlborn
43   */
44  public class CursorTest extends TestCase {
45  
46    static final List<TestDB> INDEX_CURSOR_DBS = 
47      TestDB.getSupportedForBasename(Basename.INDEX_CURSOR);
48  
49  
50    public CursorTest(String name) throws Exception {
51      super(name);
52    }
53  
54    @Override
55    protected void setUp() {
56      TestUtil.setTestAutoSync(false);
57    }
58  
59    @Override
60    protected void tearDown() {
61      TestUtil.clearTestAutoSync();
62    }
63  
64    private static List<Map<String,Object>> createTestTableData()
65      throws Exception
66    {
67      List<Map<String,Object>> expectedRows =
68        new ArrayList<Map<String,Object>>();
69      for(int i = 0; i < 10; ++i) {
70        expectedRows.add(createExpectedRow("id", i, "value", "data" + i));
71      }
72      return expectedRows;
73    }
74  
75    private static List<Map<String,Object>> createTestTableData(
76        int startIdx,
77        int endIdx)
78      throws Exception
79    {
80      List<Map<String,Object>> expectedRows = createTestTableData();
81      expectedRows.subList(endIdx, expectedRows.size()).clear();
82      expectedRows.subList(0, startIdx).clear();
83      return expectedRows;
84    }
85    
86    private static Database createTestTable(final FileFormat fileFormat) 
87      throws Exception 
88    {
89      Database db = createMem(fileFormat);
90  
91      Table table = new TableBuilder("test")
92        .addColumn(new ColumnBuilder("id", DataType.LONG))
93        .addColumn(new ColumnBuilder("value", DataType.TEXT))
94        .toTable(db);
95  
96      for(Map<String,Object> row : createTestTableData()) {
97        table.addRow(row.get("id"), row.get("value"));
98      }
99  
100     return db;
101   }
102 
103   private static List<Map<String,Object>> createUnorderedTestTableData()
104     throws Exception
105   {
106     List<Map<String,Object>> expectedRows =
107       new ArrayList<Map<String,Object>>();
108     int[] ids = new int[]{3, 7, 6, 1, 2, 9, 0, 5, 4, 8};
109     for(int i : ids) {
110       expectedRows.add(createExpectedRow("id", i, "value", "data" + i));
111     }
112     return expectedRows;
113   }  
114 
115   static Database createTestIndexTable(final TestDB indexCursorDB) 
116     throws Exception 
117   {
118     Database db = openMem(indexCursorDB);
119 
120     Table table = db.getTable("test");
121 
122     for(Map<String,Object> row : createUnorderedTestTableData()) {
123       table.addRow(row.get("id"), row.get("value"));
124     }
125 
126     return db;
127   }
128 
129   private static List<Map<String,Object>> createDupeTestTableData()
130     throws Exception
131   {
132     List<Map<String,Object>> expectedRows =
133       new ArrayList<Map<String,Object>>();
134     int[] ids = new int[]{3, 7, 6, 1, 2, 9, 0, 5, 4, 8};
135     for(int i : ids) {
136       expectedRows.add(createExpectedRow("id", i, "value", "data" + (i % 3)));
137     }
138     for(int i : ids) {
139       expectedRows.add(createExpectedRow("id", i, "value", "data" + (i % 5)));
140     }
141     return expectedRows;
142   }
143 
144   private static Database createDupeTestTable(final FileFormat fileFormat) 
145     throws Exception 
146   {
147     Database db = createMem(fileFormat);
148 
149     Table table = new TableBuilder("test")
150       .addColumn(new ColumnBuilder("id", DataType.LONG))
151       .addColumn(new ColumnBuilder("value", DataType.TEXT))
152       .toTable(db);
153 
154     for(Map<String,Object> row : createDupeTestTableData()) {
155       table.addRow(row.get("id"), row.get("value"));
156     }
157 
158     return db;
159   }
160 
161   static Database createDupeTestTable(final TestDB indexCursorDB) 
162     throws Exception 
163   {
164     Database db = openMem(indexCursorDB);
165 
166     Table table = db.getTable("test");
167 
168     for(Map<String,Object> row : createDupeTestTableData()) {
169       table.addRow(row.get("id"), row.get("value"));
170     }
171 
172     return db;
173   }
174 
175   private static Cursor createIndexSubRangeCursor(Table table,
176                                                   Index idx,
177                                                   int type)
178     throws Exception
179   {
180     return table.newCursor()
181       .setIndex(idx)
182       .setStartEntry(3 - type)
183       .setStartRowInclusive(type == 0)
184       .setEndEntry(8 + type)
185       .setEndRowInclusive(type == 0)
186       .toCursor();
187   }
188   
189   public void testRowId() throws Exception {
190     // test special cases
191     RowIdImpl rowId1 = new RowIdImpl(1, 2);
192     RowIdImpl rowId2 = new RowIdImpl(1, 3);
193     RowIdImpl rowId3 = new RowIdImpl(2, 1);
194 
195     List<RowIdImpl> sortedRowIds =
196       new ArrayList<RowIdImpl>(new TreeSet<RowIdImpl>(
197         Arrays.asList(rowId1, rowId2, rowId3, RowIdImpl.FIRST_ROW_ID,
198                       RowIdImpl.LAST_ROW_ID)));
199 
200     assertEquals(Arrays.asList(RowIdImpl.FIRST_ROW_ID, rowId1, rowId2, rowId3,
201                                RowIdImpl.LAST_ROW_ID),
202                  sortedRowIds);
203   }
204   
205   public void testSimple() throws Exception {
206     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
207       Database db = createTestTable(fileFormat);
208 
209       Table table = db.getTable("test");
210       Cursor cursor = CursorBuilder.createCursor(table);
211       doTestSimple(cursor, null);
212       db.close();
213     }
214   }
215 
216   private static void doTestSimple(Cursor cursor,
217                                    List<Map<String, Object>> expectedRows)
218     throws Exception
219   {
220     if(expectedRows == null) {
221       expectedRows = createTestTableData();
222     }
223 
224     List<Map<String, Object>> foundRows =
225       new ArrayList<Map<String, Object>>();
226     for(Map<String, Object> row : cursor) {
227       foundRows.add(row);
228     }
229     assertEquals(expectedRows, foundRows);
230   }
231 
232   public void testMove() throws Exception {
233     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
234       Database db = createTestTable(fileFormat);
235 
236       Table table = db.getTable("test");
237       Cursor cursor = CursorBuilder.createCursor(table);
238       doTestMove(cursor, null);
239 
240       db.close();
241     }
242   }
243 
244   private static void doTestMove(Cursor cursor,
245                                  List<Map<String, Object>> expectedRows)
246     throws Exception
247   {
248     if(expectedRows == null) {
249       expectedRows = createTestTableData();
250     }
251     expectedRows.subList(1, 4).clear();
252 
253     List<Map<String, Object>> foundRows =
254       new ArrayList<Map<String, Object>>();
255     assertTrue(cursor.isBeforeFirst());
256     assertFalse(cursor.isAfterLast());
257     foundRows.add(cursor.getNextRow());
258     assertEquals(3, cursor.moveNextRows(3));
259     assertFalse(cursor.isBeforeFirst());
260     assertFalse(cursor.isAfterLast());
261 
262     Map<String,Object> expectedRow = cursor.getCurrentRow();
263     Cursor.Savepoint savepoint = cursor.getSavepoint();
264     assertEquals(2, cursor.movePreviousRows(2));
265     assertEquals(2, cursor.moveNextRows(2));
266     assertTrue(cursor.moveToNextRow());
267     assertTrue(cursor.moveToPreviousRow());
268     assertEquals(expectedRow, cursor.getCurrentRow());
269     
270     while(cursor.moveToNextRow()) {
271       foundRows.add(cursor.getCurrentRow());
272     }
273     assertEquals(expectedRows, foundRows);
274     assertFalse(cursor.isBeforeFirst());
275     assertTrue(cursor.isAfterLast());
276 
277     assertEquals(0, cursor.moveNextRows(3));
278 
279     cursor.beforeFirst();
280     assertTrue(cursor.isBeforeFirst());
281     assertFalse(cursor.isAfterLast());
282 
283     cursor.afterLast();
284     assertFalse(cursor.isBeforeFirst());
285     assertTrue(cursor.isAfterLast());
286 
287     cursor.restoreSavepoint(savepoint);
288     assertEquals(expectedRow, cursor.getCurrentRow());    
289   }
290 
291   public void testMoveNoReset() throws Exception {
292     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
293       Database db = createTestTable(fileFormat);
294 
295       Table table = db.getTable("test");
296       Cursor cursor = CursorBuilder.createCursor(table);
297       doTestMoveNoReset(cursor);
298 
299       db.close();
300     }
301   }
302 
303   private static void doTestMoveNoReset(Cursor cursor)
304     throws Exception
305   {
306     List<Map<String, Object>> expectedRows = createTestTableData();
307     List<Map<String, Object>> foundRows = new ArrayList<Map<String, Object>>();
308     
309     Iterator<Row> iter = cursor.newIterable().iterator();
310 
311     for(int i = 0; i < 6; ++i) {
312       foundRows.add(iter.next());
313     }    
314 
315     iter = cursor.newIterable().reset(false).reverse().iterator();
316     iter.next();
317     Map<String, Object> row = iter.next();
318     assertEquals(expectedRows.get(4), row);
319     
320     iter = cursor.newIterable().reset(false).iterator();
321     iter.next();
322     row = iter.next();
323     assertEquals(expectedRows.get(5), row);
324     iter.next();
325 
326     iter = cursor.newIterable().reset(false).iterator();
327     for(int i = 6; i < 10; ++i) {
328       foundRows.add(iter.next());
329     }    
330 
331     assertEquals(expectedRows, foundRows);
332   }
333   
334   public void testSearch() throws Exception {
335     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
336       Database db = createTestTable(fileFormat);
337 
338       Table table = db.getTable("test");
339       Cursor cursor = CursorBuilder.createCursor(table);
340       doTestSearch(table, cursor, null, 42, -13);
341 
342       db.close();
343     }
344   }
345 
346   private static void doTestSearch(Table table, Cursor cursor, Index index,
347                                    Integer... outOfRangeValues)
348     throws Exception
349   {
350     assertTrue(cursor.findFirstRow(table.getColumn("id"), 3));
351     assertEquals(createExpectedRow("id", 3,
352                                    "value", "data" + 3),
353                  cursor.getCurrentRow());
354 
355     assertTrue(cursor.findFirstRow(createExpectedRow(
356                                     "id", 6,
357                                     "value", "data" + 6)));
358     assertEquals(createExpectedRow("id", 6,
359                                    "value", "data" + 6),
360                  cursor.getCurrentRow());
361 
362     assertFalse(cursor.findFirstRow(createExpectedRow(
363                                    "id", 8,
364                                    "value", "data" + 13)));
365     assertFalse(cursor.findFirstRow(table.getColumn("id"), 13));
366     assertEquals(createExpectedRow("id", 6,
367                                    "value", "data" + 6),
368                  cursor.getCurrentRow());
369 
370     assertTrue(cursor.findFirstRow(createExpectedRow(
371                                     "value", "data" + 7)));
372     assertEquals(createExpectedRow("id", 7,
373                                    "value", "data" + 7),
374                  cursor.getCurrentRow());
375     
376     assertTrue(cursor.findFirstRow(table.getColumn("value"), "data" + 4));
377     assertEquals(createExpectedRow("id", 4,
378                                    "value", "data" + 4),
379                  cursor.getCurrentRow());
380 
381     for(Integer outOfRangeValue : outOfRangeValues) {
382       assertFalse(cursor.findFirstRow(table.getColumn("id"),
383                                  outOfRangeValue));
384       assertFalse(cursor.findFirstRow(table.getColumn("value"),
385                                  "data" + outOfRangeValue));
386       assertFalse(cursor.findFirstRow(createExpectedRow(
387                                      "id", outOfRangeValue,
388                                      "value", "data" + outOfRangeValue)));
389     }
390     
391     assertEquals("data" + 5,
392                  CursorBuilder.findValue(table,
393                                   table.getColumn("value"),
394                                   table.getColumn("id"), 5));
395     assertEquals(createExpectedRow("id", 5,
396                                    "value", "data" + 5),
397                  CursorBuilder.findRow(table,
398                                 createExpectedRow("id", 5)));
399     if(index != null) {
400       assertEquals("data" + 5,
401                    CursorBuilder.findValue(index,
402                                     table.getColumn("value"),
403                                     table.getColumn("id"), 5));
404       assertEquals(createExpectedRow("id", 5,
405                                      "value", "data" + 5),
406                    CursorBuilder.findRow(index,
407                                   createExpectedRow("id", 5)));
408 
409       assertNull(CursorBuilder.findValue(index,
410                                   table.getColumn("value"),
411                                   table.getColumn("id"),
412                                   -17));
413       assertNull(CursorBuilder.findRow(index,
414                                 createExpectedRow("id", 13)));
415     }
416   }
417 
418   public void testReverse() throws Exception {
419     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
420       Database db = createTestTable(fileFormat);
421 
422       Table table = db.getTable("test");
423       Cursor cursor = CursorBuilder.createCursor(table);
424       doTestReverse(cursor, null);
425 
426       db.close();
427     }
428   }
429 
430   private static void doTestReverse(Cursor cursor,
431                                     List<Map<String, Object>> expectedRows)
432     throws Exception
433   {
434     if(expectedRows == null) {
435       expectedRows = createTestTableData();
436     }
437     Collections.reverse(expectedRows);
438 
439     List<Map<String, Object>> foundRows =
440       new ArrayList<Map<String, Object>>();
441     for(Map<String, Object> row : cursor.newIterable().reverse()) {
442       foundRows.add(row);
443     }
444     assertEquals(expectedRows, foundRows);    
445   }
446   
447   public void testLiveAddition() throws Exception {
448     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
449       Database db = createTestTable(fileFormat);
450 
451       Table table = db.getTable("test");
452 
453       Cursor cursor1 = CursorBuilder.createCursor(table);
454       Cursor cursor2 = CursorBuilder.createCursor(table);
455       doTestLiveAddition(table, cursor1, cursor2, 11);
456 
457       db.close();
458     }
459   }
460 
461   private static void doTestLiveAddition(Table table,
462                                          Cursor cursor1,
463                                          Cursor cursor2,
464                                          Integer newRowNum) throws Exception
465   {
466     cursor1.moveNextRows(11);
467     cursor2.moveNextRows(11);
468 
469     assertTrue(cursor1.isAfterLast());
470     assertTrue(cursor2.isAfterLast());
471 
472     table.addRow(newRowNum, "data" + newRowNum);
473     Map<String,Object> expectedRow = 
474       createExpectedRow("id", newRowNum, "value", "data" + newRowNum);
475 
476     assertFalse(cursor1.isAfterLast());
477     assertFalse(cursor2.isAfterLast());
478 
479     assertEquals(expectedRow, cursor1.getCurrentRow());
480     assertEquals(expectedRow, cursor2.getCurrentRow());
481     assertFalse(cursor1.moveToNextRow());
482     assertFalse(cursor2.moveToNextRow());
483     assertTrue(cursor1.isAfterLast());
484     assertTrue(cursor2.isAfterLast());
485   }
486 
487   
488   public void testLiveDeletion() throws Exception {
489     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
490       Database db = createTestTable(fileFormat);
491 
492       Table table = db.getTable("test");
493 
494       Cursor cursor1 = CursorBuilder.createCursor(table);
495       Cursor cursor2 = CursorBuilder.createCursor(table);
496       Cursor cursor3 = CursorBuilder.createCursor(table);
497       Cursor cursor4 = CursorBuilder.createCursor(table);
498       doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 1);
499 
500       db.close();
501     }
502   }
503 
504   private static void doTestLiveDeletion(
505           Cursor cursor1,
506           Cursor cursor2,
507           Cursor cursor3,
508           Cursor cursor4,
509           int firstValue) throws Exception
510   {
511     assertEquals(2, cursor1.moveNextRows(2));
512     assertEquals(3, cursor2.moveNextRows(3));
513     assertEquals(3, cursor3.moveNextRows(3));
514     assertEquals(4, cursor4.moveNextRows(4));
515 
516     Map<String,Object> expectedPrevRow =
517       createExpectedRow("id", firstValue, "value", "data" + firstValue);
518     ++firstValue;
519     Map<String,Object> expectedDeletedRow =
520       createExpectedRow("id", firstValue, "value", "data" + firstValue);
521     ++firstValue;
522     Map<String,Object> expectedNextRow =
523       createExpectedRow("id", firstValue, "value", "data" + firstValue);
524 
525     assertEquals(expectedDeletedRow, cursor2.getCurrentRow());
526     assertEquals(expectedDeletedRow, cursor3.getCurrentRow());
527     
528     assertFalse(cursor2.isCurrentRowDeleted());
529     assertFalse(cursor3.isCurrentRowDeleted());
530 
531     cursor2.deleteCurrentRow();
532 
533     assertTrue(cursor2.isCurrentRowDeleted());
534     assertTrue(cursor3.isCurrentRowDeleted());
535 
536     assertEquals(expectedNextRow, cursor1.getNextRow());
537     assertEquals(expectedNextRow, cursor2.getNextRow());
538     assertEquals(expectedNextRow, cursor3.getNextRow());
539     
540     assertEquals(expectedPrevRow, cursor3.getPreviousRow());
541 
542     assertTrue(cursor3.moveToNextRow());
543     cursor3.deleteCurrentRow();
544     assertTrue(cursor3.isCurrentRowDeleted());
545 
546     firstValue += 2;
547     expectedNextRow =
548       createExpectedRow("id", firstValue, "value", "data" + firstValue);
549     assertTrue(cursor3.moveToNextRow());
550     assertEquals(expectedNextRow, cursor3.getNextRow());
551 
552     cursor1.beforeFirst();
553     assertTrue(cursor1.moveToNextRow());
554     cursor1.deleteCurrentRow();
555     assertFalse(cursor1.isBeforeFirst());
556     assertFalse(cursor1.isAfterLast());
557     assertFalse(cursor1.moveToPreviousRow());
558     assertTrue(cursor1.isBeforeFirst());
559     assertFalse(cursor1.isAfterLast());
560 
561     cursor1.afterLast();
562     assertTrue(cursor1.moveToPreviousRow());
563     cursor1.deleteCurrentRow();
564     assertFalse(cursor1.isBeforeFirst());
565     assertFalse(cursor1.isAfterLast());
566     assertFalse(cursor1.moveToNextRow());
567     assertFalse(cursor1.isBeforeFirst());
568     assertTrue(cursor1.isAfterLast());
569 
570     cursor1.beforeFirst();
571     while(cursor1.moveToNextRow()) {
572       cursor1.deleteCurrentRow();
573     }
574 
575     assertTrue(cursor1.isAfterLast());
576     assertTrue(cursor2.isCurrentRowDeleted());
577     assertTrue(cursor3.isCurrentRowDeleted());
578     assertTrue(cursor4.isCurrentRowDeleted());
579   }
580 
581   public void testSimpleIndex() throws Exception {
582     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
583       Database db = createTestIndexTable(indexCursorDB);
584 
585       Table table = db.getTable("test");
586       Index idx = table.getIndexes().get(0);
587 
588       assertTable(createUnorderedTestTableData(), table);
589 
590       Cursor cursor = CursorBuilder.createCursor(idx);
591       doTestSimple(cursor, null);
592 
593       db.close();
594     }
595   }
596 
597   public void testMoveIndex() throws Exception {
598     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
599       Database db = createTestIndexTable(indexCursorDB);
600 
601       Table table = db.getTable("test");
602       Index idx = table.getIndexes().get(0);
603       Cursor cursor = CursorBuilder.createCursor(idx);
604       doTestMove(cursor, null);
605 
606       db.close();
607     }
608   }
609   
610   public void testReverseIndex() throws Exception {
611     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
612       Database db = createTestIndexTable(indexCursorDB);
613 
614       Table table = db.getTable("test");
615       Index idx = table.getIndexes().get(0);
616       Cursor cursor = CursorBuilder.createCursor(idx);
617       doTestReverse(cursor, null);
618 
619       db.close();
620     }
621   }
622 
623   public void testSearchIndex() throws Exception {
624     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
625       Database db = createTestIndexTable(indexCursorDB);
626 
627       Table table = db.getTable("test");
628       Index idx = table.getIndexes().get(0);
629       Cursor cursor = CursorBuilder.createCursor(idx);
630       doTestSearch(table, cursor, idx, 42, -13);
631 
632       db.close();
633     }
634   }
635 
636   public void testLiveAdditionIndex() throws Exception {
637     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
638       Database db = createTestIndexTable(indexCursorDB);
639 
640       Table table = db.getTable("test");
641       Index idx = table.getIndexes().get(0);
642 
643       Cursor cursor1 = CursorBuilder.createCursor(idx);
644       Cursor cursor2 = CursorBuilder.createCursor(idx);
645       doTestLiveAddition(table, cursor1, cursor2, 11);
646 
647       db.close();
648     }
649   }
650 
651   public void testLiveDeletionIndex() throws Exception {
652     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
653       Database db = createTestIndexTable(indexCursorDB);
654 
655       Table table = db.getTable("test");
656       Index idx = table.getIndexes().get(0);
657 
658       Cursor cursor1 = CursorBuilder.createCursor(idx);
659       Cursor cursor2 = CursorBuilder.createCursor(idx);
660       Cursor cursor3 = CursorBuilder.createCursor(idx);
661       Cursor cursor4 = CursorBuilder.createCursor(idx);
662       doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 1);
663 
664       db.close();
665     }
666   }
667 
668   public void testSimpleIndexSubRange() throws Exception {
669     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
670       for(int i = 0; i < 2; ++i) {
671         Database db = createTestIndexTable(indexCursorDB);
672 
673         Table table = db.getTable("test");
674         Index idx = table.getIndexes().get(0);
675 
676         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
677 
678         List<Map<String,Object>> expectedRows =
679           createTestTableData(3, 9);
680 
681         doTestSimple(cursor, expectedRows);
682 
683         db.close();
684       }
685     }
686   }
687   
688   public void testMoveIndexSubRange() throws Exception {
689     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
690       for(int i = 0; i < 2; ++i) {
691         Database db = createTestIndexTable(indexCursorDB);
692 
693         Table table = db.getTable("test");
694         Index idx = table.getIndexes().get(0);
695 
696         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
697 
698         List<Map<String,Object>> expectedRows =
699           createTestTableData(3, 9);
700 
701         doTestMove(cursor, expectedRows);
702 
703         db.close();
704       }
705     }
706   }
707   
708   public void testSearchIndexSubRange() throws Exception {
709     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
710       for(int i = 0; i < 2; ++i) {
711         Database db = createTestIndexTable(indexCursorDB);
712 
713         Table table = db.getTable("test");
714         Index idx = table.getIndexes().get(0);
715 
716         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
717 
718         doTestSearch(table, cursor, idx, 2, 9);
719 
720         db.close();
721       }
722     }
723   }
724 
725   public void testReverseIndexSubRange() throws Exception {
726     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
727       for(int i = 0; i < 2; ++i) {
728         Database db = createTestIndexTable(indexCursorDB);
729 
730         Table table = db.getTable("test");
731         Index idx = table.getIndexes().get(0);
732 
733         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
734 
735         List<Map<String,Object>> expectedRows =
736           createTestTableData(3, 9);
737 
738         doTestReverse(cursor, expectedRows);
739 
740         db.close();
741       }
742     }
743   }
744 
745   public void testLiveAdditionIndexSubRange() throws Exception {
746     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
747       for(int i = 0; i < 2; ++i) {
748         Database db = createTestIndexTable(indexCursorDB);
749 
750         Table table = db.getTable("test");
751         Index idx = table.getIndexes().get(0);
752 
753         Cursor cursor1 = createIndexSubRangeCursor(table, idx, i);
754         Cursor cursor2 = createIndexSubRangeCursor(table, idx, i);
755 
756         doTestLiveAddition(table, cursor1, cursor2, 8);
757 
758         db.close();
759       }
760     }
761   }
762   
763   public void testLiveDeletionIndexSubRange() throws Exception {
764     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
765       for(int i = 0; i < 2; ++i) {
766         Database db = createTestIndexTable(indexCursorDB);
767 
768         Table table = db.getTable("test");
769         Index idx = table.getIndexes().get(0);
770 
771         Cursor cursor1 = createIndexSubRangeCursor(table, idx, i);
772         Cursor cursor2 = createIndexSubRangeCursor(table, idx, i);
773         Cursor cursor3 = createIndexSubRangeCursor(table, idx, i);
774         Cursor cursor4 = createIndexSubRangeCursor(table, idx, i);
775 
776         doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 4);
777 
778         db.close();
779       }
780     }
781   }
782 
783   public void testFindAllIndex() throws Exception {
784     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
785       Database db = createDupeTestTable(fileFormat);
786 
787       Table table = db.getTable("test");
788       Cursor cursor = CursorBuilder.createCursor(table);
789 
790       doTestFindAll(table, cursor, null);
791 
792       db.close();
793     }
794   }
795 
796   public void testFindAll() throws Exception {
797     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
798       Database db = createDupeTestTable(indexCursorDB);
799 
800       Table table = db.getTable("test");
801       Index idx = table.getIndexes().get(0);
802       Cursor cursor = CursorBuilder.createCursor(idx);
803 
804       doTestFindAll(table, cursor, idx);
805 
806       db.close();
807     }
808   }
809 
810   private static void doTestFindAll(Table table, Cursor cursor, Index index)
811     throws Exception
812   {
813     List<? extends Map<String,Object>> rows = RowFilterTest.toList(
814         cursor.newIterable().setMatchPattern("value", "data2"));
815 
816     List<? extends Map<String, Object>> expectedRows = null;
817 
818     if(index == null) {
819       expectedRows =
820         createExpectedTable(
821             createExpectedRow(
822                 "id", 2, "value", "data2"),
823             createExpectedRow(
824                 "id", 5, "value", "data2"),
825             createExpectedRow(
826                 "id", 8, "value", "data2"),
827             createExpectedRow(
828                 "id", 7, "value", "data2"),
829             createExpectedRow(
830                 "id", 2, "value", "data2"));
831     } else {
832       expectedRows =
833         createExpectedTable(
834             createExpectedRow(
835                 "id", 2, "value", "data2"),
836             createExpectedRow(
837                 "id", 2, "value", "data2"),
838             createExpectedRow(
839                 "id", 5, "value", "data2"),
840             createExpectedRow(
841                 "id", 7, "value", "data2"),
842             createExpectedRow(
843                 "id", 8, "value", "data2"));
844     }
845     assertEquals(expectedRows, rows);
846 
847     Column valCol = table.getColumn("value");
848     rows = RowFilterTest.toList(
849         cursor.newIterable().setMatchPattern(valCol, "data4"));
850 
851     if(index == null) {
852       expectedRows =
853         createExpectedTable(
854             createExpectedRow(
855                 "id", 9, "value", "data4"),
856             createExpectedRow(
857                 "id", 4, "value", "data4"));
858     } else {
859       expectedRows =
860         createExpectedTable(
861             createExpectedRow(
862                 "id", 4, "value", "data4"),
863             createExpectedRow(
864                 "id", 9, "value", "data4"));
865     }
866     assertEquals(expectedRows, rows);
867 
868     rows = RowFilterTest.toList(
869         cursor.newIterable().setMatchPattern(valCol, "data9"));
870 
871     assertTrue(rows.isEmpty());
872 
873     rows = RowFilterTest.toList(
874         cursor.newIterable().setMatchPattern(
875             Collections.singletonMap("id", 8)));
876     
877     expectedRows =
878       createExpectedTable(
879           createExpectedRow(
880               "id", 8, "value", "data2"),
881           createExpectedRow(
882               "id", 8, "value", "data3"));
883     assertEquals(expectedRows, rows);
884 
885     for(Map<String,Object> row : table) {
886       
887       List<Map<String,Object>> tmpRows = new ArrayList<Map<String,Object>>();
888       for(Map<String,Object> tmpRow : cursor) {
889         if(row.equals(tmpRow)) {
890           tmpRows.add(tmpRow);
891         }
892       }
893       expectedRows = tmpRows;
894       assertFalse(expectedRows.isEmpty());
895       
896       rows = RowFilterTest.toList(cursor.newIterable().setMatchPattern(row));
897 
898       assertEquals(expectedRows, rows);
899     }
900 
901     rows = RowFilterTest.toList(
902         cursor.newIterable().addMatchPattern("id", 8)
903         .addMatchPattern("value", "data13"));
904     assertTrue(rows.isEmpty());
905   }
906 
907   public void testId() throws Exception
908   {
909     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
910       Database db = createTestIndexTable(indexCursorDB);
911 
912       Table table = db.getTable("test");
913       Index idx = table.getIndexes().get(0);
914 
915       Cursor tCursor = CursorBuilder.createCursor(table);
916       Cursor iCursor = CursorBuilder.createCursor(idx);
917 
918       Cursor.Savepoint tSave = tCursor.getSavepoint();
919       Cursor.Savepoint iSave = iCursor.getSavepoint();
920 
921       tCursor.restoreSavepoint(tSave);
922       iCursor.restoreSavepoint(iSave);
923 
924       try {
925         tCursor.restoreSavepoint(iSave);
926         fail("IllegalArgumentException should have been thrown");
927       } catch(IllegalArgumentException e) {
928         // success
929       }
930 
931       try {
932         iCursor.restoreSavepoint(tSave);
933         fail("IllegalArgumentException should have been thrown");
934       } catch(IllegalArgumentException e) {
935         // success
936       }
937 
938       Cursor tCursor2 = CursorBuilder.createCursor(table);
939       Cursor iCursor2 = CursorBuilder.createCursor(idx);
940 
941       tCursor2.restoreSavepoint(tSave);
942       iCursor2.restoreSavepoint(iSave);
943 
944       db.close();
945     }
946   }
947   
948   public void testColumnMatcher() throws Exception {
949     
950 
951     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
952       Database db = createTestTable(fileFormat);
953 
954       Table table = db.getTable("test");
955 
956       doTestMatchers(table, SimpleColumnMatcher.INSTANCE, false);
957       doTestMatchers(table, CaseInsensitiveColumnMatcher.INSTANCE, true);
958 
959       Cursor cursor = CursorBuilder.createCursor(table);
960       doTestMatcher(table, cursor, SimpleColumnMatcher.INSTANCE, false);
961       doTestMatcher(table, cursor, CaseInsensitiveColumnMatcher.INSTANCE, 
962                     true);
963       db.close();
964     }
965   }
966 
967   private static void doTestMatchers(Table table, ColumnMatcher columnMatcher,
968                                      boolean caseInsensitive)
969     throws Exception
970   {
971       assertTrue(columnMatcher.matches(table, "value", null, null));
972       assertFalse(columnMatcher.matches(table, "value", "foo", null));
973       assertFalse(columnMatcher.matches(table, "value", null, "foo"));
974       assertTrue(columnMatcher.matches(table, "value", "foo", "foo"));
975       assertTrue(columnMatcher.matches(table, "value", "foo", "Foo")
976                  == caseInsensitive);
977 
978       assertFalse(columnMatcher.matches(table, "value", 13, null));
979       assertFalse(columnMatcher.matches(table, "value", null, 13));
980       assertTrue(columnMatcher.matches(table, "value", 13, 13));
981   }
982   
983   private static void doTestMatcher(Table table, Cursor cursor, 
984                                     ColumnMatcher columnMatcher,
985                                     boolean caseInsensitive)
986     throws Exception
987   {
988     cursor.setColumnMatcher(columnMatcher);
989 
990     assertTrue(cursor.findFirstRow(table.getColumn("id"), 3));
991     assertEquals(createExpectedRow("id", 3,
992                                    "value", "data" + 3),
993                  cursor.getCurrentRow());
994 
995     assertTrue(cursor.findFirstRow(createExpectedRow(
996                                     "id", 6,
997                                     "value", "data" + 6)));
998     assertEquals(createExpectedRow("id", 6,
999                                    "value", "data" + 6),
1000                  cursor.getCurrentRow());
1001 
1002     assertTrue(cursor.findFirstRow(createExpectedRow(
1003                                     "id", 6,
1004                                     "value", "Data" + 6)) == caseInsensitive);
1005     if(caseInsensitive) {
1006       assertEquals(createExpectedRow("id", 6,
1007                                      "value", "data" + 6),
1008                    cursor.getCurrentRow());
1009     }
1010 
1011     assertFalse(cursor.findFirstRow(createExpectedRow(
1012                                    "id", 8,
1013                                    "value", "data" + 13)));
1014     assertFalse(cursor.findFirstRow(table.getColumn("id"), 13));
1015     assertEquals(createExpectedRow("id", 6,
1016                                    "value", "data" + 6),
1017                  cursor.getCurrentRow());
1018 
1019     assertTrue(cursor.findFirstRow(createExpectedRow(
1020                                     "value", "data" + 7)));
1021     assertEquals(createExpectedRow("id", 7,
1022                                    "value", "data" + 7),
1023                  cursor.getCurrentRow());
1024     
1025     assertTrue(cursor.findFirstRow(createExpectedRow(
1026                                     "value", "Data" + 7)) == caseInsensitive);
1027     if(caseInsensitive) {
1028       assertEquals(createExpectedRow("id", 7,
1029                                      "value", "data" + 7),
1030                    cursor.getCurrentRow());
1031     }
1032     
1033     assertTrue(cursor.findFirstRow(table.getColumn("value"), "data" + 4));
1034     assertEquals(createExpectedRow("id", 4,
1035                                    "value", "data" + 4),
1036                  cursor.getCurrentRow());
1037 
1038     assertTrue(cursor.findFirstRow(table.getColumn("value"), "Data" + 4) 
1039                == caseInsensitive);
1040     if(caseInsensitive) {
1041       assertEquals(createExpectedRow("id", 4,
1042                                      "value", "data" + 4),
1043                    cursor.getCurrentRow());
1044     }
1045 
1046     assertEquals(Arrays.asList(createExpectedRow("id", 4,
1047                                                  "value", "data" + 4)),
1048                  RowFilterTest.toList(
1049                      cursor.newIterable()
1050                      .setMatchPattern("value", "data4")
1051                      .setColumnMatcher(SimpleColumnMatcher.INSTANCE)));
1052 
1053     assertEquals(Arrays.asList(createExpectedRow("id", 3,
1054                                                  "value", "data" + 3)),
1055                  RowFilterTest.toList(
1056                      cursor.newIterable()
1057                      .setMatchPattern("value", "DaTa3")
1058                      .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE)));
1059 
1060     assertEquals(Arrays.asList(createExpectedRow("id", 2,
1061                                                  "value", "data" + 2)),
1062                  RowFilterTest.toList(
1063                      cursor.newIterable()
1064                      .addMatchPattern("value", "DaTa2")
1065                      .addMatchPattern("id", 2)
1066                      .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE)));
1067   }
1068 
1069   public void testIndexCursor() throws Exception
1070   { 
1071     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) {
1072 
1073       Database db = openMem(testDB);
1074       Table t1 = db.getTable("Table1");
1075       Index idx = t1.getIndex(IndexBuilder.PRIMARY_KEY_NAME);
1076       IndexCursor cursor = CursorBuilder.createCursor(idx);
1077 
1078       assertFalse(cursor.findFirstRowByEntry(-1));
1079       cursor.findClosestRowByEntry(-1);
1080       assertEquals(0, cursor.getCurrentRow().get("id"));
1081 
1082       assertTrue(cursor.findFirstRowByEntry(1));
1083       assertEquals(1, cursor.getCurrentRow().get("id"));
1084       
1085       cursor.findClosestRowByEntry(2);
1086       assertEquals(2, cursor.getCurrentRow().get("id"));
1087 
1088       assertFalse(cursor.findFirstRowByEntry(4));
1089       cursor.findClosestRowByEntry(4);
1090       assertTrue(cursor.isAfterLast());
1091 
1092       db.close();
1093     }    
1094   }
1095   
1096   public void testIndexCursorDelete() throws Exception
1097   { 
1098     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) {
1099 
1100       Database db = openMem(testDB);
1101       Table t1 = db.getTable("Table1");
1102       Index idx = t1.getIndex("Table2Table1");
1103       IndexCursor cursor = CursorBuilder.createCursor(idx);
1104 
1105       List<String> expectedData = new ArrayList<String>();
1106       for(Row row : cursor.newEntryIterable(1)
1107             .addColumnNames("data")) {
1108         expectedData.add(row.getString("data"));
1109       }
1110 
1111       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1112 
1113       expectedData = new ArrayList<String>();
1114       for(Iterator<? extends Row> iter = 
1115             cursor.newEntryIterable(1).iterator();
1116           iter.hasNext(); ) {
1117         expectedData.add(iter.next().getString("data"));
1118         iter.remove();
1119         try {
1120           iter.remove();
1121           fail("IllegalArgumentException should have been thrown");
1122         } catch(IllegalStateException e) {
1123           // success
1124         }
1125 
1126         if(!iter.hasNext()) {
1127           try {
1128             iter.next();
1129             fail("NoSuchElementException should have been thrown");
1130           } catch(NoSuchElementException e) {
1131             // success
1132           }
1133         }
1134       }
1135 
1136       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1137       
1138       expectedData = new ArrayList<String>();
1139       for(Row row : cursor.newEntryIterable(1)
1140             .addColumnNames("data")) {
1141         expectedData.add(row.getString("data"));
1142       }
1143 
1144       assertTrue(expectedData.isEmpty());
1145       
1146       db.close();
1147     }    
1148   }
1149   
1150   public void testCursorDelete() throws Exception
1151   { 
1152     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) {
1153 
1154       Database db = openMem(testDB);
1155       Table t1 = db.getTable("Table1");
1156       Cursor cursor = CursorBuilder.createCursor(t1);
1157 
1158       List<String> expectedData = new ArrayList<String>();
1159       for(Row row : cursor.newIterable().setColumnNames(
1160               Arrays.asList("otherfk1", "data"))) {
1161         if(row.get("otherfk1").equals(1)) {
1162           expectedData.add(row.getString("data"));
1163         }
1164       }
1165 
1166       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1167 
1168       expectedData = new ArrayList<String>();
1169       for(Iterator<? extends Row> iter = cursor.iterator();
1170           iter.hasNext(); ) {
1171         Row row = iter.next();
1172         if(row.get("otherfk1").equals(1)) {
1173           expectedData.add(row.getString("data"));
1174           iter.remove();
1175           try {
1176             iter.remove();
1177             fail("IllegalArgumentException should have been thrown");
1178           } catch(IllegalStateException e) {
1179             // success
1180           }
1181         }
1182 
1183         if(!iter.hasNext()) {
1184           try {
1185             iter.next();
1186             fail("NoSuchElementException should have been thrown");
1187           } catch(NoSuchElementException e) {
1188             // success
1189           }
1190         }
1191       }
1192 
1193       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1194       
1195       expectedData = new ArrayList<String>();
1196       for(Row row : cursor.newIterable().setColumnNames(
1197               Arrays.asList("otherfk1", "data"))) {
1198         if(row.get("otherfk1").equals(1)) {
1199           expectedData.add(row.getString("data"));
1200         }
1201       }
1202 
1203       assertTrue(expectedData.isEmpty());
1204       
1205       db.close();
1206     }    
1207   }
1208   
1209   public void testFindByRowId() throws Exception {
1210     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
1211       Database db = createTestTable(fileFormat);
1212 
1213       Table table = db.getTable("test");
1214       Cursor cursor = CursorBuilder.createCursor(table);
1215       doTestFindByRowId(cursor);
1216       db.close();
1217     }
1218   }
1219 
1220   public void testFindByRowIdIndex() throws Exception {
1221     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
1222       Database db = createTestIndexTable(indexCursorDB);
1223 
1224       Table table = db.getTable("test");
1225       Index idx = table.getIndexes().get(0);
1226 
1227       assertTable(createUnorderedTestTableData(), table);
1228 
1229       Cursor cursor = CursorBuilder.createCursor(idx);
1230       doTestFindByRowId(cursor);
1231 
1232       db.close();
1233     }
1234   }
1235 
1236   private static void doTestFindByRowId(Cursor cursor) 
1237     throws Exception
1238   {
1239     for(int i = 0; i < 3; ++i) {
1240       cursor.moveToNextRow();
1241     } 
1242 
1243     Row r1 = cursor.getCurrentRow();
1244 
1245     for(int i = 0; i < 3; ++i) {
1246       cursor.moveToNextRow();
1247     } 
1248 
1249     Row r2 = cursor.getCurrentRow();
1250 
1251     doTestFindByRowId(cursor, r1, 2);
1252 
1253     doTestFindByRowId(cursor, r2, 5);
1254   } 
1255 
1256   private static void doTestFindByRowId(Cursor cursor, Row row, int id)
1257     throws Exception
1258   {
1259     cursor.reset();
1260     assertTrue(cursor.findRow(row.getId()));
1261     Row rFound = cursor.getCurrentRow();
1262     assertEquals(id, rFound.get("id"));
1263     assertEquals(row, rFound);
1264     Cursor.Savepoint save = cursor.getSavepoint();
1265 
1266     assertTrue(cursor.moveToNextRow());
1267     assertEquals(id + 1, cursor.getCurrentRow().get("id"));
1268 
1269     cursor.restoreSavepoint(save);
1270 
1271     assertTrue(cursor.moveToPreviousRow());
1272     assertEquals(id - 1, cursor.getCurrentRow().get("id"));
1273 
1274     assertFalse(cursor.findRow(RowIdImpl.FIRST_ROW_ID));
1275 
1276     assertEquals(id - 1, cursor.getCurrentRow().get("id"));    
1277   }
1278 
1279   public void testIterationEarlyExit() throws Exception {
1280     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
1281 
1282       Database db = createMem(fileFormat);
1283 
1284       Table table = new TableBuilder("test")
1285         .addColumn(new ColumnBuilder("id", DataType.LONG))
1286         .addColumn(new ColumnBuilder("value", DataType.TEXT))
1287         .addColumn(new ColumnBuilder("memo", DataType.MEMO))
1288         .addIndex(new IndexBuilder("value_idx")
1289                   .addColumns("value"))
1290         .toTable(db);
1291 
1292       for(int i = 0; i < 20; ++i) {
1293         Object memo = "memo-" + i;
1294         table.addRow(i, "val-" + (i/2), memo);
1295       }
1296 
1297       // generate an "invalid" memo
1298       byte[] b = new byte[12];
1299       b[3] = (byte)0xC0;
1300       table.addRow(20, "val-9", ColumnImpl.rawDataWrapper(b));
1301 
1302       IndexCursor cursor = CursorBuilder.createCursor(
1303           table.getIndex("value_idx"));
1304       
1305       try {
1306         cursor.newIterable()
1307           .addMatchPattern("value", "val-9")
1308           .addMatchPattern("memo", "anything")
1309           .iterator().hasNext();
1310         fail("RuntimeIOException should have been thrown");
1311       } catch(RuntimeIOException ignored) {
1312         // success
1313       }
1314 
1315       List<Row> rows = new ArrayList<Row>();
1316       for (Row row : cursor.newIterable()
1317              .addMatchPattern("value", "val-5")
1318              .addMatchPattern("memo", "memo-11")) {
1319         rows.add(row);
1320       }
1321 
1322       assertEquals(rows, createExpectedTable(
1323                        createExpectedRow("id", 11,
1324                                          "value", "val-5",
1325                                          "memo", "memo-11")));
1326 
1327       assertFalse(cursor.newIterable()
1328                   .addMatchPattern("value", "val-31")
1329                   .addMatchPattern("memo", "anything")
1330                   .iterator().hasNext());
1331 
1332       db.close();
1333     }
1334   }
1335 
1336   public void testPartialIndexFind() throws Exception
1337   {
1338     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
1339       
1340       Database db = createMem(fileFormat);
1341 
1342       TableImpl t = (TableImpl)new TableBuilder("Test")
1343         .addColumn(new ColumnBuilder("id", DataType.LONG))
1344         .addColumn(new ColumnBuilder("data1", DataType.TEXT))
1345         .addColumn(new ColumnBuilder("num2", DataType.LONG))
1346         .addColumn(new ColumnBuilder("key3", DataType.TEXT))
1347         .addColumn(new ColumnBuilder("value", DataType.TEXT))
1348         .addIndex(new IndexBuilder("idx3").addColumns("data1", "num2", "key3"))
1349         .toTable(db);
1350 
1351       Index idx = t.findIndexForColumns(Arrays.asList("data1"), 
1352                                         TableImpl.IndexFeature.ANY_MATCH);
1353       assertEquals("idx3", idx.getName());
1354 
1355       idx = t.findIndexForColumns(Arrays.asList("data1", "num2"), 
1356                                   TableImpl.IndexFeature.ANY_MATCH);
1357       assertEquals("idx3", idx.getName());
1358 
1359       idx = t.findIndexForColumns(Arrays.asList("data1", "num2", "key3"), 
1360                                   TableImpl.IndexFeature.ANY_MATCH);
1361       assertEquals("idx3", idx.getName());
1362 
1363       assertNull(t.findIndexForColumns(Arrays.asList("num2"), 
1364                                        TableImpl.IndexFeature.ANY_MATCH));
1365       assertNull(t.findIndexForColumns(Arrays.asList("data1", "key3"), 
1366                                        TableImpl.IndexFeature.ANY_MATCH));
1367       assertNull(t.findIndexForColumns(Arrays.asList("data1"), 
1368                                        TableImpl.IndexFeature.EXACT_MATCH));
1369 
1370 
1371       new IndexBuilder("idx2")
1372         .addColumns("data1", "num2")
1373         .addToTable(t);
1374 
1375       idx = t.findIndexForColumns(Arrays.asList("data1"), 
1376                                   TableImpl.IndexFeature.ANY_MATCH);
1377       assertEquals("idx2", idx.getName());
1378 
1379       idx = t.findIndexForColumns(Arrays.asList("data1", "num2"), 
1380                                   TableImpl.IndexFeature.ANY_MATCH);
1381       assertEquals("idx2", idx.getName());
1382 
1383       idx = t.findIndexForColumns(Arrays.asList("data1", "num2", "key3"), 
1384                                   TableImpl.IndexFeature.ANY_MATCH);
1385       assertEquals("idx3", idx.getName());
1386 
1387       assertNull(t.findIndexForColumns(Arrays.asList("num2"), 
1388                                        TableImpl.IndexFeature.ANY_MATCH));
1389       assertNull(t.findIndexForColumns(Arrays.asList("data1", "key3"), 
1390                                        TableImpl.IndexFeature.ANY_MATCH));
1391       assertNull(t.findIndexForColumns(Arrays.asList("data1"), 
1392                                        TableImpl.IndexFeature.EXACT_MATCH));
1393 
1394 
1395       new IndexBuilder("idx1")
1396         .addColumns("data1")
1397         .addToTable(t);
1398 
1399       idx = t.findIndexForColumns(Arrays.asList("data1"), 
1400                                   TableImpl.IndexFeature.ANY_MATCH);
1401       assertEquals("idx1", idx.getName());
1402 
1403       idx = t.findIndexForColumns(Arrays.asList("data1", "num2"), 
1404                                   TableImpl.IndexFeature.ANY_MATCH);
1405       assertEquals("idx2", idx.getName());
1406 
1407       idx = t.findIndexForColumns(Arrays.asList("data1", "num2", "key3"), 
1408                                   TableImpl.IndexFeature.ANY_MATCH);
1409       assertEquals("idx3", idx.getName());
1410 
1411       assertNull(t.findIndexForColumns(Arrays.asList("num2"), 
1412                                        TableImpl.IndexFeature.ANY_MATCH));
1413       assertNull(t.findIndexForColumns(Arrays.asList("data1", "key3"), 
1414                                        TableImpl.IndexFeature.ANY_MATCH));
1415 
1416       db.close();
1417     }
1418   }
1419 
1420   public void testPartialIndexLookup() throws Exception
1421   {
1422     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
1423       
1424       Database db = createMem(fileFormat);
1425 
1426       TableImpl t = (TableImpl)new TableBuilder("Test")
1427         .addColumn(new ColumnBuilder("id", DataType.LONG))
1428         .addColumn(new ColumnBuilder("data1", DataType.TEXT))
1429         .addColumn(new ColumnBuilder("num2", DataType.LONG))
1430         .addColumn(new ColumnBuilder("key3", DataType.TEXT))
1431         .addColumn(new ColumnBuilder("value", DataType.TEXT))
1432         .addIndex(new IndexBuilder("idx3")
1433                   .addColumns(true, "data1")
1434                   .addColumns(false, "num2")
1435                   .addColumns(true, "key3")
1436                   )
1437         .toTable(db);
1438 
1439       int id = 1;
1440       for(String str : Arrays.asList("A", "B", "C", "D")) {
1441         for(int i = 4; i >= 0; --i) {
1442         // for(int i = 0; i < 5; ++i) {
1443           for(int j = 1; j < 3; ++j) {
1444             t.addRow(id, str, i, "K" + j, "value" + id);
1445             ++id;
1446           }
1447         }
1448       }
1449 
1450       Index idx = t.getIndex("idx3");
1451       doPartialIndexLookup(idx);
1452 
1453       idx = new IndexBuilder("idx2")
1454                   .addColumns(true, "data1")
1455                   .addColumns(false, "num2")
1456         .addToTable(t);
1457       doPartialIndexLookup(idx);
1458 
1459       idx = new IndexBuilder("idx1")
1460                   .addColumns(true, "data1")
1461         .addToTable(t);
1462       doPartialIndexLookup(idx);
1463 
1464       db.close();
1465     }
1466   }
1467 
1468   private static void doPartialIndexLookup(Index idx) throws Exception
1469   {
1470     int colCount = idx.getColumnCount();
1471     IndexCursor c = new CursorBuilder(idx.getTable()).setIndex(idx).toIndexCursor();
1472 
1473     doFindFirstByEntry(c, 21, "C");
1474     doFindFirstByEntry(c, null, "Z");
1475 
1476     if(colCount > 1) {
1477       doFindFirstByEntry(c, 23, "C", 3);
1478       doFindFirstByEntry(c, null, "C", 20);
1479     }
1480 
1481     if(colCount > 2) {
1482       doFindFirstByEntry(c, 27, "C", 1, "K1");
1483       doFindFirstByEntry(c, null, "C", 4, "K3");
1484     }
1485       
1486     try {
1487       if(colCount > 2) {
1488         c.findFirstRowByEntry("C", 4, "K1", 14);
1489       } else if(colCount > 1) {
1490         c.findFirstRowByEntry("C", 4, "K1");
1491       } else {
1492         c.findFirstRowByEntry("C", 4);
1493       }
1494       fail("IllegalArgumentException should have been thrown");
1495     } catch(IllegalArgumentException expected) {
1496       // scucess
1497     }
1498 
1499     doFindByEntryRange(c, 11, 20, "B");
1500     doFindByEntry(c, new int[]{}, "Z");
1501 
1502     if(colCount > 1) {
1503       doFindByEntryRange(c, 13, 14, "B", 3);
1504       doFindByEntry(c, new int[]{}, "B", 20);
1505     }
1506 
1507     if(colCount > 2) {
1508       doFindByEntryRange(c, 14, 14, "B", 3, "K2");
1509       doFindByEntry(c, new int[]{}, "B", 3, "K3");
1510     }
1511 
1512     doFindByRow(idx, 13, 
1513                 "data1", "B", "value", "value13");
1514     doFindByRow(idx, 13, 
1515                 "data1", "B", "key3", "K1", "value", "value13");
1516     doFindByRow(idx, 13, 
1517         "data1", "B", "num2", 3, "key3", "K1", "value", "value13");
1518     doFindByRow(idx, 13, 
1519         "num2", 3, "value", "value13");
1520     doFindByRow(idx, 13, 
1521         "value", "value13");
1522     doFindByRow(idx, null, 
1523         "data1", "B", "num2", 5, "key3", "K1", "value", "value13");
1524     doFindByRow(idx, null, 
1525         "data1", "B", "value", "value4");
1526 
1527     Column col = idx.getTable().getColumn("data1");
1528     doFindValue(idx, 21, col, "C");
1529     doFindValue(idx, null, col, "Z");    
1530     col = idx.getTable().getColumn("value");
1531     doFindValue(idx, 21, col, "value21");
1532     doFindValue(idx, null, col, "valueZ");    
1533   }
1534 
1535   private static void doFindFirstByEntry(IndexCursor c, Integer expectedId,
1536                                          Object... entry)
1537     throws Exception
1538   {
1539     if(expectedId != null) {
1540       assertTrue(c.findFirstRowByEntry(entry));
1541       assertEquals(expectedId, c.getCurrentRow().get("id"));
1542     } else {
1543       assertFalse(c.findFirstRowByEntry(entry));
1544     }
1545   }
1546     
1547   private static void doFindByEntryRange(IndexCursor c, int start, int end,
1548                                          Object... entry)
1549   {
1550     List<Integer> expectedIds = new ArrayList<Integer>();
1551     for(int i = start; i <= end; ++i) {
1552       expectedIds.add(i);
1553     }
1554     doFindByEntry(c, expectedIds, entry);
1555   }
1556     
1557   private static void doFindByEntry(IndexCursor c, int[] ids,
1558                                     Object... entry)
1559   {
1560     List<Integer> expectedIds = new ArrayList<Integer>();
1561     for(int id : ids) {
1562       expectedIds.add(id);
1563     }
1564     doFindByEntry(c, expectedIds, entry);
1565   }
1566     
1567   private static void doFindByEntry(IndexCursor c, List<Integer> expectedIds,
1568                                     Object... entry)
1569   {
1570     List<Integer> foundIds = new ArrayList<Integer>();
1571     for(Row row : c.newEntryIterable(entry)) {
1572       foundIds.add((Integer)row.get("id"));
1573     }
1574     assertEquals(expectedIds, foundIds);
1575   }
1576 
1577   private static void doFindByRow(Index idx, Integer id, Object... rowPairs)
1578     throws Exception
1579   {
1580     Map<String,Object> map = createExpectedRow(
1581         rowPairs);
1582     Row r = CursorBuilder.findRow(idx, map);
1583     if(id != null) {
1584       assertEquals(id, r.get("id"));
1585     } else {
1586       assertNull(r);
1587     }
1588   } 
1589 
1590   private static void doFindValue(Index idx, Integer id, 
1591                                   Column columnPattern, Object valuePattern)
1592     throws Exception
1593   {
1594     Object value = CursorBuilder.findValue(
1595         idx, idx.getTable().getColumn("id"), columnPattern, valuePattern);
1596     if(id != null) {
1597       assertEquals(id, value);
1598     } else {
1599       assertNull(value);
1600     }
1601   } 
1602 }
1603