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