View Javadoc
1   /*
2   Copyright (c) 2015 James Ahlborn
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.impl;
18  
19  import java.io.IOException;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.UUID;
24  
25  import com.healthmarketscience.jackcess.Column;
26  import com.healthmarketscience.jackcess.ColumnBuilder;
27  import com.healthmarketscience.jackcess.CursorBuilder;
28  import com.healthmarketscience.jackcess.DataType;
29  import com.healthmarketscience.jackcess.Database;
30  import static com.healthmarketscience.jackcess.Database.*;
31  import com.healthmarketscience.jackcess.Row;
32  import com.healthmarketscience.jackcess.Table;
33  import com.healthmarketscience.jackcess.TableBuilder;
34  import static com.healthmarketscience.jackcess.TestUtil.*;
35  import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
36  import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
37  import junit.framework.TestCase;
38  
39  /**
40   *
41   * @author James Ahlborn
42   */
43  public class AutoNumberTest extends TestCase
44  {
45  
46    public AutoNumberTest(String name) throws Exception {
47      super(name);
48    }
49  
50  
51    public void testAutoNumber() throws Exception 
52    {
53      for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
54        Database db = createMem(fileFormat);
55  
56        Table table = new TableBuilder("test")
57          .addColumn(new ColumnBuilder("a", DataType.LONG)
58                    .setAutoNumber(true))
59          .addColumn(new ColumnBuilder("b", DataType.TEXT))
60          .toTable(db);
61  
62        doTestAutoNumber(table);
63  
64        db.close();
65      }
66    }  
67  
68    public void testAutoNumberPK() throws Exception 
69    {
70      for (final TestDB testDB : SUPPORTED_DBS_TEST) {
71        Database db = openMem(testDB);
72  
73        Table table = db.getTable("Table3");
74  
75        doTestAutoNumber(table);
76  
77        db.close();
78      }
79    }  
80  
81    private static void doTestAutoNumber(Table table) throws Exception
82    {
83      Object[] row = {null, "row1"};
84      assertSame(row, table.addRow(row));
85      assertEquals(1, ((Integer)row[0]).intValue());
86      row = table.addRow(13, "row2");
87      assertEquals(2, ((Integer)row[0]).intValue());
88      row = table.addRow("flubber", "row3");
89      assertEquals(3, ((Integer)row[0]).intValue());
90  
91      table.reset();
92  
93      row = table.addRow(Column.AUTO_NUMBER, "row4");
94      assertEquals(4, ((Integer)row[0]).intValue());
95      row = table.addRow(Column.AUTO_NUMBER, "row5");
96      assertEquals(5, ((Integer)row[0]).intValue());
97  
98      Object[] smallRow = {Column.AUTO_NUMBER};
99      row = table.addRow(smallRow);
100     assertNotSame(row, smallRow);
101     assertEquals(6, ((Integer)row[0]).intValue());    
102 
103     table.reset();
104 
105     List<? extends Map<String, Object>> expectedRows =
106       createExpectedTable(
107           createExpectedRow(
108               "a", 1,
109               "b", "row1"),
110           createExpectedRow(
111               "a", 2,
112               "b", "row2"),
113           createExpectedRow(
114               "a", 3,
115               "b", "row3"),
116           createExpectedRow(
117               "a", 4,
118               "b", "row4"),
119           createExpectedRow(
120               "a", 5,
121               "b", "row5"),
122           createExpectedRow(
123               "a", 6,
124               "b", null));
125 
126     assertTable(expectedRows, table);    
127   }
128 
129   public void testAutoNumberGuid() throws Exception 
130   {
131     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
132       Database db = createMem(fileFormat);
133 
134       Table table = new TableBuilder("test")
135         .addColumn(new ColumnBuilder("a", DataType.GUID)
136                   .setAutoNumber(true))
137         .addColumn(new ColumnBuilder("b", DataType.TEXT))
138         .toTable(db);
139 
140       Object[] row = {null, "row1"};
141       assertSame(row, table.addRow(row));
142       assertTrue(ColumnImpl.isGUIDValue(row[0]));
143       row = table.addRow(13, "row2");
144       assertTrue(ColumnImpl.isGUIDValue(row[0]));
145       row = table.addRow("flubber", "row3");
146       assertTrue(ColumnImpl.isGUIDValue(row[0]));
147 
148       Object[] smallRow = {Column.AUTO_NUMBER};
149       row = table.addRow(smallRow);
150       assertNotSame(row, smallRow);
151       assertTrue(ColumnImpl.isGUIDValue(row[0]));
152 
153       db.close();
154     }
155   }  
156 
157   public void testInsertLongAutoNumber() throws Exception
158   {
159     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
160       Database db = createMem(fileFormat);
161 
162       Table table = new TableBuilder("test")
163         .addColumn(new ColumnBuilder("a", DataType.LONG)
164                   .setAutoNumber(true))
165         .addColumn(new ColumnBuilder("b", DataType.TEXT))
166         .toTable(db);
167 
168       doTestInsertLongAutoNumber(table);
169 
170       db.close();
171     }    
172   }
173 
174   public void testInsertLongAutoNumberPK() throws Exception
175   {
176     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
177       Database db = createMem(fileFormat);
178 
179       Table table = new TableBuilder("test")
180         .addColumn(new ColumnBuilder("a", DataType.LONG)
181                   .setAutoNumber(true))
182         .addColumn(new ColumnBuilder("b", DataType.TEXT))
183         .setPrimaryKey("a")
184         .toTable(db);
185 
186       doTestInsertLongAutoNumber(table);
187 
188       db.close();
189     }    
190   }
191 
192   private static void doTestInsertLongAutoNumber(Table table) throws Exception
193   {
194     assertFalse(table.getDatabase().isAllowAutoNumberInsert());
195     assertFalse(table.isAllowAutoNumberInsert());
196 
197     Object[] row = {null, "row1"};
198     assertSame(row, table.addRow(row));
199     assertEquals(1, ((Integer)row[0]).intValue());
200     row = table.addRow(13, "row2");
201     assertEquals(2, ((Integer)row[0]).intValue());
202     row = table.addRow("flubber", "row3");
203     assertEquals(3, ((Integer)row[0]).intValue());
204 
205     table.reset();
206 
207     table.setAllowAutoNumberInsert(true);
208     assertFalse(table.getDatabase().isAllowAutoNumberInsert());
209     assertTrue(table.isAllowAutoNumberInsert());
210 
211     Row row2 = CursorBuilder.findRow(
212         table, Collections.singletonMap("a", 2));
213     assertEquals("row2", row2.getString("b"));
214 
215     table.deleteRow(row2);
216 
217     row = table.addRow(Column.AUTO_NUMBER, "row4");
218     assertEquals(4, ((Integer)row[0]).intValue());
219 
220     assertEquals(4, ((TableImpl)table).getLastLongAutoNumber());
221 
222     row = table.addRow(2, "row2-redux");
223     assertEquals(2, ((Integer)row[0]).intValue());
224 
225     assertEquals(4, ((TableImpl)table).getLastLongAutoNumber());
226 
227     row2 = CursorBuilder.findRow(
228         table, Collections.singletonMap("a", 2));
229     assertEquals("row2-redux", row2.getString("b"));
230 
231     row = table.addRow(13, "row13-mindthegap");
232     assertEquals(13, ((Integer)row[0]).intValue());
233 
234     assertEquals(13, ((TableImpl)table).getLastLongAutoNumber());
235     
236     try {
237       table.addRow("not a number", "nope");
238       fail("NumberFormatException should have been thrown");
239     } catch(NumberFormatException e) {
240       // success
241     }
242 
243     assertEquals(13, ((TableImpl)table).getLastLongAutoNumber());
244 
245     table.addRow(-10, "non-positives are now allowed");
246 
247     row = table.addRow(Column.AUTO_NUMBER, "row14");
248     assertEquals(14, ((Integer)row[0]).intValue());
249 
250     Row row13 = CursorBuilder.findRow(
251         table, Collections.singletonMap("a", 13));
252     assertEquals("row13-mindthegap", row13.getString("b"));
253 
254     row13.put("a", "45");
255     row13 = table.updateRow(row13);
256     assertEquals(45, row13.get("a"));
257 
258     assertEquals(45, ((TableImpl)table).getLastLongAutoNumber());
259 
260     row13.put("a", -1);  // non-positives are now allowed
261     table.updateRow(row13);
262 
263     assertEquals(45, ((TableImpl)table).getLastLongAutoNumber());
264 
265     row13.put("a", 55);
266 
267     // reset to db-level policy (which in this case is "false")
268     table.setAllowAutoNumberInsert(null);  
269 
270     row13 = table.updateRow(row13);  // no change, as confirmed by...
271     assertEquals(-1, row13.get("a"));
272 
273     assertEquals(45, ((TableImpl)table).getLastLongAutoNumber());
274     
275   }
276 
277   public void testInsertComplexAutoNumber() throws Exception
278   {
279     for(final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMPLEX)) {
280       
281       Database db = openMem(testDB);
282 
283       Table t1 = db.getTable("Table1");
284 
285       assertFalse(t1.isAllowAutoNumberInsert());
286 
287       int lastAutoNum = ((TableImpl)t1).getLastComplexTypeAutoNumber();
288 
289       Object[] row = t1.addRow("arow");
290       ++lastAutoNum;
291       checkAllComplexAutoNums(lastAutoNum, row);
292 
293       assertEquals(lastAutoNum, ((TableImpl)t1).getLastComplexTypeAutoNumber());
294 
295       db.setAllowAutoNumberInsert(true);
296       assertTrue(db.isAllowAutoNumberInsert());
297       assertTrue(t1.isAllowAutoNumberInsert());
298 
299       row = t1.addRow("anotherrow");
300       ++lastAutoNum;
301       checkAllComplexAutoNums(lastAutoNum, row);
302 
303       assertEquals(lastAutoNum, ((TableImpl)t1).getLastComplexTypeAutoNumber());
304       
305       row = t1.addRow("row5", 5, null, null, 5, 5);
306       checkAllComplexAutoNums(5, row);
307 
308       assertEquals(lastAutoNum, ((TableImpl)t1).getLastComplexTypeAutoNumber());
309 
310       row = t1.addRow("row13", 13, null, null, 13, 13);
311       checkAllComplexAutoNums(13, row);
312 
313       assertEquals(13, ((TableImpl)t1).getLastComplexTypeAutoNumber());
314 
315       try {
316         t1.addRow("nope", "not a number");
317         fail("NumberFormatException should have been thrown");
318       } catch(NumberFormatException e) {
319         // success
320       }
321 
322       assertEquals(13, ((TableImpl)t1).getLastComplexTypeAutoNumber());
323 
324       try {
325         t1.addRow("uh-uh", -10);
326         fail("IOException should have been thrown");
327       } catch(IOException e) {
328         // success
329       }
330 
331       assertEquals(13, ((TableImpl)t1).getLastComplexTypeAutoNumber());
332 
333       try {
334         t1.addRow("wut", 6, null, null, 40, 42);
335         fail("IOException should have been thrown");
336       } catch(IOException e) {
337         // success
338       }
339 
340       row = t1.addRow("morerows");
341       checkAllComplexAutoNums(14, row);
342 
343       assertEquals(14, ((TableImpl)t1).getLastComplexTypeAutoNumber());
344       
345       Row row13 = CursorBuilder.findRow(
346           t1, Collections.singletonMap("id", "row13"));
347 
348       row13.put("VersionHistory_F5F8918F-0A3F-4DA9-AE71-184EE5012880", "45");
349       row13.put("multi-value-data", "45");
350       row13.put("attach-data", "45");
351       row13 = t1.updateRow(row13);
352       checkAllComplexAutoNums(45, row13);
353 
354       assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber());
355       
356       row13.put("attach-data", -1);
357 
358       try {
359         t1.updateRow(row13);
360         fail("IOException should have been thrown");
361       } catch(IOException e) {
362         // success
363       }
364 
365       assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber());
366 
367       row13.put("attach-data", 55);
368 
369       try {
370         t1.updateRow(row13);
371         fail("IOException should have been thrown");
372       } catch(IOException e) {
373         // success
374       }
375 
376       assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber());
377 
378       row13.put("VersionHistory_F5F8918F-0A3F-4DA9-AE71-184EE5012880", 55);
379       row13.put("multi-value-data", 55);
380 
381       db.setAllowAutoNumberInsert(null);
382 
383       row13 = t1.updateRow(row13);
384       checkAllComplexAutoNums(45, row13);
385 
386       assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber());
387 
388       db.close();
389     }
390   }
391 
392   private static void checkAllComplexAutoNums(int expected, Object[] row)
393   {
394     assertEquals(expected, ((ComplexValueForeignKey)row[1]).get());
395     assertEquals(expected, ((ComplexValueForeignKey)row[4]).get());
396     assertEquals(expected, ((ComplexValueForeignKey)row[5]).get());
397   }
398 
399   private static void checkAllComplexAutoNums(int expected, Row row)
400   {
401       assertEquals(expected, ((Number)row.get("VersionHistory_F5F8918F-0A3F-4DA9-AE71-184EE5012880")).intValue());
402       assertEquals(expected, ((Number)row.get("multi-value-data")).intValue());
403       assertEquals(expected, ((Number)row.get("attach-data")).intValue());
404   }
405 
406   public void testInsertGuidAutoNumber() throws Exception 
407   {
408     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
409       Database db = createMem(fileFormat);
410 
411       Table table = new TableBuilder("test")
412         .addColumn(new ColumnBuilder("a", DataType.GUID)
413                   .setAutoNumber(true))
414         .addColumn(new ColumnBuilder("b", DataType.TEXT))
415         .toTable(db);
416 
417       db.setAllowAutoNumberInsert(true);
418       table.setAllowAutoNumberInsert(false);
419       assertFalse(table.isAllowAutoNumberInsert());
420 
421       Object[] row = {null, "row1"};
422       assertSame(row, table.addRow(row));
423       assertTrue(ColumnImpl.isGUIDValue(row[0]));
424       row = table.addRow(13, "row2");
425       assertTrue(ColumnImpl.isGUIDValue(row[0]));
426       row = table.addRow("flubber", "row3");
427       assertTrue(ColumnImpl.isGUIDValue(row[0]));
428 
429       Object[] smallRow = {Column.AUTO_NUMBER};
430       row = table.addRow(smallRow);
431       assertNotSame(row, smallRow);
432       assertTrue(ColumnImpl.isGUIDValue(row[0]));
433 
434       table.setAllowAutoNumberInsert(null);
435       assertTrue(table.isAllowAutoNumberInsert());
436       
437       Row row2 = CursorBuilder.findRow(
438           table, Collections.singletonMap("b", "row2"));
439       assertEquals("row2", row2.getString("b"));
440       
441       String row2Guid = row2.getString("a");
442       table.deleteRow(row2);
443 
444       row = table.addRow(Column.AUTO_NUMBER, "row4");
445       assertTrue(ColumnImpl.isGUIDValue(row[0]));
446 
447       row = table.addRow(row2Guid, "row2-redux");
448       assertEquals(row2Guid, row[0]);
449 
450       row2 = CursorBuilder.findRow(
451           table, Collections.singletonMap("a", row2Guid));
452       assertEquals("row2-redux", row2.getString("b"));
453 
454       try {
455         table.addRow("not a guid", "nope");
456         fail("IOException should have been thrown");
457       } catch(IOException e) {
458         // success
459       }
460 
461       row = table.addRow(Column.AUTO_NUMBER, "row5");
462       assertTrue(ColumnImpl.isGUIDValue(row[0]));
463 
464       row2Guid = UUID.randomUUID().toString();
465       row2.put("a", row2Guid);
466 
467       row2 = table.updateRow(row2);
468       assertEquals(row2Guid, row2.get("a"));
469 
470       row2.put("a", "not a guid");
471 
472       try {
473         table.updateRow(row2);
474         fail("IOException should have been thrown");
475       } catch(IOException e) {
476         // success
477       }
478 
479       table.setAllowAutoNumberInsert(false);
480 
481       row2 = table.updateRow(row2);
482       assertTrue(ColumnImpl.isGUIDValue(row2.get("a")));
483       assertFalse(row2Guid.equals(row2.get("a")));
484 
485       db.close();
486     }
487   }  
488 
489 }