mirror of
https://github.com/opensim/opensim.git
synced 2026-08-05 08:55:56 +08:00
move Migration support into OpenSim.Data, because it
really turned out to be small enough to not need it's own assembly
This commit is contained in:
198
OpenSim/Data/Migration.cs
Normal file
198
OpenSim/Data/Migration.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using log4net;
|
||||
|
||||
namespace OpenSim.Data
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// The Migration theory is based on the ruby on rails concept.
|
||||
/// Each database driver is going to be allowed to have files in
|
||||
/// Resources that specify the database migrations. They will be
|
||||
/// of the form:
|
||||
///
|
||||
/// 001_Users.sql
|
||||
/// 002_Users.sql
|
||||
/// 003_Users.sql
|
||||
/// 001_Prims.sql
|
||||
/// 002_Prims.sql
|
||||
/// ...etc...
|
||||
///
|
||||
/// When a database driver starts up, it specifies a resource that
|
||||
/// needs to be brought up to the current revision. For instance:
|
||||
///
|
||||
/// Migration um = new Migration(DbConnection, "Users");
|
||||
/// um.Upgrade();
|
||||
///
|
||||
/// This works out which version Users is at, and applies all the
|
||||
/// revisions past it to it. If there is no users table, all
|
||||
/// revisions are applied in order. Consider each future
|
||||
/// migration to be an incremental roll forward of the tables in
|
||||
/// question.
|
||||
///
|
||||
/// </summary>
|
||||
|
||||
public class Migration
|
||||
{
|
||||
private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private string _type;
|
||||
private DbConnection _conn;
|
||||
private string _subtype;
|
||||
private Assembly _assem;
|
||||
|
||||
private static readonly string _migrations_create = "create table migrations(name varchar(100), version int)";
|
||||
private static readonly string _migrations_init = "insert into migrations values('migrations', 1)";
|
||||
private static readonly string _migrations_find = "select version from migrations where name='migrations'";
|
||||
|
||||
public Migration(DbConnection conn, Assembly assem, string type)
|
||||
{
|
||||
_type = type;
|
||||
_conn = conn;
|
||||
_assem = assem;
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// clever, eh, we figure out which migrations version we are
|
||||
int migration_version = FindVersion("migrations");
|
||||
|
||||
if (migration_version > 0)
|
||||
return;
|
||||
|
||||
// If not, create the migration tables
|
||||
DbCommand cmd = _conn.CreateCommand();
|
||||
cmd.CommandText = _migrations_create;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
UpdateVersion("migrations", 1);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
int version = 0;
|
||||
version = FindVersion(_type);
|
||||
|
||||
List<string> migrations = GetMigrationsAfter(version);
|
||||
DbCommand cmd = _conn.CreateCommand();
|
||||
foreach (string m in migrations)
|
||||
{
|
||||
cmd.CommandText = m;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
UpdateVersion(_type, MaxVersion());
|
||||
}
|
||||
|
||||
private int MaxVersion()
|
||||
{
|
||||
int max = 0;
|
||||
|
||||
string[] names = _assem.GetManifestResourceNames();
|
||||
List<string> migrations = new List<string>();
|
||||
Regex r = new Regex(@"^(\d\d\d)_" + _type + @"\.sql");
|
||||
|
||||
foreach (string s in names)
|
||||
{
|
||||
Match m = r.Match(s);
|
||||
int MigrationVersion = int.Parse(m.Groups[1].ToString());
|
||||
if ( MigrationVersion > max )
|
||||
max = MigrationVersion;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private int FindVersion(string type)
|
||||
{
|
||||
int version = 0;
|
||||
DbCommand cmd = _conn.CreateCommand();
|
||||
cmd.CommandText = "select version from migrations where name='" + type + "' limit 1";
|
||||
using (IDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
version = Convert.ToInt32(reader["version"]);
|
||||
}
|
||||
reader.Close();
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private void UpdateVersion(string type, int version)
|
||||
{
|
||||
DbCommand cmd = _conn.CreateCommand();
|
||||
cmd.CommandText = "update migrations set version=" + version + " where name='" + type + "'";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private List<string> GetAllMigrations()
|
||||
{
|
||||
return GetMigrationsAfter(0);
|
||||
}
|
||||
|
||||
private List<string> GetMigrationsAfter(int version)
|
||||
{
|
||||
string[] names = _assem.GetManifestResourceNames();
|
||||
List<string> migrations = new List<string>();
|
||||
|
||||
Regex r = new Regex(@"^(\d\d\d)_" + _type + @"\.sql");
|
||||
|
||||
foreach (string s in names)
|
||||
{
|
||||
m_log.Info("MIGRATION: Resources: " + s);
|
||||
if (s.EndsWith(_type + @"\.sql"))
|
||||
{
|
||||
Match m = r.Match(s);
|
||||
m_log.Info("MIGRATION: Match: " + m.Groups[1].ToString());
|
||||
int MigrationVersion = int.Parse(m.Groups[1].ToString());
|
||||
using (Stream resource = _assem.GetManifestResourceStream(s))
|
||||
{
|
||||
using (StreamReader resourceReader = new StreamReader(resource))
|
||||
{
|
||||
string resourceString = resourceReader.ReadToEnd();
|
||||
migrations.Add(resourceString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: once this is working, get rid of this
|
||||
if (migrations.Count < 1) {
|
||||
throw new Exception(string.Format("Resource '{0}' was not found", _type));
|
||||
}
|
||||
|
||||
return migrations;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user