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.util.CaseInsensitiveColumnMatcher;
34  import com.healthmarketscience.jackcess.util.ColumnMatcher;
35  import com.healthmarketscience.jackcess.util.RowFilterTest;
36  import com.healthmarketscience.jackcess.util.SimpleColumnMatcher;
37  import junit.framework.TestCase;
38  import static com.healthmarketscience.jackcess.TestUtil.*;
39  
40  /**
41   * @author James Ahlborn
42   */
43  public class CursorTest extends TestCase {
44  
45    static final List<TestDB> INDEX_CURSOR_DBS = 
46      TestDB.getSupportedForBasename(Basename.INDEX_CURSOR);
47  
48  
49    public CursorTest(String name) throws Exception {
50      super(name);
51    }
52  
53    @Override
54    protected void setUp() {
55      TestUtil.setTestAutoSync(false);
56    }
57  
58    @Override
59    protected void tearDown() {
60      TestUtil.clearTestAutoSync();
61    }
62  
63    private static List<Map<String,Object>> createTestTableData()
64      throws Exception
65    {
66      List<Map<String,Object>> expectedRows =
67        new ArrayList<Map<String,Object>>();
68      for(int i = 0; i < 10; ++i) {
69        expectedRows.add(createExpectedRow("id", i, "value", "data" + i));
70      }
71      return expectedRows;
72    }
73  
74    private static List<Map<String,Object>> createTestTableData(
75        int startIdx,
76        int endIdx)
77      throws Exception
78    {
79      List<Map<String,Object>> expectedRows = createTestTableData();
80      expectedRows.subList(endIdx, expectedRows.size()).clear();
81      expectedRows.subList(0, startIdx).clear();
82      return expectedRows;
83    }
84    
85    private static Database createTestTable(final FileFormat fileFormat) 
86      throws Exception 
87    {
88      Database db = createMem(fileFormat);
89  
90      Table table = new TableBuilder("test")
91        .addColumn(new ColumnBuilder("id", DataType.LONG))
92        .addColumn(new ColumnBuilder("value", DataType.TEXT))
93        .toTable(db);
94  
95      for(Map<String,Object> row : createTestTableData()) {
96        table.addRow(row.get("id"), row.get("value"));
97      }
98  
99      return db;
100   }
101 
102   private static List<Map<String,Object>> createUnorderedTestTableData()
103     throws Exception
104   {
105     List<Map<String,Object>> expectedRows =
106       new ArrayList<Map<String,Object>>();
107     int[] ids = new int[]{3, 7, 6, 1, 2, 9, 0, 5, 4, 8};
108     for(int i : ids) {
109       expectedRows.add(createExpectedRow("id", i, "value", "data" + i));
110     }
111     return expectedRows;
112   }  
113 
114   static Database createTestIndexTable(final TestDB indexCursorDB) 
115     throws Exception 
116   {
117     Database db = openMem(indexCursorDB);
118 
119     Table table = db.getTable("test");
120 
121     for(Map<String,Object> row : createUnorderedTestTableData()) {
122       table.addRow(row.get("id"), row.get("value"));
123     }
124 
125     return db;
126   }
127 
128   private static List<Map<String,Object>> createDupeTestTableData()
129     throws Exception
130   {
131     List<Map<String,Object>> expectedRows =
132       new ArrayList<Map<String,Object>>();
133     int[] ids = new int[]{3, 7, 6, 1, 2, 9, 0, 5, 4, 8};
134     for(int i : ids) {
135       expectedRows.add(createExpectedRow("id", i, "value", "data" + (i % 3)));
136     }
137     for(int i : ids) {
138       expectedRows.add(createExpectedRow("id", i, "value", "data" + (i % 5)));
139     }
140     return expectedRows;
141   }
142 
143   private static Database createDupeTestTable(final FileFormat fileFormat) 
144     throws Exception 
145   {
146     Database db = createMem(fileFormat);
147 
148     Table table = new TableBuilder("test")
149       .addColumn(new ColumnBuilder("id", DataType.LONG))
150       .addColumn(new ColumnBuilder("value", DataType.TEXT))
151       .toTable(db);
152 
153     for(Map<String,Object> row : createDupeTestTableData()) {
154       table.addRow(row.get("id"), row.get("value"));
155     }
156 
157     return db;
158   }
159 
160   static Database createDupeTestTable(final TestDB indexCursorDB) 
161     throws Exception 
162   {
163     Database db = openMem(indexCursorDB);
164 
165     Table table = db.getTable("test");
166 
167     for(Map<String,Object> row : createDupeTestTableData()) {
168       table.addRow(row.get("id"), row.get("value"));
169     }
170 
171     return db;
172   }
173 
174   private static Cursor createIndexSubRangeCursor(Table table,
175                                                   Index idx,
176                                                   int type)
177     throws Exception
178   {
179     return table.newCursor()
180       .setIndex(idx)
181       .setStartEntry(3 - type)
182       .setStartRowInclusive(type == 0)
183       .setEndEntry(8 + type)
184       .setEndRowInclusive(type == 0)
185       .toCursor();
186   }
187   
188   public void testRowId() throws Exception {
189     // test special cases
190     RowIdImpl rowId1 = new RowIdImpl(1, 2);
191     RowIdImpl rowId2 = new RowIdImpl(1, 3);
192     RowIdImpl rowId3 = new RowIdImpl(2, 1);
193 
194     List<RowIdImpl> sortedRowIds =
195       new ArrayList<RowIdImpl>(new TreeSet<RowIdImpl>(
196         Arrays.asList(rowId1, rowId2, rowId3, RowIdImpl.FIRST_ROW_ID,
197                       RowIdImpl.LAST_ROW_ID)));
198 
199     assertEquals(Arrays.asList(RowIdImpl.FIRST_ROW_ID, rowId1, rowId2, rowId3,
200                                RowIdImpl.LAST_ROW_ID),
201                  sortedRowIds);
202   }
203   
204   public void testSimple() throws Exception {
205     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
206       Database db = createTestTable(fileFormat);
207 
208       Table table = db.getTable("test");
209       Cursor cursor = CursorBuilder.createCursor(table);
210       doTestSimple(cursor, null);
211       db.close();
212     }
213   }
214 
215   private static void doTestSimple(Cursor cursor,
216                                    List<Map<String, Object>> expectedRows)
217     throws Exception
218   {
219     if(expectedRows == null) {
220       expectedRows = createTestTableData();
221     }
222 
223     List<Map<String, Object>> foundRows =
224       new ArrayList<Map<String, Object>>();
225     for(Map<String, Object> row : cursor) {
226       foundRows.add(row);
227     }
228     assertEquals(expectedRows, foundRows);
229   }
230 
231   public void testMove() throws Exception {
232     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
233       Database db = createTestTable(fileFormat);
234 
235       Table table = db.getTable("test");
236       Cursor cursor = CursorBuilder.createCursor(table);
237       doTestMove(cursor, null);
238 
239       db.close();
240     }
241   }
242 
243   private static void doTestMove(Cursor cursor,
244                                  List<Map<String, Object>> expectedRows)
245     throws Exception
246   {
247     if(expectedRows == null) {
248       expectedRows = createTestTableData();
249     }
250     expectedRows.subList(1, 4).clear();
251 
252     List<Map<String, Object>> foundRows =
253       new ArrayList<Map<String, Object>>();
254     assertTrue(cursor.isBeforeFirst());
255     assertFalse(cursor.isAfterLast());
256     foundRows.add(cursor.getNextRow());
257     assertEquals(3, cursor.moveNextRows(3));
258     assertFalse(cursor.isBeforeFirst());
259     assertFalse(cursor.isAfterLast());
260 
261     Map<String,Object> expectedRow = cursor.getCurrentRow();
262     Cursor.Savepoint savepoint = cursor.getSavepoint();
263     assertEquals(2, cursor.movePreviousRows(2));
264     assertEquals(2, cursor.moveNextRows(2));
265     assertTrue(cursor.moveToNextRow());
266     assertTrue(cursor.moveToPreviousRow());
267     assertEquals(expectedRow, cursor.getCurrentRow());
268     
269     while(cursor.moveToNextRow()) {
270       foundRows.add(cursor.getCurrentRow());
271     }
272     assertEquals(expectedRows, foundRows);
273     assertFalse(cursor.isBeforeFirst());
274     assertTrue(cursor.isAfterLast());
275 
276     assertEquals(0, cursor.moveNextRows(3));
277 
278     cursor.beforeFirst();
279     assertTrue(cursor.isBeforeFirst());
280     assertFalse(cursor.isAfterLast());
281 
282     cursor.afterLast();
283     assertFalse(cursor.isBeforeFirst());
284     assertTrue(cursor.isAfterLast());
285 
286     cursor.restoreSavepoint(savepoint);
287     assertEquals(expectedRow, cursor.getCurrentRow());    
288   }
289 
290   public void testMoveNoReset() throws Exception {
291     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
292       Database db = createTestTable(fileFormat);
293 
294       Table table = db.getTable("test");
295       Cursor cursor = CursorBuilder.createCursor(table);
296       doTestMoveNoReset(cursor);
297 
298       db.close();
299     }
300   }
301 
302   private static void doTestMoveNoReset(Cursor cursor)
303     throws Exception
304   {
305     List<Map<String, Object>> expectedRows = createTestTableData();
306     List<Map<String, Object>> foundRows = new ArrayList<Map<String, Object>>();
307     
308     Iterator<Row> iter = cursor.newIterable().iterator();
309 
310     for(int i = 0; i < 6; ++i) {
311       foundRows.add(iter.next());
312     }    
313 
314     iter = cursor.newIterable().reset(false).reverse().iterator();
315     iter.next();
316     Map<String, Object> row = iter.next();
317     assertEquals(expectedRows.get(4), row);
318     
319     iter = cursor.newIterable().reset(false).iterator();
320     iter.next();
321     row = iter.next();
322     assertEquals(expectedRows.get(5), row);
323     iter.next();
324 
325     iter = cursor.newIterable().reset(false).iterator();
326     for(int i = 6; i < 10; ++i) {
327       foundRows.add(iter.next());
328     }    
329 
330     assertEquals(expectedRows, foundRows);
331   }
332   
333   public void testSearch() throws Exception {
334     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
335       Database db = createTestTable(fileFormat);
336 
337       Table table = db.getTable("test");
338       Cursor cursor = CursorBuilder.createCursor(table);
339       doTestSearch(table, cursor, null, 42, -13);
340 
341       db.close();
342     }
343   }
344 
345   private static void doTestSearch(Table table, Cursor cursor, Index index,
346                                    Integer... outOfRangeValues)
347     throws Exception
348   {
349     assertTrue(cursor.findFirstRow(table.getColumn("id"), 3));
350     assertEquals(createExpectedRow("id", 3,
351                                    "value", "data" + 3),
352                  cursor.getCurrentRow());
353 
354     assertTrue(cursor.findFirstRow(createExpectedRow(
355                                     "id", 6,
356                                     "value", "data" + 6)));
357     assertEquals(createExpectedRow("id", 6,
358                                    "value", "data" + 6),
359                  cursor.getCurrentRow());
360 
361     assertFalse(cursor.findFirstRow(createExpectedRow(
362                                    "id", 8,
363                                    "value", "data" + 13)));
364     assertFalse(cursor.findFirstRow(table.getColumn("id"), 13));
365     assertEquals(createExpectedRow("id", 6,
366                                    "value", "data" + 6),
367                  cursor.getCurrentRow());
368 
369     assertTrue(cursor.findFirstRow(createExpectedRow(
370                                     "value", "data" + 7)));
371     assertEquals(createExpectedRow("id", 7,
372                                    "value", "data" + 7),
373                  cursor.getCurrentRow());
374     
375     assertTrue(cursor.findFirstRow(table.getColumn("value"), "data" + 4));
376     assertEquals(createExpectedRow("id", 4,
377                                    "value", "data" + 4),
378                  cursor.getCurrentRow());
379 
380     for(Integer outOfRangeValue : outOfRangeValues) {
381       assertFalse(cursor.findFirstRow(table.getColumn("id"),
382                                  outOfRangeValue));
383       assertFalse(cursor.findFirstRow(table.getColumn("value"),
384                                  "data" + outOfRangeValue));
385       assertFalse(cursor.findFirstRow(createExpectedRow(
386                                      "id", outOfRangeValue,
387                                      "value", "data" + outOfRangeValue)));
388     }
389     
390     assertEquals("data" + 5,
391                  CursorBuilder.findValue(table,
392                                   table.getColumn("value"),
393                                   table.getColumn("id"), 5));
394     assertEquals(createExpectedRow("id", 5,
395                                    "value", "data" + 5),
396                  CursorBuilder.findRow(table,
397                                 createExpectedRow("id", 5)));
398     if(index != null) {
399       assertEquals("data" + 5,
400                    CursorBuilder.findValue(index,
401                                     table.getColumn("value"),
402                                     table.getColumn("id"), 5));
403       assertEquals(createExpectedRow("id", 5,
404                                      "value", "data" + 5),
405                    CursorBuilder.findRow(index,
406                                   createExpectedRow("id", 5)));
407 
408       assertNull(CursorBuilder.findValue(index,
409                                   table.getColumn("value"),
410                                   table.getColumn("id"),
411                                   -17));
412       assertNull(CursorBuilder.findRow(index,
413                                 createExpectedRow("id", 13)));
414     }
415   }
416 
417   public void testReverse() throws Exception {
418     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
419       Database db = createTestTable(fileFormat);
420 
421       Table table = db.getTable("test");
422       Cursor cursor = CursorBuilder.createCursor(table);
423       doTestReverse(cursor, null);
424 
425       db.close();
426     }
427   }
428 
429   private static void doTestReverse(Cursor cursor,
430                                     List<Map<String, Object>> expectedRows)
431     throws Exception
432   {
433     if(expectedRows == null) {
434       expectedRows = createTestTableData();
435     }
436     Collections.reverse(expectedRows);
437 
438     List<Map<String, Object>> foundRows =
439       new ArrayList<Map<String, Object>>();
440     for(Map<String, Object> row : cursor.newIterable().reverse()) {
441       foundRows.add(row);
442     }
443     assertEquals(expectedRows, foundRows);    
444   }
445   
446   public void testLiveAddition() throws Exception {
447     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
448       Database db = createTestTable(fileFormat);
449 
450       Table table = db.getTable("test");
451 
452       Cursor cursor1 = CursorBuilder.createCursor(table);
453       Cursor cursor2 = CursorBuilder.createCursor(table);
454       doTestLiveAddition(table, cursor1, cursor2, 11);
455 
456       db.close();
457     }
458   }
459 
460   private static void doTestLiveAddition(Table table,
461                                          Cursor cursor1,
462                                          Cursor cursor2,
463                                          Integer newRowNum) throws Exception
464   {
465     cursor1.moveNextRows(11);
466     cursor2.moveNextRows(11);
467 
468     assertTrue(cursor1.isAfterLast());
469     assertTrue(cursor2.isAfterLast());
470 
471     table.addRow(newRowNum, "data" + newRowNum);
472     Map<String,Object> expectedRow = 
473       createExpectedRow("id", newRowNum, "value", "data" + newRowNum);
474 
475     assertFalse(cursor1.isAfterLast());
476     assertFalse(cursor2.isAfterLast());
477 
478     assertEquals(expectedRow, cursor1.getCurrentRow());
479     assertEquals(expectedRow, cursor2.getCurrentRow());
480     assertFalse(cursor1.moveToNextRow());
481     assertFalse(cursor2.moveToNextRow());
482     assertTrue(cursor1.isAfterLast());
483     assertTrue(cursor2.isAfterLast());
484   }
485 
486   
487   public void testLiveDeletion() throws Exception {
488     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
489       Database db = createTestTable(fileFormat);
490 
491       Table table = db.getTable("test");
492 
493       Cursor cursor1 = CursorBuilder.createCursor(table);
494       Cursor cursor2 = CursorBuilder.createCursor(table);
495       Cursor cursor3 = CursorBuilder.createCursor(table);
496       Cursor cursor4 = CursorBuilder.createCursor(table);
497       doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 1);
498 
499       db.close();
500     }
501   }
502 
503   private static void doTestLiveDeletion(
504           Cursor cursor1,
505           Cursor cursor2,
506           Cursor cursor3,
507           Cursor cursor4,
508           int firstValue) throws Exception
509   {
510     assertEquals(2, cursor1.moveNextRows(2));
511     assertEquals(3, cursor2.moveNextRows(3));
512     assertEquals(3, cursor3.moveNextRows(3));
513     assertEquals(4, cursor4.moveNextRows(4));
514 
515     Map<String,Object> expectedPrevRow =
516       createExpectedRow("id", firstValue, "value", "data" + firstValue);
517     ++firstValue;
518     Map<String,Object> expectedDeletedRow =
519       createExpectedRow("id", firstValue, "value", "data" + firstValue);
520     ++firstValue;
521     Map<String,Object> expectedNextRow =
522       createExpectedRow("id", firstValue, "value", "data" + firstValue);
523 
524     assertEquals(expectedDeletedRow, cursor2.getCurrentRow());
525     assertEquals(expectedDeletedRow, cursor3.getCurrentRow());
526     
527     assertFalse(cursor2.isCurrentRowDeleted());
528     assertFalse(cursor3.isCurrentRowDeleted());
529 
530     cursor2.deleteCurrentRow();
531 
532     assertTrue(cursor2.isCurrentRowDeleted());
533     assertTrue(cursor3.isCurrentRowDeleted());
534 
535     assertEquals(expectedNextRow, cursor1.getNextRow());
536     assertEquals(expectedNextRow, cursor2.getNextRow());
537     assertEquals(expectedNextRow, cursor3.getNextRow());
538     
539     assertEquals(expectedPrevRow, cursor3.getPreviousRow());
540 
541     assertTrue(cursor3.moveToNextRow());
542     cursor3.deleteCurrentRow();
543     assertTrue(cursor3.isCurrentRowDeleted());
544 
545     firstValue += 2;
546     expectedNextRow =
547       createExpectedRow("id", firstValue, "value", "data" + firstValue);
548     assertTrue(cursor3.moveToNextRow());
549     assertEquals(expectedNextRow, cursor3.getNextRow());
550 
551     cursor1.beforeFirst();
552     assertTrue(cursor1.moveToNextRow());
553     cursor1.deleteCurrentRow();
554     assertFalse(cursor1.isBeforeFirst());
555     assertFalse(cursor1.isAfterLast());
556     assertFalse(cursor1.moveToPreviousRow());
557     assertTrue(cursor1.isBeforeFirst());
558     assertFalse(cursor1.isAfterLast());
559 
560     cursor1.afterLast();
561     assertTrue(cursor1.moveToPreviousRow());
562     cursor1.deleteCurrentRow();
563     assertFalse(cursor1.isBeforeFirst());
564     assertFalse(cursor1.isAfterLast());
565     assertFalse(cursor1.moveToNextRow());
566     assertFalse(cursor1.isBeforeFirst());
567     assertTrue(cursor1.isAfterLast());
568 
569     cursor1.beforeFirst();
570     while(cursor1.moveToNextRow()) {
571       cursor1.deleteCurrentRow();
572     }
573 
574     assertTrue(cursor1.isAfterLast());
575     assertTrue(cursor2.isCurrentRowDeleted());
576     assertTrue(cursor3.isCurrentRowDeleted());
577     assertTrue(cursor4.isCurrentRowDeleted());
578   }
579 
580   public void testSimpleIndex() throws Exception {
581     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
582       Database db = createTestIndexTable(indexCursorDB);
583 
584       Table table = db.getTable("test");
585       Index idx = table.getIndexes().get(0);
586 
587       assertTable(createUnorderedTestTableData(), table);
588 
589       Cursor cursor = CursorBuilder.createCursor(idx);
590       doTestSimple(cursor, null);
591 
592       db.close();
593     }
594   }
595 
596   public void testMoveIndex() throws Exception {
597     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
598       Database db = createTestIndexTable(indexCursorDB);
599 
600       Table table = db.getTable("test");
601       Index idx = table.getIndexes().get(0);
602       Cursor cursor = CursorBuilder.createCursor(idx);
603       doTestMove(cursor, null);
604 
605       db.close();
606     }
607   }
608   
609   public void testReverseIndex() throws Exception {
610     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
611       Database db = createTestIndexTable(indexCursorDB);
612 
613       Table table = db.getTable("test");
614       Index idx = table.getIndexes().get(0);
615       Cursor cursor = CursorBuilder.createCursor(idx);
616       doTestReverse(cursor, null);
617 
618       db.close();
619     }
620   }
621 
622   public void testSearchIndex() throws Exception {
623     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
624       Database db = createTestIndexTable(indexCursorDB);
625 
626       Table table = db.getTable("test");
627       Index idx = table.getIndexes().get(0);
628       Cursor cursor = CursorBuilder.createCursor(idx);
629       doTestSearch(table, cursor, idx, 42, -13);
630 
631       db.close();
632     }
633   }
634 
635   public void testLiveAdditionIndex() throws Exception {
636     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
637       Database db = createTestIndexTable(indexCursorDB);
638 
639       Table table = db.getTable("test");
640       Index idx = table.getIndexes().get(0);
641 
642       Cursor cursor1 = CursorBuilder.createCursor(idx);
643       Cursor cursor2 = CursorBuilder.createCursor(idx);
644       doTestLiveAddition(table, cursor1, cursor2, 11);
645 
646       db.close();
647     }
648   }
649 
650   public void testLiveDeletionIndex() throws Exception {
651     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
652       Database db = createTestIndexTable(indexCursorDB);
653 
654       Table table = db.getTable("test");
655       Index idx = table.getIndexes().get(0);
656 
657       Cursor cursor1 = CursorBuilder.createCursor(idx);
658       Cursor cursor2 = CursorBuilder.createCursor(idx);
659       Cursor cursor3 = CursorBuilder.createCursor(idx);
660       Cursor cursor4 = CursorBuilder.createCursor(idx);
661       doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 1);
662 
663       db.close();
664     }
665   }
666 
667   public void testSimpleIndexSubRange() throws Exception {
668     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
669       for(int i = 0; i < 2; ++i) {
670         Database db = createTestIndexTable(indexCursorDB);
671 
672         Table table = db.getTable("test");
673         Index idx = table.getIndexes().get(0);
674 
675         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
676 
677         List<Map<String,Object>> expectedRows =
678           createTestTableData(3, 9);
679 
680         doTestSimple(cursor, expectedRows);
681 
682         db.close();
683       }
684     }
685   }
686   
687   public void testMoveIndexSubRange() throws Exception {
688     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
689       for(int i = 0; i < 2; ++i) {
690         Database db = createTestIndexTable(indexCursorDB);
691 
692         Table table = db.getTable("test");
693         Index idx = table.getIndexes().get(0);
694 
695         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
696 
697         List<Map<String,Object>> expectedRows =
698           createTestTableData(3, 9);
699 
700         doTestMove(cursor, expectedRows);
701 
702         db.close();
703       }
704     }
705   }
706   
707   public void testSearchIndexSubRange() throws Exception {
708     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
709       for(int i = 0; i < 2; ++i) {
710         Database db = createTestIndexTable(indexCursorDB);
711 
712         Table table = db.getTable("test");
713         Index idx = table.getIndexes().get(0);
714 
715         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
716 
717         doTestSearch(table, cursor, idx, 2, 9);
718 
719         db.close();
720       }
721     }
722   }
723 
724   public void testReverseIndexSubRange() throws Exception {
725     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
726       for(int i = 0; i < 2; ++i) {
727         Database db = createTestIndexTable(indexCursorDB);
728 
729         Table table = db.getTable("test");
730         Index idx = table.getIndexes().get(0);
731 
732         Cursor cursor = createIndexSubRangeCursor(table, idx, i);
733 
734         List<Map<String,Object>> expectedRows =
735           createTestTableData(3, 9);
736 
737         doTestReverse(cursor, expectedRows);
738 
739         db.close();
740       }
741     }
742   }
743 
744   public void testLiveAdditionIndexSubRange() throws Exception {
745     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
746       for(int i = 0; i < 2; ++i) {
747         Database db = createTestIndexTable(indexCursorDB);
748 
749         Table table = db.getTable("test");
750         Index idx = table.getIndexes().get(0);
751 
752         Cursor cursor1 = createIndexSubRangeCursor(table, idx, i);
753         Cursor cursor2 = createIndexSubRangeCursor(table, idx, i);
754 
755         doTestLiveAddition(table, cursor1, cursor2, 8);
756 
757         db.close();
758       }
759     }
760   }
761   
762   public void testLiveDeletionIndexSubRange() throws Exception {
763     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
764       for(int i = 0; i < 2; ++i) {
765         Database db = createTestIndexTable(indexCursorDB);
766 
767         Table table = db.getTable("test");
768         Index idx = table.getIndexes().get(0);
769 
770         Cursor cursor1 = createIndexSubRangeCursor(table, idx, i);
771         Cursor cursor2 = createIndexSubRangeCursor(table, idx, i);
772         Cursor cursor3 = createIndexSubRangeCursor(table, idx, i);
773         Cursor cursor4 = createIndexSubRangeCursor(table, idx, i);
774 
775         doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 4);
776 
777         db.close();
778       }
779     }
780   }
781 
782   public void testFindAllIndex() throws Exception {
783     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
784       Database db = createDupeTestTable(fileFormat);
785 
786       Table table = db.getTable("test");
787       Cursor cursor = CursorBuilder.createCursor(table);
788 
789       doTestFindAll(table, cursor, null);
790 
791       db.close();
792     }
793   }
794 
795   public void testFindAll() throws Exception {
796     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
797       Database db = createDupeTestTable(indexCursorDB);
798 
799       Table table = db.getTable("test");
800       Index idx = table.getIndexes().get(0);
801       Cursor cursor = CursorBuilder.createCursor(idx);
802 
803       doTestFindAll(table, cursor, idx);
804 
805       db.close();
806     }
807   }
808 
809   private static void doTestFindAll(Table table, Cursor cursor, Index index)
810     throws Exception
811   {
812     List<? extends Map<String,Object>> rows = RowFilterTest.toList(
813         cursor.newIterable().setMatchPattern("value", "data2"));
814 
815     List<? extends Map<String, Object>> expectedRows = null;
816 
817     if(index == null) {
818       expectedRows =
819         createExpectedTable(
820             createExpectedRow(
821                 "id", 2, "value", "data2"),
822             createExpectedRow(
823                 "id", 5, "value", "data2"),
824             createExpectedRow(
825                 "id", 8, "value", "data2"),
826             createExpectedRow(
827                 "id", 7, "value", "data2"),
828             createExpectedRow(
829                 "id", 2, "value", "data2"));
830     } else {
831       expectedRows =
832         createExpectedTable(
833             createExpectedRow(
834                 "id", 2, "value", "data2"),
835             createExpectedRow(
836                 "id", 2, "value", "data2"),
837             createExpectedRow(
838                 "id", 5, "value", "data2"),
839             createExpectedRow(
840                 "id", 7, "value", "data2"),
841             createExpectedRow(
842                 "id", 8, "value", "data2"));
843     }
844     assertEquals(expectedRows, rows);
845 
846     Column valCol = table.getColumn("value");
847     rows = RowFilterTest.toList(
848         cursor.newIterable().setMatchPattern(valCol, "data4"));
849 
850     if(index == null) {
851       expectedRows =
852         createExpectedTable(
853             createExpectedRow(
854                 "id", 9, "value", "data4"),
855             createExpectedRow(
856                 "id", 4, "value", "data4"));
857     } else {
858       expectedRows =
859         createExpectedTable(
860             createExpectedRow(
861                 "id", 4, "value", "data4"),
862             createExpectedRow(
863                 "id", 9, "value", "data4"));
864     }
865     assertEquals(expectedRows, rows);
866 
867     rows = RowFilterTest.toList(
868         cursor.newIterable().setMatchPattern(valCol, "data9"));
869 
870     assertTrue(rows.isEmpty());
871 
872     rows = RowFilterTest.toList(
873         cursor.newIterable().setMatchPattern(
874             Collections.singletonMap("id", 8)));
875     
876     expectedRows =
877       createExpectedTable(
878           createExpectedRow(
879               "id", 8, "value", "data2"),
880           createExpectedRow(
881               "id", 8, "value", "data3"));
882     assertEquals(expectedRows, rows);
883 
884     for(Map<String,Object> row : table) {
885       
886       List<Map<String,Object>> tmpRows = new ArrayList<Map<String,Object>>();
887       for(Map<String,Object> tmpRow : cursor) {
888         if(row.equals(tmpRow)) {
889           tmpRows.add(tmpRow);
890         }
891       }
892       expectedRows = tmpRows;
893       assertFalse(expectedRows.isEmpty());
894       
895       rows = RowFilterTest.toList(cursor.newIterable().setMatchPattern(row));
896 
897       assertEquals(expectedRows, rows);
898     }
899 
900     rows = RowFilterTest.toList(
901         cursor.newIterable().addMatchPattern("id", 8)
902         .addMatchPattern("value", "data13"));
903     assertTrue(rows.isEmpty());
904   }
905 
906   public void testId() throws Exception
907   {
908     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
909       Database db = createTestIndexTable(indexCursorDB);
910 
911       Table table = db.getTable("test");
912       Index idx = table.getIndexes().get(0);
913 
914       Cursor tCursor = CursorBuilder.createCursor(table);
915       Cursor iCursor = CursorBuilder.createCursor(idx);
916 
917       Cursor.Savepoint tSave = tCursor.getSavepoint();
918       Cursor.Savepoint iSave = iCursor.getSavepoint();
919 
920       tCursor.restoreSavepoint(tSave);
921       iCursor.restoreSavepoint(iSave);
922 
923       try {
924         tCursor.restoreSavepoint(iSave);
925         fail("IllegalArgumentException should have been thrown");
926       } catch(IllegalArgumentException e) {
927         // success
928       }
929 
930       try {
931         iCursor.restoreSavepoint(tSave);
932         fail("IllegalArgumentException should have been thrown");
933       } catch(IllegalArgumentException e) {
934         // success
935       }
936 
937       Cursor tCursor2 = CursorBuilder.createCursor(table);
938       Cursor iCursor2 = CursorBuilder.createCursor(idx);
939 
940       tCursor2.restoreSavepoint(tSave);
941       iCursor2.restoreSavepoint(iSave);
942 
943       db.close();
944     }
945   }
946   
947   public void testColumnMatcher() throws Exception {
948     
949 
950     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
951       Database db = createTestTable(fileFormat);
952 
953       Table table = db.getTable("test");
954 
955       doTestMatchers(table, SimpleColumnMatcher.INSTANCE, false);
956       doTestMatchers(table, CaseInsensitiveColumnMatcher.INSTANCE, true);
957 
958       Cursor cursor = CursorBuilder.createCursor(table);
959       doTestMatcher(table, cursor, SimpleColumnMatcher.INSTANCE, false);
960       doTestMatcher(table, cursor, CaseInsensitiveColumnMatcher.INSTANCE, 
961                     true);
962       db.close();
963     }
964   }
965 
966   private static void doTestMatchers(Table table, ColumnMatcher columnMatcher,
967                                      boolean caseInsensitive)
968     throws Exception
969   {
970       assertTrue(columnMatcher.matches(table, "value", null, null));
971       assertFalse(columnMatcher.matches(table, "value", "foo", null));
972       assertFalse(columnMatcher.matches(table, "value", null, "foo"));
973       assertTrue(columnMatcher.matches(table, "value", "foo", "foo"));
974       assertTrue(columnMatcher.matches(table, "value", "foo", "Foo")
975                  == caseInsensitive);
976 
977       assertFalse(columnMatcher.matches(table, "value", 13, null));
978       assertFalse(columnMatcher.matches(table, "value", null, 13));
979       assertTrue(columnMatcher.matches(table, "value", 13, 13));
980   }
981   
982   private static void doTestMatcher(Table table, Cursor cursor, 
983                                     ColumnMatcher columnMatcher,
984                                     boolean caseInsensitive)
985     throws Exception
986   {
987     cursor.setColumnMatcher(columnMatcher);
988 
989     assertTrue(cursor.findFirstRow(table.getColumn("id"), 3));
990     assertEquals(createExpectedRow("id", 3,
991                                    "value", "data" + 3),
992                  cursor.getCurrentRow());
993 
994     assertTrue(cursor.findFirstRow(createExpectedRow(
995                                     "id", 6,
996                                     "value", "data" + 6)));
997     assertEquals(createExpectedRow("id", 6,
998                                    "value", "data" + 6),
999                  cursor.getCurrentRow());
1000 
1001     assertTrue(cursor.findFirstRow(createExpectedRow(
1002                                     "id", 6,
1003                                     "value", "Data" + 6)) == caseInsensitive);
1004     if(caseInsensitive) {
1005       assertEquals(createExpectedRow("id", 6,
1006                                      "value", "data" + 6),
1007                    cursor.getCurrentRow());
1008     }
1009 
1010     assertFalse(cursor.findFirstRow(createExpectedRow(
1011                                    "id", 8,
1012                                    "value", "data" + 13)));
1013     assertFalse(cursor.findFirstRow(table.getColumn("id"), 13));
1014     assertEquals(createExpectedRow("id", 6,
1015                                    "value", "data" + 6),
1016                  cursor.getCurrentRow());
1017 
1018     assertTrue(cursor.findFirstRow(createExpectedRow(
1019                                     "value", "data" + 7)));
1020     assertEquals(createExpectedRow("id", 7,
1021                                    "value", "data" + 7),
1022                  cursor.getCurrentRow());
1023     
1024     assertTrue(cursor.findFirstRow(createExpectedRow(
1025                                     "value", "Data" + 7)) == caseInsensitive);
1026     if(caseInsensitive) {
1027       assertEquals(createExpectedRow("id", 7,
1028                                      "value", "data" + 7),
1029                    cursor.getCurrentRow());
1030     }
1031     
1032     assertTrue(cursor.findFirstRow(table.getColumn("value"), "data" + 4));
1033     assertEquals(createExpectedRow("id", 4,
1034                                    "value", "data" + 4),
1035                  cursor.getCurrentRow());
1036 
1037     assertTrue(cursor.findFirstRow(table.getColumn("value"), "Data" + 4) 
1038                == caseInsensitive);
1039     if(caseInsensitive) {
1040       assertEquals(createExpectedRow("id", 4,
1041                                      "value", "data" + 4),
1042                    cursor.getCurrentRow());
1043     }
1044 
1045     assertEquals(Arrays.asList(createExpectedRow("id", 4,
1046                                                  "value", "data" + 4)),
1047                  RowFilterTest.toList(
1048                      cursor.newIterable()
1049                      .setMatchPattern("value", "data4")
1050                      .setColumnMatcher(SimpleColumnMatcher.INSTANCE)));
1051 
1052     assertEquals(Arrays.asList(createExpectedRow("id", 3,
1053                                                  "value", "data" + 3)),
1054                  RowFilterTest.toList(
1055                      cursor.newIterable()
1056                      .setMatchPattern("value", "DaTa3")
1057                      .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE)));
1058 
1059     assertEquals(Arrays.asList(createExpectedRow("id", 2,
1060                                                  "value", "data" + 2)),
1061                  RowFilterTest.toList(
1062                      cursor.newIterable()
1063                      .addMatchPattern("value", "DaTa2")
1064                      .addMatchPattern("id", 2)
1065                      .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE)));
1066   }
1067 
1068   public void testIndexCursor() throws Exception
1069   { 
1070     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) {
1071 
1072       Database db = openMem(testDB);
1073       Table t1 = db.getTable("Table1");
1074       Index idx = t1.getIndex(IndexBuilder.PRIMARY_KEY_NAME);
1075       IndexCursor cursor = CursorBuilder.createCursor(idx);
1076 
1077       assertFalse(cursor.findFirstRowByEntry(-1));
1078       cursor.findClosestRowByEntry(-1);
1079       assertEquals(0, cursor.getCurrentRow().get("id"));
1080 
1081       assertTrue(cursor.findFirstRowByEntry(1));
1082       assertEquals(1, cursor.getCurrentRow().get("id"));
1083       
1084       cursor.findClosestRowByEntry(2);
1085       assertEquals(2, cursor.getCurrentRow().get("id"));
1086 
1087       assertFalse(cursor.findFirstRowByEntry(4));
1088       cursor.findClosestRowByEntry(4);
1089       assertTrue(cursor.isAfterLast());
1090 
1091       db.close();
1092     }    
1093   }
1094   
1095   public void testIndexCursorDelete() throws Exception
1096   { 
1097     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) {
1098 
1099       Database db = openMem(testDB);
1100       Table t1 = db.getTable("Table1");
1101       Index idx = t1.getIndex("Table2Table1");
1102       IndexCursor cursor = CursorBuilder.createCursor(idx);
1103 
1104       List<String> expectedData = new ArrayList<String>();
1105       for(Row row : cursor.newEntryIterable(1)
1106             .addColumnNames("data")) {
1107         expectedData.add(row.getString("data"));
1108       }
1109 
1110       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1111 
1112       expectedData = new ArrayList<String>();
1113       for(Iterator<? extends Row> iter = 
1114             cursor.newEntryIterable(1).iterator();
1115           iter.hasNext(); ) {
1116         expectedData.add(iter.next().getString("data"));
1117         iter.remove();
1118         try {
1119           iter.remove();
1120           fail("IllegalArgumentException should have been thrown");
1121         } catch(IllegalStateException e) {
1122           // success
1123         }
1124 
1125         if(!iter.hasNext()) {
1126           try {
1127             iter.next();
1128             fail("NoSuchElementException should have been thrown");
1129           } catch(NoSuchElementException e) {
1130             // success
1131           }
1132         }
1133       }
1134 
1135       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1136       
1137       expectedData = new ArrayList<String>();
1138       for(Row row : cursor.newEntryIterable(1)
1139             .addColumnNames("data")) {
1140         expectedData.add(row.getString("data"));
1141       }
1142 
1143       assertTrue(expectedData.isEmpty());
1144       
1145       db.close();
1146     }    
1147   }
1148   
1149   public void testCursorDelete() throws Exception
1150   { 
1151     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) {
1152 
1153       Database db = openMem(testDB);
1154       Table t1 = db.getTable("Table1");
1155       Cursor cursor = CursorBuilder.createCursor(t1);
1156 
1157       List<String> expectedData = new ArrayList<String>();
1158       for(Row row : cursor.newIterable().setColumnNames(
1159               Arrays.asList("otherfk1", "data"))) {
1160         if(row.get("otherfk1").equals(1)) {
1161           expectedData.add(row.getString("data"));
1162         }
1163       }
1164 
1165       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1166 
1167       expectedData = new ArrayList<String>();
1168       for(Iterator<? extends Row> iter = cursor.iterator();
1169           iter.hasNext(); ) {
1170         Row row = iter.next();
1171         if(row.get("otherfk1").equals(1)) {
1172           expectedData.add(row.getString("data"));
1173           iter.remove();
1174           try {
1175             iter.remove();
1176             fail("IllegalArgumentException should have been thrown");
1177           } catch(IllegalStateException e) {
1178             // success
1179           }
1180         }
1181 
1182         if(!iter.hasNext()) {
1183           try {
1184             iter.next();
1185             fail("NoSuchElementException should have been thrown");
1186           } catch(NoSuchElementException e) {
1187             // success
1188           }
1189         }
1190       }
1191 
1192       assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData);
1193       
1194       expectedData = new ArrayList<String>();
1195       for(Row row : cursor.newIterable().setColumnNames(
1196               Arrays.asList("otherfk1", "data"))) {
1197         if(row.get("otherfk1").equals(1)) {
1198           expectedData.add(row.getString("data"));
1199         }
1200       }
1201 
1202       assertTrue(expectedData.isEmpty());
1203       
1204       db.close();
1205     }    
1206   }
1207   
1208   public void testFindByRowId() throws Exception {
1209     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
1210       Database db = createTestTable(fileFormat);
1211 
1212       Table table = db.getTable("test");
1213       Cursor cursor = CursorBuilder.createCursor(table);
1214       doTestFindByRowId(cursor);
1215       db.close();
1216     }
1217   }
1218 
1219   public void testFindByRowIdIndex() throws Exception {
1220     for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
1221       Database db = createTestIndexTable(indexCursorDB);
1222 
1223       Table table = db.getTable("test");
1224       Index idx = table.getIndexes().get(0);
1225 
1226       assertTable(createUnorderedTestTableData(), table);
1227 
1228       Cursor cursor = CursorBuilder.createCursor(idx);
1229       doTestFindByRowId(cursor);
1230 
1231       db.close();
1232     }
1233   }
1234 
1235   private static void doTestFindByRowId(Cursor cursor) 
1236     throws Exception
1237   {
1238     for(int i = 0; i < 3; ++i) {
1239       cursor.moveToNextRow();
1240     } 
1241 
1242     Row r1 = cursor.getCurrentRow();
1243 
1244     for(int i = 0; i < 3; ++i) {
1245       cursor.moveToNextRow();
1246     } 
1247 
1248     Row r2 = cursor.getCurrentRow();
1249 
1250     doTestFindByRowId(cursor, r1, 2);
1251 
1252     doTestFindByRowId(cursor, r2, 5);
1253   } 
1254 
1255   private static void doTestFindByRowId(Cursor cursor, Row row, int id)
1256     throws Exception
1257   {
1258     cursor.reset();
1259     assertTrue(cursor.findRow(row.getId()));
1260     Row rFound = cursor.getCurrentRow();
1261     assertEquals(id, rFound.get("id"));
1262     assertEquals(row, rFound);
1263     Cursor.Savepoint save = cursor.getSavepoint();
1264 
1265     assertTrue(cursor.moveToNextRow());
1266     assertEquals(id + 1, cursor.getCurrentRow().get("id"));
1267 
1268     cursor.restoreSavepoint(save);
1269 
1270     assertTrue(cursor.moveToPreviousRow());
1271     assertEquals(id - 1, cursor.getCurrentRow().get("id"));
1272 
1273     assertFalse(cursor.findRow(RowIdImpl.FIRST_ROW_ID));
1274 
1275     assertEquals(id - 1, cursor.getCurrentRow().get("id"));    
1276   }
1277 
1278   public void testIterationEarlyExit() throws Exception {
1279     for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
1280 
1281       Database db = createMem(fileFormat);
1282 
1283       Table table = new TableBuilder("test")
1284         .addColumn(new ColumnBuilder("id", DataType.LONG))
1285         .addColumn(new ColumnBuilder("value", DataType.TEXT))
1286         .addColumn(new ColumnBuilder("memo", DataType.MEMO))
1287         .addIndex(new IndexBuilder("value_idx")
1288                   .addColumns("value"))
1289         .toTable(db);
1290 
1291       for(int i = 0; i < 20; ++i) {
1292         Object memo = "memo-" + i;
1293         table.addRow(i, "val-" + (i/2), memo);
1294       }
1295 
1296       // generate an "invalid" memo
1297       byte[] b = new byte[12];
1298       b[3] = (byte)0xC0;
1299       table.addRow(20, "val-9", ColumnImpl.rawDataWrapper(b));
1300 
1301       IndexCursor cursor = CursorBuilder.createCursor(
1302           table.getIndex("value_idx"));
1303       
1304       try {
1305         cursor.newIterable()
1306           .addMatchPattern("value", "val-9")
1307           .addMatchPattern("memo", "anything")
1308           .iterator().hasNext();
1309         fail("RuntimeIOException should have been thrown");
1310       } catch(RuntimeIOException ignored) {
1311         // success
1312       }
1313 
1314       List<Row> rows = new ArrayList<Row>();
1315       for (Row row : cursor.newIterable()
1316              .addMatchPattern("value", "val-5")
1317              .addMatchPattern("memo", "memo-11")) {
1318         rows.add(row);
1319       }
1320 
1321       assertEquals(rows, createExpectedTable(
1322                        createExpectedRow("id", 11,
1323                                          "value", "val-5",
1324                                          "memo", "memo-11")));
1325 
1326       assertFalse(cursor.newIterable()
1327                   .addMatchPattern("value", "val-31")
1328                   .addMatchPattern("memo", "anything")
1329                   .iterator().hasNext());
1330 
1331       db.close();
1332     }
1333   }
1334 
1335 }
1336