提交 d11e8557 authored 作者: Thomas Mueller's avatar Thomas Mueller

A tool to migrate an old database from the non-page store format to the newest version.

上级 2a680d0b
...@@ -18,7 +18,10 @@ Change Log ...@@ -18,7 +18,10 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>When using temporary table, the database didn't shrink sometimes when closing. <ul><li>A tool to migrate an old database from the non-page store format to the
newest version has been added in src/tools/org/h2/dev/util/Migrate.java. This file is not
included in the jar file currently.
</li><li>When using temporary table, the database didn't shrink sometimes when closing.
</li><li>Large transactions could run out of heap space. Fixed. </li><li>Large transactions could run out of heap space. Fixed.
</li><li>The default setting for the system property h2.webMaxValueLength is now 100000 (it was 10000 before). </li><li>The default setting for the system property h2.webMaxValueLength is now 100000 (it was 10000 before).
</li><li>Creating a database was delayed about 2 seconds if the directory didn't exist. </li><li>Creating a database was delayed about 2 seconds if the directory didn't exist.
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.util;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import org.h2.tools.RunScript;
/**
* Migrate a H2 database version 1.1.x (page store not enabled) to 1.2.x (page
* store format). This will download the H2 jar file version 1.2.127 from
* maven.org if it doesn't exist, execute the Script tool (using Runtime.exec)
* to create a backup.sql script, rename the old database file to *.backup,
* created a new database (using the H2 jar file in the class path) using the
* Script tool, and then delete the backup.sql file. Most utility methods are
* copied from h2/src/tools/org/h2/build/BuildBase.java.
*/
public class Migrate {
private static final String USER = "sa";
private static final String PASSWORD = "sa";
private static final File OLD_H2_FILE = new File("./h2-1.2.127.jar");
private static final String DOWNLOAD_URL = "http://repo2.maven.org/maven2/com/h2database/h2/1.2.127/h2-1.2.127.jar";
private static final String CHECKSUM = "056e784c7cf009483366ab9cd8d21d02fe47031a";
private static final String TEMP_SCRIPT = "backup.sql";
private PrintStream sysOut = System.out;
private boolean quiet;
/**
* Migrate databases. The user name and password are both "sa".
*
* @param args the path (default is the current directory)
* @throws Exception if conversion fails
*/
public static void main(String... args) throws Exception {
new Migrate().execute(new File(args.length == 1 ? args[0] : "."), true, USER, PASSWORD, false);
}
/**
* Migrate a database.
*
* @param file the database file (must end with .data.db) or directory
* @param recursive if the file parameter is in fact a directory (in which
* case the directory is scanned recursively)
* @param user the user name of the database
* @param password the password
* @param runQuiet to run in quiet mode
* @throws Exception if conversion fails
*/
public void execute(File file, boolean recursive, String user, String password, boolean runQuiet) throws Exception {
this.quiet = runQuiet;
if (file.isDirectory() && recursive) {
for (File f : file.listFiles()) {
execute(f, recursive, user, password, runQuiet);
}
return;
}
if (!file.getName().endsWith(".data.db")) {
return;
}
println("Migrating " + file.getName());
if (!OLD_H2_FILE.exists()) {
download(OLD_H2_FILE.getAbsolutePath(), DOWNLOAD_URL, CHECKSUM);
}
String url = "jdbc:h2:" + file.getAbsolutePath();
url = url.substring(0, url.length() - ".data.db".length());
StringList args = args(
"-Xmx128m",
"-cp", OLD_H2_FILE.getAbsolutePath(),
"org.h2.tools.Script",
"-script", TEMP_SCRIPT,
"-url", url,
"-user", user,
"-password", password);
exec("java", args);
file.renameTo(new File(file.getAbsoluteFile() + ".backup"));
RunScript.execute(url, user, password, TEMP_SCRIPT, "UTF-8", true);
new File(TEMP_SCRIPT).delete();
}
private StringList args(String...args) {
return new StringList(args);
}
private void download(String target, String fileURL, String sha1Checksum) {
File targetFile = new File(target);
if (targetFile.exists()) {
return;
}
mkdirs(targetFile.getAbsoluteFile().getParentFile());
ByteArrayOutputStream buff = new ByteArrayOutputStream();
try {
println("Downloading " + fileURL);
URL url = new URL(fileURL);
InputStream in = new BufferedInputStream(url.openStream());
long last = System.currentTimeMillis();
int len = 0;
while (true) {
long now = System.currentTimeMillis();
if (now > last + 1000) {
println("Downloaded " + len + " bytes");
last = now;
}
int x = in.read();
len++;
if (x < 0) {
break;
}
buff.write(x);
}
in.close();
} catch (IOException e) {
throw new RuntimeException("Error downloading", e);
}
byte[] data = buff.toByteArray();
String got = getSHA1(data);
if (sha1Checksum == null) {
println("SHA1 checksum: " + got);
} else {
if (!got.equals(sha1Checksum)) {
throw new RuntimeException("SHA1 checksum mismatch; got: " + got);
}
}
writeFile(targetFile, data);
}
private void mkdirs(File f) {
if (!f.exists()) {
if (!f.mkdirs()) {
throw new RuntimeException("Can not create directory " + f.getAbsolutePath());
}
}
}
private void println(String s) {
if (!quiet) {
sysOut.println(s);
}
}
private void print(String s) {
if (!quiet) {
sysOut.print(s);
}
}
private String getSHA1(byte[] data) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
return convertBytesToString(md.digest(data));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
private String convertBytesToString(byte[] value) {
StringBuilder buff = new StringBuilder(value.length * 2);
for (byte c : value) {
int x = c & 0xff;
buff.append(Integer.toString(x >> 4, 16)).
append(Integer.toString(x & 0xf, 16));
}
return buff.toString();
}
public static void writeFile(File file, byte[] data) {
try {
RandomAccessFile ra = new RandomAccessFile(file, "rw");
ra.write(data);
ra.setLength(data.length);
ra.close();
} catch (IOException e) {
throw new RuntimeException("Error writing to file " + file, e);
}
}
private int exec(String command, StringList args) {
try {
print(command);
StringList cmd = new StringList();
cmd = cmd.plus(command);
if (args != null) {
for (String a : args) {
print(" " + a);
}
cmd.addAll(args);
}
println("");
Process p = Runtime.getRuntime().exec(cmd.array());
copyInThread(p.getInputStream(), quiet ? null : sysOut);
copyInThread(p.getErrorStream(), quiet ? null : sysOut);
p.waitFor();
return p.exitValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void copyInThread(final InputStream in, final OutputStream out) {
new Thread() {
public void run() {
try {
while (true) {
int x = in.read();
if (x < 0) {
return;
}
if (out != null) {
out.write(x);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} .start();
}
/**
* A list of strings.
*/
public static class StringList extends ArrayList<String> {
private static final long serialVersionUID = 1L;
StringList(String... args) {
super();
addAll(Arrays.asList(args));
}
public StringList plus(String...args) {
StringList newList = new StringList();
newList.addAll(this);
newList.addAll(Arrays.asList(args));
return newList;
}
public String[] array() {
String[] list = new String[size()];
for (int i = 0; i < size(); i++) {
list[i] = get(i);
}
return list;
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论