treat(... as ...) queries a collection that cannot exist and drops the discriminator restriction

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Unknown
    • None
    • Affects Version/s: None
    • Component/s: Query
    • None
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      Hibernate ORM narrows treat(... as ...) over a single table by way of an inline view whose name
      is a SQL select. That name reaches the aggregate command unchanged, so the command names a
      collection that cannot exist. Querying a nonexistent collection is not an error in MongoDB, it
      matches nothing, so the query returns an empty result with no indication that anything went wrong.

      Separately, when treat appears in the where clause the emitted $match carries no
      discriminator restriction, so the query matches rows of sibling subtypes.

      Reproducer

      @Entity(name = "Vehicle")
      @Table(name = "vehicles")
      @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
      @DiscriminatorColumn(name = "kind", discriminatorType = DiscriminatorType.STRING)
      static class Vehicle {
          @Id int id;
          String name;
      }
      
      @Entity(name = "Car")
      @DiscriminatorValue("CAR")
      static class Car extends Vehicle {
          int doors;
      }
      

      Two rows in vehicles: a CAR named "saloon" with 4 doors, and a BIKE named "quadracycle"
      that also carries a doors value of 4. Only the discriminator distinguishes them.

      treat in the select clause

      select treat(v as Car).doors from Vehicle v
      
      {"aggregate": "(select * from vehicles t where t.kind='CAR')",
       "pipeline": [{"$project": {"doors": true}}]}
      

      Returns no rows. select treat(v as Car) from Vehicle v behaves the same way.

      treat in the where clause

      select v.name from Vehicle v where treat(v as Car).doors = 4
      
      {"aggregate": "vehicles",
       "pipeline": [{"$match": {"doors": {"$eq": 4}}}, {"$project": {"name": true}}]}
      

      Returns ["saloon", "quadracycle"]. The bike is not a Car, so it should not match.

      Proposed fix

      Resolve the treated table reference back to the underlying collection, and render the discriminator
      restriction into $match in both clause positions.

      If a narrowing form cannot be translated, it has to fail with FeatureNotSupportedException
      rather than run. Both failures here are silent, one returning too few rows and the other too many.

      Acceptance

      Selecting an attribute of a treated root, selecting the treated root itself, and filtering on an
      attribute of a treated root each return exactly the rows of the named subtype, asserted by value.

            Assignee:
            Unassigned
            Reporter:
            Jeffrey Yemin
            None
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: