aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Data/Migration.cs
diff options
context:
space:
mode:
authorSean Dague2008-06-09 21:40:16 +0000
committerSean Dague2008-06-09 21:40:16 +0000
commit504ab4477dc21e2fb3ef319d45eeb5e69a6e7227 (patch)
treecf61fde97fa95f0cb50ffcc957b0162ac6a202dd /OpenSim/Data/Migration.cs
parentfill out some more migration facilities (diff)
downloadopensim-SC-504ab4477dc21e2fb3ef319d45eeb5e69a6e7227.zip
opensim-SC-504ab4477dc21e2fb3ef319d45eeb5e69a6e7227.tar.gz
opensim-SC-504ab4477dc21e2fb3ef319d45eeb5e69a6e7227.tar.bz2
opensim-SC-504ab4477dc21e2fb3ef319d45eeb5e69a6e7227.tar.xz
move Migration support into OpenSim.Data, because it
really turned out to be small enough to not need it's own assembly
Diffstat (limited to 'OpenSim/Data/Migration.cs')
-rw-r--r--OpenSim/Data/Migration.cs198
1 files changed, 198 insertions, 0 deletions
diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs
new file mode 100644
index 0000000..8487db8
--- /dev/null
+++ b/OpenSim/Data/Migration.cs
@@ -0,0 +1,198 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Data;
31using System.Data.Common;
32using System.IO;
33using System.Reflection;
34using System.Text.RegularExpressions;
35using log4net;
36
37namespace OpenSim.Data
38{
39 /// <summary>
40 ///
41 /// The Migration theory is based on the ruby on rails concept.
42 /// Each database driver is going to be allowed to have files in
43 /// Resources that specify the database migrations. They will be
44 /// of the form:
45 ///
46 /// 001_Users.sql
47 /// 002_Users.sql
48 /// 003_Users.sql
49 /// 001_Prims.sql
50 /// 002_Prims.sql
51 /// ...etc...
52 ///
53 /// When a database driver starts up, it specifies a resource that
54 /// needs to be brought up to the current revision. For instance:
55 ///
56 /// Migration um = new Migration(DbConnection, "Users");
57 /// um.Upgrade();
58 ///
59 /// This works out which version Users is at, and applies all the
60 /// revisions past it to it. If there is no users table, all
61 /// revisions are applied in order. Consider each future
62 /// migration to be an incremental roll forward of the tables in
63 /// question.
64 ///
65 /// </summary>
66
67 public class Migration
68 {
69 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
70
71 private string _type;
72 private DbConnection _conn;
73 private string _subtype;
74 private Assembly _assem;
75
76 private static readonly string _migrations_create = "create table migrations(name varchar(100), version int)";
77 private static readonly string _migrations_init = "insert into migrations values('migrations', 1)";
78 private static readonly string _migrations_find = "select version from migrations where name='migrations'";
79
80 public Migration(DbConnection conn, Assembly assem, string type)
81 {
82 _type = type;
83 _conn = conn;
84 _assem = assem;
85 }
86
87 private void Initialize()
88 {
89 // clever, eh, we figure out which migrations version we are
90 int migration_version = FindVersion("migrations");
91
92 if (migration_version > 0)
93 return;
94
95 // If not, create the migration tables
96 DbCommand cmd = _conn.CreateCommand();
97 cmd.CommandText = _migrations_create;
98 cmd.ExecuteNonQuery();
99
100 UpdateVersion("migrations", 1);
101 }
102
103 public void Update()
104 {
105 int version = 0;
106 version = FindVersion(_type);
107
108 List<string> migrations = GetMigrationsAfter(version);
109 DbCommand cmd = _conn.CreateCommand();
110 foreach (string m in migrations)
111 {
112 cmd.CommandText = m;
113 cmd.ExecuteNonQuery();
114 }
115 UpdateVersion(_type, MaxVersion());
116 }
117
118 private int MaxVersion()
119 {
120 int max = 0;
121
122 string[] names = _assem.GetManifestResourceNames();
123 List<string> migrations = new List<string>();
124 Regex r = new Regex(@"^(\d\d\d)_" + _type + @"\.sql");
125
126 foreach (string s in names)
127 {
128 Match m = r.Match(s);
129 int MigrationVersion = int.Parse(m.Groups[1].ToString());
130 if ( MigrationVersion > max )
131 max = MigrationVersion;
132 }
133 return max;
134 }
135
136 private int FindVersion(string type)
137 {
138 int version = 0;
139 DbCommand cmd = _conn.CreateCommand();
140 cmd.CommandText = "select version from migrations where name='" + type + "' limit 1";
141 using (IDataReader reader = cmd.ExecuteReader())
142 {
143 if (reader.Read())
144 {
145 version = Convert.ToInt32(reader["version"]);
146 }
147 reader.Close();
148 }
149 return version;
150 }
151
152 private void UpdateVersion(string type, int version)
153 {
154 DbCommand cmd = _conn.CreateCommand();
155 cmd.CommandText = "update migrations set version=" + version + " where name='" + type + "'";
156 cmd.ExecuteNonQuery();
157 }
158
159 private List<string> GetAllMigrations()
160 {
161 return GetMigrationsAfter(0);
162 }
163
164 private List<string> GetMigrationsAfter(int version)
165 {
166 string[] names = _assem.GetManifestResourceNames();
167 List<string> migrations = new List<string>();
168
169 Regex r = new Regex(@"^(\d\d\d)_" + _type + @"\.sql");
170
171 foreach (string s in names)
172 {
173 m_log.Info("MIGRATION: Resources: " + s);
174 if (s.EndsWith(_type + @"\.sql"))
175 {
176 Match m = r.Match(s);
177 m_log.Info("MIGRATION: Match: " + m.Groups[1].ToString());
178 int MigrationVersion = int.Parse(m.Groups[1].ToString());
179 using (Stream resource = _assem.GetManifestResourceStream(s))
180 {
181 using (StreamReader resourceReader = new StreamReader(resource))
182 {
183 string resourceString = resourceReader.ReadToEnd();
184 migrations.Add(resourceString);
185 }
186 }
187 }
188 }
189
190 // TODO: once this is working, get rid of this
191 if (migrations.Count < 1) {
192 throw new Exception(string.Format("Resource '{0}' was not found", _type));
193 }
194
195 return migrations;
196 }
197 }
198} \ No newline at end of file