Skip to content

Commit

Permalink
Merge pull request #31 from ayende/master
Browse files Browse the repository at this point in the history
Merge latest ayende changes to repo
  • Loading branch information
ayende authored Mar 1, 2017
2 parents 5e43005 + f05d94c commit a783ad9
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 18 deletions.
10 changes: 8 additions & 2 deletions Rhino.Etl.Cmd/Rhino.Etl.Cmd.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<IsWebBootstrapper>false</IsWebBootstrapper>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
Expand All @@ -31,6 +31,7 @@
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down Expand Up @@ -88,7 +89,6 @@
<ItemGroup>
<Compile Include="RhinoEtlRunner.cs" />
<Compile Include="RhinoEtlSetup.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RhinoEtlCommandLineOptions.cs" />
</ItemGroup>
<ItemGroup>
Expand Down Expand Up @@ -137,6 +137,12 @@
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
3 changes: 3 additions & 0 deletions Rhino.Etl.Cmd/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
6 changes: 4 additions & 2 deletions Rhino.Etl.Core/Infrastructure/SqlCommandSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ public void Append(SqlCommand command)
/// <param name="command"></param>
private static void AssertHasParameters(SqlCommand command)
{
if(command.Parameters.Count==0)
if (command.Parameters.Count == 0 &&
(RuntimeInfo.Version.Contains("2.0") || RuntimeInfo.Version.Contains("1.1")))
{
throw new ArgumentException("A command in SqlCommandSet must have parameters. You can't pass hardcoded sql strings.");
throw new ArgumentException(
"A command in SqlCommandSet must have parameters. You can't pass hardcoded sql strings.");
}
}

Expand Down
7 changes: 4 additions & 3 deletions Rhino.Etl.Core/Operations/SqlBatchOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public int Timeout
/// Initializes a new instance of the <see cref="SqlBatchOperation"/> class.
/// </summary>
/// <param name="connectionStringName">Name of the connection string.</param>
public SqlBatchOperation(string connectionStringName)
protected SqlBatchOperation(string connectionStringName)
: this(ConfigurationManager.ConnectionStrings[connectionStringName])
{
}
Expand All @@ -47,7 +47,7 @@ public SqlBatchOperation(string connectionStringName)
/// Initializes a new instance of the <see cref="SqlBatchOperation"/> class.
/// </summary>
/// <param name="connectionStringSettings">The connection string settings to use.</param>
public SqlBatchOperation(ConnectionStringSettings connectionStringSettings)
protected SqlBatchOperation(ConnectionStringSettings connectionStringSettings)
: base(connectionStringSettings)
{
base.paramPrefix = "@";
Expand All @@ -66,11 +66,12 @@ public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
{
SqlCommandSet commandSet = null;
CreateCommandSet(connection, transaction, ref commandSet, timeout);

foreach (Row row in rows)
{
SqlCommand command = new SqlCommand();
PrepareCommand(row, command);
if (command.Parameters.Count == 0) //workaround around a framework bug
if (command.Parameters.Count == 0 && (RuntimeInfo.Version.Contains("2.0") || RuntimeInfo.Version.Contains("1.1"))) //workaround around a framework bug
{
Guid guid = Guid.NewGuid();
command.Parameters.AddWithValue(guid.ToString(), guid);
Expand Down
109 changes: 108 additions & 1 deletion Rhino.Etl.Core/Operations/SqlBulkInsertOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
namespace Rhino.Etl.Core.Operations
{
using System;
using System.Linq;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using DataReaders;

Expand Down Expand Up @@ -228,7 +230,15 @@ public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
{
sqlBulkCopy = CreateSqlBulkCopy(connection, transaction);
DictionaryEnumeratorDataReader adapter = new DictionaryEnumeratorDataReader(_inputSchema, rows);
sqlBulkCopy.WriteToServer(adapter);
try
{
sqlBulkCopy.WriteToServer(adapter);
}
catch (InvalidOperationException)
{
CompareSqlColumns(connection, transaction, rows);
throw;
}

if (PipelineExecuter.HasErrors)
{
Expand Down Expand Up @@ -276,5 +286,102 @@ private SqlBulkCopy CreateSqlBulkCopy(SqlConnection connection, SqlTransaction t
copy.BulkCopyTimeout = Timeout;
return copy;
}

private void CompareSqlColumns(SqlConnection connection, SqlTransaction transaction, IEnumerable<Row> rows)
{
var command = connection.CreateCommand();
command.CommandText = "select * from {TargetTable} where 1=0".Replace("{TargetTable}", TargetTable);
command.CommandType = CommandType.Text;
command.Transaction = transaction;

using (var reader = command.ExecuteReader(CommandBehavior.KeyInfo))
{
var schemaTable = reader.GetSchemaTable();
var databaseColumns = schemaTable.Rows
.OfType<DataRow>()
.Select(r => new
{
Name = (string)r["ColumnName"],
Type = (Type)r["DataType"],
IsNullable = (bool)r["AllowDBNull"],
MaxLength = (int)r["ColumnSize"]
})
.ToArray();

var missingColumns = _schema.Keys.Except(
databaseColumns.Select(c => c.Name));
if (missingColumns.Any())
throw new InvalidOperationException(
"The following columns are not in the target table: " +
string.Join(", ", missingColumns.ToArray()));
var differentColumns = _schema
.Select(s => new
{
Name = s.Key,
SchemaType = s.Value,
DatabaseType = databaseColumns.Single(c => c.Name == s.Key)
})
.Where(c => !TypesMatch(c.SchemaType, c.DatabaseType.Type, c.DatabaseType.IsNullable));
if (differentColumns.Any())
throw new InvalidOperationException(
"The following columns have different types in the target table: " +
string.Join(", ", differentColumns
//.Select(c => $"{c.Name}: is {GetFriendlyName(c.SchemaType)}, but should be {GetFriendlyName(c.DatabaseType.Type)}{(c.DatabaseType.IsNullable ? "?" : "")}.")
// c.Name, GetFriendlyName(c.SchemaType), GetFriendlyName(c.DatabaseType.Type), (c.DatabaseType.IsNullable ? \"?\" : \"\")
.Select(c => string.Format("{0}: is {1}, but should be {2}{3}.", c.Name,
GetFriendlyName(c.SchemaType), GetFriendlyName(c.DatabaseType.Type),
(c.DatabaseType.IsNullable ? "?" : "")))
.ToArray()
));
var stringsTooLong =
(from column in databaseColumns
where column.Type == typeof(string)
from mapping in Mappings
where mapping.Value == column.Name
let name = mapping.Key
from row in rows
let value = (string)row[name]
where value != null && value.Length > column.MaxLength
select new { column.Name, column.MaxLength, Value = value })
.ToArray();
if (stringsTooLong.Any())
throw new InvalidOperationException(
"The folowing columns have values too long for the target table: " +
string.Join(", ", stringsTooLong
.Select(s => "{s.Name}: max length is {s.MaxLength}, value is {s.Value}."
.Replace("{s.Name}", s.Name)
.Replace("{s.MaxLength}", s.MaxLength.ToString())
.Replace("{s.Value}", s.Value)
)
.ToArray()));
}
}

private static string GetFriendlyName(Type type)
{
var friendlyName = type.Name;
if (!type.IsGenericType)
return friendlyName;

var iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0)
friendlyName = friendlyName.Remove(iBacktick);

var genericParameters = type.GetGenericArguments()
.Select(x => GetFriendlyName(x))
.ToArray();
friendlyName += "<" + string.Join(", ", genericParameters) + ">";

return friendlyName;
}

private bool TypesMatch(Type schemaType, Type databaseType, bool isNullable)
{
if (schemaType == databaseType)
return true;
if (isNullable && schemaType == typeof(Nullable<>).MakeGenericType(databaseType))
return true;
return false;
}
}
}
10 changes: 8 additions & 2 deletions Rhino.Etl.Core/Rhino.Etl.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand All @@ -43,6 +44,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>Rhino.Etl.Core.XML</DocumentationFile>
<NoWarn>1607</NoWarn>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
Expand All @@ -52,6 +54,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>Rhino.Etl.Core.XML</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Boo.Lang">
Expand Down Expand Up @@ -122,12 +125,12 @@
<Compile Include="Pipelines\PipelineExecutionException.cs" />
<Compile Include="Pipelines\SingleThreadedNonCachedPipelineExecuter.cs" />
<Compile Include="Pipelines\ThreadPoolPipelineExecuter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Operations\SqlBulkInsertOperation.cs" />
<Compile Include="Pipelines\SingleThreadedPipelineExecuter.cs" />
<Compile Include="Exceptions\RhinoEtlException.cs" />
<Compile Include="QuackingDictionary.cs" />
<Compile Include="Row.cs" />
<Compile Include="RuntimeInfo.cs" />
<Compile Include="WithLoggingMixin.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -150,6 +153,9 @@
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
20 changes: 20 additions & 0 deletions Rhino.Etl.Core/RuntimeInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Rhino.Etl.Core
{
internal static class RuntimeInfo
{
public static string Version
{
get
{
var asm = Assembly.GetEntryAssembly();
return asm.ImageRuntimeVersion;
}
}
}
}
7 changes: 5 additions & 2 deletions Rhino.Etl.Dsl/Rhino.Etl.Dsl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down Expand Up @@ -101,7 +102,6 @@
<Compile Include="Macros\RowProcessedMacro.cs" />
<Compile Include="Macros\SqlBulkInsertMacro.cs" />
<Compile Include="Macros\TerminateMacro.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Rhino.Etl.Core\Rhino.Etl.Core.csproj">
Expand Down Expand Up @@ -129,6 +129,9 @@
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
8 changes: 3 additions & 5 deletions Rhino.Etl.Tests/App.config
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="test"
connectionString="Data Source=.\SQLExpress;Initial Catalog=test;Integrated Security=SSPI;Timeout=30;"
providerName="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<add name="test" connectionString="Data Source=.\SQLExpress;Initial Catalog=test;Integrated Security=SSPI;Timeout=30;" providerName="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</connectionStrings>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
3 changes: 2 additions & 1 deletion Rhino.Etl.Tests/Rhino.Etl.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down

0 comments on commit a783ad9

Please sign in to comment.