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

   ## Description
   
   `count()` returns `Long` instead of `Integer`, matching what the datastore 
actually produces.
   
   ### The problem
   
   `GormStaticOperations` declares:
   
   ```groovy
   /**
    * Counts the number of persisted entities
    * @return The number of persisted entities
    */
   Integer count()
   ```
   
   but no datastore produces an `Integer` here — `COUNT(*)` is a 64-bit value 
in SQL. Every backend narrowed the result to satisfy the declared type, and 
each did it a different way:
   
   | Backend | Narrowing |
   | --- | --- |
   | `GormStaticApi` | `((Number)res).intValue()` |
   | Hibernate 5 | builds a `CriteriaQuery<Long>`, then casts `(Integer)` |
   | Hibernate 7 | `select count(*) ... as Integer` |
   
   `intValue()` truncates silently, so a table with more than 
`Integer.MAX_VALUE` rows reported a wrong and possibly negative count with no 
error at all. Declaring the type the query returns removes the narrowing rather 
than moving it around.
   
   `RestfulServiceController` gets simpler as a direct consequence — it was 
calling `Math.toIntExact(getService().count(params))` on a service that already 
returned a `Long`, narrowing purely to fit the controller signature. That call 
is gone.
   
   ### Scope
   
   `GormStaticOperations.count()` / `getCount()`, the `count()` / `count` 
members generated on every domain class via `GormEntity`, 
`TenantDelegatingGormOperations`, both Hibernate backends, and 
`RestfulController.countResources()`.
   
   `grails-datamapping-rx` needed no change: `RxGormStaticOperations.count()` 
already returns `Observable<Number>` and never committed to `Integer`.
   
   ### Compatibility
   
   Dynamic Groovy is unaffected. `Book.count() == 5`, `int n = Book.count()`, 
and arithmetic on the result all continue to work, because Groovy converts 
between numeric types on assignment and compares them by value. What breaks is 
statically compiled code assigning the result to an `Integer`, Java callers 
relying on unboxing, and subclasses overriding `countResources()`. Documented 
as section 54 of the 8.0 upgrade notes with before/after examples.
   
   ### Typed views
   
   Worth calling out, because it is the same defect twice. Typed view models 
are bound by reflective `Field.set` with **no coercion** — in GSP 
(`GroovyPage.applyModelFieldsFromBinding`) and in JSON views 
(`WritableScriptTemplate.FieldSetter`) alike. A view that names a concrete 
numeric type therefore fails outright when a controller supplies the other one, 
and the two scaffolding paths supply different types: `generate-views` pairs 
with a generated service declaring `Long count()`, while `static scaffold = X` 
is backed by `RestfulController.countResources()`.
   
   So the scaffolded `index.gsp` and both `product/index.gson` views now 
declare `Number`, which accepts whatever a controller supplies. 
`HalViewHelper.paginate` accordingly takes a `Number total` (converted once at 
the internal `getPaginationLinks` boundary) so a count can reach it without 
being narrowed again on the way in.
   
   The `paginate` signature change is the one piece here that is not strictly 
about `count()`. It is included because without it the in-tree JSON views 
cannot compile against a `Long` count. Happy to split it out if reviewers 
prefer.
   
   ## Relationship to #16322
   
   #16322 changes the scaffolded `index.gsp` count field to `Number` for the 
same underlying reason. This branch is cut from `8.0.x`, where that file still 
declares `Integer`, and the `grails-test-examples-gorm` scaffolding functional 
tests fail without it — so the identical one-line change is included here to 
keep this PR green on its own. The two resolve to the same line and should 
merge cleanly in either order.
   
   ## Verification
   
   - `grails-datamapping-core`, `grails-rest-transforms`, `grails-scaffolding`, 
`grails-views-gson` — unit tests pass.
   - All five datastore backends compile; the full 
`grails-data-hibernate5-core` suite passes.
   - `grails-test-examples-gorm:integrationTest` passes, including 
`ScaffoldingFunctionalSpec`, which fails on this branch without the `Number` 
view declaration.
   - `grails-test-examples-views-functional-tests:integrationTest` passes with 
`--rerun-tasks` (a cached result is not evidence), including `ProductSpec`, 
which exercises the HAL pagination path.
   - Added a test pinning both the returned value and the declared contract on 
`GormStaticOperations.count()` and `getCount()`.
   
   ## Contributor Checklist
   
   Please review the following checklist before submitting your pull request. 
Pull requests that do not meet these requirements may be closed without review.
   
   ### Issue and Scope
   
   - [ ] This PR is linked to an existing issue that has been **acknowledged or 
approved** by the project team. If no approved issue exists, please give 
background on why this change is necessary.  Tickets are preferred for release 
change log history.
   - [ ] This PR addresses the **complete scope** of the linked issue. Partial 
implementations or unfinished work should not be submitted for review.
   - [ ] This PR contains a **single, focused change**. Unrelated changes 
should be submitted as separate pull requests.
   - [ ] This PR targets the **correct branch** for the type of change:
       - **Patch release branches** (e.g., `7.0.x`): Bug fixes only. No new 
features or API changes.
       - **Minor release branches** (e.g., `7.1.x`): New features are welcome, 
but breaking existing APIs must be avoided.
       - **Major release branches** (e.g., `8.0.x`): Reserved for major 
changes. Breaking API changes are permitted.
   
   ### Code Quality
   
   - [ ] I have **added or updated tests** that cover the changes introduced in 
this PR. All code contributions are expected to include appropriate test 
coverage.
   - [ ] I have verified that all existing tests pass by running `./gradlew 
build --rerun-tasks`.
   - [ ] My code follows the project's **code style** guidelines. I have run 
`./gradlew codeStyle` and resolved any violations. See [Code 
Style](../CONTRIBUTING.md#code-style) for details.
   - [ ] This PR does **not** include mass reformatting, style-only changes, or 
large-scale refactoring unless it was **explicitly approved** in the linked 
issue. Unsolicited reformatting will not be accepted.
   - [ ] If generative AI tooling was used in preparing this contribution, a 
quality model was used to ensure contributions are **consistent with the 
project's quality standards**.
   
   ### Licensing and Attribution
   
   - [ ] All contributed code is provided under the [Apache License 
2.0](https://www.apache.org/licenses/LICENSE-2.0), and new source files include 
the appropriate **Apache license header**.
   - [ ] I have the necessary rights to submit this contribution and confirm it 
is my own original work (see [Legal 
Notice](../CONTRIBUTING.md#i-want-to-contribute)).
   - [ ] If generative AI tooling was used in preparing this contribution, I 
have followed the [Apache Software Foundation's policy on generative 
tooling](https://www.apache.org/legal/generative-tooling.html) and have 
properly attributed its use.
   


-- 
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