Skip to content

Commit ec5be1f

Browse files
committed
Merge remote-tracking branch 'origin/oraclesupport'
2 parents 28e894d + 0844439 commit ec5be1f

20 files changed

+680
-7
lines changed

src/DbUp.sln

+10-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dbup-sqlite-mono", "dbup-sq
3535
EndProject
3636
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{ACA9F575-BFC0-4FE1-BAB6-AE59FD8E5C26}"
3737
EndProject
38-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApplication", "Samples\SampleApplication\SampleApplication.csproj", "{397F41BC-1076-4D1B-9620-C6051E8104F4}"
38+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApplication", "Samples\SampleApplication\SampleApplication.csproj", "{397F41BC-1076-4D1B-9620-C6051E8104F4}"
39+
EndProject
40+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dbup-oracle", "dbup-oracle\dbup-oracle.csproj", "{8DAD25CC-ACF6-4A3A-A008-090C55E8FFA0}"
3941
EndProject
4042
Global
4143
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -83,17 +85,21 @@ Global
8385
{397F41BC-1076-4D1B-9620-C6051E8104F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
8486
{397F41BC-1076-4D1B-9620-C6051E8104F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
8587
{397F41BC-1076-4D1B-9620-C6051E8104F4}.Release|Any CPU.Build.0 = Release|Any CPU
88+
{8DAD25CC-ACF6-4A3A-A008-090C55E8FFA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
89+
{8DAD25CC-ACF6-4A3A-A008-090C55E8FFA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
90+
{8DAD25CC-ACF6-4A3A-A008-090C55E8FFA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
91+
{8DAD25CC-ACF6-4A3A-A008-090C55E8FFA0}.Release|Any CPU.Build.0 = Release|Any CPU
8692
EndGlobalSection
8793
GlobalSection(SolutionProperties) = preSolution
8894
HideSolutionNode = FALSE
8995
EndGlobalSection
96+
GlobalSection(NestedProjects) = preSolution
97+
{397F41BC-1076-4D1B-9620-C6051E8104F4} = {ACA9F575-BFC0-4FE1-BAB6-AE59FD8E5C26}
98+
EndGlobalSection
9099
GlobalSection(ExtensibilityGlobals) = postSolution
91100
SolutionGuid = {6807402B-EE7A-4324-A615-28F3499B1E9F}
92101
EndGlobalSection
93102
GlobalSection(MonoDevelopProperties) = preSolution
94103
StartupItem = DbUp\DbUp.csproj
95104
EndGlobalSection
96-
GlobalSection(NestedProjects) = preSolution
97-
{397F41BC-1076-4D1B-9620-C6051E8104F4} = {ACA9F575-BFC0-4FE1-BAB6-AE59FD8E5C26}
98-
EndGlobalSection
99105
EndGlobal

src/dbup-core/Support/TableJournal.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ protected virtual void OnTableCreated(Func<IDbCommand> dbCommandFactory)
169169
// TODO: Now we could run any migration scripts on it using some mechanism to make sure the table is ready for use.
170170
}
171171

172-
public void EnsureTableExistsAndIsLatestVersion(Func<IDbCommand> dbCommandFactory)
172+
public virtual void EnsureTableExistsAndIsLatestVersion(Func<IDbCommand> dbCommandFactory)
173173
{
174174
if (!journalExists && !DoesTableExist(dbCommandFactory))
175175
{
@@ -200,6 +200,8 @@ protected bool DoesTableExist(Func<IDbCommand> dbCommandFactory)
200200
return false;
201201
if (executeScalar is long)
202202
return (long) executeScalar == 1;
203+
if (executeScalar is decimal)
204+
return (decimal)executeScalar == 1;
203205
return (int) executeScalar == 1;
204206
}
205207
}
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
using DbUp.Support;
6+
7+
namespace DbUp.Oracle
8+
{
9+
public class OracleCommandReader : SqlCommandReader
10+
{
11+
private const string DelimiterKeyword = "DELIMITER";
12+
13+
/// <summary>
14+
/// Creates an instance of MySqlCommandReader
15+
/// </summary>
16+
public OracleCommandReader(string sqlText) : base(sqlText, ";", delimiterRequiresWhitespace: false)
17+
{
18+
}
19+
20+
/// <summary>
21+
/// Hook to support custom statements
22+
/// </summary>
23+
protected override bool IsCustomStatement
24+
{
25+
get
26+
{
27+
string statement;
28+
return TryPeek(DelimiterKeyword.Length, out statement) &&
29+
string.Equals(DelimiterKeyword, statement, StringComparison.OrdinalIgnoreCase);
30+
}
31+
}
32+
33+
/// <summary>
34+
/// Read a custom statement
35+
/// </summary>
36+
protected override void ReadCustomStatement()
37+
{
38+
// Move past Delimiter keyword
39+
var count = DelimiterKeyword.Length + 1;
40+
Read(new char[count], 0, count);
41+
42+
SkipWhitespace();
43+
// Read until we hit the end of line.
44+
var delimiter = new StringBuilder();
45+
do
46+
{
47+
delimiter.Append(CurrentChar);
48+
if (Read() == FailedRead)
49+
{
50+
break;
51+
}
52+
}
53+
while (!IsEndOfLine && !IsWhiteSpace);
54+
55+
Delimiter = delimiter.ToString();
56+
}
57+
58+
private void SkipWhitespace()
59+
{
60+
while (char.IsWhiteSpace(CurrentChar))
61+
{
62+
Read();
63+
}
64+
}
65+
}
66+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace DbUp.Oracle
6+
{
7+
public class OracleCommandSplitter
8+
{
9+
/// <summary>
10+
/// Splits a script with multiple delimited commands into commands
11+
/// </summary>
12+
/// <param name="scriptContents"></param>
13+
/// <returns></returns>
14+
public IEnumerable<string> SplitScriptIntoCommands(string scriptContents)
15+
{
16+
using (var reader = new OracleCommandReader(scriptContents))
17+
{
18+
var commands = new List<string>();
19+
reader.ReadAllCommands(c => commands.Add(c));
20+
return commands;
21+
}
22+
}
23+
}
24+
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using DbUp.Engine.Transactions;
5+
using Oracle.ManagedDataAccess.Client;
6+
7+
namespace DbUp.Oracle
8+
{
9+
public class OracleConnectionManager : DatabaseConnectionManager
10+
{
11+
/// <summary>
12+
/// Creates a new MySql database connection.
13+
/// </summary>
14+
/// <param name="connectionString">The MySql connection string.</param>
15+
public OracleConnectionManager(string connectionString) : base(new DelegateConnectionFactory(l => new OracleConnection(connectionString)))
16+
{
17+
}
18+
19+
public override IEnumerable<string> SplitScriptIntoCommands(string scriptContents)
20+
{
21+
var commandSplitter = new OracleCommandSplitter();
22+
var scriptStatements = commandSplitter.SplitScriptIntoCommands(scriptContents);
23+
return scriptStatements;
24+
}
25+
}
26+
}

src/dbup-oracle/OracleExtensions.cs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using DbUp.Builder;
6+
using DbUp.Engine.Output;
7+
using DbUp.Engine.Transactions;
8+
9+
namespace DbUp.Oracle
10+
{
11+
public static class OracleExtensions
12+
{
13+
public static UpgradeEngineBuilder OracleDatabase(this SupportedDatabases supported, string connectionString)
14+
{
15+
foreach (var pair in connectionString.Split(';').Select(s => s.Split('=')).Where(pair => pair.Length == 2).Where(pair => pair[0].ToLower() == "database"))
16+
{
17+
return OracleDatabase(new OracleConnectionManager(connectionString), pair[1]);
18+
}
19+
20+
return OracleDatabase(new OracleConnectionManager(connectionString));
21+
}
22+
23+
/// <summary>
24+
/// Creates an upgrader for MySql databases.
25+
/// </summary>
26+
/// <param name="supported">Fluent helper type.</param>
27+
/// <param name="connectionString">MySql database connection string.</param>
28+
/// <param name="schema">Which MySql schema to check for changes</param>
29+
/// <returns>
30+
/// A builder for a database upgrader designed for MySql databases.
31+
/// </returns>
32+
public static UpgradeEngineBuilder OracleDatabase(this SupportedDatabases supported, string connectionString, string schema)
33+
{
34+
return OracleDatabase(new OracleConnectionManager(connectionString), schema);
35+
}
36+
37+
/// <summary>
38+
/// Creates an upgrader for MySql databases.
39+
/// </summary>
40+
/// <param name="supported">Fluent helper type.</param>
41+
/// <param name="connectionManager">The <see cref="MySqlConnectionManager"/> to be used during a database upgrade.</param>
42+
/// <returns>
43+
/// A builder for a database upgrader designed for MySql databases.
44+
/// </returns>
45+
public static UpgradeEngineBuilder OracleDatabase(this SupportedDatabases supported, IConnectionManager connectionManager)
46+
=> OracleDatabase(connectionManager);
47+
48+
/// <summary>
49+
/// Creates an upgrader for MySql databases.
50+
/// </summary>
51+
/// <param name="connectionManager">The <see cref="MySqlConnectionManager"/> to be used during a database upgrade.</param>
52+
/// <returns>
53+
/// A builder for a database upgrader designed for MySql databases.
54+
/// </returns>
55+
public static UpgradeEngineBuilder OracleDatabase(IConnectionManager connectionManager)
56+
{
57+
return OracleDatabase(connectionManager, null);
58+
}
59+
60+
/// <summary>
61+
/// Creates an upgrader for MySql databases.
62+
/// </summary>
63+
/// <param name="connectionManager">The <see cref="MySqlConnectionManager"/> to be used during a database upgrade.</param>
64+
/// /// <param name="schema">Which MySQL schema to check for changes</param>
65+
/// <returns>
66+
/// A builder for a database upgrader designed for MySql databases.
67+
/// </returns>
68+
public static UpgradeEngineBuilder OracleDatabase(IConnectionManager connectionManager, string schema)
69+
{
70+
var builder = new UpgradeEngineBuilder();
71+
builder.Configure(c => c.ConnectionManager = connectionManager);
72+
builder.Configure(c => c.ScriptExecutor = new OracleScriptExecutor(() => c.ConnectionManager, () => c.Log, null, () => c.VariablesEnabled, c.ScriptPreprocessors, () => c.Journal));
73+
builder.Configure(c => c.Journal = new OracleTableJournal(() => c.ConnectionManager, () => c.Log, schema, "schemaversions"));
74+
builder.WithPreprocessor(new OraclePreprocessor());
75+
return builder;
76+
}
77+
}
78+
}

src/dbup-oracle/OracleObjectParser.cs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using DbUp.Support;
5+
6+
namespace DbUp.Oracle
7+
{
8+
public class OracleObjectParser : SqlObjectParser
9+
{
10+
public OracleObjectParser() : base("\"", "\"")
11+
{
12+
}
13+
}
14+
}

src/dbup-oracle/OraclePreprocessor.cs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using DbUp.Engine;
5+
6+
namespace DbUp.Oracle
7+
{
8+
public class OraclePreprocessor : IScriptPreprocessor
9+
{
10+
public string Process(string contents)
11+
{
12+
return contents;
13+
}
14+
}
15+
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using DbUp.Engine;
5+
using DbUp.Engine.Output;
6+
using DbUp.Engine.Transactions;
7+
using DbUp.Support;
8+
using Oracle.ManagedDataAccess.Client;
9+
10+
namespace DbUp.Oracle
11+
{
12+
public class OracleScriptExecutor : ScriptExecutor
13+
{
14+
/// <summary>
15+
/// Initializes an instance of the <see cref="OracleScriptExecutor"/> class.
16+
/// </summary>
17+
/// <param name="connectionManagerFactory"></param>
18+
/// <param name="log">The logging mechanism.</param>
19+
/// <param name="schema">The schema that contains the table.</param>
20+
/// <param name="variablesEnabled">Function that returns <c>true</c> if variables should be replaced, <c>false</c> otherwise.</param>
21+
/// <param name="scriptPreprocessors">Script Preprocessors in addition to variable substitution</param>
22+
/// <param name="journalFactory">Database journal</param>
23+
public OracleScriptExecutor(Func<IConnectionManager> connectionManagerFactory, Func<IUpgradeLog> log, string schema, Func<bool> variablesEnabled,
24+
IEnumerable<IScriptPreprocessor> scriptPreprocessors, Func<IJournal> journalFactory)
25+
: base(connectionManagerFactory, new OracleObjectParser(), log, schema, variablesEnabled, scriptPreprocessors, journalFactory)
26+
{
27+
28+
}
29+
30+
protected override string GetVerifySchemaSql(string schema)
31+
{
32+
throw new NotSupportedException();
33+
}
34+
35+
/// <summary>
36+
/// Executes the specified script against a database at a given connection string.
37+
/// </summary>
38+
/// <param name="script">The script.</param>
39+
public override void Execute(SqlScript script)
40+
{
41+
Execute(script, null);
42+
}
43+
44+
protected override void ExecuteCommandsWithinExceptionHandler(int index, SqlScript script, Action excuteCommand)
45+
{
46+
try
47+
{
48+
excuteCommand();
49+
}
50+
catch (OracleException exception)
51+
{
52+
#if MY_SQL_DATA_6_9_5
53+
var code = exception.ErrorCode;
54+
#else
55+
var code = exception.ErrorCode;
56+
#endif
57+
Log().WriteInformation("Oracle exception has occured in script: '{0}'", script.Name);
58+
Log().WriteError("Oracle error code: {0}; Number {1}; Message: {2}", index, code, exception.Number, exception.Message);
59+
Log().WriteError(exception.ToString());
60+
throw;
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)