Skip to content

Feature/licensing infrastructure#93

Merged
fupelaqu merged 4 commits into
mainfrom
feature/licensingInfrastructure
May 31, 2026
Merged

Feature/licensing infrastructure#93
fupelaqu merged 4 commits into
mainfrom
feature/licensingInfrastructure

Conversation

@fupelaqu
Copy link
Copy Markdown
Contributor

No description provided.

fupelaqu and others added 4 commits May 30, 2026 13:53
Three changes that together close issue #92 — UPDATE with a string-literal
RHS silently overwrote the target column with null.

1. Update.sql (sql module)

Render literals via `value.sql` instead of `value.value`. For StringValue
the two differ: `.value` returns the bare scala string `USA`, while `.sql`
returns the quoted form `'USA'`. For numerics and booleans the two
coincide, so this change is identity for non-string literals.

The bug surfaced because GatewayApi.run(update: Update) previously did
`api.updateByQuery(update.table, update.sql)` — re-emitting the AST as a
string and re-parsing it. The unquoted re-emission turned the bare token
`USA` into an Identifier on the second parse, which routed
`Update.customPipeline` through `ScriptProcessor.fromScript` and produced
`def param1 = ctx.USA; ctx.country = param1`. With `ignore_failure = true`
the field-miss was swallowed and the column was set to null.

A new regression test in ParserSpec exercises the full
parse → Update.sql → re-parse cycle and asserts the resulting
customPipeline produces a SetProcessor (not a ScriptProcessor).

2. IndicesApi.updateByQuery (core module)

Add an overload that accepts an already-parsed Update AST and skips the
SQL parser entirely. The string-based overload is retained for callers
submitting raw JSON. Both overloads share a private `runUpdateByQuery`
helper for the post-parse path (pipeline extraction → WHERE → JSON →
execute), so behaviour is identical regardless of entry point.

ElasticClientDelegator and MetricsElasticClient forward the new
overload.

3. GatewayApi.run (core module)

Dispatch SQL UPDATE statements to the AST overload so the parse runs
exactly once. This is the structural fix that closes the entire
"AST → re-emit → re-parse" failure mode — even a future literal-rendering
regression would no longer silently corrupt data through this path.

Side effect of pulling the JSON content out of `parseQueryForUpdate`'s
Right branch inside the shared helper: a latent bug where SELECT queries
pre-converted to JSON were being discarded in favour of the raw input
string is also fixed.

Closes #92.
…Query

Continuation of the issue #92 fix. The same audit applied to DELETE and
INSERT — neither has the silent-data-corruption bug UPDATE had
(Where.sql and Insert.sql round-trip safely through the parser), but
both paid the same double-parse cost: GatewayApi.run was emitting
`delete.sql` / `insert.sql` and IndicesApi was re-parsing the result.

Mirror the updateByQuery refactor:

- `IndicesApi`
  - Add `deleteByQuery(index, delete: Delete, refresh)` overload that
    skips parseSqlQueryForDeletion. Shared `runDeleteByQuery` helper
    holds the post-parse path (exists-check → openIfNeeded → execute
    → restore). Small `validateDeleteIndex` and `finalizeDeleteByQuery`
    helpers keep both entry points readable.
  - Add `insertByQuery(index, insert: Insert, refresh)` overload that
    skips parseInsertQuery. Shared `runInsertByQuery` helper holds the
    post-parse path (load index metadata → validate ON CONFLICT →
    validate AS SELECT columns → derive bulk options → build source →
    bulk insert). `validateInsertIndex` and `insertResultFinalize`
    factor out the index check and the recover-to-ElasticFailure
    plumbing.

- `GatewayApi.run`
  - `case delete: Delete` and `case insert: Insert` now call the AST
    overloads directly. Each SQL DML statement is parsed exactly once
    on its way through the dispatcher. The other `insertByQuery` call
    site (INSERT … AS SELECT constructed from scratch for index
    population) keeps the string overload — it isn't a round-trip.

- `ElasticClientDelegator` and `MetricsElasticClient`
  - Forward both new overloads (Delete / Insert imports added).

`BulkResult` is now imported explicitly in IndicesApi so the
`runInsertByQuery` helper can annotate its `Future[BulkResult]` return
type without leaking it through the existing public API.

No behaviour change for callers — the string overloads still exist and
do the exact same work — the AST overloads just skip one parse. core
compiles clean; ParserSpec UPDATE tests (including the round-trip
regression from the previous commit) all pass.
…cation

Narrow `LicenseRefreshStrategyFactory.resolveMode` from `private` to
`private[licensing]` so the licensing module can verify the resolution
contract directly, without going through SPI resolution. Add
`LicenseRefreshStrategyFactoryDriverModeSpec` covering both branches:
`refresh.enabled = false` -> Driver, `refresh.enabled = true` -> LongRunning.

Companion to softclient4es-jdbc Story 10.3 (Review patch D2) - JDBC's own
integration test cannot verify mode resolution end-to-end because its
priority-1 TestLicenseManagerSpi ignores the mode parameter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fupelaqu fupelaqu marked this pull request as ready for review May 31, 2026 05:50
@fupelaqu fupelaqu merged commit cc48731 into main May 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant