/*******************************************************************************
* Copyright (c) 2012 Arapiki Solutions Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* psmith - initial API and
* implementation and/or initial documentation
*******************************************************************************/
package com.buildml.model.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.buildml.model.BuildStoreVersionException;
import com.buildml.model.FatalBuildStoreError;
import com.buildml.model.ISlotTypes;
/**
* Static class for upgrading a pre-existing database file to the latest schema. Whenever
* the schema (in BuildStoreDB) is changed, the appropriate upgrade statements should also
* be added here.
*
* @author Peter Smith <psmith@arapiki.com>
*/
public class UpgradeDB {
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/**
* If necessary, perform the upgrade of the database to the current software's schema version.
* Downgrading is not possible.
*
* @param databaseFileName Name of the database file to open.
* @throws BuildStoreVersionException We couldn't upgrade from the database file's schema.
* @throws FileNotFoundException The database file doesn't exist.
*/
public static void upgrade(String databaseFileName)
throws BuildStoreVersionException, FileNotFoundException {
Connection dbConn = null;
Statement stat = null;
ResultSet rs = null;
int dbVersion = 0;
/* check that the file actually exists */
if (!new File(databaseFileName).exists()) {
throw new FileNotFoundException("Database file: " + databaseFileName + " not found.");
}
/* Load the sqlite JDBC connector */
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
throw new FatalBuildStoreError("Unable to access the SQLite driver", e);
}
/* open the database */
try {
dbConn = DriverManager.getConnection("jdbc:sqlite:" + databaseFileName);
} catch (SQLException e) {
throw new BuildStoreVersionException("The database couldn't be opened. " + e.getMessage());
}
/* fetch the version number of the database file. */
try {
stat = dbConn.createStatement();
} catch (SQLException e) {
throw new FatalBuildStoreError("Unable to create a SQL statement", e);
}
try {
rs = stat.executeQuery("select version from schemaVersion");
if (rs.next()) {
dbVersion = rs.getInt("version");
}
rs.close();
stat.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
dbVersion = 0;
}
/* check the version number for "update-ness" */
if (dbVersion == 0) {
throw new BuildStoreVersionException("The database doesn't contain a version number.");
}
if (dbVersion > BuildStoreDB.SCHEMA_VERSION) {
throw new BuildStoreVersionException(
"This database schema is too new to be supported by this software.");
}
/* perform the upgrade */
if (dbVersion < BuildStoreDB.SCHEMA_VERSION) {
performUpgradeSteps(dbVersion, dbConn);
}
/* else, the version numbers are consistent - nothing to do */
try {
dbConn.close();
} catch (SQLException e) {
throw new BuildStoreVersionException("Upgrade of database failed for some reason.");
}
}
/*=====================================================================================*
* PRIVATE METHODS
*=====================================================================================*/
/**
* Perform the actual database upgrade. This is essentially a big list of upgrade
* steps, allowing upgrade from any old version to the current version.
*
* @param dbVersion Current version of the database schema.
* @param dbConn Connection to the database.
*/
private static void performUpgradeSteps(int dbVersion, Connection dbConn) {
/* fetch the version number of the database file. */
try {
Statement stat = dbConn.createStatement();
/* upgrade from 400 to 401 */
if (dbVersion < 401) {
stat.executeUpdate("create table fileGroups (id integer primary key, pkgId integer, " +
"type integer)");
stat.executeUpdate("create table fileGroupPaths (groupId integer, pathId integer, " +
"pathString text, pos integer)");
}
/* upgrade to 402 */
if (dbVersion < 402) {
stat.executeUpdate("alter table buildActions add column actionType integer");
stat.executeUpdate("update buildActions set actionType = 1");
stat.executeUpdate("create table slotValues (ownerType integer, ownerId integer, " +
"slotId integer, value text)");
}
/* upgrade to 403 */
if (dbVersion < 403) {
/*
* Create the new packageMembers table - although ideally we should generate
* the content from the files table. There are no production databases (yet), so this
* would be a waste of time.
*/
stat.executeUpdate("create table packageMembers (memberType integer, memberId integer, " +
"pkgId integer, scopeId integer, x integer, y integer)");
/* drop the pkgId and pkgScopeId columns from the files table */
stat.executeUpdate("alter table files rename to filestmp");
stat.executeUpdate("create table files ( id integer primary key, parentId integer, trashed integer, " +
"pathType integer, name text not null)");
stat.executeUpdate("insert into files select id, parentId, trashed, pathType, name from filestmp");
stat.executeUpdate("drop table filestmp");
/* drop the pkgId column from the fileGroups table */
stat.executeUpdate("alter table fileGroups rename to fileGroupstmp");
stat.executeUpdate("create table fileGroups (id integer primary key, type integer)");
stat.executeUpdate("insert into fileGroups select id, type from fileGroupstmp");
stat.executeUpdate("drop table fileGroupstmp");
/* add the new slotTypes table */
stat.executeUpdate("create table slotTypes (slotId integer primary key, ownerType integer, " +
"ownerId integer, slotName text, slotType integer, slotPos integer, " +
"slotCard integer, defaultValue text, enumId integer)");
}
/* update to 404 */
if (dbVersion < 404) {
/* note: create of the "Main" package is done within BuildStore */
}
/* update to 405 */
if (dbVersion < 405) {
stat.executeUpdate("alter table fileGroups add column predId integer");
}
/* update to 406:
* - create slot records for "Command" and "Directory"
* - drop the pkgId column from the buildActions table
*/
if (dbVersion < 406) {
/* move command/dir info from buildActions to slotValues */
int cmdSlotId = 2; /* this is constant - as of 405 */
int dirSlotId = 3; /* this is constant - as of 405 */
stat.executeUpdate("insert into slotValues select " +
ISlotTypes.SLOT_OWNER_ACTION + ", actionId, " + cmdSlotId + ", command " +
"from buildActions");
stat.executeUpdate("insert into slotValues select " +
ISlotTypes.SLOT_OWNER_ACTION + ", actionId, " + dirSlotId + ", actionDirId " +
"from buildActions");
/* move unneeded fields from buildActions */
stat.executeUpdate("alter table buildActions rename to buildActionstmp");
stat.executeUpdate("create table buildActions ( actionId integer primary key, " +
"parentActionId integer, trashed integer, actionType integer)");
stat.executeUpdate("insert into buildActions select actionId, parentActionId, trashed, " +
"actionType from buildActionstmp");
stat.executeUpdate("drop table buildActionstmp");
}
/*
* Update to 407 - add the subPackages table.
*/
if (dbVersion < 407) {
stat.executeUpdate("create table subPackages (subPkgId integer primary key, pkgTypeId integer, " +
"trashed integer)");
}
/*
* Update to 408 - add the slotDescr and trashed fields into the slotTypes tables.
*/
if (dbVersion < 408) {
stat.executeUpdate("alter table slotTypes rename to slotTypestmp");
stat.executeUpdate("create table slotTypes (slotId integer primary key, ownerType integer, " +
"ownerId integer, slotName text, slotDescr text, slotType integer, slotPos integer, " +
"slotCard integer, defaultValue text, enumId integer, trashed integer)");
stat.executeUpdate("insert into slotTypes select slotId, ownerType, ownerId, slotName, null, " +
"slotType, slotPos, slotCard, defaultValue, enumId, 0 from slotTypestmp");
stat.executeUpdate("drop table slotTypestmp");
}
/*
* Update to 409 - Create the package exports table
*/
if (dbVersion < 409) {
stat.executeUpdate("create table pkgExports (fileGroupId integer, slotId integer)");
}
/* finish by setting the new version number */
stat.executeUpdate("update schemaVersion set version=" + BuildStoreDB.SCHEMA_VERSION);
} catch (SQLException e) {
throw new FatalBuildStoreError("Error while upgrading database: " + e.getMessage());
}
}
/*-------------------------------------------------------------------------------------*/
}