Skip to content

Invalid parameter conversion for enums mapped in sub-classes #2649

@ia64mail

Description

@ia64mail

Invalid parameter conversion for enums mapped in sub-classes in NHibernate 5.3.
Most probably related to this issue - #2439

After upgrading to NHibernate 5.3.5 from 5.2.7 some unit tests failed with the following error:

NHibernate.Exceptions.GenericADOException : could not execute query
Data:
  Severity: ERROR
  InvariantSeverity: ERROR
  SqlState: 42883
  MessageText: operator does not exist: text = integer
  Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
  Position: 197
  File: parse_oper.c
  Line: 726
  Routine: op_error
  actual-sql-query: select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0)

After digging into the issue, we found that System.Enum properly cast to a string value in most of the cases. Although in case when enum property is mapped not in the super-class of the persistent entity but rather in sub-classes, NHibernate fails to cast type of SQL parameter correctly.

Here is the example of such entities, their mapping and query.

    public class Zoo
    {
        public virtual ICollection<Animal> Animals { get; } = new List<Animal>();
    }

    public enum HabitatEnum
    {
        Europe,
        America,
        Australia
    }

    public abstract class Animal
    {
        public virtual Zoo Zoo { get; set; }
    }

    public class Raccoon : Animal
    {
        public virtual HabitatEnum Habitat { get; set; }
    } 
    public class ZooMapping : ClassMap<Zoo>
    {
        public ZooMapping()
        {
            Table("zoo");

            HasMany(x => x.Animals)
                .AsList()
                .LazyLoad()
                .Inverse();
        }
    }
    
    public class AnimalMapping : ClassMap<Animal>
    {
        public AnimalMapping()
        {
            Table("animal");
            
            DiscriminateSubClassesOnColumn("type").Not.Nullable();

            References(x => x.Zoo)
                .Column("zoo_id")
                .Not.Nullable()
                .LazyLoad();
        }
    }
    
    public class RaccoonMapping : SubclassMap<Raccoon>
    {
        public RaccoonMapping()
        {
            DiscriminatorValue("raccoon");;
            
            Map(x => x.Habitat)
                .Column("habitat")
                .Not.Nullable();                
        }
    }

And finally the query

            Session.Query<Zoo>()
                .Where(zoo =>
                    zoo.Animals
                        .OfType<Raccoon>()
                        .Any(animal => animal.Habitat == HabitatEnum.America))
                .ToList();            

This example works just fine with NHibernate 5.2.7. It also works correctly if enum moved from sub-class Racoon to the super-class Animal in NHibernate 5.3.5.

Full stack trace is listed below:

NHibernate.Exceptions.GenericADOException : could not execute query
[ select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0) ]
  Name:p1 - Value:1
[SQL: select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0)]
  ----> Npgsql.PostgresException : 42883: operator does not exist: text = integer
Data:
  Severity: ERROR
  InvariantSeverity: ERROR
  SqlState: 42883
  MessageText: operator does not exist: text = integer
  Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
  Position: 197
  File: parse_oper.c
  Line: 726
  Routine: op_error
  actual-sql-query: select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0)

   at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder)
   at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters)
   at NHibernate.Loader.Hql.QueryLoader.List(ISessionImplementor session, QueryParameters queryParameters)
   at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.List(ISessionImplementor session, QueryParameters queryParameters)
   at NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results)
   at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results, Object filterConnection)
   at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results)
   at NHibernate.Impl.AbstractSessionImpl.List[T](IQueryExpression query, QueryParameters parameters)
   at NHibernate.Impl.AbstractQueryImpl2.List[T]()
   at NHibernate.Linq.DefaultQueryProvider.ExecuteList[TResult](Expression expression)
   at NHibernate.Linq.NhQueryable`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

System info

  • Net Framework 4.7.2
  • PostgreSQL 11
  • NHibernate 5.3.5
  • FluentNHibernate 3.1.0
  • Npgsql 5.0.1.1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions