You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IndexTest.java 25KB

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