Repository navigation
Declare query columns an entity does not carry - #744
Merged
Merged
Conversation
A query may name a column that the entity record does not hold: a stored generated column such as a folded title, or a trigger-filled column that serves a search or a sort. Entities declare it with @QueryColumn, which gives the metamodel a field for it while keeping it out of every SELECT, INSERT and row mapping. Model lists the query columns apart from its columns. The metamodel processors (APT and KSP) emit a field per declaration, so Owner_.lastNameFolded works like any other field in search, where and orderBy. The schema validator checks that the database holds the column and fills it on its own, reporting QUERY_COLUMN_NOT_FILLED otherwise; validate = false opts a column out for triggers the metadata cannot see. DatabaseSchema reads the generated flag defensively since not every driver reports it. Docs cover the annotation in the entities page, the validation table and the search page, where the stored folded column is the optional step for a search the expression index leaves slow. Skills carry the same guidance. Fixes #743
| /** | ||
| * A column a record declares through {@code @QueryColumn}: it gets a metamodel field but has no record component. | ||
| * | ||
| * @param field the metamodel field name. |
| * A column a record declares through {@code @QueryColumn}: it gets a metamodel field but has no record component. | ||
| * | ||
| * @param field the metamodel field name. | ||
| * @param type the column's value type, as the annotation names it. |
| * | ||
| * @param field the metamodel field name. | ||
| * @param type the column's value type, as the annotation names it. | ||
| * @param nullable whether the column allows null. |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…th Trivy The OWASP Dependency-Check job never completed: the NVD API throttles GitHub-hosted runners to a page every few minutes, the first pull needs the whole feed, and the job ran into its timeout on every run while the Maven cache, already saved by the build job, kept none of the data. The gate now writes a CycloneDX bill of materials of compile and runtime scope with cyclonedx-maven-plugin and scans it with Trivy, which carries its own advisory database. No key is needed, so the scan also runs on Dependabot pull requests, and it finishes in seconds after the build. The first scan found two High advisories in the shipped graph: storm-ktor-test carries httpcore5 5.3.6 through ktor-server-test-host and the Apache HTTP client (CVE-2026-54399, CVE-2026-54428). The module now declares httpcore5 and httpcore5-h2 5.4.4 directly, so the fixed line also wins in an application's test classpath.
For an insert, a query column is a column the entity does not map: every insert leaves it out. The unmapped column check already reports a NOT NULL column without a default that the database does not generate, so a query column now falls under that rule instead of a kind of its own, and the validate attribute, which only escaped the separate kind, goes with it.
The Java 21 test model declares the folded last name as a query column, as the Kotlin one does, and its query builder test reads it through the generated metamodel, through a path and from the model. The metamodel factory test resolves a query column of a record without a generated metamodel, which exercises the reflective metamodel and its refusals, and a query column behind an unknown path. The schema test reads columns through a driver whose metadata lacks IS_GENERATEDCOLUMN. The enum mapping in ModelImpl no longer guards against a query column, which never reaches it: a record value is mapped per record column only.
A column is carried by a field or named by a query column, never both. The model builder compares a query column's resolved column name with the columns the fields of the same table map and refuses a match, naming the field, as it refuses two query columns of one type naming the same column.
The keyset sort check resolves the record field first and consults the query column only where no field exists at the path, so an ordinary sort field costs what it did. Query columns are read once per declaring type through a ClassValue instead of assembling the repeatable annotation on every call, and the model builder returns at once for a type that declares none instead of collecting its component and column names.
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A query may name a column that the entity record does not hold: a stored generated column such as a folded title, or a trigger-filled column that serves a search or a sort. Entities now declare such a column with
@QueryColumn, which gives the metamodel a field for it while keeping it out of every SELECT, INSERT and row mapping.@QueryColumn(repeatable,field,type, optionalcolumn,nullable) in storm-foundation.Model.queryColumns()lists them apart fromcolumns(); a query column has index 0 and never reaches the row mapper.Owner_.lastNameFoldedworks insearch,where,orderByand keyset sorting (nullablefeeds the sort check).UNMAPPED_COLUMNwarning rather than a kind of its own. Unmapped generated columns no longer triggerUNMAPPED_COLUMN.DatabaseSchemareadsIS_GENERATEDCOLUMNdefensively since not every driver reports it.Docs: the entities page gains a Query Columns section, the validation table lists the new error, the metamodel page mentions the generated field, and the search page describes the stored folded column as the optional step for a search the expression index leaves slow, folding the input in the database so the recheck stays gone. Skills (entity, entity-from-schema, validate, query, Java and Kotlin) carry the same guidance.
Dependency gate
The
dependency-checkjob has not completed since #740 merged: the NVD API throttles GitHub-hosted runners to a page every few minutes, the first pull needs the whole feed, and the job ran into its 40-minute timeout on every run (shown as cancelled) while the Maven cache, already saved by the build job, kept none of the data. The gate now writes a CycloneDX bill of materials of compile and runtime scope withcyclonedx-maven-pluginand scans it with Trivy, failing on High or Critical. Trivy carries its own advisory database, so there is no key, the scan also runs on Dependabot pull requests, and it finishes in seconds after the build. TheNVD_API_KEYsecret is no longer read.The first scan found two High advisories in the shipped graph: storm-ktor-test carries httpcore5 5.3.6 through
ktor-server-test-hostand the Apache HTTP client (CVE-2026-54399, CVE-2026-54428). The module now declares httpcore5 and httpcore5-h2 5.4.4 directly, so the fixed line also wins in an application's test classpath, where a managed version would not reach. The storm-ktor-test suite passes on 5.4.4 and the BOM scans clean.Test plan
QueryColumnIntegrationTest,SchemaValidatorTestandDatabaseSchemaTestcasesQueryBuilderTest, each with a query column test over its own test modelFixes #743