29 using System.Collections.Generic;
31 using System.Data.Common;
33 using System.Reflection;
34 using System.Text.RegularExpressions;
37 namespace OpenSim.Data
72 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
78 private Regex _match_old;
79 private Regex _match_new;
89 public Migration(DbConnection conn, Assembly assem,
string subtype,
string type)
91 Initialize(conn, assem, type, subtype);
94 public Migration(DbConnection conn, Assembly assem,
string type)
96 Initialize(conn, assem, type,
"");
108 public void Initialize (DbConnection conn, Assembly assem,
string type,
string subtype)
113 _match_old =
new Regex(subtype +
@"\.(\d\d\d)_" + _type +
@"\.sql");
114 string s = String.IsNullOrEmpty(subtype) ? _type : _type +
@"\." + subtype;
115 _match_new =
new Regex(
@"\." + s +
@"\.migrations(?:\.(?<ver>\d+)$|.*)");
122 int ver = FindVersion(_conn,
"migrations");
126 ExecuteScript(
"create table migrations(name varchar(100), version int)");
127 InsertVersion(
"migrations", 1);
142 using (DbCommand cmd = conn.CreateCommand())
144 cmd.CommandTimeout = 0;
145 foreach (
string sql
in script)
147 cmd.CommandText = sql;
150 cmd.ExecuteNonQuery();
154 throw new Exception(e.Message +
" in SQL: " + sql);
162 ExecuteScript(conn,
new string[]{sql});
167 ExecuteScript(_conn, sql);
172 ExecuteScript(_conn, script);
177 InitMigrationsTable();
179 int version = FindVersion(_conn, _type);
181 SortedList<int, string[]> migrations = GetMigrationsAfter(version);
182 if (migrations.Count < 1)
186 m_log.InfoFormat(
"[MIGRATIONS]: Upgrading {0} to latest revision {1}.", _type, migrations.Keys[migrations.Count - 1]);
187 m_log.Info(
"[MIGRATIONS]: NOTE - this may take a while, don't interrupt this process!");
189 foreach (KeyValuePair<
int,
string[]> kvp
in migrations)
191 int newversion = kvp.Key;
202 ExecuteScript(kvp.Value);
206 m_log.DebugFormat(
"[MIGRATIONS]: Cmd was {0}", e.Message.Replace(
"\n",
" "));
207 m_log.Debug(
"[MIGRATIONS]: An error has occurred in the migration. If you're running OpenSim for the first time then you can probably safely ignore this, since certain migration commands attempt to fetch data out of old tables. However, if you're using an existing database and you see database related errors while running OpenSim then you will need to fix these problems manually. Continuing.");
208 ExecuteScript(
"ROLLBACK;");
213 InsertVersion(_type, newversion);
217 UpdateVersion(_type, newversion);
219 version = newversion;
225 get {
return FindVersion(_conn, _type); }
229 InsertVersion(_type, value);
233 UpdateVersion(_type, value);
241 using (DbCommand cmd = conn.CreateCommand())
245 cmd.CommandText =
"select version from migrations where name='" + type +
"' order by version desc";
246 using (DbDataReader reader = cmd.ExecuteReader())
250 version = Convert.ToInt32(reader[
"version"]);
264 private void InsertVersion(
string type,
int version)
266 m_log.InfoFormat(
"[MIGRATIONS]: Creating {0} at version {1}", type, version);
267 ExecuteScript(
"insert into migrations(name, version) values('" + type +
"', " + version +
")");
270 private void UpdateVersion(
string type,
int version)
272 m_log.InfoFormat(
"[MIGRATIONS]: Updating {0} to version {1}", type, version);
273 ExecuteScript(
"update migrations set version=" + version +
" where name='" + type +
"'");
276 private delegate
void FlushProc();
286 private SortedList<int, string[]> GetMigrationsAfter(
int after)
288 SortedList<int, string[]> migrations =
new SortedList<int, string[]>();
290 string[] names = _assem.GetManifestResourceNames();
291 if (names.Length == 0)
296 int nLastVerFound = 0;
298 string sFile = Array.FindLast(names, nm => { m = _match_new.Match(nm);
return m.Success; });
300 if ((m != null) && !String.IsNullOrEmpty(sFile))
313 if (m.Groups.Count > 1 &&
int.TryParse(m.Groups[1].Value, out nLastVerFound))
315 if (nLastVerFound <= after)
319 System.Text.StringBuilder sb =
new System.Text.StringBuilder(4096);
322 List<string> script =
new List<string>();
324 FlushProc flush = delegate()
328 script.Add(sb.ToString());
332 if ((nVersion > 0) && (nVersion > after) && (script.Count > 0) && !migrations.ContainsKey(nVersion))
334 migrations[nVersion] = script.ToArray();
339 using (Stream resource = _assem.GetManifestResourceStream(sFile))
340 using (StreamReader resourceReader =
new StreamReader(resource))
343 while (!resourceReader.EndOfStream)
345 string sLine = resourceReader.ReadLine();
348 if (
String.IsNullOrEmpty(sLine) || sLine.StartsWith(
"#"))
351 if (sLine.Trim().Equals(
":GO", StringComparison.InvariantCultureIgnoreCase))
353 if (sb.Length == 0)
continue;
354 if (nVersion > after)
355 script.Add(sb.ToString());
360 if (sLine.StartsWith(
":VERSION ", StringComparison.InvariantCultureIgnoreCase))
364 int n = sLine.IndexOf(
'#');
366 sLine = sLine.Substring(0, n);
368 if (!
int.TryParse(sLine.Substring(9).Trim(), out nVersion))
370 m_log.ErrorFormat(
"[MIGRATIONS]: invalid version marker at {0}: line {1}. Migration failed!", sFile, nLineNo);
376 sb.AppendLine(sLine);
382 if (after < nVersion)
389 foreach (
string s
in names)
391 m = _match_old.Match(s);
394 int version = int.Parse(m.Groups[1].ToString());
395 if ((version > after) && !migrations.ContainsKey(version))
397 using (Stream resource = _assem.GetManifestResourceStream(s))
399 using (StreamReader resourceReader =
new StreamReader(resource))
401 string sql = resourceReader.ReadToEnd();
402 migrations.Add(version,
new string[]{sql});
409 if (migrations.Count < 1)
410 m_log.DebugFormat(
"[MIGRATIONS]: {0} data tables already up to date at revision {1}", _type, after);
virtual void ExecuteScript(DbConnection conn, string[] script)
Executes a script, possibly in a database-specific way. It can be redefined for a specific DBMS...
Migration(DbConnection conn, Assembly assem, string type)
void ExecuteScript(string[] script)
void ExecuteScript(string sql)
Migration(DbConnection conn, Assembly assem, string subtype, string type)
void Initialize(DbConnection conn, Assembly assem, string type, string subtype)
Must be called after creating with the parameterless constructor. NOTE that the Migration class now d...
Migration()
Have the parameterless constructor just so we can specify it as a generic parameter with the new() co...
virtual int FindVersion(DbConnection conn, string type)
void ExecuteScript(DbConnection conn, string sql)
void InitMigrationsTable()