Carry CalciteEvalCommandIT through the helper-managed index path#5407
Conversation
The IT's init() previously created the in-test test_eval index via direct
`PUT /test_eval/_doc/N` requests, relying on dynamic mapping. Two
problems with that:
1. The doc PUTs auto-create the index with whatever settings the
cluster defaults to. The analytics-engine compatibility path
(force-routing on; tests.analytics.parquet_indices=true) needs
parquet-backed indices, which TestUtils.createIndexByRestClient
applies via TestUtils.makeParquetBacked when the system property
is set. Direct PUTs sidestep that helper, so test_eval lands as
Lucene-backed and the analytics planner rejects it with
"No backend can scan all requested fields on index [test_eval]".
Three of the four tests fail at execution; the fourth uses BANK
(loadIndex'd) and passes.
2. init() runs before every @test method. The doc PUTs are doc-level
idempotent, so re-running was wasteful but not failing. Once we
switch to createIndexByRestClient, the index-level PUT is no longer
idempotent and re-running throws "resource_already_exists_exception".
Both addressed in one change:
- test_eval is created via TestUtils.createIndexByRestClient with an
explicit mapping (name/title=keyword, age=long). The helper honours
tests.analytics.parquet_indices=true and produces a parquet-backed
index for the analytics-engine sweep; on the v2 path the helper
is a no-op around the index PUT, so behaviour is unchanged.
- The whole init body is guarded by TestUtils.isIndexExist — same
idempotency idiom that loadIndex uses for predefined fixtures. First
@test method provisions; subsequent methods skip.
Also pins the projection order on testEvalStringConcatenation. The
original query (`source=test_eval | eval greeting = 'Hello ' + name`)
had no `| fields` clause and relied on the implicit projection's column
order — v2 returns Lucene-source insertion order, analytics returns
parquet-storage order (alphabetical), so the assertion only matched on
v2 by coincidence. Adding `| fields name, title, age, greeting` makes
the assertion deterministic across paths; the existing expected rows
(`rows("Alice", "Engineer", 25, "Hello Alice")`) already match this
order, so v2 behaviour is preserved.
No semantic change for the v2 path: the explicit mapping types
(keyword, long, keyword) resolve to the same PPL types ("string",
"bigint", "string") that dynamic mapping inferred, and eval reads from
_source either way. Analytics-route compatibility goes from 1/4 to 4/4
once the corresponding analytics-engine wiring lands in core.
Signed-off-by: Kai Huang <ahkcs@amazon.com>
PR Reviewer Guide 🔍(Review updated until commit 435827e)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 435827e
Previous suggestionsSuggestions up to commit 28a5853
|
Gradle 9.4.1 has a ClassCastException in TestEventReporterAsListener.started
(line 58 of org/gradle/api/internal/tasks/testing/junit/result/TestEventReporterAsListener.class
inside plugins/gradle-testing-base-9.4.1.jar):
((GroupTestEventReporterInternal) reportersById.get(parent.getId()))
.reportTestDirectly(...)
The bridge stores test descriptor reporters keyed by id and casts the
parent's stored reporter to GroupTestEventReporterInternal when a child
event arrives. A class-level @ignore produces a non-composite parent
descriptor with a leaf TestEventReporter (not a group reporter), so the
cast fails. The first failure in CI was on
CalciteInformationSchemaCommandIT.testTablesFromPrometheusCatalog —
that IT is @ignore'd at the class level pointing at issue opensearch-project#3455.
Once any @ignore'd class is loaded by the test runner, the cast aborts
the whole integTest task with "Test process encountered an unexpected
problem", even though no actual test assertion failed and the tests
themselves would have been skipped at the JUnit layer.
The bridge is instantiated by Gradle's own AbstractTestTask (verified
via javap of `gradle-testing-base-9.4.1.jar` — `new TestEventReporterAsListener`
at AbstractTestTask:263), so we cannot prevent its registration from
user code. The only available remedy until Gradle ships a fix is to
keep @ignore'd classes off the test runner's input set entirely.
Net behaviour for CI:
- @ignore'd tests were already skipped at the JUnit layer; skipping
them at the Gradle layer instead changes "skipped" to "not run".
For metrics/dashboards that count skipped tests, this is a one-time
minor delta. For pass/fail status, identical.
- Pre-existing exclude block (`OrderIT`, `ExplainIT`, etc.) already
excludes some of the same classes; this commit covers the remaining
@ignore'd ones. OrderIT not duplicated.
- integTest task no longer aborts mid-run; per-test FAILED messages
(testLogging.events "failed") are preserved.
Remove this block once Gradle 9.4.x bug is fixed upstream. The list
mirrors `grep -rln '^@ignore' integ-test/src/test/java` minus
already-excluded paths.
Signed-off-by: Kai Huang <ahkcs@amazon.com>
|
Persistent review updated to latest commit 435827e |
6ffba26
into
opensearch-project:feature/mustang-ppl-integration
Mirrors the trailing portion of #5414 onto feature/mustang-ppl-integration. The Gradle 9.4.1 wrapper bump (#5406) and the integTest exclude block for @ignore'd classes (#5407) already landed on this branch, but the matching cleanup of `CalciteNoPushdownIT` did not — the @suite still listed four classes that are now build-time excluded from integTest: - CalciteInformationSchemaCommandIT - CalciteJsonFunctionsIT - CalcitePrometheusDataSourceCommandsIT - CalciteShowDataSourcesCommandIT Leaving them in the suite means the JUnit runner attempts to load each class, finds it referenced from the suite, and registers a descriptor — which (combined with the class-level @ignore) reproduces the same TestEventReporterAsListener.started cast that the integTest exclude was meant to avoid. Dropping the entries here keeps the no-pushdown suite consistent with the exclude block: a class excluded at the build layer must not also appear in any @Suite.SuiteClasses list. No new tests are introduced or removed — the four classes are already @ignore'd and have been since they were written. This change is purely about keeping the no-pushdown suite metadata aligned with the exclusion pattern carried over from main. Signed-off-by: Kai Huang <ahkcs@amazon.com>
Same shape as opensearch-project#5407 for CalciteEvalCommandIT. The IT's init() previously created the in-test test_eval index via direct `PUT /test_eval/_doc/N` requests, relying on dynamic mapping. Two problems: 1. The doc PUTs auto-create the index with whatever settings the cluster defaults to. The analytics-engine compatibility path (force-routing on; tests.analytics.parquet_indices=true) needs parquet-backed indices, which TestUtils.createIndexByRestClient applies via TestUtils.makeParquetBacked when the system property is set. Direct PUTs sidestep that helper, so test_eval lands as Lucene-backed and the analytics planner rejects it with "No backend can scan all requested fields on index [test_eval]". All four working tests fail at execution. 2. init() runs before every @test method. The doc PUTs are doc-level idempotent, so re-running was wasteful but not failing. Once we switch to createIndexByRestClient, the index-level PUT is no longer idempotent and re-running throws "resource_already_exists_exception". Both addressed in one change: - test_eval is created via TestUtils.createIndexByRestClient with an explicit mapping (name/title=keyword, age=long). The helper honours tests.analytics.parquet_indices=true and produces a parquet-backed index for the analytics-engine sweep; on the v2 path the helper is a no-op around the index PUT, so behaviour is unchanged. - The whole init body is guarded by TestUtils.isIndexExist — same idempotency idiom that loadIndex uses for predefined fixtures. First @test method provisions; subsequent methods skip. Also pins the projection order on testFieldFormatStringConcatenation. The original query (`source=test_eval | fieldformat greeting = 'Hello ' + name`) had no `| fields` clause and relied on the implicit projection's column order — v2 returns Lucene-source insertion order, analytics returns parquet-storage order (alphabetical), so the assertion only matched on v2 by coincidence. Adding `| fields name, title, age, greeting` makes the assertion deterministic across paths; the existing expected rows (`rows("Alice", "Engineer", 25, "Hello Alice")`) already match this order, so v2 behaviour is preserved. The other four tests already had explicit `| fields ...` clauses, so no change there. No semantic change for the v2 path: the explicit mapping types (keyword, long, keyword) resolve to the same PPL types ("string", "bigint", "string") that dynamic mapping inferred, and fieldformat reads from _source either way. Analytics-route compatibility goes from 1/5 to 4/5 (verified locally against a runTask cluster with analytics-engine + opensearch-sql-plugin). The remaining `testFieldFormatStringConcatenationWithNullFieldToString` needs a `tostring()` UDF on the analytics path — a multi-mode UDF (binary / hex / commas / duration) tracked separately as out of scope. Test plan: - ./gradlew :integ-test:integTest --tests 'org.opensearch.sql.calcite.remote.CalciteFieldFormatCommandIT' -> 5/5 green (v2 path, no regression). - ./gradlew :integ-test:analyticsCompatibilityTest --tests 'org.opensearch.sql.calcite.remote.CalciteFieldFormatCommandIT' -> 4/5 pass; the 5th fails on `tostring`'s missing capability registration, which is the documented out-of-scope category. Signed-off-by: Kai Huang <ahkcs@amazon.com>
…th (#5417) Same shape as #5407 for CalciteEvalCommandIT. The IT's init() previously created the in-test test_eval index via direct `PUT /test_eval/_doc/N` requests, relying on dynamic mapping. Two problems: 1. The doc PUTs auto-create the index with whatever settings the cluster defaults to. The analytics-engine compatibility path (force-routing on; tests.analytics.parquet_indices=true) needs parquet-backed indices, which TestUtils.createIndexByRestClient applies via TestUtils.makeParquetBacked when the system property is set. Direct PUTs sidestep that helper, so test_eval lands as Lucene-backed and the analytics planner rejects it with "No backend can scan all requested fields on index [test_eval]". All four working tests fail at execution. 2. init() runs before every @test method. The doc PUTs are doc-level idempotent, so re-running was wasteful but not failing. Once we switch to createIndexByRestClient, the index-level PUT is no longer idempotent and re-running throws "resource_already_exists_exception". Both addressed in one change: - test_eval is created via TestUtils.createIndexByRestClient with an explicit mapping (name/title=keyword, age=long). The helper honours tests.analytics.parquet_indices=true and produces a parquet-backed index for the analytics-engine sweep; on the v2 path the helper is a no-op around the index PUT, so behaviour is unchanged. - The whole init body is guarded by TestUtils.isIndexExist — same idempotency idiom that loadIndex uses for predefined fixtures. First @test method provisions; subsequent methods skip. Also pins the projection order on testFieldFormatStringConcatenation. The original query (`source=test_eval | fieldformat greeting = 'Hello ' + name`) had no `| fields` clause and relied on the implicit projection's column order — v2 returns Lucene-source insertion order, analytics returns parquet-storage order (alphabetical), so the assertion only matched on v2 by coincidence. Adding `| fields name, title, age, greeting` makes the assertion deterministic across paths; the existing expected rows (`rows("Alice", "Engineer", 25, "Hello Alice")`) already match this order, so v2 behaviour is preserved. The other four tests already had explicit `| fields ...` clauses, so no change there. No semantic change for the v2 path: the explicit mapping types (keyword, long, keyword) resolve to the same PPL types ("string", "bigint", "string") that dynamic mapping inferred, and fieldformat reads from _source either way. Analytics-route compatibility goes from 1/5 to 4/5 (verified locally against a runTask cluster with analytics-engine + opensearch-sql-plugin). The remaining `testFieldFormatStringConcatenationWithNullFieldToString` needs a `tostring()` UDF on the analytics path — a multi-mode UDF (binary / hex / commas / duration) tracked separately as out of scope. Test plan: - ./gradlew :integ-test:integTest --tests 'org.opensearch.sql.calcite.remote.CalciteFieldFormatCommandIT' -> 5/5 green (v2 path, no regression). - ./gradlew :integ-test:analyticsCompatibilityTest --tests 'org.opensearch.sql.calcite.remote.CalciteFieldFormatCommandIT' -> 4/5 pass; the 5th fails on `tostring`'s missing capability registration, which is the documented out-of-scope category. Signed-off-by: Kai Huang <ahkcs@amazon.com>
The feature branch advanced 9 commits since this PR was opened. Bring the catch-up branch up to that tip so the PR is mergeable into feature/mustang-ppl-integration. Resolutions: integ-test/build.gradle: took feature. Both sides added the same @ignore exclusion block; feature has alphabetical ordering and a more detailed comment explaining the Gradle 9.4.1 TestEventReporterAsListener cast bug. integ-test/.../CalciteEvalCommandIT.java: composed both sides. Took feature's helper-managed test_eval provisioning (createIndexByRestClient + isIndexExist guard, from opensearch-project#5407) so analytics-engine compatibility runs get a parquet-backed index. Added back PR HEAD's test_eval_agent setup (needed by the dotted-path eval tests for opensearch-project#5351) wrapped in its own isIndexExist guard for the same parquet-aware idempotency. plugin/.../TransportPPLQueryAction.java: took feature. PR opensearch-project#5403 made analytics-engine an optional dependency by moving QueryPlanExecutor from a required constructor parameter to an @Inject(optional=true) setter. Feature's design supersedes our prior wiring. plugin/.../SQLPlugin.java: took feature. The same opensearch-project#5403 simplification removed loadExtensions/EngineExtensionsHolder/executionEngineExtensions plumbing (no longer needed once analytics-engine is optionally bound). Feature retains the createSqlAnalyticsRouter method this PR introduced. plugin/.../config/EngineExtensionsHolder.java: deleted. Unreferenced after taking feature's SQLPlugin/TransportPPLQueryAction; not present on feature branch. Build: :api, :core, :opensearch-sql-plugin, :legacy compile + :integ-test compileTestJava all pass; unit tests pass; spotless clean. Signed-off-by: Kai Huang <ahkcs@amazon.com>
…ation Brings the catch-up branch up to current upstream/main (4 commits since this PR was opened) and current feature/mustang-ppl-integration (9 commits since this PR was opened), so the PR is mergeable into feature/mustang-ppl-integration without conflicts. Squashed (rather than two real merge commits) for the same DCO reason the original commit was squashed: upstream commits authored by many contributors with inconsistent or missing Signed-off-by trailers would otherwise be brought into this PR's history. Newer main commits absorbed (4): - opensearch-project#5419 (LENGTH/REGEXP_REPLACE/DATE_TRUNC unified function spec) - opensearch-project#5408 (datetime type normalization) - opensearch-project#5414 (Gradle wrapper bump + @ignore exclusion) - opensearch-project#5399 (FGAC-scoped SQL cursor continuation) Newer feature commits absorbed (9): - opensearch-project#5403 (analytics-engine optional dependency — major rewiring) - opensearch-project#5407 (Carry CalciteEvalCommandIT through helper-managed index path) - opensearch-project#5413 (Default plugins.calcite.enabled=true on unified path) - opensearch-project#5415, opensearch-project#5416, opensearch-project#5417, opensearch-project#5409, opensearch-project#5400, opensearch-project#5406 (smaller carryovers + bumps) Conflict resolutions (10 from main side, 3 from feature side): api/spec/* (LanguageSpec, UnifiedFunctionSpec, UnifiedPplSpec, UnifiedSqlSpec): took main. Main is a strict superset — adds postAnalysisRules and preCompilationRules extension points, the new FunctionSpecBuilder DSL, SCALAR category for length/regexp_replace/ date_trunc, the DatetimeExtension on PPL spec, and the CoreExtension wiring on SQL spec. PR's RELEVANCE category is preserved unchanged. api/UnifiedQueryPlanner.java, api/compiler/UnifiedQueryCompiler.java: took main. Both adopt the new postAnalysisRules / preCompilationRules hooks introduced in opensearch-project#5408 / opensearch-project#5419. core/executor/QueryService.java: composed both sides — kept HEAD's CalciteClassLoaderHelper.withCalciteClassLoader wrapper around main's StageErrorHandler stage tracking. Same pattern as the original PR resolution; both improvements are orthogonal. legacy/plugin/RestSqlAction.java: took HEAD. The 3-way merge produced a duplicated handleException/getRawErrorCode block; HEAD already contained both the delegateToV2Engine refactor and the ErrorReport unwrap from main, so HEAD is the correct superset. integ-test/build.gradle: took feature. Both sides added the same @ignore exclusion block; feature has alphabetical ordering and a more detailed comment explaining the Gradle 9.4.1 TestEventReporterAsListener cast bug. integ-test/.../CalciteEvalCommandIT.java: composed both sides. Took feature's helper-managed test_eval provisioning (createIndexByRestClient + isIndexExist guard, from opensearch-project#5407) so analytics-engine compatibility runs get a parquet-backed index. Added back PR HEAD's test_eval_agent setup (needed by the dotted-path eval tests for opensearch-project#5351) wrapped in its own isIndexExist guard for the same parquet-aware idempotency. plugin/.../TransportPPLQueryAction.java: took feature. PR opensearch-project#5403 made analytics-engine an optional dependency by moving QueryPlanExecutor from a required constructor parameter to an @Inject(optional=true) setter. Feature's design supersedes our prior wiring. plugin/.../SQLPlugin.java: took feature. The same opensearch-project#5403 simplification removed loadExtensions/EngineExtensionsHolder/executionEngineExtensions plumbing (no longer needed once analytics-engine is optionally bound). Feature retains the createSqlAnalyticsRouter method this PR introduced. plugin/.../config/EngineExtensionsHolder.java: deleted. Unreferenced after taking feature's SQLPlugin/TransportPPLQueryAction; not present on feature branch. Build: :api, :core, :opensearch-sql-plugin, :legacy compileJava + :integ-test compileTestJava all pass; unit tests pass; spotlessCheck clean. Signed-off-by: Kai Huang <ahkcs@amazon.com>
Single squashed commit on top of feature/mustang-ppl-integration that absorbs upstream/main's commits not yet on the feature branch. Replaces the prior catch-up squash (opensearch-project#5396 base + the original af831d3 rebase commit) so this PR is a fast-forward into feature/mustang-ppl-integration. Squashed (rather than a merge commit) because upstream main commits were authored by many contributors with inconsistent or missing Signed-off-by trailers; DCO would otherwise reject those commits. Main commits absorbed (54 since divergence; 4 since the original catch-up squash was made on 2026-04-30): - opensearch-project#5419 (LENGTH/REGEXP_REPLACE/DATE_TRUNC unified function spec) - opensearch-project#5408 (datetime type normalization) - opensearch-project#5414 (Gradle wrapper bump + @ignore exclusion) - opensearch-project#5399 (FGAC-scoped SQL cursor continuation) - opensearch-project#5394 (SQL Vector Search), opensearch-project#5361 (OpenSearch 3.7), opensearch-project#5360 (unified SQL language spec), opensearch-project#5240 (PPL Union), and 46 others. Conflict resolutions: api/spec/* (LanguageSpec, UnifiedFunctionSpec, UnifiedPplSpec, UnifiedSqlSpec): took main. Main is a strict superset — adds postAnalysisRules and preCompilationRules extension points, the new FunctionSpecBuilder DSL, SCALAR category for length/regexp_replace/ date_trunc, the DatetimeExtension on PPL spec, and the CoreExtension wiring on SQL spec. RELEVANCE category is preserved unchanged. api/UnifiedQueryPlanner.java, api/compiler/UnifiedQueryCompiler.java: took main. Both adopt the new postAnalysisRules / preCompilationRules hooks introduced in opensearch-project#5408 / opensearch-project#5419. core/executor/QueryService.java: composed both sides — kept feature's CalciteClassLoaderHelper.withCalciteClassLoader wrapper around main's StageErrorHandler stage tracking; both improvements are orthogonal. legacy/plugin/RestSqlAction.java: took feature. The 3-way merge produced a duplicated handleException/getRawErrorCode block; feature already contained both the delegateToV2Engine refactor and the ErrorReport unwrap from main, so feature is the correct superset. CLAUDE.md, docs/user/ppl/functions/condition.md: took main. explain_streamstats_global{,_null_bucket}.yaml: took main (post-opensearch-project#5359 shape). core/CalciteRelNodeVisitor + utils/PlanUtils: took main (collation utility hoisted from CalciteRelNodeVisitor.backtrackForCollation into PlanUtils.findInputCollation). integ-test/CalciteNoPushdownIT.java: added CalciteMixedFieldTypeIT. ppl/antlr/OpenSearchPPLParser.g4: added unionCommand. ppl/calcite/CalcitePPLStreamstatsTest.java: added testMultipleStreamstatsWithWindow. integ-test/build.gradle: took feature. Both sides added the same @ignore exclusion block; feature has alphabetical ordering and a more detailed comment explaining the Gradle 9.4.1 cast bug. integ-test/.../CalciteEvalCommandIT.java: composed both sides. Took feature's helper-managed test_eval provisioning (createIndexByRestClient + isIndexExist guard, from opensearch-project#5407) so analytics-engine compatibility runs get a parquet-backed index. Added the test_eval_agent setup (needed by the dotted-path eval tests for opensearch-project#5351) wrapped in its own isIndexExist guard for the same parquet-aware idempotency. plugin/.../TransportPPLQueryAction.java, plugin/.../SQLPlugin.java: took feature. PR opensearch-project#5403 made analytics-engine an optional dependency by moving QueryPlanExecutor from a required constructor parameter to an @Inject(optional=true) setter, and removed the loadExtensions / EngineExtensionsHolder / executionEngineExtensions plumbing. Feature retains the createSqlAnalyticsRouter method this catch-up introduced. plugin/.../config/EngineExtensionsHolder.java: deleted (unreferenced post-opensearch-project#5403; not present on feature). Build: :api, :core, :opensearch-sql-plugin, :legacy compileJava + :integ-test compileTestJava all pass; unit tests pass; spotlessCheck clean. Signed-off-by: Kai Huang <ahkcs@amazon.com>
…5397) Single squashed commit on top of feature/mustang-ppl-integration that absorbs upstream/main's commits not yet on the feature branch. Replaces the prior catch-up squash (#5396 base + the original af831d3 rebase commit) so this PR is a fast-forward into feature/mustang-ppl-integration. Squashed (rather than a merge commit) because upstream main commits were authored by many contributors with inconsistent or missing Signed-off-by trailers; DCO would otherwise reject those commits. Main commits absorbed (54 since divergence; 4 since the original catch-up squash was made on 2026-04-30): - #5419 (LENGTH/REGEXP_REPLACE/DATE_TRUNC unified function spec) - #5408 (datetime type normalization) - #5414 (Gradle wrapper bump + @ignore exclusion) - #5399 (FGAC-scoped SQL cursor continuation) - #5394 (SQL Vector Search), #5361 (OpenSearch 3.7), #5360 (unified SQL language spec), #5240 (PPL Union), and 46 others. Conflict resolutions: api/spec/* (LanguageSpec, UnifiedFunctionSpec, UnifiedPplSpec, UnifiedSqlSpec): took main. Main is a strict superset — adds postAnalysisRules and preCompilationRules extension points, the new FunctionSpecBuilder DSL, SCALAR category for length/regexp_replace/ date_trunc, the DatetimeExtension on PPL spec, and the CoreExtension wiring on SQL spec. RELEVANCE category is preserved unchanged. api/UnifiedQueryPlanner.java, api/compiler/UnifiedQueryCompiler.java: took main. Both adopt the new postAnalysisRules / preCompilationRules hooks introduced in #5408 / #5419. core/executor/QueryService.java: composed both sides — kept feature's CalciteClassLoaderHelper.withCalciteClassLoader wrapper around main's StageErrorHandler stage tracking; both improvements are orthogonal. legacy/plugin/RestSqlAction.java: took feature. The 3-way merge produced a duplicated handleException/getRawErrorCode block; feature already contained both the delegateToV2Engine refactor and the ErrorReport unwrap from main, so feature is the correct superset. CLAUDE.md, docs/user/ppl/functions/condition.md: took main. explain_streamstats_global{,_null_bucket}.yaml: took main (post-#5359 shape). core/CalciteRelNodeVisitor + utils/PlanUtils: took main (collation utility hoisted from CalciteRelNodeVisitor.backtrackForCollation into PlanUtils.findInputCollation). integ-test/CalciteNoPushdownIT.java: added CalciteMixedFieldTypeIT. ppl/antlr/OpenSearchPPLParser.g4: added unionCommand. ppl/calcite/CalcitePPLStreamstatsTest.java: added testMultipleStreamstatsWithWindow. integ-test/build.gradle: took feature. Both sides added the same @ignore exclusion block; feature has alphabetical ordering and a more detailed comment explaining the Gradle 9.4.1 cast bug. integ-test/.../CalciteEvalCommandIT.java: composed both sides. Took feature's helper-managed test_eval provisioning (createIndexByRestClient + isIndexExist guard, from #5407) so analytics-engine compatibility runs get a parquet-backed index. Added the test_eval_agent setup (needed by the dotted-path eval tests for #5351) wrapped in its own isIndexExist guard for the same parquet-aware idempotency. plugin/.../TransportPPLQueryAction.java, plugin/.../SQLPlugin.java: took feature. PR #5403 made analytics-engine an optional dependency by moving QueryPlanExecutor from a required constructor parameter to an @Inject(optional=true) setter, and removed the loadExtensions / EngineExtensionsHolder / executionEngineExtensions plumbing. Feature retains the createSqlAnalyticsRouter method this catch-up introduced. plugin/.../config/EngineExtensionsHolder.java: deleted (unreferenced post-#5403; not present on feature). Build: :api, :core, :opensearch-sql-plugin, :legacy compileJava + :integ-test compileTestJava all pass; unit tests pass; spotlessCheck clean. Signed-off-by: Kai Huang <ahkcs@amazon.com>
Description
Two things this PR carries together (commit-separated for clean review):
Carry
CalciteEvalCommandITthrough the helper-managed index path (28a58536d) — provisions the inlinetest_evalindex viaTestUtils.createIndexByRestClientinstead of directPUT /test_eval/_doc/N, so the analytics-engine compatibility run (tests.analytics.parquet_indices=true) gets a parquet-backed index. Mirrors the helper-managed pattern thatCalciteFillNullCommandITandCalcitePPLAppendCommandITalready use vialoadIndex(Index.X).Workaround for Gradle 9.4.1
TestEventReporterAsListenercast bug (435827e99) — excludes@Ignore-annotated test classes fromintegTestso the task no longer aborts mid-run on this branch's CI.1. Helper-managed
test_evalprovisioningWhy this matters
When the analytics-engine compatibility path is active (
tests.analytics.force_routing=trueandtests.analytics.parquet_indices=true), every test-created index needs parquet-backed storage so the DataFusion backend can scan it —TestUtils.makeParquetBackedhandles the injection insidecreateIndexByRestClient. DirectPUT /test_eval/_doc/Ncalls bypass that helper, so the index lands Lucene-backed and the analytics planner rejects every query withNo backend can scan all requested fields on index [test_eval].The three tests that use
test_evalfail on the analytics route today; onlytestEvalStringConcatenationWithExistingData(which usesloadIndex(Index.BANK)) survives. With this change,test_evalis created via the same helper as every other fixture and inherits the parquet flag automatically.The companion analytics-engine wiring (CONCAT/CAST/SAFE_CAST capabilities + null-propagating concat adapter) lands separately in opensearch-project/OpenSearch#21498. On its own, this PR is a no-op for the v2 / Calcite path — once both this and the core PR are merged,
CalciteEvalCommandITgoes from 1/4 to 4/4 on the analytics route.Changes
Helper-managed
test_evalprovisioning.init()now callsTestUtils.createIndexByRestClient(client(), "test_eval", testEvalMapping)with an explicit mapping (name/title=keyword,age=long). The helper is a thin wrapper around the index PUT on the v2 path (no-op whentests.analytics.parquet_indicesis unset), so v2 behaviour is unchanged.Idempotency guard. The whole init body is wrapped in
TestUtils.isIndexExist(client(), "test_eval")— same patternloadIndexuses for predefined fixtures.init()runs before every@Testmethod; first run provisions, subsequent runs skip. Without the guard,createIndexByRestClient(unlike the original doc-level PUT) would throwresource_already_exists_exceptionon the second invocation.Pinned projection order on
testEvalStringConcatenation. The original query had no| fieldsclause and relied on the implicit projection's column order, which differs between paths (v2 returns_source-iteration order, analytics returns parquet-storage order). Added| fields name, title, age, greetingto make the assertion deterministic. Expected rows already match this order, so v2 behaviour is preserved.V2-path impact
namePPL typestring(text → string)string(keyword → string)agePPL typebigintbiginttitlePPL typestringstring_source_source"Hello Alice""Hello Alice"[name, title, age, greeting](matched by coincidence)[name, title, age, greeting](now pinned)verifySchema(schema("name", "string"))matches bothtext+keyword(dynamic) andkeyword(explicit) — PPL maps both to the same logical type. No assertion changes were needed.2. Gradle 9.4.1
@Ignorecast workaroundWhy this is in this PR
CI on this branch (post-#5406 wrapper bump to 9.4.1) aborts the
:integ-test:integTesttask with:…even though no test assertion fails. Without a workaround, this PR's tests would be invisible — the task crashes before final result aggregation. Folding the workaround into this PR unblocks our CI; the workaround is a base-branch concern but the symptom blocks every PR until landed.
Root cause (verified via
javapagainstgradle-testing-base-9.4.1.jar)org/gradle/api/tasks/testing/AbstractTestTask(line 263) instantiatesTestEventReporterAsListener— Gradle's internal bridge fromTestListenerInternalto the newTestEventReporterAPI.TestEventReporterAsListener.startedline 58 stores a per-descriptor reporter in a map keyed by id and, on each child event, casts the parent's stored reporter toGroupTestEventReporterInternal.@Ignoreproduces a non-composite parent descriptor with a leafTestEventReporterrather than a group reporter — the cast fails. First failure in CI:CalciteInformationSchemaCommandIT.testTablesFromPrometheusCatalog(that class is@Ignore'd for [FEATURE] Support Prometheus DataSource with Calcite #3455).AbstractTestTask, not byOpenSearchTestBasePlugin.addTestListener(ErrorReportingTestListener). MigratingErrorReportingTestListenerto the new event-reporter API would not stop the bridge from firing.Workaround
Exclude all
@Ignore-annotated test classes fromintegTest. The classes were already skipped at the JUnit layer; this just moves the skip earlier (build layer) so the buggy bridge never sees them.@Ignore'd test runtime status@Ignore'dintegTesttask aborts mid-runtestLogging.events "failed"per-test message24 classes excluded (one —
OrderIT— was already excluded by the existing block). Source:grep -rln '^@Ignore' integ-test/src/test/javaminus already-excluded paths.This block should be removed once Gradle ships a fix for the bridge cast. Given the bug is in Gradle's own internal class, the proper long-term fix is upstream — file an issue on
gradle/gradlereferencing theTestEventReporterAsListener.started:58site if not already filed.Test results
testEvalStringConcatenationtestEvalStringConcatenationWithNullFieldtestEvalStringConcatenationWithLiteralstestEvalStringConcatenationWithExistingData:integ-test:integTesttaskForward note
The same direct-
PUTpattern that this PR fixes forCalciteEvalCommandITexists in roughly a dozen other Calcite ITs (CalcitePPLAggregationIT,CalcitePPLBasicIT,CalciteFieldFormatCommandIT,CalcitePPLCaseFunctionIT, etc.). Each will need the same one-line carryover when its corresponding command lands on the analytics route. This PR addresses only the eval surface.By submitting this pull request