Fix constraints overwriting mapping index configuration (#15680)#15700
Fix constraints overwriting mapping index configuration (#15680)#15700codeconsole wants to merge 4 commits into
Conversation
|
The PR comments file appears to be empty (only the header row is present). I cannot analyze the suggestion or provide context-specific advice without actual review comments. Please ensure the file contains the necessary data for evaluation. |
dbba9f5 to
88020b4
Compare
Updated based on reviewPushed an amended commit with the following tweaks (verified by an independent multi-agent review of the original PR): Test coverage improvedThe original test (now renamed "constraints closure does not overwrite mapping properties (standard DSL flow)") passed even with the fix reverted — both closures route through The new spec adds "mapping property in builder.properties is preserved when target.propertyConfigs has a different instance for the same key" which:
Verified: this new test FAILS against the pre-fix Also added "target.propertyConfigs entries fill in for keys the mapping closure never touched" to lock in that constraint-only configuration for properties the mapping never mentions still propagates correctly. Code comment addedAdded javadoc to
Known follow-upThe dual-store design ( |
When a domain property has both `index: true, indexAttributes: [unique: true]` in `static mapping` and `unique: true` in `static constraints`, the constraint evaluation overwrites the mapping-configured property. The root cause is in `DefaultMappingConfigurationBuilder.getProperties()` which uses `putAll` to merge `Entity.propertyConfigs` (populated by constraint evaluation) over `builder.properties` (populated by mapping evaluation), destroying the mapping-set `index` and `indexAttributes` values. The fix changes `getProperties()` to use `putIfAbsent` semantics — properties already configured by the mapping closure are preserved when the constraint closure provides a conflicting entry. Test: DefaultMappingConfigurationBuilderSpec verifies that mapping-set index properties survive constraint evaluation.
88020b4 to
365956b
Compare
|
The code style & licensing issues need addressed |
DefaultMappingConfigurationBuilderSpec was committed without the ASF license header that every other source file in grails-datastore-core carries, and imported org.grails.datastore.mapping.config.Property without using it. Add the standard header and remove the unused import.
The multi-line log.debug message used double-quoted strings with no GString
interpolation, which the project's UnnecessaryGString CodeNarc rule flags
(codenarcMain). Convert the three non-interpolated continuation lines to
single quotes; the first line keeps double quotes because it embeds '{}'.
✅ All tests passed ✅🏷️ Commit: 9c6e41a Learn more about TestLens at testlens.app. |
Summary
Fixes #15680.
When a domain property has both
index: true, indexAttributes: [unique: true]instatic mappingandunique: trueinstatic constraints, the constraint evaluation overwrites the mapping-configured property, so the database-level unique index is silently never created.Root Cause
AbstractGormMappingFactory.createMappedForm()evaluates both closures through the sameDefaultMappingConfigurationBuilder:name index: true, indexAttributes: [unique: true]) goes throughbuilder.invokeMethod, which stores aMongoAttributewithindex=true, indexAttributes={unique:true}inbuilder.properties['name'].name unique: true) goes throughEntity.methodMissing, which creates a SEPARATE instance intarget.propertyConfigs['name']containing onlyunique=true.builder.getProperties()then doesproperties.putAll(target.propertyConfigs), overwriting the mapping-set property with the constraint-only version.indexreverts tofalseandindexAttributesbecomesnull.MongoDatastore.initializeIndices()readsproperty.getMapping().getMappedForm().isIndex(), which now returnsfalse, so the MongoDB index is silently never created.Fix
Change
getProperties()to useputIfAbsentsemantics — properties already configured by the mapping closure are preserved when the constraint closure provides a conflicting entry.Test
DefaultMappingConfigurationBuilderSpeccovers the scenario directly: a property simulating GORM'sEntity.methodMissingpath against a builder that already has a mapping-configured property. The test FAILS before the fix and PASSES after.Verified
Tested against a real Pixoto3 Grails 7.2 app with embedded MongoDB:
Category.namedeclaredindex: true, indexAttributes: [unique: true]in mapping +unique: truein constraints → only_id_index in MongoDBname_1unique index correctly created in MongoDBBackward Compatibility
Behavior change: if a user previously relied on constraints overwriting mapping (unlikely — that's the bug), they would observe their mapping-configured property surviving instead. The opposite direction (constraint values being applied on top of an existing mapping property) was already happening through
builder.invokeMethodreusing the existing instance, so the typical "add a constraint to a mapped property" flow is unaffected.