@@ -241,13 +241,13 @@ constant.Value is CallSite site &&
241
241
protected HqlTreeNode VisitNhAverage ( NhAverageExpression expression )
242
242
{
243
243
var hqlExpression = VisitExpression ( expression . Expression ) . AsExpression ( ) ;
244
- hqlExpression = IsCastRequired ( expression . Expression , expression . Type )
244
+ hqlExpression = IsCastRequired ( expression . Expression , expression . Type , out _ )
245
245
? ( HqlExpression ) _hqlTreeBuilder . Cast ( hqlExpression , expression . Type )
246
246
: _hqlTreeBuilder . TransparentCast ( hqlExpression , expression . Type ) ;
247
247
248
- return IsCastRequired ( expression . Type , " avg" )
249
- ? ( HqlTreeNode ) _hqlTreeBuilder . Cast ( _hqlTreeBuilder . Average ( hqlExpression ) , expression . Type )
250
- : _hqlTreeBuilder . TransparentCast ( _hqlTreeBuilder . Average ( hqlExpression ) , expression . Type ) ;
248
+ // In Oracle the avg function can return a number with up to 40 digits which cannot be retrieved from the data reader due to the lack of such
249
+ // numeric type in .NET. In order to avoid that we have to add a cast to trim the number so that it can be converted into a .NET numeric type.
250
+ return _hqlTreeBuilder . Cast ( _hqlTreeBuilder . Average ( hqlExpression ) , expression . Type ) ;
251
251
}
252
252
253
253
protected HqlTreeNode VisitNhCount ( NhCountExpression expression )
@@ -267,7 +267,7 @@ protected HqlTreeNode VisitNhMax(NhMaxExpression expression)
267
267
268
268
protected HqlTreeNode VisitNhSum ( NhSumExpression expression )
269
269
{
270
- return IsCastRequired ( expression . Type , "sum" )
270
+ return IsCastRequired ( expression . Type , "sum" , out _ )
271
271
? ( HqlTreeNode ) _hqlTreeBuilder . Cast ( _hqlTreeBuilder . Sum ( VisitExpression ( expression . Expression ) . AsExpression ( ) ) , expression . Type )
272
272
: _hqlTreeBuilder . TransparentCast ( _hqlTreeBuilder . Sum ( VisitExpression ( expression . Expression ) . AsExpression ( ) ) , expression . Type ) ;
273
273
}
@@ -483,9 +483,12 @@ protected HqlTreeNode VisitUnaryExpression(UnaryExpression expression)
483
483
case ExpressionType . Convert :
484
484
case ExpressionType . ConvertChecked :
485
485
case ExpressionType . TypeAs :
486
- return IsCastRequired ( expression . Operand , expression . Type )
486
+ return IsCastRequired ( expression . Operand , expression . Type , out var existType )
487
487
? _hqlTreeBuilder . Cast ( VisitExpression ( expression . Operand ) . AsExpression ( ) , expression . Type )
488
- : VisitExpression ( expression . Operand ) ;
488
+ // Make a transparent cast when an IType exists, so that it can be used to retrieve the value from the data reader
489
+ : existType
490
+ ? _hqlTreeBuilder . TransparentCast ( VisitExpression ( expression . Operand ) . AsExpression ( ) , expression . Type )
491
+ : VisitExpression ( expression . Operand ) ;
489
492
}
490
493
491
494
throw new NotSupportedException ( expression . ToString ( ) ) ;
@@ -587,63 +590,75 @@ protected HqlTreeNode VisitNewArrayExpression(NewArrayExpression expression)
587
590
return _hqlTreeBuilder . ExpressionSubTreeHolder ( expressionSubTree ) ;
588
591
}
589
592
590
- private bool IsCastRequired ( Expression expression , System . Type toType )
593
+ private bool IsCastRequired ( Expression expression , System . Type toType , out bool existType )
591
594
{
592
- return toType != typeof ( object ) && IsCastRequired ( GetType ( expression ) , TypeFactory . GetDefaultTypeFor ( toType ) ) ;
595
+ existType = false ;
596
+ return toType != typeof ( object ) && IsCastRequired ( GetType ( expression ) , TypeFactory . GetDefaultTypeFor ( toType ) , out existType ) ;
593
597
}
594
598
595
- private bool IsCastRequired ( IType type , IType toType )
599
+ private bool IsCastRequired ( IType type , IType toType , out bool existType )
596
600
{
597
601
// A type can be null when casting an entity into a base class, in that case we should not cast
598
602
if ( type == null || toType == null || Equals ( type , toType ) )
599
603
{
604
+ existType = false ;
600
605
return false ;
601
606
}
602
607
603
608
var sqlTypes = type . SqlTypes ( _parameters . SessionFactory ) ;
604
609
var toSqlTypes = toType . SqlTypes ( _parameters . SessionFactory ) ;
605
610
if ( sqlTypes . Length != 1 || toSqlTypes . Length != 1 )
606
611
{
612
+ existType = false ;
607
613
return false ; // Casting a multi-column type is not possible
608
614
}
609
615
616
+ existType = true ;
610
617
if ( sqlTypes [ 0 ] . DbType == toSqlTypes [ 0 ] . DbType )
611
618
{
612
619
return false ;
613
620
}
614
621
615
622
if ( type . ReturnedClass . IsEnum && sqlTypes [ 0 ] . DbType == DbType . String )
616
623
{
624
+ existType = false ;
617
625
return false ; // Never cast an enum that is mapped as string, the type will provide a string for the parameter value
618
626
}
619
627
620
628
// Some dialects can map several sql types into one, cast only if the dialect types are different
621
- var castTypeName = _parameters . SessionFactory . Dialect . GetCastTypeName ( sqlTypes [ 0 ] ) ;
622
- var toCastTypeName = _parameters . SessionFactory . Dialect . GetCastTypeName ( toSqlTypes [ 0 ] ) ;
629
+ if ( ! _parameters . SessionFactory . Dialect . TryGetCastTypeName ( sqlTypes [ 0 ] , out var castTypeName ) ||
630
+ ! _parameters . SessionFactory . Dialect . TryGetCastTypeName ( toSqlTypes [ 0 ] , out var toCastTypeName ) )
631
+ {
632
+ return false ; // The dialect does not support such cast
633
+ }
634
+
623
635
return castTypeName != toCastTypeName ;
624
636
}
625
637
626
- private bool IsCastRequired ( System . Type type , string sqlFunctionName )
638
+ private bool IsCastRequired ( System . Type type , string sqlFunctionName , out bool existType )
627
639
{
628
640
if ( type == typeof ( object ) )
629
641
{
642
+ existType = false ;
630
643
return false ;
631
644
}
632
645
633
646
var toType = TypeFactory . GetDefaultTypeFor ( type ) ;
634
647
if ( toType == null )
635
648
{
649
+ existType = false ;
636
650
return true ; // Fallback to the old behavior
637
651
}
638
652
653
+ existType = true ;
639
654
var sqlFunction = _parameters . SessionFactory . SQLFunctionRegistry . FindSQLFunction ( sqlFunctionName ) ;
640
655
if ( sqlFunction == null )
641
656
{
642
657
return true ; // Fallback to the old behavior
643
658
}
644
659
645
660
var fnReturnType = sqlFunction . ReturnType ( toType , _parameters . SessionFactory ) ;
646
- return fnReturnType == null || IsCastRequired ( fnReturnType , toType ) ;
661
+ return fnReturnType == null || IsCastRequired ( fnReturnType , toType , out existType ) ;
647
662
}
648
663
649
664
private IType GetType ( Expression expression )
0 commit comments