|
|
|
@ -0,0 +1,841 @@ |
|
|
|
|
using System; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.Data; |
|
|
|
|
using System.Data.Common; |
|
|
|
|
using System.Globalization; |
|
|
|
|
using System.IO; |
|
|
|
|
using System.Linq; |
|
|
|
|
using System.Text; |
|
|
|
|
using System.Text.RegularExpressions; |
|
|
|
|
using System.Data.OracleClient; |
|
|
|
|
//using Devart.Data.Oracle; |
|
|
|
|
|
|
|
|
|
namespace Patcher.DB |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
class OracleDBTraits : IDBTraits |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
public static readonly IDBTraits instance = new OracleDBTraits(); |
|
|
|
|
|
|
|
|
|
protected OracleDBTraits() |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static readonly Regex ALPHANUMERIC = new Regex("^[a-zA-Z]\\w*$", RegexOptions.Compiled | RegexOptions.Singleline); |
|
|
|
|
|
|
|
|
|
private static string _EscapeName(string name) |
|
|
|
|
{ |
|
|
|
|
if(!ALPHANUMERIC.IsMatch(name)) throw new ApplicationException("Name should contain only alphanumeric characters"); |
|
|
|
|
return string.Format("\"{0}\"", name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string IDBTraits.EscapeName(string name) |
|
|
|
|
{ |
|
|
|
|
return _EscapeName(name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DbConnection IDBTraits.CreateConnection(string connectionString) |
|
|
|
|
{ |
|
|
|
|
#pragma warning disable 0618 |
|
|
|
|
OracleConnection connection = new OracleConnection(connectionString); |
|
|
|
|
#pragma warning restore 0618 |
|
|
|
|
connection.Open(); |
|
|
|
|
return connection; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string IDBTraits.MarkParam(string paramName) |
|
|
|
|
{ |
|
|
|
|
if(!ALPHANUMERIC.IsMatch(paramName)) throw new ApplicationException("Name should contain only alphanumeric characters"); |
|
|
|
|
return ":" + paramName; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string IDBTraits.ParamName(string paramName) |
|
|
|
|
{ |
|
|
|
|
if(!ALPHANUMERIC.IsMatch(paramName)) throw new ApplicationException("Name should contain only alphanumeric characters"); |
|
|
|
|
return paramName; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void AddParam(DbCommand command, string name, DbType type, object value) |
|
|
|
|
{ |
|
|
|
|
var param = command.CreateParameter(); |
|
|
|
|
param.ParameterName = name; |
|
|
|
|
param.DbType = type; |
|
|
|
|
param.Value = value; |
|
|
|
|
command.Parameters.Add(param); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static bool ParseBoolString(string value) |
|
|
|
|
{ |
|
|
|
|
switch(value.ToLower()) |
|
|
|
|
{ |
|
|
|
|
case "n": |
|
|
|
|
return false; |
|
|
|
|
case "y": |
|
|
|
|
return true; |
|
|
|
|
default: |
|
|
|
|
throw new ApplicationException(string.Format("Unknown value {0}", value)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static T CastResult<T>(object value) where T : class
|
|
|
|
|
{ |
|
|
|
|
if(DBNull.Value.Equals(value)) |
|
|
|
|
{ |
|
|
|
|
return null; |
|
|
|
|
} else |
|
|
|
|
{ |
|
|
|
|
return (T)value; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static T? CastScalarResult<T>(object value) where T : struct
|
|
|
|
|
{ |
|
|
|
|
if(DBNull.Value.Equals(value)) |
|
|
|
|
{ |
|
|
|
|
return null; |
|
|
|
|
} else |
|
|
|
|
{ |
|
|
|
|
return (T)value; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static readonly SQLQueryManager _SQLQueryManager = new SQLQueryManager(_EscapeName); |
|
|
|
|
|
|
|
|
|
private static string[] GetPackage(Func<DbCommand> commandCreator, string package, string type) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT LINE, TEXT FROM USER_SOURCE WHERE TYPE = :ptype AND NAME = :pname order by line"; |
|
|
|
|
AddParam(command, "ptype", DbType.String, type); |
|
|
|
|
AddParam(command, "pname", DbType.String, package); |
|
|
|
|
List<string> lines = new List<string>(); |
|
|
|
|
using(DbDataReader reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
while(reader.Read()) |
|
|
|
|
{ |
|
|
|
|
lines.Add(reader.GetValue(reader.GetOrdinal("TEXT")).ToString()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return lines.ToArray(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static readonly Regex PROCEDURE_BODY_HEADER_REGEX = new Regex( |
|
|
|
|
"^procedure" + |
|
|
|
|
"\\s+" + |
|
|
|
|
"(?<name>\\w+)" + |
|
|
|
|
"\\s*" + |
|
|
|
|
"\\(" + |
|
|
|
|
"(?<params>" + |
|
|
|
|
"(\\w+\\s+(in|out)\\s+\\w+)" + |
|
|
|
|
"(\\,\\s*\\w+\\s+(in|out)\\s+\\w+)*" + |
|
|
|
|
")?" + |
|
|
|
|
"\\)$", |
|
|
|
|
RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Compiled |
|
|
|
|
); |
|
|
|
|
private static readonly Regex PROCEDURE_BODY_FOOTER_REGEX = new Regex( |
|
|
|
|
"^end" + |
|
|
|
|
"\\s+" + |
|
|
|
|
"(?<name>\\w+)" + |
|
|
|
|
";$", |
|
|
|
|
RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Compiled |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
private static readonly Dictionary<StoredProcedureReference.ParameterDirection, string> DIRECTIONS = new Dictionary<StoredProcedureReference.ParameterDirection, string> |
|
|
|
|
{ |
|
|
|
|
{ StoredProcedureReference.ParameterDirection.In, "in" }, |
|
|
|
|
{ StoredProcedureReference.ParameterDirection.Out, "out" }, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private struct ProcedureBodyPosition |
|
|
|
|
{ |
|
|
|
|
public int header; |
|
|
|
|
public int begin; |
|
|
|
|
public int end; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private class ProcedureNotFoundException : FormattableException |
|
|
|
|
{ |
|
|
|
|
public ProcedureNotFoundException(string message, params object[] args) : base(message, args) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private class ViewNotFoundException : FormattableException |
|
|
|
|
{ |
|
|
|
|
public ViewNotFoundException(string message, params object[] args) : base(message, args) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static int FindProcedureHeader(string[] package, StoredProcedureReference procedure) |
|
|
|
|
{ |
|
|
|
|
for(int i=0; i<package.Length; i++) |
|
|
|
|
{ |
|
|
|
|
var line = package[i].Trim().TrimEnd(';'); |
|
|
|
|
if (PROCEDURE_BODY_HEADER_REGEX.IsMatch(line)) |
|
|
|
|
{ |
|
|
|
|
var match = PROCEDURE_BODY_HEADER_REGEX.Match(line); |
|
|
|
|
string name = match.Groups["name"].Value; |
|
|
|
|
string[] parameters = match.Groups["params"].Value.Split(','); |
|
|
|
|
if(name == procedure.procedureName) |
|
|
|
|
{ |
|
|
|
|
if(parameters.Length != procedure.parameters.Length) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Parameters number mismatch for procedure {0}: expected {1}, got {2}", procedure.procedureName, procedure.parameters.Length, parameters.Length)); |
|
|
|
|
} |
|
|
|
|
for(int j=0; j<parameters.Length; j++) |
|
|
|
|
{ |
|
|
|
|
string[] parts = parameters[j].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); |
|
|
|
|
if(parts[0] != procedure.parameters[j].name) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Parameter {1} name mismatch for procedure {0}: expected {2}, got {3}", j, procedure.procedureName, procedure.parameters[j].name, parts[0])); |
|
|
|
|
} |
|
|
|
|
if(parts[1].ToLower() != DIRECTIONS[procedure.parameters[j].direction]) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Parameter {1} direction mismatch for procedure {0}: expected {2}, got {3}", procedure.procedureName, procedure.parameters[j].name, DIRECTIONS[procedure.parameters[j].direction], parts[1])); |
|
|
|
|
} |
|
|
|
|
if(parts[2] != procedure.parameters[j].type) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Parameter {1} type mismatch for procedure {0}: expected {2}, got {3}", j, procedure.procedureName, procedure.parameters[j].type, parts[2])); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return i; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
throw new ProcedureNotFoundException(String.Format("Procedure {0} header not found", procedure.procedureName)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static ProcedureBodyPosition FindProcedureBodyPosition(string[] package, StoredProcedureReference procedure) |
|
|
|
|
{ |
|
|
|
|
int header = FindProcedureHeader(package, procedure); |
|
|
|
|
int? begin = null; |
|
|
|
|
int? end = null; |
|
|
|
|
if(package.Length <= header+2) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Unexpected package end"); |
|
|
|
|
} |
|
|
|
|
if(package[header+1].Trim().ToLower() != "is") |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Incorrect line follows procedure {0} header: '{1}'", procedure.procedureName, package[header+1].Trim())); |
|
|
|
|
} |
|
|
|
|
for(int i=header+2; i<package.Length; i++) |
|
|
|
|
{ |
|
|
|
|
if(package[i].Trim().ToLower() == "begin") |
|
|
|
|
{ |
|
|
|
|
begin = i; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if(!begin.HasValue) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Unable to find body begin for procedure {0}", procedure.procedureName)); |
|
|
|
|
} |
|
|
|
|
if(package.Length <= begin+1) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Unexpected package end"); |
|
|
|
|
} |
|
|
|
|
for(int i=begin.Value+1; i<package.Length; i++) |
|
|
|
|
{ |
|
|
|
|
string line = package[i].Trim(); |
|
|
|
|
if(PROCEDURE_BODY_FOOTER_REGEX.IsMatch(line)) |
|
|
|
|
{ |
|
|
|
|
var match = PROCEDURE_BODY_FOOTER_REGEX.Match(line); |
|
|
|
|
var name = match.Groups["name"].Value; |
|
|
|
|
if(name != procedure.procedureName) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException(String.Format("Wrong procedure footer: expected {0}, got {1}", procedure.procedureName, name)); |
|
|
|
|
} |
|
|
|
|
end = i; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if(!end.HasValue) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Procedure footer not found"); |
|
|
|
|
} |
|
|
|
|
return new ProcedureBodyPosition |
|
|
|
|
{ |
|
|
|
|
header = header, |
|
|
|
|
begin = begin.Value, |
|
|
|
|
end = end.Value, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
StoredProcedureBody IDBTraits.GetStoredProcedureBody(Func<DbCommand> commandCreator, StoredProcedureReference procedure) |
|
|
|
|
{ |
|
|
|
|
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY"); |
|
|
|
|
var position = FindProcedureBodyPosition(package, procedure); |
|
|
|
|
StringBuilder declarationsBuilder = new StringBuilder(); |
|
|
|
|
for(int i=position.header+2; i<position.begin; i++) |
|
|
|
|
{ |
|
|
|
|
declarationsBuilder.Append(package[i]); |
|
|
|
|
} |
|
|
|
|
StringBuilder bodyBuilder = new StringBuilder(); |
|
|
|
|
for(int i=position.begin+1; i<position.end; i++) |
|
|
|
|
{ |
|
|
|
|
bodyBuilder.AppendFormat(package[i]); |
|
|
|
|
} |
|
|
|
|
return new StoredProcedureBody(declarationsBuilder.ToString(), bodyBuilder.ToString()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static string FormatStoredProcedureHeader(StoredProcedureReference procedure) |
|
|
|
|
{ |
|
|
|
|
return string.Format("procedure {0}({1})", procedure.procedureName, String.Join(", ", (from param in procedure.parameters select String.Format("{0} {1} {2}", param.name, DIRECTIONS[param.direction], param.type)).ToArray())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static string FormatStoredProcedure(StoredProcedureReference procedure, StoredProcedureBody body) |
|
|
|
|
{ |
|
|
|
|
StringBuilder result = new StringBuilder(); |
|
|
|
|
result.Append(FormatStoredProcedureHeader(procedure)); |
|
|
|
|
result.AppendLine(); |
|
|
|
|
result.AppendLine("is"); |
|
|
|
|
if(body.declarations.Trim() != "") |
|
|
|
|
{ |
|
|
|
|
result.AppendLine(body.declarations.TrimEnd().Trim('\r', '\n')); |
|
|
|
|
} |
|
|
|
|
result.AppendLine("begin"); |
|
|
|
|
if(body.body.Trim() != "") |
|
|
|
|
{ |
|
|
|
|
result.AppendLine(body.body.TrimEnd().Trim('\r', '\n')); |
|
|
|
|
} |
|
|
|
|
result.AppendFormat("end {0};", procedure.procedureName); |
|
|
|
|
result.AppendLine(); |
|
|
|
|
return result.ToString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.ReplaceStoredProcedureBody(Func<DbCommand> commandCreator, StoredProcedureReference procedure, StoredProcedureBody newBody) |
|
|
|
|
{ |
|
|
|
|
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY"); |
|
|
|
|
var position = FindProcedureBodyPosition(package, procedure); |
|
|
|
|
StringBuilder packageBuilder = new StringBuilder(); |
|
|
|
|
packageBuilder.AppendLine("create or replace"); |
|
|
|
|
for(int i=0; i<position.header; i++) |
|
|
|
|
{ |
|
|
|
|
packageBuilder.Append(package[i]); |
|
|
|
|
} |
|
|
|
|
packageBuilder.Append(FormatStoredProcedure(procedure, newBody)); |
|
|
|
|
for(int i=position.end+1; i<package.Length; i++) |
|
|
|
|
{ |
|
|
|
|
packageBuilder.AppendFormat(package[i]); |
|
|
|
|
} |
|
|
|
|
/*Console.WriteLine(); |
|
|
|
|
Console.WriteLine("===NEW PACKAGE==="); |
|
|
|
|
Console.WriteLine(packageBuilder.ToString()); |
|
|
|
|
Console.WriteLine("===END PACKAGE===");*/ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = packageBuilder.ToString(); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.RemoveStoredProcedure(Func<DbCommand> commandCreator, StoredProcedureReference procedure) |
|
|
|
|
{ |
|
|
|
|
{ |
|
|
|
|
string[] packageSpec = GetPackage(commandCreator, procedure.packageName, "PACKAGE"); |
|
|
|
|
int position = FindProcedureHeader(packageSpec, procedure); |
|
|
|
|
StringBuilder packageSpecBuilder = new StringBuilder(); |
|
|
|
|
packageSpecBuilder.AppendLine("create or replace"); |
|
|
|
|
for(int i=0; i<position; i++) |
|
|
|
|
{ |
|
|
|
|
packageSpecBuilder.Append(packageSpec[i]); |
|
|
|
|
} |
|
|
|
|
for(int i=position+1; i<packageSpec.Length; i++) |
|
|
|
|
{ |
|
|
|
|
packageSpecBuilder.Append(packageSpec[i]); |
|
|
|
|
} |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = packageSpecBuilder.ToString(); |
|
|
|
|
/*Console.WriteLine(); |
|
|
|
|
Console.WriteLine("=====PACKAGE SPEC====="); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
Console.WriteLine("=====END PACKAGE SPEC=====");*/ |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY"); |
|
|
|
|
var position = FindProcedureBodyPosition(package, procedure); |
|
|
|
|
StringBuilder packageBuilder = new StringBuilder(); |
|
|
|
|
packageBuilder.AppendLine("create or replace"); |
|
|
|
|
for(int i=0; i<position.header; i++) |
|
|
|
|
{ |
|
|
|
|
packageBuilder.Append(package[i]); |
|
|
|
|
} |
|
|
|
|
for(int i=position.end+1; i<package.Length; i++) |
|
|
|
|
{ |
|
|
|
|
packageBuilder.Append(package[i]); |
|
|
|
|
} |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = packageBuilder.ToString(); |
|
|
|
|
/*Console.WriteLine(); |
|
|
|
|
Console.WriteLine("=====PACKAGE BODY====="); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
Console.WriteLine("=====END PACKAGE BODY=====");*/ |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.CreateView(Func<DbCommand> commandCreator, string viewName, string body) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = string.Format("CREATE VIEW {0} as {1}", _EscapeName(viewName), body); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.RemoveView(Func<DbCommand> commandCreator, string viewName) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = string.Format("DROP VIEW {0}", _EscapeName(viewName)); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.CreateStoredProcedure(Func<DbCommand> commandCreator, StoredProcedureReference procedure, StoredProcedureBody body) |
|
|
|
|
{ |
|
|
|
|
string packageEnd = string.Format("end {0};", procedure.packageName); |
|
|
|
|
string packageEndMarker = packageEnd.ToLower(); |
|
|
|
|
{ |
|
|
|
|
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY"); |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var position = FindProcedureBodyPosition(package, procedure); |
|
|
|
|
throw new ApplicationException(String.Format("Procedure {0} body is already declared", procedure.procedureName)); |
|
|
|
|
} catch(ProcedureNotFoundException) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
StringBuilder packageBuilder = new StringBuilder(); |
|
|
|
|
packageBuilder.AppendLine("create or replace"); |
|
|
|
|
for(int i=0; i<package.Length; i++) |
|
|
|
|
{ |
|
|
|
|
if(package[i].Trim().ToLower() == packageEndMarker) |
|
|
|
|
{ |
|
|
|
|
if(package.Length > i+1) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Unexpected package end"); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
packageBuilder.Append(package[i]); |
|
|
|
|
} |
|
|
|
|
packageBuilder.Append(FormatStoredProcedure(procedure, body)); |
|
|
|
|
packageBuilder.AppendLine(packageEnd); |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = packageBuilder.ToString(); |
|
|
|
|
/*Console.WriteLine(); |
|
|
|
|
Console.WriteLine("=====PACKAGE BODY====="); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
Console.WriteLine("=====END PACKAGE BODY=====");*/ |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
string[] packageSpec = GetPackage(commandCreator, procedure.packageName, "PACKAGE"); |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var position = FindProcedureHeader(packageSpec, procedure); |
|
|
|
|
throw new ApplicationException(String.Format("Procedure {0} is already declared", procedure.procedureName)); |
|
|
|
|
} catch(ProcedureNotFoundException) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
StringBuilder packageSpecBuilder = new StringBuilder(); |
|
|
|
|
packageSpecBuilder.AppendLine("create or replace"); |
|
|
|
|
for(int i=0; i<packageSpec.Length; i++) |
|
|
|
|
{ |
|
|
|
|
if(packageSpec[i].Trim().ToLower() == packageEndMarker) |
|
|
|
|
{ |
|
|
|
|
if(packageSpec.Length > i+1) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Unexpected package end"); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
packageSpecBuilder.Append(packageSpec[i]); |
|
|
|
|
} |
|
|
|
|
packageSpecBuilder.Append(FormatStoredProcedureHeader(procedure)); |
|
|
|
|
packageSpecBuilder.AppendLine(";"); |
|
|
|
|
packageSpecBuilder.AppendLine(packageEnd); |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = packageSpecBuilder.ToString(); |
|
|
|
|
/*Console.WriteLine(); |
|
|
|
|
Console.WriteLine("=====PACKAGE SPEC====="); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
Console.WriteLine("=====END PACKAGE SPEC=====");*/ |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string IDBTraits.GetViewBody(Func<DbCommand> commandCreator, string name) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = :pname"; |
|
|
|
|
AddParam(command, "pname", DbType.String, name); |
|
|
|
|
using(DbDataReader reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
if(!reader.Read()) |
|
|
|
|
{ |
|
|
|
|
throw new ViewNotFoundException("View {0} not found", name); |
|
|
|
|
} |
|
|
|
|
return reader.GetValue("TEXT").ToString(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ColumnOptions IDBTraits.GetColumnOptions(Func<DbCommand> commandCreator, ColumnReference column) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "select * from user_tab_columns where table_name = :ptable and column_name = :pcolumn"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, column.tableName); |
|
|
|
|
AddParam(command, "pcolumn", DbType.String, column.columnName); |
|
|
|
|
using(DbDataReader reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
if(!reader.Read()) |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Column not found"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string dataType = (string)reader.GetValue("DATA_TYPE"); |
|
|
|
|
|
|
|
|
|
decimal? dataLength = CastScalarResult<decimal>(reader.GetValue("DATA_LENGTH")); |
|
|
|
|
decimal? dataPrecision = CastScalarResult<decimal>(reader.GetValue("DATA_PRECISION")); |
|
|
|
|
decimal? dataScale = CastScalarResult<decimal>(reader.GetValue("DATA_SCALE")); |
|
|
|
|
string sizeSpecifier = null; |
|
|
|
|
if(dataPrecision.HasValue) |
|
|
|
|
{ |
|
|
|
|
if(dataScale.HasValue) |
|
|
|
|
{ |
|
|
|
|
sizeSpecifier = string.Format("{0}, {1}", dataPrecision, dataScale); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
sizeSpecifier = dataPrecision.ToString(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if(dataLength.HasValue) |
|
|
|
|
{ |
|
|
|
|
sizeSpecifier = dataLength.ToString(); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
throw new ApplicationException("Field without a length"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string defaultValue = CastResult<string>(reader.GetValue(reader.GetOrdinal("DATA_DEFAULT"))); |
|
|
|
|
|
|
|
|
|
return new ColumnOptions( |
|
|
|
|
string.Format("{0}({1})", dataType, sizeSpecifier), |
|
|
|
|
defaultValue != null ? defaultValue.Trim() : null, |
|
|
|
|
!ParseBoolString((string)reader.GetValue(reader.GetOrdinal("NULLABLE"))) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.RemoveColumn(Func<DbCommand> commandCreator, ColumnReference column) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.RemoveColumn(column); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.CreateColumn(Func<DbCommand> commandCreator, ColumnDescription description) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.CreateColumn(description); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.ModifyColumn(Func<DbCommand> commandCreator, ColumnDescription description) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.ModifyColumnOracleStyle(description); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static string GetStringRepresentation(ForeignKeyConstraint.ReferentialAction action) |
|
|
|
|
{ |
|
|
|
|
switch(action) |
|
|
|
|
{ |
|
|
|
|
case ForeignKeyConstraint.ReferentialAction.NoAction: |
|
|
|
|
return "NO ACTION"; |
|
|
|
|
case ForeignKeyConstraint.ReferentialAction.Cascade: |
|
|
|
|
return "CASCADE"; |
|
|
|
|
case ForeignKeyConstraint.ReferentialAction.SetNull: |
|
|
|
|
return "SET NULL"; |
|
|
|
|
case ForeignKeyConstraint.ReferentialAction.SetDefault: |
|
|
|
|
return "SET DEFAULT"; |
|
|
|
|
default: |
|
|
|
|
throw new ApplicationException("Unknown referential action"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void CheckConstraint(Func<DbCommand> commandCreator, ForeignKeyConstraint constraint) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
string referenced; |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = :ptable and CONSTRAINT_NAME = :pconstraint"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, constraint.table); |
|
|
|
|
AddParam(command, "pconstraint", DbType.String, constraint.name); |
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
if(!reader.Read()) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(reader.GetValue("CONSTRAINT_TYPE").ToString() != "R") |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "R", reader.GetValue("CONSTRAINT_TYPE")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(reader.GetValue("DELETE_RULE").ToString() != GetStringRepresentation(constraint.onDelete)) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong on delete action: expected {2}, got {3}", constraint.table, constraint.name, GetStringRepresentation(constraint.onDelete), reader.GetValue("DELETE_RULE")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
referenced = reader.GetValue("R_CONSTRAINT_NAME").ToString(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE CONSTRAINT_NAME = :pconstraint AND CONSTRAINT_TYPE = :ptype"; |
|
|
|
|
AddParam(command, "pconstraint", DbType.String, referenced); |
|
|
|
|
AddParam(command, "ptype", DbType.String, "P"); |
|
|
|
|
|
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
reader.Read(); |
|
|
|
|
if(reader.GetValue("TABLE_NAME").ToString() != constraint.referencedTable) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong referenced table: expected {2}, got {3}", constraint.table, constraint.name, constraint.referencedTable, reader.GetValue("TABLE_NAME")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM USER_CONS_COLUMNS WHERE TABLE_NAME = :ptable AND CONSTRAINT_NAME = :pconstraint"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, constraint.table); |
|
|
|
|
AddParam(command, "pconstraint", DbType.String, constraint.name); |
|
|
|
|
|
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
reader.Read(); |
|
|
|
|
if(reader.GetValue("COLUMN_NAME").ToString() != constraint.column) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong column: expected {2}, got {3}", constraint.table, constraint.name, constraint.column, reader.GetValue("COLUMN_NAME")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void CheckConstraint(Func<DbCommand> commandCreator, UniqueConstraint constraint) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = :ptable and CONSTRAINT_NAME = :pconstraint"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, constraint.table); |
|
|
|
|
AddParam(command, "pconstraint", DbType.String, constraint.name); |
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
if(!reader.Read()) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(reader.GetValue("CONSTRAINT_TYPE").ToString() != "U") |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "U", reader.GetValue("CONSTRAINT_TYPE")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM USER_CONS_COLUMNS WHERE TABLE_NAME = :ptable AND CONSTRAINT_NAME = :pconstraint"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, constraint.table); |
|
|
|
|
AddParam(command, "pconstraint", DbType.String, constraint.name); |
|
|
|
|
|
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
HashSet<string> dbColumns = new HashSet<string>(); |
|
|
|
|
while(reader.Read()) |
|
|
|
|
{ |
|
|
|
|
dbColumns.Add(reader.GetValue("COLUMN_NAME").ToString()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!dbColumns.IsSubsetOf(constraint.columns)) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Some columns are not mentioned in constraint definition: {0}", string.Join(",", dbColumns.Except(constraint.columns).ToArray())); |
|
|
|
|
} |
|
|
|
|
if(!dbColumns.IsSupersetOf(constraint.columns)) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Some columns are missed in DB: {0}", string.Join(",", constraint.columns.Except(dbColumns).ToArray())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void CheckConstraint(Func<DbCommand> commandCreator, CheckConstraint constraint) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = :ptable and CONSTRAINT_NAME = :pconstraint"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, constraint.table); |
|
|
|
|
AddParam(command, "pconstraint", DbType.String, constraint.name); |
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
if(!reader.Read()) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(reader.GetValue("CONSTRAINT_TYPE").ToString() != "C") |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "C", reader.GetValue("CONSTRAINT_TYPE")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(reader.GetValue("SEARCH_CONDITION").ToString() != constraint.condition) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Constraint {0}.{1} wrong condition: expected {2}, got {3}", constraint.table, constraint.name, constraint.condition, reader.GetValue("SEARCH_CONDITION")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void CheckConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint) |
|
|
|
|
{ |
|
|
|
|
constraint.Accept(fkc => CheckConstraint(commandCreator, fkc), uc => CheckConstraint(commandCreator, uc), cc => CheckConstraint(commandCreator, cc)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void RemoveConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint) |
|
|
|
|
{ |
|
|
|
|
CheckConstraint(commandCreator, constraint); |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.DropConstraint(constraint); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void CreateConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint) |
|
|
|
|
{ |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.CreateConstraint(constraint); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void CreateTable(Func<DbCommand> commandCreator, TableDescription table) { |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.CreateTable(table); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void CheckTable(Func<DbCommand> commandCreator, TableDescription table) { |
|
|
|
|
|
|
|
|
|
HashSet<string> columns = new HashSet<string>(from column in table.columns select column.column.columnName); |
|
|
|
|
columns.Add(table.primaryKey.column.columnName); |
|
|
|
|
|
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = "SELECT * FROM user_tab_columns WHERE TABLE_NAME = :ptable"; |
|
|
|
|
AddParam(command, "ptable", DbType.String, table.table); |
|
|
|
|
|
|
|
|
|
using(var reader = command.ExecuteReader()) |
|
|
|
|
{ |
|
|
|
|
HashSet<string> dbColumns = new HashSet<string>(); |
|
|
|
|
while(reader.Read()) |
|
|
|
|
{ |
|
|
|
|
dbColumns.Add(reader.GetValue("COLUMN_NAME").ToString()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!dbColumns.IsSubsetOf(columns)) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Some columns are not mentioned in table definition: {0}", string.Join(",", dbColumns.Except(columns).ToArray())); |
|
|
|
|
} |
|
|
|
|
if(!dbColumns.IsSupersetOf(columns)) |
|
|
|
|
{ |
|
|
|
|
throw new FormattableException("Some columns are missed in DB: {0}", string.Join(",", columns.Except(dbColumns).ToArray())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var options = (this as IDBTraits).GetColumnOptions(commandCreator, table.primaryKey.column); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine("'{0}' vs '{1}'", table.primaryKey.options.type, options.type); |
|
|
|
|
Console.WriteLine("'{0}' vs '{1}'", table.primaryKey.options.defaultValue, options.defaultValue); |
|
|
|
|
Console.WriteLine("'{0}' vs '{1}'", table.primaryKey.options.isNotNull, options.isNotNull); |
|
|
|
|
if(!table.primaryKey.options.Equals((this as IDBTraits).GetColumnOptions(commandCreator, table.primaryKey.column))) { |
|
|
|
|
throw new FormattableException("Column {0} definition mismatch", table.primaryKey.column.columnName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach(var column in table.columns) { |
|
|
|
|
options = (this as IDBTraits).GetColumnOptions(commandCreator, column.column); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine("'{0}' vs '{1}'", column.options.type, options.type); |
|
|
|
|
Console.WriteLine("'{0}' vs '{1}'", column.options.defaultValue, options.defaultValue); |
|
|
|
|
Console.WriteLine("'{0}' vs '{1}'", column.options.isNotNull, options.isNotNull); |
|
|
|
|
if(!column.options.Equals((this as IDBTraits).GetColumnOptions(commandCreator, column.column))) { |
|
|
|
|
throw new FormattableException("Column {0} definition mismatch", column.column.columnName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void IDBTraits.RemoveTable(Func<DbCommand> commandCreator, TableDescription table) { |
|
|
|
|
this.CheckTable(commandCreator, table); |
|
|
|
|
using(DbCommand command = commandCreator()) |
|
|
|
|
{ |
|
|
|
|
command.CommandText = _SQLQueryManager.DropTable(table.table); |
|
|
|
|
Console.WriteLine(); |
|
|
|
|
Console.WriteLine(command.CommandText); |
|
|
|
|
command.ExecuteNonQuery(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IDBTraits.IsDDLTransactional |
|
|
|
|
{ |
|
|
|
|
get { return false; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |