Skip to content

Comments

[Feature] Add grammar bundle generation API for PPL language features#5162

Open
mengweieric wants to merge 13 commits intoopensearch-project:mainfrom
mengweieric:feature/grammar-bundle
Open

[Feature] Add grammar bundle generation API for PPL language features#5162
mengweieric wants to merge 13 commits intoopensearch-project:mainfrom
mengweieric:feature/grammar-bundle

Conversation

@mengweieric
Copy link
Collaborator

@mengweieric mengweieric commented Feb 20, 2026

Description

Implements the backend grammar metadata API for PPL autocomplete support.

This endpoint serves a versioned grammar bundle containing all ANTLR metadata required for downstream consumers (e.g. OpenSearch Dashboards) to reconstruct a functional PPL lexer and parser at runtime using antlr4ng's interpreter APIs. This enables full client-side parsing and autocomplete with zero per-keystroke server calls, with the backend as the single source of truth for the PPL grammar.

What the bundle contains:

  • Serialized lexer and parser ATNs via ATNSerializer.serialize().toArray(), compatible with antlr4ng's ATNDeserializer.deserialize()
  • Token vocabulary (literal and symbolic names), rule names, channel names, and mode names for LexerInterpreter/ParserInterpreter reconstruction
  • grammarHash (SHA-256 of ATN data + ANTLR version) for client-side change detection
  • bundleVersion and antlrVersion for compatibility validation

Backend behavior:

  • Lazy initialization — bundle is built on first request and cached in a volatile field for the node lifecycle
  • Deterministic output — all nodes running the same plugin JAR produce identical bundles
  • Marked @experimentalapi as it is subject to change

Also included:

  • THIRD-PARTY updated to reflect ANTLR 4.13.2

Related Issues

Resolves #[Issue number to be closed when this PR is merged]

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

Adds a new GET endpoint /_plugins/_ppl/_grammar that serves a cached, serialized ANTLR grammar bundle; introduces GrammarBundle and PPLGrammarBundleBuilder, a RestPPLGrammarAction handler with tests, unit tests for the builder, and updates ANTLR third‑party metadata to 4.13.2.

Changes

Cohort / File(s) Summary
Third‑party notices
THIRD-PARTY
Updated ANTLR version from 4.7.1 to 4.13.2 and copyright year range from 2012-2017 to 2012-2024 in license headers.
Plugin registration
plugin/src/main/java/org/opensearch/sql/plugin/SQLPlugin.java
Imported and registered RestPPLGrammarAction to expose the new PPL grammar endpoint.
REST handler
plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java
New REST handler serving GET /_plugins/_ppl/_grammar with lazy thread‑safe caching (double‑checked locking), JSON serialization via XContentBuilder, error handling (500), and test hooks (buildBundle, invalidateCache).
REST handler tests
plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java
Unit tests for route/name, successful response structure, caching behavior, cache invalidation, and error path (500). Includes MockRestChannel to capture responses.
Grammar data model
ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java
New immutable Lombok @Value/@Builder container for serialized ANTLR grammar data (bundleVersion, antlrVersion, grammarHash, lexer/parser ATNs, rule names, channels, modes, vocabulary).
Bundle builder
ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java
New builder that instantiates ANTLR lexer/parser, serializes ATNs, extracts token/rule/channel/mode names, computes SHA‑256 grammar hash, and constructs GrammarBundle.
Builder tests
ppl/src/test/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilderTest.java
Tests validating bundle contents, deterministic builds, hash format, start rule resolution, and non‑empty ATNs/names.
Manifest
pom.xml
Minor manifest changes related to dependency/metadata updates (ANTLR bump).

Sequence Diagram

sequenceDiagram
    participant Client
    participant Handler as RestPPLGrammarAction
    participant Cache
    participant Builder as PPLGrammarBundleBuilder
    participant Serializer as XContentBuilder

    Client->>Handler: GET /_plugins/_ppl/_grammar
    Handler->>Cache: check cached bundle
    alt cache hit
        Cache-->>Handler: return Bundle
    else cache miss
        Handler->>Builder: buildBundle()
        Builder->>Builder: inspect lexer/parser, serialize ATNs, compute hash
        Builder-->>Handler: Bundle
        Handler->>Cache: store Bundle
    end
    Handler->>Serializer: serialize Bundle to JSON
    Serializer-->>Handler: JSON payload
    Handler-->>Client: HTTP 200 + JSON
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • penghuo
  • GumpacG
  • Swiddis
  • dai-chen
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main feature being added: a grammar bundle generation API for PPL language autocomplete support, which aligns with the primary changeset focus.
Description check ✅ Passed The pull request description clearly outlines the implementation of a backend grammar metadata API for PPL autocomplete support, detailing bundle contents, backend behavior, and related updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mengweieric mengweieric changed the title Feature/grammar bundle [Feature] Add grammar bundle generation for PPL autocomplete features Feb 20, 2026
@mengweieric mengweieric force-pushed the feature/grammar-bundle branch from d288392 to eabe8ec Compare February 20, 2026 23:00
@mengweieric mengweieric added PPL Piped processing language feature labels Feb 20, 2026
@mengweieric mengweieric changed the title [Feature] Add grammar bundle generation for PPL autocomplete features [Feature] Add grammar bundle generation API for PPL language features Feb 20, 2026
@mengweieric mengweieric force-pushed the feature/grammar-bundle branch from eabe8ec to eb12632 Compare February 22, 2026 21:25
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
@mengweieric mengweieric force-pushed the feature/grammar-bundle branch from 73883a5 to 3f36846 Compare February 23, 2026 06:46
…y, and tests

- Hash full 32-bit ints in grammarHash to avoid collisions with ANTLR 4.13.2 ATN serialization
- Use RuntimeMetaData.getRuntimeVersion() instead of unreliable JAR manifest lookup
- Make GrammarBundle immutable with @value instead of @DaTa
- Update THIRD-PARTY to reflect ANTLR 4.13.2
- Harden tests with JSON parsing and add antlrVersion assertion

Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
@mengweieric mengweieric force-pushed the feature/grammar-bundle branch from 3f36846 to c838750 Compare February 23, 2026 06:49
@mengweieric mengweieric marked this pull request as ready for review February 23, 2026 07:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java (1)

87-93: invalidateCache() is public but is a testing-only hook — restrict its visibility.

A public method on a production REST handler that voids the bundle cache can be called inadvertently by any code. Since @VisibleForTesting alone is not a compile-time guard, the visibility should be protected (or package-private) to match buildBundle().

🔧 Proposed fix
-  public void invalidateCache() {
+  protected void invalidateCache() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`
around lines 87 - 93, The invalidateCache() method is currently public but only
intended for tests; change its visibility to a non-public scope (e.g., protected
or package-private) to prevent external production code from calling it, while
keeping its synchronized body that nulls cachedBundle under bundleLock; this
should mirror the visibility of buildBundle() and retain use of the bundleLock
and cachedBundle fields.
ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java (1)

27-51: @Value does not deep-copy array fields — the cached bundle could be mutated.

Lombok's generated constructor stores int[]/String[] references directly. For the long-lived cached singleton this means any caller holding the same array reference can corrupt the bundle. The lexer/parserSerializedATN already arrive as fresh copies from IntegerList.toArray(), but the String[] fields (lexerRuleNames, channelNames, modeNames, parserRuleNames, literalNames, symbolicNames) are references into static arrays of the generated ANTLR classes.

Add @Builder.Default defensive copies, or add a @EqualsAndHashCode.Include note. At minimum, document that fields must be treated as read-only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java`
around lines 27 - 51, The GrammarBundle holds array fields (lexerRuleNames,
channelNames, modeNames, parserRuleNames, literalNames, symbolicNames) that
Lombok's `@Value` constructor stores by reference, allowing external mutation of
the cached singleton; fix by making defensive copies for these String[] fields
(and int[] if not already copied) when constructing the bundle (e.g. in a
`@Builder.Default` initializer or custom constructor/factory used by
GrammarBundle) so each field is Arrays.copyOf(original, original.length) or
wrapped as an unmodifiable List, or otherwise ensure immutability; update the
GrammarBundle construction path (the class and any builder/factory) to perform
the copies and/or add a short Javadoc on the fields if you choose to only
document the read-only contract.
ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java (1)

29-30: Prefer the static ATNSerializer.getSerialized() over the instance constructor.

The ANTLR 4.13.2 API exposes public static IntegerList getSerialized(ATN atn) as the canonical convenience entry point. Using it removes the need to instantiate ATNSerializer manually.

♻️ Proposed refactor
-    int[] lexerATN = new ATNSerializer(lexer.getATN()).serialize().toArray();
-    int[] parserATN = new ATNSerializer(parser.getATN()).serialize().toArray();
+    int[] lexerATN = ATNSerializer.getSerialized(lexer.getATN()).toArray();
+    int[] parserATN = ATNSerializer.getSerialized(parser.getATN()).toArray();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java`
around lines 29 - 30, Update PPLGrammarBundleBuilder to use the static
convenience method instead of constructing ATNSerializer instances: replace the
two lines that call new ATNSerializer(lexer.getATN()).serialize().toArray() and
new ATNSerializer(parser.getATN()).serialize().toArray() with calls to
ATNSerializer.getSerialized(lexer.getATN()).toArray() and
ATNSerializer.getSerialized(parser.getATN()).toArray() respectively so the
serialized ATNs are obtained via the static API.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`:
- Around line 25-29: The class-level comment for RestPPLGrammarAction is using a
block comment (/* ... */) so the `@opensearch.experimental` tag is not emitted as
Javadoc; change the top-of-class comment to a Javadoc comment (/** ... */) so
the `@opensearch.experimental` annotation is processed correctly — locate the
comment immediately above the RestPPLGrammarAction class declaration and replace
the opening /* with /** (keeping the existing text and tag intact).

In
`@plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java`:
- Around line 242-245: The test's override of bytesOutput() in
RestPPLGrammarAction returns null which can cause a NullPointerException if any
code (e.g., BaseRestHandler or test logic) calls channel.bytesOutput(); replace
the null return with a real BytesStreamOutput instance (e.g., new
BytesStreamOutput()) so channel.bytesOutput() is safe to use; update the
bytesOutput() method in RestPPLGrammarAction to return a non-null
BytesStreamOutput to prevent latent NPEs.

In
`@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java`:
- Around line 33-39: The code writes vocabulary.getLiteralName(i) /
vocabulary.getSymbolicName(i) into arrays that will contain null entries for
tokens without a literal/symbolic form; update the contract by adding
documentation on GrammarBundle.literalNames and GrammarBundle.symbolicNames (or
a Javadoc comment on the class/fields) stating that array elements may be null
and clients must handle sparse arrays, and reference the behavior coming from
PPLGrammarBundleBuilder (use the symbols vocabulary.getLiteralName,
vocabulary.getSymbolicName, literalNames, symbolicNames, and the
GrammarBundle.literalNames/GrammarBundle.symbolicNames fields to locate where to
document).

In
`@ppl/src/test/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilderTest.java`:
- Around line 24-107: Rename the test methods in PPLGrammarBundleBuilderTest so
each follows the test<Functionality><Condition> convention (prefix with "test");
e.g., rename bundleIsNotNull -> testBundleIsNotNull, bundleVersionIsSet ->
testBundleVersionIsSet, antlrVersionIsSet -> testAntlrVersionIsSet,
grammarHashHasExpectedFormat -> testGrammarHashHasExpectedFormat,
startRuleIndexIsZero -> testStartRuleIndexIsZero, lexerATNIsNonEmpty ->
testLexerATNIsNonEmpty, parserATNIsNonEmpty -> testParserATNIsNonEmpty,
lexerRuleNamesAreNonEmpty -> testLexerRuleNamesAreNonEmpty,
parserRuleNamesAreNonEmpty -> testParserRuleNamesAreNonEmpty,
channelNamesAreNonEmpty -> testChannelNamesAreNonEmpty, modeNamesAreNonEmpty ->
testModeNamesAreNonEmpty, vocabularyIsNonEmpty -> testVocabularyIsNonEmpty,
buildIsDeterministic -> testBuildIsDeterministic; update any references to these
methods (e.g., test runner annotations or logs) so PPLGrammarBundleBuilderTest
and references to bundle/second/GrammarBundle remain consistent.

---

Nitpick comments:
In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`:
- Around line 87-93: The invalidateCache() method is currently public but only
intended for tests; change its visibility to a non-public scope (e.g., protected
or package-private) to prevent external production code from calling it, while
keeping its synchronized body that nulls cachedBundle under bundleLock; this
should mirror the visibility of buildBundle() and retain use of the bundleLock
and cachedBundle fields.

In `@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java`:
- Around line 27-51: The GrammarBundle holds array fields (lexerRuleNames,
channelNames, modeNames, parserRuleNames, literalNames, symbolicNames) that
Lombok's `@Value` constructor stores by reference, allowing external mutation of
the cached singleton; fix by making defensive copies for these String[] fields
(and int[] if not already copied) when constructing the bundle (e.g. in a
`@Builder.Default` initializer or custom constructor/factory used by
GrammarBundle) so each field is Arrays.copyOf(original, original.length) or
wrapped as an unmodifiable List, or otherwise ensure immutability; update the
GrammarBundle construction path (the class and any builder/factory) to perform
the copies and/or add a short Javadoc on the fields if you choose to only
document the read-only contract.

In
`@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java`:
- Around line 29-30: Update PPLGrammarBundleBuilder to use the static
convenience method instead of constructing ATNSerializer instances: replace the
two lines that call new ATNSerializer(lexer.getATN()).serialize().toArray() and
new ATNSerializer(parser.getATN()).serialize().toArray() with calls to
ATNSerializer.getSerialized(lexer.getATN()).toArray() and
ATNSerializer.getSerialized(parser.getATN()).toArray() respectively so the
serialized ATNs are obtained via the static API.

- Assert ATN serialization version 4 for both lexer and parser to enforce
  antlr4ng compatibility contract
- Resolve startRuleIndex by looking up "root" rule name instead of hardcoding 0
- Fix MockRestChannel.bytesOutput() to return real BytesStreamOutput
- Document nullable elements in literalNames/symbolicNames Javadoc
- Rename test methods to follow testXxx() convention per ppl/plugin modules

Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
@mengweieric mengweieric force-pushed the feature/grammar-bundle branch from fd3529d to fe41aea Compare February 23, 2026 07:29
@mengweieric mengweieric self-assigned this Feb 23, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java (1)

87-93: @VisibleForTesting + public is contradictory — use protected.

public makes invalidateCache() part of the effective API surface, allowing any production code holding a RestPPLGrammarAction reference to force cache invalidation and trigger an expensive PPLGrammarBundleBuilder.build() on the next request. buildBundle() correctly uses protected for the same testing purpose — invalidateCache() should match.

♻️ Proposed fix
-  public void invalidateCache() {
+  protected void invalidateCache() {
     synchronized (bundleLock) {
       cachedBundle = null;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`
around lines 87 - 93, Change the visibility of invalidateCache() from public to
protected to match the testing-only intent indicated by `@VisibleForTesting` and
to avoid exposing cache invalidation in the public API; locate the method named
invalidateCache in class RestPPLGrammarAction and change its modifier to
protected, preserving the synchronized(bundleLock) block and the existing
assignment cachedBundle = null so tests can still call it (and buildBundle()
remains protected).
ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c838750 and fd3529d.

📒 Files selected for processing (1)
  • plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`:
- Line 62: The method visibility for invalidateCache in RestPPLGrammarAction
should be narrowed from public to protected since it is only exposed for tests;
update the method signature to protected while keeping the `@VisibleForTesting`
annotation and the synchronized (bundleLock) { cachedBundle = null; } body
intact so RestPPLGrammarActionTest can still call it but it is no longer part of
the public API surface.

---

Nitpick comments:
In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`:
- Around line 87-93: Change the visibility of invalidateCache() from public to
protected to match the testing-only intent indicated by `@VisibleForTesting` and
to avoid exposing cache invalidation in the public API; locate the method named
invalidateCache in class RestPPLGrammarAction and change its modifier to
protected, preserving the synchronized(bundleLock) block and the existing
assignment cachedBundle = null so tests can still call it (and buildBundle()
remains protected).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java (1)

163-182: Consider adding a test for buildBundle() returning null.

The current error path only tests an exception. If buildBundle() returns null (e.g., an implementation that silently returns null instead of throwing), the action's behavior is untested and could produce an NPE or an unexpected response rather than a clean 500. A one-line variant of the failing action covering that path would close the gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java`
around lines 163 - 182, Add a test variant to cover the case where
RestPPLGrammarAction.buildBundle() returns null: create a failingAction subclass
of RestPPLGrammarAction that overrides buildBundle() to return null, invoke
failingAction.handleRequest(...) with the same FakeRestRequest/MockRestChannel
setup used in testGetGrammar_ErrorPath_Returns500, and assert that
channel.getResponse().status() equals RestStatus.INTERNAL_SERVER_ERROR to ensure
a null bundle produces a 500 response rather than an NPE or other status.
ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java (1)

27-57: Shallow immutability: array fields are mutable through getters.

@Value only ensures "no setters are generated and the fields are defined as final so that their references can't be changed" — but the contents of int[] and String[] fields remain fully mutable. Since the PR caches a single GrammarBundle for the entire node lifecycle, a caller that mutates getLexerSerializedATN()[0] = … would silently corrupt the cached bundle for all future requests.

In practice, current consumers only read (serialize) the arrays, so the immediate risk is low; but the class uses @Value to signal immutability. Consider wrapping arrays with Arrays.copyOf in the generated getter or storing unmodifiable wrappers:

♻️ Suggested defensive-copy approach
-  `@NonNull` private int[] lexerSerializedATN;
+  `@NonNull` `@Getter`(AccessLevel.NONE) private int[] lexerSerializedATN;

Then add an explicit getter:

public int[] getLexerSerializedATN() {
    return Arrays.copyOf(lexerSerializedATN, lexerSerializedATN.length);
}

Apply the same pattern for all other array fields (parserSerializedATN, lexerRuleNames, channelNames, modeNames, parserRuleNames, literalNames, symbolicNames).

Alternatively, switch array types to List<Integer> / List<String> (wrapped with Collections.unmodifiableList or List.of) to gain true shallow immutability with no extra boilerplate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java`
around lines 27 - 57, The array fields (lexerSerializedATN, parserSerializedATN,
lexerRuleNames, channelNames, modeNames, parserRuleNames, literalNames,
symbolicNames) are mutable via generated getters; make them defensively
immutable by returning copies or unmodifiable collections: either replace array
types with List<Integer>/List<String> and store them as unmodifiable lists
(Collections.unmodifiableList or List.of) or add explicit getters (e.g.,
getLexerSerializedATN, getParserSerializedATN, getLexerRuleNames,
getChannelNames, getModeNames, getParserRuleNames, getLiteralNames,
getSymbolicNames) that return Arrays.copyOf(...) / Arrays.copyOf(...) converted
to arrays or return the unmodifiable List view, and ensure the constructor/state
stores copies as well to avoid exposing internal arrays.
ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd3529d and fe41aea.

📒 Files selected for processing (4)
  • plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java
  • ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java
  • ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java
  • ppl/src/test/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilderTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • ppl/src/test/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilderTest.java
  • ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/PPLGrammarBundleBuilder.java
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java`:
- Around line 133-161: Modify testInvalidateCache to assert that buildBundle()
is invoked twice by instrumenting the bundle builder with an AtomicInteger
counter (same approach as testGetGrammar_BundleIsCached): add an AtomicInteger,
increment it inside the component/method that constructs the grammar bundle
(buildBundle), call action.handleRequest(...) once, call
action.invalidateCache(), call action.handleRequest(...) again, and then assert
the counter equals 2 to prove rebuild occurred; reference testInvalidateCache,
action.invalidateCache(), and buildBundle() when making the change.
- Around line 1-30: The plugin module's JUnit4 tests (e.g.,
RestPPLGrammarActionTest using `@Before`, `@Test`, and org.junit.Assert) are skipped
because only JUnit Jupiter is declared; add the JUnit Vintage engine to the
plugin's build.gradle test runtime so JUnit 4 tests run on the JUnit 5 platform
by adding testRuntimeOnly("org.junit.vintage:junit-vintage-engine") with the
exclusion of org.hamcrest:hamcrest-core as shown in the review comment.

---

Nitpick comments:
In
`@plugin/src/test/java/org/opensearch/sql/plugin/rest/RestPPLGrammarActionTest.java`:
- Around line 163-182: Add a test variant to cover the case where
RestPPLGrammarAction.buildBundle() returns null: create a failingAction subclass
of RestPPLGrammarAction that overrides buildBundle() to return null, invoke
failingAction.handleRequest(...) with the same FakeRestRequest/MockRestChannel
setup used in testGetGrammar_ErrorPath_Returns500, and assert that
channel.getResponse().status() equals RestStatus.INTERNAL_SERVER_ERROR to ensure
a null bundle produces a 500 response rather than an NPE or other status.

In `@ppl/src/main/java/org/opensearch/sql/ppl/autocomplete/GrammarBundle.java`:
- Around line 27-57: The array fields (lexerSerializedATN, parserSerializedATN,
lexerRuleNames, channelNames, modeNames, parserRuleNames, literalNames,
symbolicNames) are mutable via generated getters; make them defensively
immutable by returning copies or unmodifiable collections: either replace array
types with List<Integer>/List<String> and store them as unmodifiable lists
(Collections.unmodifiableList or List.of) or add explicit getters (e.g.,
getLexerSerializedATN, getParserSerializedATN, getLexerRuleNames,
getChannelNames, getModeNames, getParserRuleNames, getLiteralNames,
getSymbolicNames) that return Arrays.copyOf(...) / Arrays.copyOf(...) converted
to arrays or return the unmodifiable List view, and ensure the constructor/state
stores copies as well to avoid exposing internal arrays.

Consistent with buildBundle() which is also @VisibleForTesting protected.

Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
@mengweieric mengweieric force-pushed the feature/grammar-bundle branch from a14b255 to da2e537 Compare February 23, 2026 19:57
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java (1)

25-29: ⚠️ Potential issue | 🟡 Minor

Convert the class header to Javadoc so @opensearch.experimental is emitted.

The experimental tag won’t be processed in a block comment. Switch to a Javadoc comment.

🔧 Proposed fix
-/*
- * REST handler for {`@code` GET /_plugins/_ppl/_grammar}.
- *
- * `@opensearch.experimental`
- */
+/**
+ * REST handler for {`@code` GET /_plugins/_ppl/_grammar}.
+ *
+ * `@opensearch.experimental`
+ */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`
around lines 25 - 29, Change the block comment above the RestPPLGrammarAction
class to a Javadoc comment so the `@opensearch.experimental` tag is emitted:
replace the /* ... */ header with /** ... */ immediately above the
RestPPLGrammarAction class declaration, keep the same text ("REST handler for
{`@code` GET /_plugins/_ppl/_grammar}.", "@opensearch.experimental") and ensure
the Javadoc format is used so tooling processes the experimental tag.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java`:
- Around line 25-29: Change the block comment above the RestPPLGrammarAction
class to a Javadoc comment so the `@opensearch.experimental` tag is emitted:
replace the /* ... */ header with /** ... */ immediately above the
RestPPLGrammarAction class declaration, keep the same text ("REST handler for
{`@code` GET /_plugins/_ppl/_grammar}.", "@opensearch.experimental") and ensure
the Javadoc format is used so tooling processes the experimental tag.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe41aea and da2e537.

📒 Files selected for processing (1)
  • plugin/src/main/java/org/opensearch/sql/plugin/rest/RestPPLGrammarAction.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature PPL Piped processing language

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant