@@ -241,13 +241,13 @@ constant.Value is CallSite site &&
241241 protected HqlTreeNode VisitNhAverage ( NhAverageExpression expression )
242242 {
243243 var hqlExpression = VisitExpression ( expression . Expression ) . AsExpression ( ) ;
244- hqlExpression = IsCastRequired ( expression . Expression , expression . Type )
244+ hqlExpression = IsCastRequired ( expression . Expression , expression . Type , out _ )
245245 ? ( HqlExpression ) _hqlTreeBuilder . Cast ( hqlExpression , expression . Type )
246246 : _hqlTreeBuilder . TransparentCast ( hqlExpression , expression . Type ) ;
247247
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 ) ;
251251 }
252252
253253 protected HqlTreeNode VisitNhCount ( NhCountExpression expression )
@@ -267,7 +267,7 @@ protected HqlTreeNode VisitNhMax(NhMaxExpression expression)
267267
268268 protected HqlTreeNode VisitNhSum ( NhSumExpression expression )
269269 {
270- return IsCastRequired ( expression . Type , "sum" )
270+ return IsCastRequired ( expression . Type , "sum" , out _ )
271271 ? ( HqlTreeNode ) _hqlTreeBuilder . Cast ( _hqlTreeBuilder . Sum ( VisitExpression ( expression . Expression ) . AsExpression ( ) ) , expression . Type )
272272 : _hqlTreeBuilder . TransparentCast ( _hqlTreeBuilder . Sum ( VisitExpression ( expression . Expression ) . AsExpression ( ) ) , expression . Type ) ;
273273 }
@@ -483,9 +483,12 @@ protected HqlTreeNode VisitUnaryExpression(UnaryExpression expression)
483483 case ExpressionType . Convert :
484484 case ExpressionType . ConvertChecked :
485485 case ExpressionType . TypeAs :
486- return IsCastRequired ( expression . Operand , expression . Type )
486+ return IsCastRequired ( expression . Operand , expression . Type , out var existType )
487487 ? _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 ) ;
489492 }
490493
491494 throw new NotSupportedException ( expression . ToString ( ) ) ;
@@ -587,63 +590,75 @@ protected HqlTreeNode VisitNewArrayExpression(NewArrayExpression expression)
587590 return _hqlTreeBuilder . ExpressionSubTreeHolder ( expressionSubTree ) ;
588591 }
589592
590- private bool IsCastRequired ( Expression expression , System . Type toType )
593+ private bool IsCastRequired ( Expression expression , System . Type toType , out bool existType )
591594 {
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 ) ;
593597 }
594598
595- private bool IsCastRequired ( IType type , IType toType )
599+ private bool IsCastRequired ( IType type , IType toType , out bool existType )
596600 {
597601 // A type can be null when casting an entity into a base class, in that case we should not cast
598602 if ( type == null || toType == null || Equals ( type , toType ) )
599603 {
604+ existType = false ;
600605 return false ;
601606 }
602607
603608 var sqlTypes = type . SqlTypes ( _parameters . SessionFactory ) ;
604609 var toSqlTypes = toType . SqlTypes ( _parameters . SessionFactory ) ;
605610 if ( sqlTypes . Length != 1 || toSqlTypes . Length != 1 )
606611 {
612+ existType = false ;
607613 return false ; // Casting a multi-column type is not possible
608614 }
609615
616+ existType = true ;
610617 if ( sqlTypes [ 0 ] . DbType == toSqlTypes [ 0 ] . DbType )
611618 {
612619 return false ;
613620 }
614621
615622 if ( type . ReturnedClass . IsEnum && sqlTypes [ 0 ] . DbType == DbType . String )
616623 {
624+ existType = false ;
617625 return false ; // Never cast an enum that is mapped as string, the type will provide a string for the parameter value
618626 }
619627
620628 // 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+
623635 return castTypeName != toCastTypeName ;
624636 }
625637
626- private bool IsCastRequired ( System . Type type , string sqlFunctionName )
638+ private bool IsCastRequired ( System . Type type , string sqlFunctionName , out bool existType )
627639 {
628640 if ( type == typeof ( object ) )
629641 {
642+ existType = false ;
630643 return false ;
631644 }
632645
633646 var toType = TypeFactory . GetDefaultTypeFor ( type ) ;
634647 if ( toType == null )
635648 {
649+ existType = false ;
636650 return true ; // Fallback to the old behavior
637651 }
638652
653+ existType = true ;
639654 var sqlFunction = _parameters . SessionFactory . SQLFunctionRegistry . FindSQLFunction ( sqlFunctionName ) ;
640655 if ( sqlFunction == null )
641656 {
642657 return true ; // Fallback to the old behavior
643658 }
644659
645660 var fnReturnType = sqlFunction . ReturnType ( toType , _parameters . SessionFactory ) ;
646- return fnReturnType == null || IsCastRequired ( fnReturnType , toType ) ;
661+ return fnReturnType == null || IsCastRequired ( fnReturnType , toType , out existType ) ;
647662 }
648663
649664 private IType GetType ( Expression expression )
0 commit comments