Skip to content

Support PPL queries when having trailing pipes and/or empty pipes#5161

Merged
Swiddis merged 5 commits intoopensearch-project:mainfrom
anasalkouz:feature/ignoreTrailingPipe
Feb 26, 2026
Merged

Support PPL queries when having trailing pipes and/or empty pipes#5161
Swiddis merged 5 commits intoopensearch-project:mainfrom
anasalkouz:feature/ignoreTrailingPipe

Conversation

@anasalkouz
Copy link
Member

@anasalkouz anasalkouz commented Feb 20, 2026

Signed-off-by: Anas Alkouz aalkouz@amazon.com

Description

This PR enhances the PPL (Piped Processing Language) grammar to support trailing pipes at the end of queries and empty pipes in the middle of command sequences. This improvement makes the PPL syntax more forgiving and user-friendly.

Related Issues

Resolves #4032

Motivation

Users often accidentally add trailing pipes when writing queries, especially when:

  • Copy-pasting query fragments
  • Building queries incrementally
  • Using query builders or auto-completion tools

Previously, these queries would fail with syntax errors. This change allows such queries to execute successfully, improving the overall user experience.

Changes

Grammar Modifications

File: ppl/src/main/antlr/OpenSearchPPLParser.g4

Modified two key grammar rules to support optional trailing and empty pipes:

The pattern (PIPE commands?)* elegantly handles:

  • Trailing pipes: A pipe with no command following it at the end of a query
  • Empty pipes: A pipe with no command in the middle of a command sequence

Test Coverage

1. Syntax Parser Tests

File: ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java

Added tests for:

  • Multiple consecutive trailing pipes
  • Trailing pipes with whitespace
  • Empty pipes in the middle of queries
  • Complex queries with both empty and trailing pipes

2. AST Builder Tests

File: ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java

Added tests to verify correct AST generation for:

  • Trailing pipe after source command
  • Trailing pipe after stats command
  • Trailing pipe with complex multi-command queries
  • Empty pipes after source
  • Empty pipes in the middle of command sequences
  • Multiple empty pipes
  • Combination of empty and trailing pipes

3. Integration Tests

File: integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java

Created integration tests that validate:

  • Trailing pipe after source returns same results as without
  • Trailing pipe after fields command
  • Trailing pipe after head command
  • Trailing pipe with complex queries (where, stats, sort)
  • Empty pipes in middle are properly ignored
  • Multiple empty pipes handling
  • Combination of empty and trailing pipes

Examples

Before (Would Fail)

source=logs | where status=200 | fields timestamp, message |

After (Now Works)

source=logs | where status=200 | fields timestamp, message |

Empty Pipes (Also Supported)

source=logs | | where status=200 | fields timestamp, message

Testing

  • ✅ All existing unit tests pass
  • ✅ All new unit tests pass (syntax parser, AST builder)
  • ✅ All integration tests pass
  • ✅ Code formatting applied with ./gradlew spotlessApply
  • ✅ Grammar regenerated successfully

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

Summary by CodeRabbit

  • Bug Fixes

    • Parser now accepts trailing pipes and treats empty/multiple empty pipes as no-ops across sources, field/stats/head and complex pipelines.
  • Tests

    • Added extensive integration and unit tests covering trailing-pipe, empty-pipe, middle-empty-pipe, and malformed-pipe scenarios to ensure consistent behavior.

Walkthrough

Makes pipe-following commands optional in the PPL grammar and adds unit and integration tests to ensure trailing and empty pipes are accepted and ignored during parsing, AST building, and execution.

Changes

Cohort / File(s) Summary
Grammar
ppl/src/main/antlr/OpenSearchPPLParser.g4
Relaxed grammar: changed (PIPE commands)* to (PIPE commands?)* in subPipeline, queryStatement, and subSearch to allow standalone/empty trailing pipes.
Parser unit tests
ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java
Added tests for multiple/trailing pipes, whitespace handling, middle empty pipes, complex trailing-pipe queries, sub-search trailing pipe, and invalid command-after-pipe failure case.
AST builder tests
ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java
Added tests verifying ASTs are identical when queries contain trailing or empty pipes in various positions, plus a malformed-pipe syntax error test.
Integration tests
integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java
New integration test class with tests comparing results of queries with and without trailing/empty pipes across source, fields, head, complex pipelines, and multiple empty-pipe scenarios.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Parser as Parser\n(OpenSearchPPLParser)
  participant AST as ASTBuilder
  participant Exec as Executor
  participant Index as DataStore

  Client->>Parser: submit PPL query (may end with pipe)
  Parser-->>Client: produce parse tree (allows empty command after PIPE)
  Parser->>AST: build AST (ignore empty pipeline nodes)
  AST-->>Parser: AST
  AST->>Exec: execute AST
  Exec->>Index: query index (e.g., ACCOUNT)
  Index-->>Exec: results
  Exec-->>Client: final query results (same as without trailing pipe)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for trailing pipes and empty pipes in PPL queries.
Description check ✅ Passed The description is comprehensive and clearly related to the changeset, explaining the motivation, changes, test coverage, and examples.
Linked Issues check ✅ Passed The PR fully addresses the requirement from #4032 to handle trailing pipes in PPL queries by modifying the grammar and adding comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly related to the objective of supporting trailing pipes and empty pipes in PPL queries as specified in #4032.

✏️ 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.

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 (4)
ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java (1)

950-989: Missing negative test case for the new grammar behaviour.

All new tests verify that parse trees are produced (positive cases). Per coding guidelines, grammar tests should include negative cases. Consider adding a test verifying that an invalid command token following a pipe still raises a SyntaxCheckException, so regressions in error detection are caught:

`@Test`(expected = SyntaxCheckException.class)
public void testPipeWithInvalidCommandShouldFail() {
    new PPLSyntaxParser().parse("source=t | | 123invalidcommand");
}

Based on learnings: "Applies to **/{grammar,parser}/**/*Test.java: Include edge cases and boundary conditions in parser and grammar tests" and "Applies to **/{grammar,parser}/**/*.java: Test new grammar rules with positive and negative cases for parser development".

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

In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`
around lines 950 - 989, Add a negative parser test to ensure invalid tokens
after a pipe throw SyntaxCheckException: create a new test method (e.g.,
testPipeWithInvalidCommandShouldFail) that calls new
PPLSyntaxParser().parse("source=t | | 123invalidcommand") and expects
SyntaxCheckException; place it alongside the existing positive tests (those
invoking PPLSyntaxParser.parse in PPLSyntaxParserTest) so grammar regressions
are caught.
ppl/src/main/antlr/OpenSearchPPLParser.g4 (1)

568-574: appendcolCommand and appendCommand not updated — inconsistent trailing-pipe behaviour.

After this change, appendpipe [ stats count() | ] succeeds (via subPipeline), but the equivalent appendcol [ stats count() | ] still fails because appendcolCommand and appendCommand still use the old (PIPE commands)* pattern (command required after pipe).

🔧 Suggested fix to align `appendcolCommand` with the PR's intent
 appendcolCommand
-  : APPENDCOL (OVERRIDE EQUAL override = booleanLiteral)? LT_SQR_PRTHS commands (PIPE commands)* RT_SQR_PRTHS
+  : APPENDCOL (OVERRIDE EQUAL override = booleanLiteral)? LT_SQR_PRTHS commands (PIPE commands?)* RT_SQR_PRTHS
   ;

 appendCommand
-  : APPEND LT_SQR_PRTHS searchCommand? (PIPE commands)* RT_SQR_PRTHS
+  : APPEND LT_SQR_PRTHS searchCommand? (PIPE commands?)* RT_SQR_PRTHS
   ;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ppl/src/main/antlr/OpenSearchPPLParser.g4` around lines 568 - 574, The
grammar rules appendcolCommand and appendCommand still require a commands after
every PIPE (using (PIPE commands)*), causing appendcol [...] with a trailing
pipe to fail; update both rules to allow a PIPE with optional commands by
changing the repetition from (PIPE commands)* to (PIPE commands?)* (i.e., make
commands optional after PIPE) so trailing pipes are accepted; reference rules:
appendcolCommand, appendCommand, token PIPE and rule commands.
ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java (1)

1657-1724: Add at least one negative test for completeness.

The new tests cover positive cases well. Per guidelines, parser/grammar tests should include both positive and negative cases. Consider adding a test verifying that a genuinely malformed pipe sequence (e.g., pipe with an invalid command token still present) produces a SyntaxCheckException.

Additionally, there is no coverage for the subPipeline context (i.e., trailing pipe inside appendpipe [...]). Since appendPipeCommand uses subPipeline, which was also updated, a test like:

`@Test`
public void testTrailingPipeInAppendPipe() {
    assertEqual(
        "source=t | appendpipe [ stats count() | ]",
        /* expected AST for appendpipe */ ...
    );
}

would close the coverage gap.

Based on learnings: "Applies to **/{grammar,parser}/**/*Test.java: Include edge cases and boundary conditions in parser and grammar tests" and "Applies to **/{grammar,parser}/**/*.java: Test new grammar rules with positive and negative cases for parser development".

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

In `@ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java` around
lines 1657 - 1724, Add negative and subPipeline coverage in AstBuilderTest: add
a test method (e.g., testMalformedPipeProducesSyntaxError) that parses a
deliberately malformed pipeline like "source=t | invalidCmd |" and asserts a
SyntaxCheckException is thrown (use the test harness/assertThrows pattern used
elsewhere and reference SyntaxCheckException). Also add a positive test (e.g.,
testTrailingPipeInAppendPipe) for the appendpipe/subPipeline case using the
existing AST helper builders (relation(...), agg(...), projectWithArg(...),
etc.) to assert that "source=t | appendpipe [ stats count() | ]" produces the
expected AST (reference the grammar rule appendPipeCommand and subPipeline to
locate the parser behavior to exercise). Ensure both tests are added to
AstBuilderTest with clear names so future diffs cover negative and subPipeline
boundary cases.
integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java (1)

23-31: toString() JSON comparison is fragile — prefer a dedicated equality helper.

JSONObject.toString() ordering is not guaranteed to be stable across library versions or platforms. If the response JSON keys reorder between calls, the assertion would produce a false failure even when the data is identical.

Other integration tests in this codebase use structured comparison helpers. Consider comparing the total count and a sample of hits, or use a deep-equality helper available in the base class, rather than relying on string serialization order.

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

In `@integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java` around
lines 23 - 31, The testTrailingPipeAfterSource relies on JSONObject.toString()
ordering; instead assert structural equality: call executeQuery(...) into
resultWithout/resultWith and compare resultWithout.getInt("total") with
resultWith.getInt("total") and compare the hits arrays element-wise (e.g.,
getJSONArray("hits") and compare lengths and each JSONObject entry using
JSONObject.equals or a deep-equality helper from the test base). Replace the
assertEquals(resultWithout.toString(), resultWith.toString()) with these
targeted assertions so ordering of serialized keys won't cause false failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`:
- Around line 950-956: Update the misleading test comment in
testQueryWithMultipleTrailingPipesShouldPass: the test string passed to
PPLSyntaxParser.parse ("search source=t a=1 b=2 | fields a,b | |") contains two
consecutive empty pipe tokens (an empty pipeline segment plus a trailing empty
pipe), so change the comment to accurately state that the test verifies handling
of two consecutive trailing pipes (an empty middle pipe and a trailing empty
pipe) rather than "one optional trailing pipe"; keep the assertion and parse
call unchanged.

---

Nitpick comments:
In `@integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java`:
- Around line 23-31: The testTrailingPipeAfterSource relies on
JSONObject.toString() ordering; instead assert structural equality: call
executeQuery(...) into resultWithout/resultWith and compare
resultWithout.getInt("total") with resultWith.getInt("total") and compare the
hits arrays element-wise (e.g., getJSONArray("hits") and compare lengths and
each JSONObject entry using JSONObject.equals or a deep-equality helper from the
test base). Replace the assertEquals(resultWithout.toString(),
resultWith.toString()) with these targeted assertions so ordering of serialized
keys won't cause false failures.

In `@ppl/src/main/antlr/OpenSearchPPLParser.g4`:
- Around line 568-574: The grammar rules appendcolCommand and appendCommand
still require a commands after every PIPE (using (PIPE commands)*), causing
appendcol [...] with a trailing pipe to fail; update both rules to allow a PIPE
with optional commands by changing the repetition from (PIPE commands)* to (PIPE
commands?)* (i.e., make commands optional after PIPE) so trailing pipes are
accepted; reference rules: appendcolCommand, appendCommand, token PIPE and rule
commands.

In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`:
- Around line 950-989: Add a negative parser test to ensure invalid tokens after
a pipe throw SyntaxCheckException: create a new test method (e.g.,
testPipeWithInvalidCommandShouldFail) that calls new
PPLSyntaxParser().parse("source=t | | 123invalidcommand") and expects
SyntaxCheckException; place it alongside the existing positive tests (those
invoking PPLSyntaxParser.parse in PPLSyntaxParserTest) so grammar regressions
are caught.

In `@ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java`:
- Around line 1657-1724: Add negative and subPipeline coverage in
AstBuilderTest: add a test method (e.g., testMalformedPipeProducesSyntaxError)
that parses a deliberately malformed pipeline like "source=t | invalidCmd |" and
asserts a SyntaxCheckException is thrown (use the test harness/assertThrows
pattern used elsewhere and reference SyntaxCheckException). Also add a positive
test (e.g., testTrailingPipeInAppendPipe) for the appendpipe/subPipeline case
using the existing AST helper builders (relation(...), agg(...),
projectWithArg(...), etc.) to assert that "source=t | appendpipe [ stats count()
| ]" produces the expected AST (reference the grammar rule appendPipeCommand and
subPipeline to locate the parser behavior to exercise). Ensure both tests are
added to AstBuilderTest with clear names so future diffs cover negative and
subPipeline boundary cases.

Swiddis
Swiddis previously approved these changes Feb 23, 2026
anasalkouz added a commit to anasalkouz/sql that referenced this pull request Feb 24, 2026
Add detailed Javadoc documentation to all test methods in TrailingPipeIT
class to improve code documentation coverage and meet the 80% threshold
requirement.

Each test method now includes:
- Clear description of what the test validates
- @throws IOException annotation for exception handling

This addresses the docstring coverage issue flagged by CodeRabbit in PR opensearch-project#5161.
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

♻️ Duplicate comments (1)
ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java (1)

950-955: Comment still inaccurately describes the test query.

Line 952 reads "Multiple consecutive trailing pipes should be handled gracefully," but the query "search source=t a=1 b=2 | fields a,b | |" has a normal | fields a,b stage, then an empty middle pipe, then a trailing pipe — the first | is not trailing at all.

✏️ Suggested comment fix
-    // Multiple consecutive trailing pipes should be handled gracefully
+    // Empty middle pipe followed by a trailing pipe should both parse successfully
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`
around lines 950 - 955, The test comment for
testQueryWithMultipleTrailingPipesShouldPass is inaccurate for the query "search
source=t a=1 b=2 | fields a,b | |" because the first '|' is not trailing (there
is a normal "fields" stage, an empty middle stage, then a trailing pipe); update
either the test name (e.g., testQueryWithEmptyStageAndTrailingPipeShouldPass) or
the inline comment to accurately state "Query contains an empty intermediate
stage and a trailing pipe" so the description matches the actual query in the
test method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`:
- Around line 950-988: Add missing boundary-case unit tests in
PPLSyntaxParserTest: create a test method (e.g.,
testSingleSourceTrailingPipeShouldPass) that calls new
PPLSyntaxParser().parse("source=t |") and asserts the ParseTree is not null, and
another test (e.g., testThreeConsecutiveEmptyPipesShouldPass) that parses a
query with three or more consecutive trailing pipes (e.g., "source=t | | |") and
asserts non-null. Place these alongside the existing tests so they exercise
PPLSyntaxParser.parse and cover the canonical single-source trailing pipe and
the repetition boundary for (PIPE commands?)*.

---

Duplicate comments:
In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`:
- Around line 950-955: The test comment for
testQueryWithMultipleTrailingPipesShouldPass is inaccurate for the query "search
source=t a=1 b=2 | fields a,b | |" because the first '|' is not trailing (there
is a normal "fields" stage, an empty middle stage, then a trailing pipe); update
either the test name (e.g., testQueryWithEmptyStageAndTrailingPipeShouldPass) or
the inline comment to accurately state "Query contains an empty intermediate
stage and a trailing pipe" so the description matches the actual query in the
test method.

ℹ️ 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 f9f56cc and 0e71348.

📒 Files selected for processing (3)
  • integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java
  • ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java
  • ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java
  • integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java

Comment on lines +950 to +988
@Test
public void testQueryWithMultipleTrailingPipesShouldPass() {
// Multiple consecutive trailing pipes should be handled gracefully
ParseTree tree = new PPLSyntaxParser().parse("search source=t a=1 b=2 | fields a,b | |");
assertNotEquals(null, tree);
}

@Test
public void testQueryWithTrailingPipeAndWhitespaceShouldPass() {
ParseTree tree = new PPLSyntaxParser().parse("search source=t a=1 b=2 | fields a,b | ");
assertNotEquals(null, tree);
}

@Test
public void testQueryWithMiddleEmptyPipe() {
ParseTree tree = new PPLSyntaxParser().parse("search source=t a=1 b=2 | | fields a,b");
assertNotEquals(null, tree);
}

@Test
public void testQueryWithMiddleEmptyPipeAndTrailingPipe() {
ParseTree tree = new PPLSyntaxParser().parse("search source=t a=1 b=2 | | fields a,b | ");
assertNotEquals(null, tree);
}

@Test
public void testComplexQueryWithTrailingPipeShouldPass() {
ParseTree tree =
new PPLSyntaxParser()
.parse("source=t | where x > 5 | stats count() by status | sort -count |");
assertNotEquals(null, tree);
}

@Test
public void testSubSearchWithTrailingPipeShouldPass() {
ParseTree tree =
new PPLSyntaxParser().parse("source=outer | join a [source=inner | fields x,y |]");
assertNotEquals(null, tree);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing minimal "source=t |" test — the canonical scenario from issue #4032.

None of the new positive tests cover the simplest single-source trailing pipe (source=t |), which is the exact motivating example from the linked issue. testComplexQueryWithTrailingPipeShouldPass does end with |, but burying the baseline scenario inside a four-stage pipeline makes it a less obvious regression anchor. Adding a standalone test for this, and for three or more consecutive empty pipes (boundary of the (PIPE commands?)* repetition), would satisfy the boundary-condition requirement.

✏️ Suggested additions
+  `@Test`
+  public void testQueryWithSingleTrailingPipeShouldPass() {
+    // Minimal case: single trailing pipe (motivating example from issue `#4032`)
+    ParseTree tree = new PPLSyntaxParser().parse("source=t |");
+    assertNotEquals(null, tree);
+  }
+
+  `@Test`
+  public void testQueryWithMultipleConsecutiveEmptyPipesShouldPass() {
+    // Boundary: three or more consecutive empty/trailing pipes
+    ParseTree tree = new PPLSyntaxParser().parse("source=t | | | fields a");
+    assertNotEquals(null, tree);
+  }

Based on learnings: "Include edge cases and boundary conditions in parser and grammar tests" and "Test new grammar rules with positive and negative cases for parser development."

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

In `@ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java`
around lines 950 - 988, Add missing boundary-case unit tests in
PPLSyntaxParserTest: create a test method (e.g.,
testSingleSourceTrailingPipeShouldPass) that calls new
PPLSyntaxParser().parse("source=t |") and asserts the ParseTree is not null, and
another test (e.g., testThreeConsecutiveEmptyPipesShouldPass) that parses a
query with three or more consecutive trailing pipes (e.g., "source=t | | |") and
asserts non-null. Place these alongside the existing tests so they exercise
PPLSyntaxParser.parse and cover the canonical single-source trailing pipe and
the repetition boundary for (PIPE commands?)*.

@anasalkouz anasalkouz requested a review from Swiddis February 25, 2026 17:11
Swiddis
Swiddis previously approved these changes Feb 25, 2026
dai-chen
dai-chen previously approved these changes Feb 25, 2026
@dai-chen
Copy link
Collaborator

Please check and fix the DCO failure. Thanks!

Add detailed Javadoc documentation to all test methods in TrailingPipeIT
class to improve code documentation coverage and meet the 80% threshold
requirement.

Each test method now includes:
- Clear description of what the test validates
- @throws IOException annotation for exception handling

This addresses the docstring coverage issue flagged by CodeRabbit in PR opensearch-project#5161.

Signed-off-by: Anas Alkouz <aalkouz@amazon.com>
Signed-off-by: Anas Alkouz <aalkouz@amazon.com>
Signed-off-by: Anas Alkouz <aalkouz@amazon.com>
@anasalkouz anasalkouz force-pushed the feature/ignoreTrailingPipe branch from 0e71348 to e74ba19 Compare February 26, 2026 18:39
@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

PR Reviewer Guide 🔍

(Review updated until commit 5bea79f)

Here are some key observations to aid the review process:

🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

Grammar Ambiguity

The pattern (PIPE commands?)* allows unlimited consecutive empty pipes (e.g., | | | | |), which may not be the intended behavior. Consider if there should be a limit or if this could cause performance issues with deeply nested parse trees.

: PIPE? commands (PIPE commands?)*
Test Coverage Gap

The test testPipeWithInvalidCommandShouldFail only checks one invalid scenario. Consider adding tests for other edge cases like pipes with only whitespace between them, or queries starting with multiple pipes.

public void testPipeWithInvalidCommandShouldFail() {
  assertThrows(
      SyntaxCheckException.class,
      () -> new PPLSyntaxParser().parse("source=t | | 123invalidcommand"));
}

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

PR Code Suggestions ✨

Latest suggestions up to 5bea79f
Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Add descriptive assertion messages

Add descriptive assertion messages to all assertTrue calls to provide clear failure
context. When tests fail, the message should indicate which specific scenario failed
(e.g., "Query with trailing pipe should produce same results as without").

integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java [41]

-assertTrue(resultWithout.similar(resultWith));
+assertTrue("Query with trailing pipe should produce same results as without", resultWithout.similar(resultWith));
Suggestion importance[1-10]: 5

__

Why: Adding descriptive assertion messages improves test maintainability and debugging experience. However, this is a minor enhancement that doesn't affect functionality or correctness.

Low
Use assertNotNull for null checks

Replace assertNotEquals(null, tree) with assertNotNull(tree) for better readability
and more appropriate null checking semantics. This makes the test intent clearer and
follows JUnit best practices.

ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java [954]

-assertNotEquals(null, tree);
+assertNotNull(tree);
Suggestion importance[1-10]: 4

__

Why: Using assertNotNull is more idiomatic and clearer than assertNotEquals(null, tree), but this is a minor style improvement that doesn't impact test correctness or functionality.

Low
Prevent infinite consecutive empty pipes

The grammar allows infinite consecutive empty pipes (e.g., source=t | | | | | ...),
which could lead to performance issues or stack overflow with deeply nested parse
trees. Consider limiting consecutive empty pipes or adding validation to reject
excessive empty pipes.

ppl/src/main/antlr/OpenSearchPPLParser.g4 [23-42]

 subPipeline
-   : PIPE? commands (PIPE commands?)*
+   : PIPE? commands (PIPE+ commands)*
    ;
 
 queryStatement
-   : (PIPE)? pplCommands (PIPE commands?)*
+   : (PIPE)? pplCommands (PIPE+ commands)*
    ;
 
 ...
 
 subSearch
-   : searchCommand (PIPE commands?)*
+   : searchCommand (PIPE+ commands)*
    ;
Suggestion importance[1-10]: 2

__

Why: The suggestion misunderstands the grammar change. The improved_code using PIPE+ would actually break the intended functionality by requiring at least one pipe between commands, preventing single pipes. The current grammar with (PIPE commands?)* correctly handles trailing and empty pipes as intended by the PR.

Low

Previous suggestions

Suggestions up to commit e74ba19
CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent ambiguous parsing with consecutive pipes

Making commands optional after PIPE allows consecutive pipes without commands
between them. This can lead to ambiguous parsing and may accept syntactically
invalid queries. Consider using PIPE+ to consume multiple consecutive pipes as a
single token, or add explicit validation to reject empty pipe sequences.

ppl/src/main/antlr/OpenSearchPPLParser.g4 [23-42]

 subPipeline
-   : PIPE? commands (PIPE commands?)*
+   : PIPE? commands (PIPE+ commands)*
    ;
 
 queryStatement
-   : (PIPE)? pplCommands (PIPE commands?)*
+   : (PIPE)? pplCommands (PIPE+ commands)*
    ;
 
 subSearch
-   : searchCommand (PIPE commands?)*
+   : searchCommand (PIPE+ commands)*
    ;
Suggestion importance[1-10]: 3

__

Why: The suggestion raises a valid concern about potential ambiguity, but the PR's approach of making commands optional after PIPE is intentional to support trailing and empty pipes. The extensive test coverage demonstrates this works correctly. Using PIPE+ would change the intended behavior by requiring multiple consecutive pipes to be treated as a single token, which contradicts the PR's goal of handling individual empty pipes gracefully.

Low

@anasalkouz anasalkouz closed this Feb 26, 2026
@anasalkouz anasalkouz force-pushed the feature/ignoreTrailingPipe branch from e74ba19 to acf5fcb Compare February 26, 2026 18:42
Signed-off-by: Anas Alkouz <aalkouz@amazon.com>
@anasalkouz anasalkouz reopened this Feb 26, 2026
@anasalkouz anasalkouz dismissed stale reviews from Swiddis and dai-chen via 5bea79f February 26, 2026 19:03
@github-actions
Copy link
Contributor

Persistent review updated to latest commit 5bea79f

1 similar comment
@github-actions
Copy link
Contributor

Persistent review updated to latest commit 5bea79f

@Swiddis Swiddis merged commit c00b8b7 into opensearch-project:main Feb 26, 2026
90 of 93 checks passed
@anasalkouz anasalkouz deleted the feature/ignoreTrailingPipe branch February 27, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request PPL Piped processing language

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] UX Improvement - Handle Trailing Pipes

4 participants