-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Unknown
-
None
-
Affects Version/s: None
-
Component/s: Query
-
None
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Summary
Each select-list position is independent, so the same column may appear at more than one position.
Hibernate ORM produces such select lists on its own for ordinary mappings. A $project stage is a
BSON document, so repeating a field name collapses the entries. The stage then yields fewer fields
than the select list has positions, and MongoResultSet maps positions to fields by index, so
every position after the first duplicate reads the wrong field.
Reproducer 1: a discriminator also mapped as an attribute
@Entity(name = "Animal") @Table(name = "animals") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) static class Animal { @Id int id; String name; @Column(name = "type", insertable = false, updatable = false) String type; } @Entity(name = "Cat") @DiscriminatorValue("CAT") static class Cat extends Animal { int lives; String colour; }
session.find(Animal.class, 1) emits six projection specifications, of which type appears
twice:
{"aggregate": "animals", "pipeline": [
{"$match": {"_id": {"$eq": 1}}},
{"$project": {"_id": true, "type": true, "name": true, "type": true, "colour": true, "lives": true}}
]}
The document carries five fields, so reading position 6 fails:
org.hibernate.JDBCException: Could not extract column [5] from JDBC ResultSet [Failed to get value from column [index: 5]]
Reproducer 2: an entity reachable by two paths of one query
No annotation is used unusually here. Owner is both the query root and the target of the
association held by the embeddable of Shelter.
@Entity(name = "Owner") @Table(name = "owners") static class Owner { @Id int id; String description; @ManyToOne Shelter shelter; } @Entity(name = "Shelter") @Table(name = "shelters") static class Shelter { @Id int id; @Embedded Location location; } @Embeddable static class Location { String town; @ManyToOne Owner owner; }
_id and description are each projected twice, so seven specifications produce five fields:
{"aggregate": "owners", "pipeline": [
{"$lookup": {"from": "shelters", "localField": "shelter_id", "foreignField": "_id", "as": "#s1_0"}},
{"$unwind": {"path": "$#s1_0", "preserveNullAndEmptyArrays": true}},
{"$match": {"_id": {"$eq": 1}}},
{"$project": {"_id": true, "description": true, "s1_0#_id": "$#s1_0._id",
"_id": true, "description": true, "shelter_id": true, "s1_0#town": "$#s1_0.town"}}
]}
org.hibernate.JDBCException: Could not extract column [6] from JDBC ResultSet [Invalid column index [6]; cannot be under 1 or over the current number of fields [5]]
Severity
The overrun at the end of the row is what raises the exception. Positions between the duplicate and
the end of the row resolve to the wrong field and are read without complaint, so a select list whose
duplicate is not followed by an overrun returns wrong values silently.
Proposed fix
Give each select-list position its own projection field name, and have MongoResultSet resolve
positions against the names the translator assigned rather than against whatever the document
happens to contain.
Acceptance
Both reproducers above load the entity and read the intended values, asserted by value and not
merely by the absence of an exception.