jamesfredley opened a new pull request, #15393:
URL: https://github.com/apache/grails-core/pull/15393

   ## Expected Behavior
   
   When a domain class implements `MultiTenant` AND declares an explicit 
non-default datasource via `datasource 'secondary'` in its `static mapping` 
block, `GormEnhancer.allQualifiers()` should preserve the explicit datasource 
qualifier. GORM operations (`.save()`, `.get()`, `.count()`, etc.) should route 
to the declared datasource.
   
   ## Actual Behaviour
   
   `allQualifiers()` unconditionally clears the explicit datasource qualifier 
for any `MultiTenant` entity and replaces it with `ConnectionSource.DEFAULT` 
plus all known connection sources. This causes all GORM operations to silently 
route to the **default datasource** instead of the declared one.
   
   For DISCRIMINATOR multi-tenancy mode, `findTenantId()` returns 
`ConnectionSource.DEFAULT`, so `findStaticApi()` resolves to the DEFAULT 
qualifier — routing data to the wrong database with no error or warning.
   
   ## Steps To Reproduce
   
   1. Create a Grails 7 app with two H2 datasources (default + secondary)
   2. Configure DISCRIMINATOR multi-tenancy
   3. Create a domain class that implements `MultiTenant` with `datasource 
'secondary'`
   4. Call `.save()` on an instance of that domain class
   5. Observe that data is written to the default database, not the secondary 
one
   
   **Example application**: 
https://github.com/jamesfredley/grails-multitenant-datasource-bug
   
   ## Root Cause
   
   In `GormEnhancer.groovy` lines 180-193, the `allQualifiers()` method:
   
   ```groovy
   if ((MultiTenant.isAssignableFrom(entity.javaClass) || 
qualifiers.contains(ConnectionSource.ALL))
       && (datastore instanceof ConnectionSourcesProvider)) {
       qualifiers.clear()                        // ← CLEARS the explicit 
'secondary' mapping!
       qualifiers.add(ConnectionSource.DEFAULT)   // ← Replaces with DEFAULT
       // adds all connection sources from ConnectionSourcesProvider
   }
   ```
   
   The `MultiTenant` check unconditionally triggers qualifier expansion, 
treating `MultiTenant` the same as `ConnectionSource.ALL`. This is intended for 
DATABASE multi-tenancy mode (each tenant = separate connection), but 
incorrectly fires for DISCRIMINATOR mode where tenants share one DB.
   
   ## Fix
   
   The fix adds a check: when a `MultiTenant` entity has an explicit 
non-default, non-ALL datasource declaration, the explicit qualifiers are 
preserved instead of being cleared. The qualifier expansion only fires for:
   - `MultiTenant` entities on the default datasource (DATABASE multi-tenancy)
   - Entities declared with `ConnectionSource.ALL`
   
   **7 Spock tests added** covering all combinations of 
MultiTenant/non-MultiTenant × explicit/default/ALL datasource declarations.
   
   ## Environment Information
   
   - Grails: 7.0.7
   - Spring Boot: 3.5.10
   - Groovy: 4.0.30
   - GORM: 7.1 (grails-datamapping-core)
   - JDK: 25
   
   ## Version
   
   7.0.7


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to