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