/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.upgrade;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.UUID;
import org.h2.engine.ConnectionInfo;
import org.h2.engine.Constants;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
import org.h2.util.StringUtils;
import org.h2.util.Utils;

This class starts the conversion from older database versions to the current version if the respective classes are found.
/** * This class starts the conversion from older database versions to the current * version if the respective classes are found. */
public class DbUpgrade { private static final boolean UPGRADE_CLASSES_PRESENT; private static boolean scriptInTempDir; private static boolean deleteOldDb; static { UPGRADE_CLASSES_PRESENT = Utils.isClassPresent("org.h2.upgrade.v1_1.Driver"); }
If the upgrade classes are present, upgrade the database, or connect using the old version (if the parameter NO_UPGRADE is set to true). If the database is upgraded, or if no upgrade is possible or needed, this methods returns null.
Params:
  • url – the database URL
  • info – the properties
Returns:the connection if connected with the old version (NO_UPGRADE)
/** * If the upgrade classes are present, upgrade the database, or connect * using the old version (if the parameter NO_UPGRADE is set to true). If * the database is upgraded, or if no upgrade is possible or needed, this * methods returns null. * * @param url the database URL * @param info the properties * @return the connection if connected with the old version (NO_UPGRADE) */
public static Connection connectOrUpgrade(String url, Properties info) throws SQLException { if (!UPGRADE_CLASSES_PRESENT) { return null; } Properties i2 = new Properties(); i2.putAll(info); // clone so that the password (if set as a char array) is not cleared Object o = info.get("password"); if (o instanceof char[]) { i2.put("password", StringUtils.cloneCharArray((char[]) o)); } info = i2; ConnectionInfo ci = new ConnectionInfo(url, info); if (ci.isRemote() || !ci.isPersistent()) { return null; } String name = ci.getName(); if (FileUtils.exists(name + Constants.SUFFIX_PAGE_FILE)) { return null; } if (!FileUtils.exists(name + Constants.SUFFIX_OLD_DATABASE_FILE)) { return null; } if (ci.removeProperty("NO_UPGRADE", false)) { return connectWithOldVersion(url, info); } synchronized (DbUpgrade.class) { upgrade(ci, info); return null; } }
The conversion script file will per default be created in the db directory. Use this method to change the directory to the temp directory.
Params:
  • scriptInTempDir – true if the conversion script should be located in the temp directory.
/** * The conversion script file will per default be created in the db * directory. Use this method to change the directory to the temp * directory. * * @param scriptInTempDir true if the conversion script should be * located in the temp directory. */
public static void setScriptInTempDir(boolean scriptInTempDir) { DbUpgrade.scriptInTempDir = scriptInTempDir; }
Old files will be renamed to .backup after a successful conversion. To delete them after the conversion, use this method with the parameter 'true'.
Params:
  • deleteOldDb – if true, the old db files will be deleted.
/** * Old files will be renamed to .backup after a successful conversion. To * delete them after the conversion, use this method with the parameter * 'true'. * * @param deleteOldDb if true, the old db files will be deleted. */
public static void setDeleteOldDb(boolean deleteOldDb) { DbUpgrade.deleteOldDb = deleteOldDb; } private static Connection connectWithOldVersion(String url, Properties info) throws SQLException { url = "jdbc:h2v1_1:" + url.substring("jdbc:h2:".length()) + ";IGNORE_UNKNOWN_SETTINGS=TRUE"; return DriverManager.getConnection(url, info); } private static void upgrade(ConnectionInfo ci, Properties info) throws SQLException { String name = ci.getName(); String data = name + Constants.SUFFIX_OLD_DATABASE_FILE; String index = name + ".index.db"; String lobs = name + ".lobs.db"; String backupData = data + ".backup"; String backupIndex = index + ".backup"; String backupLobs = lobs + ".backup"; String script = null; try { if (scriptInTempDir) { new File(Utils.getProperty("java.io.tmpdir", ".")).mkdirs(); script = File.createTempFile( "h2dbmigration", "backup.sql").getAbsolutePath(); } else { script = name + ".script.sql"; } String oldUrl = "jdbc:h2v1_1:" + name + ";UNDO_LOG=0;LOG=0;LOCK_MODE=0"; String cipher = ci.getProperty("CIPHER", null); if (cipher != null) { oldUrl += ";CIPHER=" + cipher; } Connection conn = DriverManager.getConnection(oldUrl, info); Statement stat = conn.createStatement(); String uuid = UUID.randomUUID().toString(); if (cipher != null) { stat.execute("script to '" + script + "' cipher aes password '" + uuid + "' --hide--"); } else { stat.execute("script to '" + script + "'"); } conn.close(); FileUtils.move(data, backupData); FileUtils.move(index, backupIndex); if (FileUtils.exists(lobs)) { FileUtils.move(lobs, backupLobs); } ci.removeProperty("IFEXISTS", false); conn = new JdbcConnection(ci, true); stat = conn.createStatement(); if (cipher != null) { stat.execute("runscript from '" + script + "' cipher aes password '" + uuid + "' --hide--"); } else { stat.execute("runscript from '" + script + "'"); } stat.execute("analyze"); stat.execute("shutdown compact"); stat.close(); conn.close(); if (deleteOldDb) { FileUtils.delete(backupData); FileUtils.delete(backupIndex); FileUtils.deleteRecursive(backupLobs, false); } } catch (Exception e) { if (FileUtils.exists(backupData)) { FileUtils.move(backupData, data); } if (FileUtils.exists(backupIndex)) { FileUtils.move(backupIndex, index); } if (FileUtils.exists(backupLobs)) { FileUtils.move(backupLobs, lobs); } FileUtils.delete(name + ".h2.db"); throw DbException.toSQLException(e); } finally { if (script != null) { FileUtils.delete(script); } } } }