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.io.IOException;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.SortedSet;
26  import java.util.TreeSet;
27  
28  import static com.healthmarketscience.jackcess.Database.*;
29  import com.healthmarketscience.jackcess.impl.ByteUtil;
30  import com.healthmarketscience.jackcess.impl.IndexCodesTest;
31  import com.healthmarketscience.jackcess.impl.IndexData;
32  import com.healthmarketscience.jackcess.impl.IndexImpl;
33  import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
34  import com.healthmarketscience.jackcess.impl.RowIdImpl;
35  import com.healthmarketscience.jackcess.impl.TableImpl;
36  import junit.framework.TestCase;
37  import static com.healthmarketscience.jackcess.TestUtil.*;
38  
39  /**
40   * @author James Ahlborn
41   */
42  public class IndexTest extends TestCase {
43  
44    public IndexTest(String name) {
45      super(name);
46    }
47  
48    @Override
49    protected void setUp() {
50      TestUtil.setTestAutoSync(false);
51    }
52  
53    @Override
54    protected void tearDown() {
55      TestUtil.clearTestAutoSync();
56    }
57  
58    public void testByteOrder() throws Exception {
59      byte b1 = (byte)0x00;
60      byte b2 = (byte)0x01;
61      byte b3 = (byte)0x7F;
62      byte b4 = (byte)0x80;
63      byte b5 = (byte)0xFF;
64  
65      assertTrue(ByteUtil.asUnsignedByte(b1) < ByteUtil.asUnsignedByte(b2));
66      assertTrue(ByteUtil.asUnsignedByte(b2) < ByteUtil.asUnsignedByte(b3));
67      assertTrue(ByteUtil.asUnsignedByte(b3) < ByteUtil.asUnsignedByte(b4));
68      assertTrue(ByteUtil.asUnsignedByte(b4) < ByteUtil.asUnsignedByte(b5));
69    }
70    
71    public void testByteCodeComparator() {
72      byte[] b0 = null;
73      byte[] b1 = new byte[]{(byte)0x00};
74      byte[] b2 = new byte[]{(byte)0x00, (byte)0x00};
75      byte[] b3 = new byte[]{(byte)0x00, (byte)0x01};
76      byte[] b4 = new byte[]{(byte)0x01};
77      byte[] b5 = new byte[]{(byte)0x80};
78      byte[] b6 = new byte[]{(byte)0xFF};
79      byte[] b7 = new byte[]{(byte)0xFF, (byte)0x00};
80      byte[] b8 = new byte[]{(byte)0xFF, (byte)0x01};
81  
82      List<byte[]> expectedList = Arrays.<byte[]>asList(b0, b1, b2, b3, b4,
83                                                        b5, b6, b7, b8);
84      SortedSet<byte[]> sortedSet = new TreeSet<byte[]>(
85          IndexData.BYTE_CODE_COMPARATOR);
86      sortedSet.addAll(expectedList);
87      assertEquals(expectedList, new ArrayList<byte[]>(sortedSet));
88      
89    }
90  
91    public void testPrimaryKey() throws Exception {
92      for (final TestDB testDB : SUPPORTED_DBS_TEST_FOR_READ) {
93        Table table = open(testDB).getTable("Table1");
94        Map<String, Boolean> foundPKs = new HashMap<String, Boolean>();
95        Index pkIndex = null;
96        for(Index index : table.getIndexes()) {
97          foundPKs.put(index.getColumns().iterator().next().getName(),
98                       index.isPrimaryKey());
99          if(index.isPrimaryKey()) {
100           pkIndex= index;
101         
102         }
103       }
104       Map<String, Boolean> expectedPKs = new HashMap<String, Boolean>();
105       expectedPKs.put("A", Boolean.TRUE);
106       expectedPKs.put("B", Boolean.FALSE);
107       assertEquals(expectedPKs, foundPKs);
108       assertSame(pkIndex, table.getPrimaryKeyIndex());
109     }
110   }
111   
112   public void testLogicalIndexes() throws Exception
113   {
114     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX, true)) {
115       Database mdb = open(testDB);
116 
117       TableImpl table = (TableImpl)mdb.getTable("Table1");
118       for(IndexImpl idx : table.getIndexes()) {
119         idx.initialize();
120       }
121       assertEquals(4, table.getIndexes().size());
122       assertEquals(4, table.getLogicalIndexCount());
123       checkIndexColumns(table,
124                         "id", "id",
125                         "PrimaryKey", "id",
126                         "Table2Table1", "otherfk1",
127                         "Table3Table1", "otherfk2");
128 
129       table = (TableImpl)mdb.getTable("Table2");
130       for(IndexImpl idx : table.getIndexes()) {
131         idx.initialize();
132       }
133       assertEquals(3, table.getIndexes().size());
134       assertEquals(2, table.getIndexDatas().size());
135       assertEquals(3, table.getLogicalIndexCount());
136       checkIndexColumns(table,
137                         "id", "id",
138                         "PrimaryKey", "id",
139                         ".rC", "id");
140 
141       IndexImpl pkIdx = table.getIndex("PrimaryKey");
142       IndexImpl fkIdx = table.getIndex(".rC");
143       assertNotSame(pkIdx, fkIdx);
144       assertTrue(fkIdx.isForeignKey());
145       assertSame(pkIdx.getIndexData(), fkIdx.getIndexData());
146       IndexData indexData = pkIdx.getIndexData();
147       assertEquals(Arrays.asList(pkIdx, fkIdx), indexData.getIndexes());
148       assertSame(pkIdx, indexData.getPrimaryIndex());
149 
150       table = (TableImpl)mdb.getTable("Table3");
151       for(IndexImpl idx : table.getIndexes()) {
152         idx.initialize();
153       }
154       assertEquals(3, table.getIndexes().size());
155       assertEquals(2, table.getIndexDatas().size());
156       assertEquals(3, table.getLogicalIndexCount());
157       checkIndexColumns(table,
158                         "id", "id",
159                         "PrimaryKey", "id",
160                         ".rC", "id");
161 
162       pkIdx = table.getIndex("PrimaryKey");
163       fkIdx = table.getIndex(".rC");
164       assertNotSame(pkIdx, fkIdx);
165       assertTrue(fkIdx.isForeignKey());
166       assertSame(pkIdx.getIndexData(), fkIdx.getIndexData());
167       indexData = pkIdx.getIndexData();
168       assertEquals(Arrays.asList(pkIdx, fkIdx), indexData.getIndexes());
169       assertSame(pkIdx, indexData.getPrimaryIndex());
170     }
171   }
172 
173   public void testComplexIndex() throws Exception
174   {
175     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMP_INDEX)) {
176       // this file has an index with "compressed" entries and node pages
177       Database db = open(testDB);
178       TableImpl t = (TableImpl)db.getTable("Table1");
179       IndexImpl index = t.getIndexes().get(0);
180       assertFalse(index.isInitialized());
181       assertEquals(512, countRows(t));
182       assertEquals(512, index.getIndexData().getEntryCount());
183       db.close();
184 
185       // copy to temp file and attempt to edit
186       db = openCopy(testDB);
187       t = (TableImpl)db.getTable("Table1");
188       index = t.getIndexes().get(0);
189 
190       t.addRow(99, "abc", "def");
191     }
192   }
193 
194   public void testEntryDeletion() throws Exception {
195     for (final TestDB testDB : SUPPORTED_DBS_TEST) {
196       Table table = openCopy(testDB).getTable("Table1");
197 
198       for(int i = 0; i < 10; ++i) {
199         table.addRow("foo" + i, "bar" + i, (byte)42 + i, (short)53 + i, 13 * i,
200                      (6.7d / i), null, null, true);
201       }
202       table.reset();
203       assertRowCount(12, table);
204 
205       for(Index index : table.getIndexes()) {
206         assertEquals(12, ((IndexImpl)index).getIndexData().getEntryCount());
207       }
208 
209       table.reset();
210       table.getNextRow();
211       table.getNextRow();
212       table.getDefaultCursor().deleteCurrentRow();
213       table.getNextRow();
214       table.getDefaultCursor().deleteCurrentRow();
215       table.getNextRow();
216       table.getNextRow();
217       table.getDefaultCursor().deleteCurrentRow();
218       table.getNextRow();
219       table.getNextRow();
220       table.getNextRow();
221       table.getDefaultCursor().deleteCurrentRow();
222 
223       table.reset();
224       assertRowCount(8, table);
225 
226       for(Index index : table.getIndexes()) {
227         assertEquals(8, ((IndexImpl)index).getIndexData().getEntryCount());
228       }
229     }
230   }
231 
232   public void testIgnoreNulls() throws Exception
233   {
234     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX_PROPERTIES)) {
235       Database db = openCopy(testDB);
236 
237       doTestIgnoreNulls(db, "TableIgnoreNulls1");
238       doTestIgnoreNulls(db, "TableIgnoreNulls2");
239 
240       db.close();
241     }
242   }
243 
244   private void doTestIgnoreNulls(Database db, String tableName)
245     throws Exception
246   {
247     Table orig = db.getTable(tableName);
248     IndexImpl origI = (IndexImpl)orig.getIndex("DataIndex");
249     Table temp = db.getTable(tableName + "_temp");
250     IndexImpl tempI = (IndexImpl)temp.getIndex("DataIndex");
251 
252     // copy from orig table to temp table
253     for(Map<String,Object> row : orig) {
254       temp.addRow(orig.asRow(row));
255     }
256 
257     assertEquals(origI.getIndexData().getEntryCount(), 
258                  tempI.getIndexData().getEntryCount());
259 
260     Cursor origC = origI.newCursor().toCursor();
261     Cursor tempC = tempI.newCursor().toCursor();
262 
263     while(true) {
264       boolean origHasNext = origC.moveToNextRow();
265       boolean tempHasNext = tempC.moveToNextRow();
266       assertTrue(origHasNext == tempHasNext);
267       if(!origHasNext) {
268         break;
269       }
270 
271       Map<String,Object> origRow = origC.getCurrentRow();
272       Cursor.Position origCurPos = origC.getSavepoint().getCurrentPosition();
273       Map<String,Object> tempRow = tempC.getCurrentRow();
274       Cursor.Position tempCurPos = tempC.getSavepoint().getCurrentPosition();
275       
276       assertEquals(origRow, tempRow);
277       assertEquals(IndexCodesTest.entryToString(origCurPos),
278                    IndexCodesTest.entryToString(tempCurPos));
279     }
280   }
281 
282   public void testUnique() throws Exception
283   {
284     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX_PROPERTIES)) {
285       Database db = openCopy(testDB);
286 
287       Table t = db.getTable("TableUnique1_temp");
288       Index index = t.getIndex("DataIndex");
289 
290       doTestUnique(index, 1,
291                    null, true,
292                    "unique data", true,
293                    null, true,
294                    "more", false,
295                    "stuff", false,
296                    "unique data", false);
297 
298       t = db.getTable("TableUnique2_temp");
299       index = t.getIndex("DataIndex");
300 
301       doTestUnique(index, 2,
302                    null, null, true,
303                    "unique data", 42, true,
304                    "unique data", null, true,
305                    null, null, true,
306                    "some", 42, true,
307                    "more unique data", 13, true,
308                    null, -4242, true,
309                    "another row", -3462, false,
310                    null, 49, false,
311                    "more", null, false,
312                    "unique data", 42, false,
313                    "unique data", null, false,
314                    null, -4242, false);
315 
316       db.close();
317     }
318   }
319 
320   private void doTestUnique(Index index, int numValues,
321                             Object... testData)
322     throws Exception
323   {
324     for(int i = 0; i < testData.length; i += (numValues + 1)) {
325       Object[] row = new Object[numValues + 1];
326       row[0] = "testRow" + i;
327       for(int j = 1; j < (numValues + 1); ++j) {
328         row[j] = testData[i + j - 1];
329       }
330       boolean expectedSuccess = (Boolean)testData[i + numValues];
331 
332       IOException failure = null;
333       try {
334         ((IndexImpl)index).getIndexData().prepareAddRow(
335             row, new RowIdImpl(400 + i, 0), null).commit();
336       } catch(IOException e) {
337         failure = e;
338       }
339       if(expectedSuccess) {
340         assertNull(failure);
341       } else {
342         assertTrue(failure != null);
343         assertTrue(failure.getMessage().contains("uniqueness"));
344       }
345     }
346   }
347 
348   public void testUniqueEntryCount() throws Exception {
349     for (final TestDB testDB : SUPPORTED_DBS_TEST) {
350       Database db = openCopy(testDB);
351       Table table = db.getTable("Table1");
352       IndexImpl indA = (IndexImpl)table.getIndex("PrimaryKey");
353       IndexImpl indB = (IndexImpl)table.getIndex("B");
354 
355       assertEquals(2, indA.getUniqueEntryCount());
356       assertEquals(2, indB.getUniqueEntryCount());
357 
358       List<String> bElems = Arrays.asList("bar", null, "baz", "argle", null,
359                                           "bazzle", "37", "bar", "bar", "BAZ");
360 
361       for(int i = 0; i < 10; ++i) {
362         table.addRow("foo" + i, bElems.get(i), (byte)42 + i, (short)53 + i,
363                      13 * i, (6.7d / i), null, null, true);
364       }
365 
366       assertEquals(12, indA.getIndexData().getEntryCount());
367       assertEquals(12, indB.getIndexData().getEntryCount());
368 
369       assertEquals(12, indA.getUniqueEntryCount());
370       assertEquals(8, indB.getUniqueEntryCount());
371 
372       table = null;
373       indA = null;
374       indB = null;
375 
376       table = db.getTable("Table1");
377       indA = (IndexImpl)table.getIndex("PrimaryKey");
378       indB = (IndexImpl)table.getIndex("B");
379 
380       assertEquals(12, indA.getIndexData().getEntryCount());
381       assertEquals(12, indB.getIndexData().getEntryCount());
382 
383       assertEquals(12, indA.getUniqueEntryCount());
384       assertEquals(8, indB.getUniqueEntryCount());
385 
386       Cursor c = CursorBuilder.createCursor(table);
387       assertTrue(c.moveToNextRow());
388 
389       final Row row = c.getCurrentRow();
390       // Row order is arbitrary, so v2007 row order difference is valid
391       if (testDB.getExpectedFileFormat().ordinal() >= 
392           Database.FileFormat.V2007.ordinal()) {
393         TestUtil.checkTestDBTable1RowA(testDB, table, row);
394       } else {
395         TestUtil.checkTestDBTable1RowABCDEFG(testDB, table, row);
396       }
397       c.deleteCurrentRow();
398 
399       assertEquals(11, indA.getIndexData().getEntryCount());
400       assertEquals(11, indB.getIndexData().getEntryCount());
401 
402       assertEquals(12, indA.getUniqueEntryCount());
403       assertEquals(8, indB.getUniqueEntryCount());
404 
405       db.close();
406     }
407   }
408   
409   public void testReplId() throws Exception
410   {
411     for (final TestDB testDB : SUPPORTED_DBS_TEST) {
412       Database db = openCopy(testDB);
413       Table table = db.getTable("Table4");
414 
415       for(int i = 0; i< 20; ++i) {
416         table.addRow("row" + i, Column.AUTO_NUMBER);
417       }
418 
419       assertEquals(20, table.getRowCount());
420 
421       db.close();
422     }
423   }
424 
425   public void testIndexCreation() throws Exception
426   {
427     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
428       Database db = create(fileFormat);
429 
430       Table t = new TableBuilder("TestTable")
431         .addColumn(new ColumnBuilder("id", DataType.LONG))
432         .addColumn(new ColumnBuilder("data", DataType.TEXT))
433         .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
434                   .addColumns("id").setPrimaryKey())
435         .toTable(db);
436 
437       assertEquals(1, t.getIndexes().size());
438       IndexImpl idx = (IndexImpl)t.getIndexes().get(0);
439       
440       assertEquals(IndexBuilder.PRIMARY_KEY_NAME, idx.getName());       
441       assertEquals(1, idx.getColumns().size());
442       assertEquals("id", idx.getColumns().get(0).getName());
443       assertTrue(idx.getColumns().get(0).isAscending());
444       assertTrue(idx.isPrimaryKey());
445       assertTrue(idx.isUnique());
446       assertFalse(idx.shouldIgnoreNulls());
447       assertNull(idx.getReference());
448 
449       t.addRow(2, "row2");
450       t.addRow(1, "row1");
451       t.addRow(3, "row3");
452 
453       Cursor c = t.newCursor()
454         .setIndexByName(IndexBuilder.PRIMARY_KEY_NAME).toCursor();
455 
456       for(int i = 1; i <= 3; ++i) {
457         Map<String,Object> row = c.getNextRow();
458         assertEquals(i, row.get("id"));
459         assertEquals("row" + i, row.get("data"));
460       }
461       assertFalse(c.moveToNextRow());
462     }    
463   }
464   
465   public void testIndexCreationSharedData() throws Exception
466   {
467     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
468       Database db = create(fileFormat);
469 
470       Table t = new TableBuilder("TestTable")
471         .addColumn(new ColumnBuilder("id", DataType.LONG))
472         .addColumn(new ColumnBuilder("data", DataType.TEXT))
473         .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
474                   .addColumns("id").setPrimaryKey())
475         .addIndex(new IndexBuilder("Index1").addColumns("id"))
476         .addIndex(new IndexBuilder("Index2").addColumns("id"))
477         .addIndex(new IndexBuilder("Index3").addColumns(false, "id"))
478         .toTable(db);
479 
480       assertEquals(4, t.getIndexes().size());
481       IndexImpl idx = (IndexImpl)t.getIndexes().get(0);
482       
483       assertEquals(IndexBuilder.PRIMARY_KEY_NAME, idx.getName());       
484       assertEquals(1, idx.getColumns().size());
485       assertEquals("id", idx.getColumns().get(0).getName());
486       assertTrue(idx.getColumns().get(0).isAscending());
487       assertTrue(idx.isPrimaryKey());
488       assertTrue(idx.isUnique());
489       assertFalse(idx.shouldIgnoreNulls());
490       assertNull(idx.getReference());
491 
492       IndexImpl idx1 = (IndexImpl)t.getIndexes().get(1);
493       IndexImpl idx2 = (IndexImpl)t.getIndexes().get(2);
494       IndexImpl idx3 = (IndexImpl)t.getIndexes().get(3);
495 
496       assertNotSame(idx.getIndexData(), idx1.getIndexData());
497       assertSame(idx1.getIndexData(), idx2.getIndexData());
498       assertNotSame(idx2.getIndexData(), idx3.getIndexData());
499 
500       t.addRow(2, "row2");
501       t.addRow(1, "row1");
502       t.addRow(3, "row3");
503 
504       Cursor c = t.newCursor()
505         .setIndexByName(IndexBuilder.PRIMARY_KEY_NAME).toCursor();
506 
507       for(int i = 1; i <= 3; ++i) {
508         Map<String,Object> row = c.getNextRow();
509         assertEquals(i, row.get("id"));
510         assertEquals("row" + i, row.get("data"));
511       }
512       assertFalse(c.moveToNextRow());
513     }
514   }
515   
516   public void testGetForeignKeyIndex() throws Exception
517   {
518     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX, true)) {
519       Database db = open(testDB);
520       Table t1 = db.getTable("Table1");
521       Table t2 = db.getTable("Table2");
522       Table t3 = db.getTable("Table3");
523 
524       IndexImpl t2t1 = (IndexImpl)t1.getIndex("Table2Table1");
525       IndexImpl t3t1 = (IndexImpl)t1.getIndex("Table3Table1");
526 
527 
528       assertTrue(t2t1.isForeignKey());
529       assertNotNull(t2t1.getReference());
530       assertFalse(t2t1.getReference().isPrimaryTable());
531       assertFalse(t2t1.getReference().isCascadeUpdates());
532       assertTrue(t2t1.getReference().isCascadeDeletes());
533       doCheckForeignKeyIndex(t1, t2t1, t2);
534 
535       assertTrue(t3t1.isForeignKey());
536       assertNotNull(t3t1.getReference());
537       assertFalse(t3t1.getReference().isPrimaryTable());
538       assertTrue(t3t1.getReference().isCascadeUpdates());
539       assertFalse(t3t1.getReference().isCascadeDeletes());
540       doCheckForeignKeyIndex(t1, t3t1, t3);
541       
542       Index t1pk = t1.getIndex(IndexBuilder.PRIMARY_KEY_NAME);
543       assertNotNull(t1pk);
544       assertNull(((IndexImpl)t1pk).getReference());
545       assertNull(t1pk.getReferencedIndex());
546     }    
547   }
548 
549   public void testConstraintViolation() throws Exception
550   {
551     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
552       Database db = create(fileFormat);
553 
554       Table t = new TableBuilder("TestTable")
555         .addColumn(new ColumnBuilder("id", DataType.LONG))
556         .addColumn(new ColumnBuilder("data", DataType.TEXT))
557         .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
558                   .addColumns("id").setPrimaryKey())
559         .addIndex(new IndexBuilder("data_ind")
560                   .addColumns("data").setUnique())
561         .toTable(db);
562 
563       for(int i = 0; i < 5; ++i) {
564         t.addRow(i, "row" + i);
565       }
566 
567       try {
568         t.addRow(3, "badrow");
569         fail("ConstraintViolationException should have been thrown");
570       } catch(ConstraintViolationException ce) {
571         // success
572       }
573 
574       assertEquals(5, t.getRowCount());
575 
576       List<Row> expectedRows =
577         createExpectedTable(
578             createExpectedRow(
579                 "id", 0, "data", "row0"),
580             createExpectedRow(
581                 "id", 1, "data", "row1"),
582             createExpectedRow(
583                 "id", 2, "data", "row2"),
584             createExpectedRow(
585                 "id", 3, "data", "row3"),
586             createExpectedRow(
587                 "id", 4, "data", "row4"));
588 
589       assertTable(expectedRows, t);
590 
591       IndexCursor pkCursor = CursorBuilder.createPrimaryKeyCursor(t);
592       assertCursor(expectedRows, pkCursor);
593 
594       assertCursor(expectedRows, 
595                    CursorBuilder.createCursor(t.getIndex("data_ind")));
596 
597       List<Object[]> batch = new ArrayList<Object[]>();
598       batch.add(new Object[]{5, "row5"});
599       batch.add(new Object[]{6, "row6"});
600       batch.add(new Object[]{7, "row2"});
601       batch.add(new Object[]{8, "row8"});
602 
603       try {
604         t.addRows(batch);
605         fail("BatchUpdateException should have been thrown");
606       } catch(BatchUpdateException be) {
607         // success
608         assertTrue(be.getCause() instanceof ConstraintViolationException);
609         assertEquals(2, be.getUpdateCount());
610       }
611 
612       expectedRows = new ArrayList<Row>(expectedRows);
613       expectedRows.add(createExpectedRow("id", 5, "data", "row5"));
614       expectedRows.add(createExpectedRow("id", 6, "data", "row6"));
615 
616       assertTable(expectedRows, t);
617       
618       assertCursor(expectedRows, pkCursor);
619 
620       assertCursor(expectedRows, 
621                    CursorBuilder.createCursor(t.getIndex("data_ind")));
622 
623       pkCursor.findFirstRowByEntry(4);
624       Row row4 = pkCursor.getCurrentRow();
625 
626       row4.put("id", 3);
627 
628       try {
629         t.updateRow(row4);
630         fail("ConstraintViolationException should have been thrown");
631       } catch(ConstraintViolationException ce) {
632         // success
633       }
634       
635       assertTable(expectedRows, t);
636       
637       assertCursor(expectedRows, pkCursor);
638 
639       assertCursor(expectedRows, 
640                    CursorBuilder.createCursor(t.getIndex("data_ind")));
641 
642       db.close();
643     }    
644   }
645 
646   public void testAutoNumberRecover() throws Exception
647   {
648     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
649       Database db = create(fileFormat);
650 
651       Table t = new TableBuilder("TestTable")
652         .addColumn(new ColumnBuilder("id", DataType.LONG).setAutoNumber(true))
653         .addColumn(new ColumnBuilder("data", DataType.TEXT))
654         .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
655                   .addColumns("id").setPrimaryKey())
656         .addIndex(new IndexBuilder("data_ind")
657                   .addColumns("data").setUnique())
658         .toTable(db);
659 
660       for(int i = 1; i < 3; ++i) {
661         t.addRow(null, "row" + i);
662       }
663 
664       try {
665         t.addRow(null, "row1");
666         fail("ConstraintViolationException should have been thrown");
667       } catch(ConstraintViolationException ce) {
668         // success
669       }
670       
671       t.addRow(null, "row3");
672 
673       assertEquals(3, t.getRowCount());
674 
675       List<Row> expectedRows =
676         createExpectedTable(
677             createExpectedRow(
678                 "id", 1, "data", "row1"),
679             createExpectedRow(
680                 "id", 2, "data", "row2"),
681             createExpectedRow(
682                 "id", 3, "data", "row3"));
683 
684       assertTable(expectedRows, t);
685 
686       IndexCursor pkCursor = CursorBuilder.createPrimaryKeyCursor(t);
687       assertCursor(expectedRows, pkCursor);
688 
689       assertCursor(expectedRows, 
690                    CursorBuilder.createCursor(t.getIndex("data_ind")));
691 
692       List<Object[]> batch = new ArrayList<Object[]>();
693       batch.add(new Object[]{null, "row4"});
694       batch.add(new Object[]{null, "row5"});
695       batch.add(new Object[]{null, "row3"});
696 
697       try {
698         t.addRows(batch);
699         fail("BatchUpdateException should have been thrown");
700       } catch(BatchUpdateException be) {
701         // success
702         assertTrue(be.getCause() instanceof ConstraintViolationException);
703         assertEquals(2, be.getUpdateCount());
704       }
705 
706       expectedRows = new ArrayList<Row>(expectedRows);
707       expectedRows.add(createExpectedRow("id", 4, "data", "row4"));
708       expectedRows.add(createExpectedRow("id", 5, "data", "row5"));
709 
710       assertTable(expectedRows, t);
711       
712       assertCursor(expectedRows, pkCursor);
713 
714       assertCursor(expectedRows, 
715                    CursorBuilder.createCursor(t.getIndex("data_ind")));
716 
717       db.close();
718     }
719   }
720   
721   public void testBinaryIndex() throws Exception
722   {
723     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.BINARY_INDEX)) {
724       Database db = open(testDB);
725 
726       Table table = db.getTable("Test");
727 
728       Index idx = table.getIndex("BinAscIdx");
729       doTestBinaryIndex(idx, "BinAsc", false);
730 
731       idx = table.getIndex("BinDscIdx");
732       doTestBinaryIndex(idx, "BinDsc", true);
733 
734       db.close();
735     }
736   }
737 
738   private static void doTestBinaryIndex(Index idx, String colName, boolean forward)
739     throws Exception
740   {
741     IndexCursor ic = CursorBuilder.createCursor(idx);
742 
743     for(Row row : idx.getTable().getDefaultCursor().newIterable().setForward(forward)) {
744       int id = row.getInt("ID");
745       byte[] data = row.getBytes(colName);
746 
747       boolean found = false;
748       for(Row idxRow : ic.newEntryIterable(data)) {
749           
750         assertTrue(Arrays.equals(data, idxRow.getBytes(colName)));
751         if(id == idxRow.getInt("ID")) {
752           found = true;
753         }
754       }
755 
756       assertTrue(found);
757     }
758   }
759 
760   private void doCheckForeignKeyIndex(Table ta, Index ia, Table tb)
761     throws Exception
762   {
763     IndexImpl ib = (IndexImpl)ia.getReferencedIndex();
764     assertNotNull(ib);
765     assertSame(tb, ib.getTable());
766 
767     assertNotNull(ib.getReference());
768     assertSame(ia, ib.getReferencedIndex());
769     assertTrue(ib.getReference().isPrimaryTable());
770   }
771 
772   private void checkIndexColumns(Table table, String... idxInfo)
773     throws Exception
774   {
775     Map<String, String> expectedIndexes = new HashMap<String, String>();
776     for(int i = 0; i < idxInfo.length; i+=2) {
777       expectedIndexes.put(idxInfo[i], idxInfo[i+1]);
778     }
779 
780     for(Index idx : table.getIndexes()) {
781       String colName = expectedIndexes.get(idx.getName());
782       assertEquals(1, idx.getColumns().size());
783       assertEquals(colName, idx.getColumns().get(0).getName());
784       if("PrimaryKey".equals(idx.getName())) {
785         assertTrue(idx.isPrimaryKey());
786       } else {
787         assertFalse(idx.isPrimaryKey());
788       }
789     }
790   }
791 
792 }