Conversation
Adds FeatureTransactions.PropertyUpdate and Session.patchFeature with a
default unsupported implementation. SqlMutationSession implements it as
native SQL on the session's open connection so partial updates see prior
writes from the same transaction:
- main-table SET for scalar/datetime/boolean columns
- geometry via the existing toWkt path: GeoJSON in -> Geometry ->
ST_GeomFromText (with ST_ForcePolygonCW for POLYGON / MULTI_POLYGON),
matching the encoding the INSERT path emits
- VALUE_ARRAY and OBJECT_ARRAY junctions: DELETE existing rows by
parent_pk + INSERT new rows on the same session connection
OBJECT_ARRAY elements with nested OBJECT children, M:N junctions and
FEATURE_REF arrays remain unsupported and are rejected with a clear error.
3 tasks
Adds the Session-level extensions the executor needs to drive retire-and-insert (Replace), retire-in-place / clone-and-patch (Update), and retire-only (Delete) flows on versioned collections: - createFeatures(featureType, sources, crs, roleOverrides) - retireFeature(featureType, id, ts[, expectedStart]) - patchOpenVersion(featureType, id, updates, idFilter[, expectedStart]) - assertNoConflictingVersion(featureType, id, ts) - getOpenVersionStart(featureType, id) SqlMutationSession implements these as hand-built UPDATE / SELECT statements on the session's JDBC connection so they share the atomic transaction with the executor's pre-existing patch/insert flows. retireFeature and patchOpenVersion accept an optional expectedStart predicate (If-Unmodified-Since-style guard for composite-id flows). createFeatures gains a post-INSERT pass that emits a follow-up UPDATE for role-bound columns the encoder can't reach because the property is scoped read+filter but not write (e.g. denorm predecessor pointers populated by the session, not by clients). Two new SchemaBase roles, PREDECESSOR_INTERVAL_START and SUCCESSOR_INTERVAL_START, let versioned collections declare their denorm pointer columns to the same lookup machinery the existing interval roles use.
Each FeatureSchema property now carries an optional originObjectType: the objectType of the schema fragment that originally listed the property. LocalSchemaFragmentResolver tags every property contributed by a merged fragment with the fragment's objectType (recursively, with outer-fragment tagging winning over inner). The tag is @JsonIgnore + @DocIgnore: it is populated only by schema resolution, not from YAML. FeatureTokenDecoderGml's namespace-expectation chain now reads the property's own originObjectType first, then — only when no origin is set and the parent is a NESTED object (not the feature root) — falls back to the parent's objectType. The feature root's objectType no longer propagates down to property children, which is what lets a feature in a domain namespace nest standard properties inherited from a base fragment in the application's default namespace without dragging the feature's prefix onto them. Existing nested-object behaviour is unchanged: a property declared inline under, say, an ISO 19115 metadata object still inherits the nested object's namespace via the parent walk.
The versioned-insert pre-flight is now a plain id-existence check (SELECT 1 FROM main WHERE idCol = ? LIMIT 1). The previous three-predicate SQL silently allowed Insert on a retired feature id. Clients add new versions through Replace / Update / Delete; Insert is reserved for brand-new ids. cloneAndPatchFeature ships (was throwing UnsupportedOperationException): capture the open row's PK + start, clone the main row with inline overrides (start = ts, end = NULL, predecessor, successor) and main-table scalar patches, clone each junction table's rows redirecting the FK to the new PK, retire the old row with the same startCol < ts guard as retireFeature, then apply junction-backed patches via the existing patchInternal path. An expectedStart overload threads the composite-id If-Unmodified-Since predicate through; empty OLD result surfaces as 409 or 412. Specs cover the new SQL shape and fail-fast contracts.
- SchemaBase.Role.getLinkRelation() with PREDECESSOR_INTERVAL_START and SUCCESSOR_INTERVAL_START mapped to the predecessor-version / successor-version link relations. - FeatureTokenTransformerLinkRoles strips role-as-link values from the token stream and surfaces them via Result.getRoleLinks() / context. - FeatureTokenTransformerVersionIntervals captures the (PRIMARY_INTERVAL_START, PRIMARY_INTERVAL_END) tuples per feature for the Time Map endpoint. - FeatureTokenTransformerExtension SPI lets a FeatureQueryExtension contribute a token-stream transformer; FeatureStreamImpl wires contributed transformers in the pre-format slot alongside LinkRoles. - FeatureEventHandler ModifiableContext gains roleLinks() and canonicalFeatureId() (with the mirroring setters on FeatureEventHandlerSimple). - FeatureStream Result and ResultReduced expose getRoleLinks() and getVersionIntervals() so the queries handler can build HTTP Link headers without re-decoding the response. - FeatureTokenTransformerMappings propagates the per-feature context state (roleLinks, canonicalFeatureId) into its newContext before flushing the buffer; without that propagation upstream transformer state was dropped at the format-transformation boundary. - DeterminePipelineStepsThatCannotBeSkipped keeps MAPPING_VALUES when a schema property carries a versioned-features role (ID, PRIMARY_INTERVAL_START/END, or a role that declares a link relation) so the default DATETIME_FORMAT transformer runs and the captured timestamps reach the new transformers in ISO 8601.
…-id bounds SqlQueryTemplatesDeriver only treated a bare In(_ID_, …) as id-bounded and computed a separate surrogate-key range guard otherwise. When a single-feature query carries an extra predicate (e.g. TIntersects for the datetime parameter), the filter becomes And(In, TIntersects) and the id-filter check missed it, so meta-skip mode emitted "A.id >= 0 AND A.id <= 0" — a closed empty range — and returned no rows. Recurse into And's args so the id-list short-circuits even when combined with other predicates.
The LinkRoles transformer fed every feature's role values into the result builder via `putAllRoleLinks`, which is backed by `ImmutableMap.Builder`. For a multi-version single-feature stream, repeated keys (`predecessor-version` etc.) collided at `.build()` time and the whole query aborted with `IllegalArgumentException: Multiple entries with same key …` — the response handler then surfaced a generic 404. Switch the setter to `roleLinks(map)` (replace) and choose explicitly which feature's roles drive the result-level map: the one with the greatest `PRIMARY_INTERVAL_START`, i.e. the latest version. For non-versioned single-feature responses (no start) the only feature wins. The result-level emission is gated on `context.metadata().isSingleFeature()` so list responses no longer pollute `Result.getRoleLinks()` with arbitrary per-feature data. Per-feature roles on the context (`context.setRoleLinks`) — what writers use for per-feature link items — are unchanged.
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.
Property-level partial updates
Adds FeatureTransactions.PropertyUpdate and Session.patchFeature with a default unsupported implementation. SqlMutationSession implements it as native SQL on the session's open connection:
Session API for versioned-features mutations
Adds the Session-level extensions the executor needs to drive retire-and-insert (Replace), retire-in-place / clone-and-patch (Update), and retire-only (Delete) flows on versioned collections.
SqlMutationSession implements these as hand-built UPDATE / SELECT statements on the session's JDBC connection so they share the atomic transaction with the executor's pre-existing patch/insert flows. retireFeature and patchOpenVersion accept an optional expectedStart predicate (If-Unmodified-Since-style guard for composite-id flows). createFeatures gains a post-INSERT pass that emits a follow-up UPDATE for role-bound columns the encoder can't reach because the property is scoped read+filter but not write (e.g. denorm predecessor pointers populated by the session, not by clients).
Two new SchemaBase roles, PREDECESSOR_INTERVAL_START and SUCCESSOR_INTERVAL_START, let versioned collections declare their denorm pointer columns to the same lookup machinery the existing interval roles use.