@@ -243,13 +243,13 @@ constant.Value is CallSite site &&
243243 protected HqlTreeNode VisitNhAverage ( NhAverageExpression expression )
244244 {
245245 var hqlExpression = VisitExpression ( expression . Expression ) . AsExpression ( ) ;
246- hqlExpression = IsCastRequired ( expression . Expression , expression . Type )
246+ hqlExpression = IsCastRequired ( expression . Expression , expression . Type , out _ )
247247 ? ( HqlExpression ) _hqlTreeBuilder . Cast ( hqlExpression , expression . Type )
248248 : _hqlTreeBuilder . TransparentCast ( hqlExpression , expression . Type ) ;
249249
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 ) ;
253253 }
254254
255255 protected HqlTreeNode VisitNhCount ( NhCountExpression expression )
@@ -269,7 +269,7 @@ protected HqlTreeNode VisitNhMax(NhMaxExpression expression)
269269
270270 protected HqlTreeNode VisitNhSum ( NhSumExpression expression )
271271 {
272- return IsCastRequired ( expression . Type , "sum" )
272+ return IsCastRequired ( expression . Type , "sum" , out _ )
273273 ? ( HqlTreeNode ) _hqlTreeBuilder . Cast ( _hqlTreeBuilder . Sum ( VisitExpression ( expression . Expression ) . AsExpression ( ) ) , expression . Type )
274274 : _hqlTreeBuilder . TransparentCast ( _hqlTreeBuilder . Sum ( VisitExpression ( expression . Expression ) . AsExpression ( ) ) , expression . Type ) ;
275275 }
@@ -485,9 +485,12 @@ protected HqlTreeNode VisitUnaryExpression(UnaryExpression expression)
485485 case ExpressionType . Convert :
486486 case ExpressionType . ConvertChecked :
487487 case ExpressionType . TypeAs :
488- return IsCastRequired ( expression . Operand , expression . Type )
488+ return IsCastRequired ( expression . Operand , expression . Type , out var existType )
489489 ? _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 ) ;
491494 }
492495
493496 throw new NotSupportedException ( expression . ToString ( ) ) ;
@@ -589,63 +592,75 @@ protected HqlTreeNode VisitNewArrayExpression(NewArrayExpression expression)
589592 return _hqlTreeBuilder . ExpressionSubTreeHolder ( expressionSubTree ) ;
590593 }
591594
592- private bool IsCastRequired ( Expression expression , System . Type toType )
595+ private bool IsCastRequired ( Expression expression , System . Type toType , out bool existType )
593596 {
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 ) ;
595599 }
596600
597- private bool IsCastRequired ( IType type , IType toType )
601+ private bool IsCastRequired ( IType type , IType toType , out bool existType )
598602 {
599603 // A type can be null when casting an entity into a base class, in that case we should not cast
600604 if ( type == null || toType == null || Equals ( type , toType ) )
601605 {
606+ existType = false ;
602607 return false ;
603608 }
604609
605610 var sqlTypes = type . SqlTypes ( _parameters . SessionFactory ) ;
606611 var toSqlTypes = toType . SqlTypes ( _parameters . SessionFactory ) ;
607612 if ( sqlTypes . Length != 1 || toSqlTypes . Length != 1 )
608613 {
614+ existType = false ;
609615 return false ; // Casting a multi-column type is not possible
610616 }
611617
618+ existType = true ;
612619 if ( sqlTypes [ 0 ] . DbType == toSqlTypes [ 0 ] . DbType )
613620 {
614621 return false ;
615622 }
616623
617624 if ( type . ReturnedClass . IsEnum && sqlTypes [ 0 ] . DbType == DbType . String )
618625 {
626+ existType = false ;
619627 return false ; // Never cast an enum that is mapped as string, the type will provide a string for the parameter value
620628 }
621629
622630 // 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+
625637 return castTypeName != toCastTypeName ;
626638 }
627639
628- private bool IsCastRequired ( System . Type type , string sqlFunctionName )
640+ private bool IsCastRequired ( System . Type type , string sqlFunctionName , out bool existType )
629641 {
630642 if ( type == typeof ( object ) )
631643 {
644+ existType = false ;
632645 return false ;
633646 }
634647
635648 var toType = TypeFactory . GetDefaultTypeFor ( type ) ;
636649 if ( toType == null )
637650 {
651+ existType = false ;
638652 return true ; // Fallback to the old behavior
639653 }
640654
655+ existType = true ;
641656 var sqlFunction = _parameters . SessionFactory . SQLFunctionRegistry . FindSQLFunction ( sqlFunctionName ) ;
642657 if ( sqlFunction == null )
643658 {
644659 return true ; // Fallback to the old behavior
645660 }
646661
647662 var fnReturnType = sqlFunction . ReturnType ( toType , _parameters . SessionFactory ) ;
648- return fnReturnType == null || IsCastRequired ( fnReturnType , toType ) ;
663+ return fnReturnType == null || IsCastRequired ( fnReturnType , toType , out existType ) ;
649664 }
650665
651666 private IType GetType ( Expression expression )
0 commit comments