The GitHub Actions job "Coverage" on grails-core.git/8.0.x has succeeded. Run started by GitHub user borinquenkid (triggered by borinquenkid).
Head commit for run: bfa709384d5e03de3a4bd77c3e12e9b80d37e4e2 / Walter B Duque de Estrada <[email protected]> Hibernate 7: name nested composite foreign key columns in the referenced key's order (#16539) * fix: name the foreign key columns of a nested composite identifier in the referenced key's order A foreign key to an entity whose composite identifier contains a to-one to another composite entity (leaf -> middle(grandParent, name) -> grandParent(name String, luckyNumber Integer)) named the expanded columns in the mapped order of the nested parts, while Hibernate types them positionally from the referenced key, whose nested parts are sorted by property name. The leaf table got cfk_middle_grand_parent_name INTEGER and cfk_middle_grand_parent_lucky_number VARCHAR. Data round-trips (the order is consistent positionally) but the columns are mislabelled for schema tooling and native SQL. The nested parts are now sorted by name. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * test: prove the composite foreign key column order against a real mapping The earlier assertions checked each column's name and type, which an integer paired with a string would break but two same-typed parts written in the wrong order would not. The Hibernate-backed spec now reads the imported keys from JDBC metadata in KEY_SEQ order and asserts each foreign key column against the referenced primary key column, including cfk_middle_name, for parts declared out of name order and for the nested chain. It fails when the sort in CompositeIdentifierToManyToOneBinder is removed. The mock-based binder spec no longer claims the ordering. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * fix: permute the columns of a composite identifier by part, not by property A foreign key into an entity whose composite identifier has a part that spans several columns (a to-one to another composite entity) was sorted with Hibernate's property permutation applied to the column list. That only lines up when every part has one column, so a nested part declared after a plain part that sorts before it left the foreign key columns pointing at the wrong primary key columns and the row failed to save. The permutation is now expanded to move each part with its whole column span, in both sortOrIndexForeignKeyColumns and getReferencedIdentifierColumns. Also drops the unused Transactional import from the spec. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * test: reference a composite key whose multi-column part sits between two plain parts The two PrbSpanLeaf features checked the same PrbLeaf -> PrbMiddle key as the features before them: composite('zed', 'middle', 'ace') only laid out PrbSpanLeaf's own primary key, which Hibernate orders itself and nothing references, so the column-span permutation of a multi-column part between two plain parts never ran. PrbHubRef and the join table of PrbHub's unidirectional one-to-many now reference such a key, so both the many-to-one and the collection paths assert the KEY_SEQ pairs, and a hub with a tag and a reference is saved and reloaded. With the plain property permutation the pairs come out crossed and the save fails. Co-Authored-By: Claude Fable 5.1 <[email protected]> * fix: sort a composite foreign key once the identifier it references is bound CompositeIdentifierToManyToOneBinder sorted a key against the referenced composite identifier while binding the property. When the referenced entity had not been bound yet, the columns kept their declared order, got sequential type indexes and were marked sorted, so Hibernate's own ToOne.sortProperties() skipped them too, and PropertyFromValueCreator created Hibernate's foreign key, which pairs the columns by position. Whether a key came out right therefore depended on the registration order of the entities, which an application does not choose: PrbGrand, PrbLeaf, PrbMiddle left the leaf's key in declared order while its values were bound in key order, and saving a leaf failed with a referential integrity violation; a middle registered before its grand parent crossed the alpha and zeta columns of every key referencing it. The binder now aligns a key only once the referenced identifier is bound, down to the identifiers its to-one parts reference. Otherwise it registers a CompositeForeignKeySecondPass through InFlightMetadataCollector.addSecondPass, the mechanism the binder already uses for collections, which Hibernate runs before it compiles the foreign keys; this mirrors ToOne.sortProperties(), which resolves the referenced identifier lazily from the second passes. The alignment is idempotent and aligns the pending to-one parts of the referenced identifier first, so a key referencing a middle whose own key is still pending sees the middle's sorted columns; a key bound during the second passes is aligned at once. PropertyFromValueCreator leaves the foreign key of a many-to-one to a composite identifier to the composite binder, which pairs each column with the identifier column it references; for a referenced entity registered first the binder had already disabled Hibernate's key, so those keys are unchanged. CompositeForeignKeyRegistrationOrderSpec boots the entities of CompositeForeignKeyColumnTypesSpec in five registration orders and asserts the KEY_SEQ pairs and a save and reload; four of them failed before this change. Co-Authored-By: Claude Fable 5.1 <[email protected]> * fix: leave the foreign key of a bidirectional one-to-many to its to-one side The inverse collection's key added a second foreign key for the same association. Collection.createAllKeys(), run from the collection's second pass, creates a positional key for the key columns under the same implicit name as the explicit key the composite binder creates for the to-one side: the name is hashed from the two tables and the column names, not from the referenced columns or their order. Both keys were in the metadata in every registration order; the schema export created the first one registered and logged "Constraint already exists" for the other. Registered as PrbMiddle, PrbLeaf, PrbGrand, the collection's second pass ran before the to-one's deferred alignment, so the positional key reached the database, paired the wrong columns and saving a leaf failed with a referential integrity violation. A bidirectional hasMany into the PrbHub layout failed the same way registered as hub, child, grand. BidirectionalOneToManyLinker.link now disables the key's foreign key, so the to-one side's key is the only key of the association. The to-one side always creates one: ToOne.createForeignKey(), called from PropertyFromValueCreator, for a simple identifier, and the composite binder, with the referenced identifier columns, for a composite one. This applies to every inverse one-to-many rather than only to composite owners because for a simple identifier the two keys were one and the same ForeignKey, deduplicated by Table.createForeignKey, so the key that remains keeps the implicit name and columns an existing schema has. CompositeForeignKeyRegistrationOrderSpec now runs every registration order (6 of the chain, 24 of the hub set, 6 of grand, hub and the new bidirectional PrbHubChild) and asserts that no foreign key name appears twice in the Hibernate metadata; a simple-identifier pair pins the unchanged implicit name and columns of its single key. Before the fix the two orders above failed with the integrity violation and every other composite order failed the duplicate-name assertion. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test: pin the empty inverse hasMany of a composite identifier declared out of name order An inverse hasMany on an entity whose composite identifier is not declared in name order loads no elements: BidirectionalOneToManyLinker.link copies the to-one's columns, which are already in key order, and key.sortProperties() applies the owner identifier's permutation a second time, so the collection is loaded with the identifier values bound to the wrong columns. Declared in name order, the permutation is the identity and the collection loads. This predates the composite foreign key fixes and does not need a nested composite, so it is left to a follow-up issue. The spec records the repro as a @PendingFeature next to the name-order variant that passes, so the fix will surface as "passes unexpectedly" and the annotation is removed with it. Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Sonnet 5.5 <[email protected]> Report URL: https://github.com/apache/grails-core/actions/runs/38078097353 With regards, GitHub Actions via GitBox
