Skip to content

Commit

Permalink
Fixed hash joins with keys of different types
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMpn committed Mar 8, 2022
1 parent 6911749 commit 8a2f028
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 7 deletions.
18 changes: 13 additions & 5 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/HashJoinNode.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand Down Expand Up @@ -31,10 +32,20 @@ protected override IEnumerable<Entity> ExecuteInternal(IDictionary<string, DataS
// Build the hash table
var leftSchema = LeftSource.GetSchema(dataSources, parameterTypes);
leftSchema.ContainsColumn(LeftAttribute.GetColumnName(), out var leftCol);
var leftColType = leftSchema.Schema[leftCol].ToNetType(out _);
var rightSchema = RightSource.GetSchema(dataSources, parameterTypes);
rightSchema.ContainsColumn(RightAttribute.GetColumnName(), out var rightCol);
var rightColType = rightSchema.Schema[rightCol].ToNetType(out _);

if (!SqlTypeConverter.CanMakeConsistentTypes(leftColType, rightColType, out var keyType))
throw new QueryExecutionException($"Cannot match key types {leftColType.Name} and {rightColType.Name}");

var leftKeyConverter = SqlTypeConverter.GetConversion(leftColType, keyType);
var rightKeyConverter = SqlTypeConverter.GetConversion(rightColType, keyType);

foreach (var entity in LeftSource.Execute(dataSources, options, parameterTypes, parameterValues))
{
var key = entity[leftCol];
var key = leftKeyConverter(entity[leftCol]);

if (!_hashTable.TryGetValue(key, out var list))
{
Expand All @@ -46,12 +57,9 @@ protected override IEnumerable<Entity> ExecuteInternal(IDictionary<string, DataS
}

// Probe the hash table using the right source
var rightSchema = RightSource.GetSchema(dataSources, parameterTypes);
rightSchema.ContainsColumn(RightAttribute.GetColumnName(), out var rightCol);

foreach (var entity in RightSource.Execute(dataSources, options, parameterTypes, parameterValues))
{
var key = entity[rightCol];
var key = rightKeyConverter(entity[rightCol]);
var matched = false;

if (_hashTable.TryGetValue(key, out var list))
Expand Down
18 changes: 16 additions & 2 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/SqlTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -860,13 +860,27 @@ public static object ChangeType(object value, Type type)
if (value != null && value.GetType() == type)
return value;

var key = value.GetType().FullName + " -> " + type.FullName;
var conversion = _conversions.GetOrAdd(key, _ => CompileConversion(value.GetType(), type));
var conversion = GetConversion(value.GetType(), type);
return conversion(value);
}

/// <summary>
/// Gets a function to convert from one type to another
/// </summary>
/// <param name="sourceType">The type to convert from</param>
/// <param name="destType">The type to convert to</param>
/// <returns>A function that converts between the requested types</returns>
public static Func<object, object> GetConversion(Type sourceType, Type destType)
{
var key = sourceType.FullName + " -> " + destType.FullName;
return _conversions.GetOrAdd(key, _ => CompileConversion(sourceType, destType));
}

private static Func<object, object> CompileConversion(Type sourceType, Type destType)
{
if (sourceType == destType)
return (object value) => value;

var param = Expression.Parameter(typeof(object));
var expression = (Expression) Expression.Convert(param, sourceType);

Expand Down

0 comments on commit 8a2f028

Please sign in to comment.