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