The GitHub Actions job "Coverage" on 
grails-core.git/feat/gorm-count-returns-long-8.0.x has succeeded.
Run started by GitHub user codeconsole (triggered by codeconsole).

Head commit for run:
4d95dc61ef39bae9a27383937574e2408fc9e3ff / Scott Murphy Heiberg 
<[email protected]>
Return Long from count() instead of narrowing to Integer

GormStaticOperations declared `Integer count()`. How wide the value
underneath actually is depends on the datastore: Hibernate counts with SQL
COUNT(*) and Neo4j with Cypher count(*), both 64-bit, while MongoDB
aggregates with {$sum: 1}, which returns Int32 and promotes to Int64 once
the total exceeds it. Only the in-memory datastore is fixed at 32 bits.
GormStaticApi normalises all of them with longValue(), and the declared
return type then narrowed the result again with intValue(). That second
step truncates silently, so a collection or table with more than
Integer.MAX_VALUE rows reported a wrong and possibly negative count
without failing — and MongoDB, where the width varies with collection
size, is exactly where that is most likely to bite.

Long is the only type that holds every value a datastore can produce here.
Declaring it removes the narrowing rather than moving it around, in
GormStaticApi, both Hibernate backends, TenantDelegatingGormOperations,
GormEntity, RestfulController and the GraphQL count fetcher, whose schema
field was typed Int and truncated for the same reason. Paged GraphQL
results already type totalCount as Long through the same type manager, so
the count field now matches a scalar the schema used rather than adding
one. MongoDB and Neo4j inherit GormStaticApi and needed no change.

RestfulServiceController loses its Math.toIntExact call, because the
service it delegates to already returned a Long and was narrowing only to
fit the controller signature.

Dynamic Groovy is unaffected: `Book.count() == 5` and `int n = Book.count()`
still work, because Groovy converts and compares numeric types by value.
Statically compiled callers, Java callers relying on unboxing, and
overrides of these methods need updating, as does any generated GraphQL
client, since the schema field becomes `personCount: Long`. All of this is
documented in the 8.0 upgrade notes.

Typed views bind their model by reflective Field.set with no coercion, in
GSP and in JSON views alike, so a view naming a concrete numeric type
fails when the other scaffolding path supplies the other type. The
scaffolded index.gsp declares Number, which accepts either, and the two
product index.gson views declare Long to match what the controller now
supplies.

The JSON view pagination carries a count, so it is widened with it rather
than left to narrow on the way in: paginate, getPaginationLinks and the
offsets derived from a total are all Long. An offset computed from a total
past 2^31 would otherwise overflow the moment the total stopped doing so.
Parameters already exposed long(name, default) for the request path. The
pre-existing links(Map, Object, Number total) keeps its signature, since
changing it would reject callers passing an Integer, but it now widens
with longValue() instead of truncating with intValue().

The scaffolded index.gsp change also appears in the fix for the Integer
declaration it previously carried; the two resolve to the same line.

Report URL: https://github.com/apache/grails-core/actions/runs/34397145049

With regards,
GitHub Actions via GitBox

Reply via email to