diff --git a/CHANGELOG b/CHANGELOG index 9f50adc9..33143f25 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,7 +24,8 @@ CHANGELOG 2020-Jan-05 =========== - T.Zaeschke - - Fixed missing cleanup for exmpty transactions. Tighter control over open iterators in FSM. + - Issue #115: Extend ZooCheckDb tool to show file format versions, even when not compatible. + - Fixed missing cleanup for empty transactions. Tighter control over open iterators in FSM. 2018-Dec-18 =========== diff --git a/src/org/zoodb/internal/server/SessionFactory.java b/src/org/zoodb/internal/server/SessionFactory.java index 3ce7ec53..a0c45ed8 100644 --- a/src/org/zoodb/internal/server/SessionFactory.java +++ b/src/org/zoodb/internal/server/SessionFactory.java @@ -146,4 +146,8 @@ public static void cleanUp(File dbFile) { throw DBLogger.newFatal("Failed while acessing path: " + dbFile, e); } } + + public static FileHeader readHeader(Path path) { + return SessionManager.readHeader(path); + } } diff --git a/src/org/zoodb/internal/server/SessionManager.java b/src/org/zoodb/internal/server/SessionManager.java index a731b14c..409c3d15 100644 --- a/src/org/zoodb/internal/server/SessionManager.java +++ b/src/org/zoodb/internal/server/SessionManager.java @@ -75,35 +75,14 @@ public SessionManager(Path path) { StorageChannelInput in = rootChannel.createReader(false); //read header - in.seekPageForRead(PAGE_TYPE.DB_HEADER, 0); - int fid = in.readInt(); - if (fid != DiskIO.DB_FILE_TYPE_ID) { - throw DBLogger.newFatal("This is not a ZooDB file (illegal file ID: " + fid + ")"); + FileHeader header = FileHeader.read(in); + if (!header.successfulRead()) { + file.close(); + throw DBLogger.newFatal(header.errorMsg().get(0)); } - int maj = in.readInt(); - int min = in.readInt(); - if (maj != DiskIO.DB_FILE_VERSION_MAJ) { - throw DBLogger.newFatal("Illegal major file version: " + maj + "." + min + - "; Software version: " + - DiskIO.DB_FILE_VERSION_MAJ + "." + DiskIO.DB_FILE_VERSION_MIN); - } - if (min != DiskIO.DB_FILE_VERSION_MIN) { - throw DBLogger.newFatal("Illegal minor file version: " + maj + "." + min + - "; Software version: " + - DiskIO.DB_FILE_VERSION_MAJ + "." + DiskIO.DB_FILE_VERSION_MIN); - } - - int pageSize = in.readInt(); - if (pageSize != ZooConfig.getFilePageSize()) { - //TODO actually, in this case would should just close the file and reopen it with the - //correct page size. - throw DBLogger.newFatal("Incompatible page size: " + pageSize); - } - - //main directory + rootPages[0] = header.getRootPages()[0]; + rootPages[1] = header.getRootPages()[1]; rootPage = new RootPage(); - rootPages[0] = in.readInt(); - rootPages[1] = in.readInt(); //check root pages //we have two root pages. They are used alternatingly. @@ -158,6 +137,15 @@ public SessionManager(Path path) { fileOut = rootChannel.createWriter(false); } + static FileHeader readHeader(Path path) { + StorageRoot file = createPageAccessFile(path, "rw", new FreeSpaceManager()); + IOResourceProvider rootChannel = file.getIndexChannel(); + StorageChannelInput in = rootChannel.createReader(false); + FileHeader header = FileHeader.read(in); + file.close(); + return header; + } + /** * Writes the main page. * @param pageCount diff --git a/src/org/zoodb/tools/ZooCheckDb.java b/src/org/zoodb/tools/ZooCheckDb.java index 20b01c54..d44c1e58 100644 --- a/src/org/zoodb/tools/ZooCheckDb.java +++ b/src/org/zoodb/tools/ZooCheckDb.java @@ -20,11 +20,16 @@ */ package org.zoodb.tools; +import java.nio.file.Path; +import java.nio.file.Paths; + import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import org.zoodb.internal.Session; +import org.zoodb.internal.server.FileHeader; +import org.zoodb.internal.server.SessionFactory; import org.zoodb.jdo.ZooJdoProperties; import org.zoodb.tools.internal.ZooCommandLineTool; @@ -37,7 +42,7 @@ public class ZooCheckDb extends ZooCommandLineTool { //private static final String DB_NAME = "StackBicycles"; //private static final String DB_NAME = "StackServerFault"; - public static void main(String[] args) { + public static void main(String ... args) { String dbName; if (args.length == 0) { dbName = DB_NAME; @@ -50,6 +55,29 @@ public static void main(String[] args) { return; } + //read header + out.println("Database file info: " + dbName); + Path path = Paths.get(ZooHelper.getDataStoreManager().getDbPath(dbName)); + FileHeader header = SessionFactory.readHeader(path); + if (!header.successfulRead()) { + out.println("ERROR reading file: " + header.errorMsg()); + } + out.println("magic number: " + Integer.toHexString(header.getFileID())); + out.println("format version: " + header.getVersionMajor() + "." + header.getVersionMinor()); + out.println("page size: " + header.getPageSize()); + out.print("root page IDs: "); + for (int id : header.getRootPages()) { + out.print(id + ", "); + } + out.println(); + out.println(); + + if (!header.successfulRead()) { + out.println("Aborting due to error."); + return; + } + + out.println("Checking database: " + dbName); ZooJdoProperties props = new ZooJdoProperties(dbName); diff --git a/tst/org/zoodb/test/jdo/Test_017_DatabaseVersioning.java b/tst/org/zoodb/test/jdo/Test_017_DatabaseVersioning.java new file mode 100644 index 00000000..2107f8c3 --- /dev/null +++ b/tst/org/zoodb/test/jdo/Test_017_DatabaseVersioning.java @@ -0,0 +1,230 @@ +/* + * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. + * + * This file is part of ZooDB. + * + * ZooDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ZooDB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ZooDB. If not, see . + * + * See the README and COPYING files for further information. + */ +package org.zoodb.test.jdo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import javax.jdo.PersistenceManager; +import javax.jdo.Query; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.zoodb.internal.util.DBLogger; +import org.zoodb.test.testutil.TestTools; +import org.zoodb.tools.ZooCheckDb; +import org.zoodb.tools.ZooCompareDb; + +public class Test_017_DatabaseVersioning { + + private static final String DB2 = TestTools.getDbName() + "2"; + private static final String DB_0_5_2 = "TestDb_0.5.2.zdb"; + private static final String DB_0_0_0 = "TestDb_0.0.0.zdb"; + private static final String DB_999_999_999 = "TestDb_999.999.999.zdb"; + private static final String DB_0_5_2_wrecked = "TestDb_0.5.2.wrecked.zdb"; + + + @Before + public void before() { + TestTools.createDb(); + ZooCompareDb.logToConsole = false; + } + + @After + public void after() { + ZooCompareDb.logToConsole = true; + TestTools.closePM(); + TestTools.removeDb(); + TestTools.removeDb(DB2); + removeFile( FileSystems.getDefault().getPath(TestTools.getDbFileName() + "2") ); + removeFile( FileSystems.getDefault().getPath(TestTools.getDbFileName()) ); + } + + private void removeFile(Path p) { + if (Files.exists(p)) { + try { + Files.delete(p); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private String copyDB(String orig) { + String origPath = + Test_017_DatabaseVersioning.class.getResource(orig).getPath().substring(1); + try { + Path p2 = FileSystems.getDefault().getPath(TestTools.getDbFileName() + "2"); + removeFile(p2); + Path pOrig = Paths.get(origPath); + Files.copy(pOrig, p2); + } catch (IOException e) { + throw new RuntimeException(e); + } + return origPath; + } + + private void populateSimple() { + TestTools.defineSchema(TestClass.class); + TestTools.defineIndex(TestClass.class, "_int", true); + TestTools.defineIndex(TestClass.class, "_long", false); + TestTools.defineIndex(TestClass.class, "_string", false); + TestTools.defineIndex(TestClass.class, "_double", true); + PersistenceManager pm = TestTools.openPM(); + pm.currentTransaction().begin(); + + pm.newQuery(TestClass.class).deletePersistentAll(); + + TestClass tc1 = new TestClass(); + tc1.setData(1, false, 'c', (byte)127, (short)32000, 1234567890L, "xyz", new byte[]{1,2}, + -1.1f, 35); + pm.makePersistent(tc1); + tc1 = new TestClass(); + tc1.setData(12, false, 'd', (byte)127, (short)32000, 1234567890L, "xyz", new byte[]{1,2}, + -0.1f, 34); + pm.makePersistent(tc1); + tc1 = new TestClass(); + tc1.setData(123, false, 'e', (byte)127, (short)32000, 1234567890L, "xyz", new byte[]{1,2}, + 0.1f, 3.0); + pm.makePersistent(tc1); + tc1 = new TestClass(); + tc1.setData(1234, false, 'f', (byte)127, (short)32000, 1234567890L, "xyz", new byte[]{1,2}, + 1.1f, -0.01); + pm.makePersistent(tc1); + tc1 = new TestClass(); + tc1.setData(12345, false, 'g', (byte)127, (short)32000, 1234567890L, "xyz", new byte[]{1,2}, + 11.1f, -35); + pm.makePersistent(tc1); + + pm.currentTransaction().commit(); + TestTools.closePM(); + } + + + @SuppressWarnings("unchecked") + @Test + public void testSimpleClasses_0_5_2() { + // populate DB + populateSimple(); + + // Copy DB_0_5_2 to DB2 + String db2Path = copyDB(DB_0_5_2); + + // Check DB2 file version + ZooCheckDb.enableStringOutput(); + ZooCheckDb.main(db2Path); + String output = ZooCheckDb.getStringOutput(); + assertTrue(output.contains("format version: 1.5")); + + // Compare TODO this should fail in future unless we evolve the DB + String result = ZooCompareDb.run(TestTools.getDbName(), DB2); + assertEquals("", result); + + // Run some tests + PersistenceManager pm = TestTools.openPM(DB2); + pm.currentTransaction().begin(); + // Test index query + Query q = pm.newQuery(TestClass.class, "_int == 12 || _int == 123"); + List c = (List) q.execute(); + assertTrue(c.get(0).getInt() == 12); + assertTrue(c.get(1).getInt() == 123); + assertEquals(2, c.size()); + q.close(c); + + pm.currentTransaction().commit(); + pm.close(); + } + + + @Test + public void testFailure_0_0_0() { + // Copy DB_0_5_2 to DB2 + String db2Path = copyDB(DB_0_0_0); + + // Check DB2 file version + ZooCheckDb.enableStringOutput(); + ZooCheckDb.main(db2Path); + String output = ZooCheckDb.getStringOutput(); + assertTrue(output.contains("format version: 0.0")); + assertTrue(output.contains("Illegal major file version: ")); + + try { + TestTools.openPM(DB2); + fail(); + } catch (RuntimeException e) { + DBLogger.isFatalDataStoreException(e); + assertTrue(e.getMessage().contains("Illegal major file version: ")); + } + } + + @Test + public void testFailure_999_999_999() { + // Copy DB_0_5_2 to DB2 + String db2Path = copyDB(DB_999_999_999); + + // Check DB2 file version + ZooCheckDb.enableStringOutput(); + ZooCheckDb.main(db2Path); + String output = ZooCheckDb.getStringOutput(); + assertTrue(output.contains("format version: 999.999")); + assertTrue(output.contains("Illegal major file version: ")); + + try { + TestTools.openPM(DB2); + fail(); + } catch (RuntimeException e) { + DBLogger.isFatalDataStoreException(e); + assertTrue(e.getMessage().contains("Illegal major file version: ")); + } + } + + @Test + public void testFailure_0_5_2_wrecked() { + // Copy DB_0_5_2_wrecked to DB2 + String db2Path = copyDB(DB_0_5_2_wrecked); + + // Check DB2 file version + ZooCheckDb.enableStringOutput(); + ZooCheckDb.main(db2Path); + String output = ZooCheckDb.getStringOutput(); + assertTrue(output.contains("This is not a ZooDB file (illegal file ID:")); + + // try to open DB + try { + TestTools.openPM(DB2); + fail(); + } catch (RuntimeException e) { + DBLogger.isFatalDataStoreException(e); + assertTrue(e.getMessage().contains("This is not a ZooDB file (illegal file ID:")); + } + TestTools.closePM(); + } + +} diff --git a/tstresources/org/zoodb/test/jdo/TestDb_0.0.0.zdb b/tstresources/org/zoodb/test/jdo/TestDb_0.0.0.zdb new file mode 100644 index 00000000..1f9ae5ac Binary files /dev/null and b/tstresources/org/zoodb/test/jdo/TestDb_0.0.0.zdb differ diff --git a/tstresources/org/zoodb/test/jdo/TestDb_0.5.2.wrecked.zdb b/tstresources/org/zoodb/test/jdo/TestDb_0.5.2.wrecked.zdb new file mode 100644 index 00000000..8166e9d5 Binary files /dev/null and b/tstresources/org/zoodb/test/jdo/TestDb_0.5.2.wrecked.zdb differ diff --git a/tstresources/org/zoodb/test/jdo/TestDb_0.5.2.zdb b/tstresources/org/zoodb/test/jdo/TestDb_0.5.2.zdb new file mode 100644 index 00000000..6bb8fc10 Binary files /dev/null and b/tstresources/org/zoodb/test/jdo/TestDb_0.5.2.zdb differ diff --git a/tstresources/org/zoodb/test/jdo/TestDb_999.999.999.zdb b/tstresources/org/zoodb/test/jdo/TestDb_999.999.999.zdb new file mode 100644 index 00000000..37d26621 Binary files /dev/null and b/tstresources/org/zoodb/test/jdo/TestDb_999.999.999.zdb differ