Skip to content

Comments

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

Open
anasalkouz wants to merge 1 commit intoopensearch-project:mainfrom
anasalkouz:feature/ignoreTrailingPipe
Open

Support PPL queries when having trailing pipes and/or empty pipes#5161
anasalkouz wants to merge 1 commit 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.

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

Related Issues

Resolves #4032

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

    • PPL parser now accepts queries with trailing pipes without errors.
    • Empty pipes in query pipelines are properly ignored and do not affect results.
    • Trailing pipes are now supported after source selections, field operations, and complex query constructs.
  • Tests

    • Added comprehensive tests validating trailing pipe and empty pipe handling across various query scenarios.

Walkthrough

This change adds support for trailing pipes in OpenSearch PPL queries by relaxing grammar requirements to make pipe-command segments optional, with comprehensive test coverage across unit and integration tests validating parser behavior with trailing and empty pipes.

Changes

Cohort / File(s) Summary
Grammar Updates
ppl/src/main/antlr/OpenSearchPPLParser.g4
Modified grammar productions to make trailing PIPE-commands optional by changing (PIPE commands)* to (PIPE commands?)* in subPipeline, queryStatement, and subSearch rules, allowing standalone pipes or absent commands after pipes.
Parser Unit Tests
ppl/src/test/java/org/opensearch/sql/ppl/antlr/PPLSyntaxParserTest.java
Added six new test methods validating parser robustness with multiple trailing pipes, whitespace handling, middle empty pipes, complex queries, and sub-searches containing trailing pipes.
AST Builder Unit Tests
ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java
Added seven new test methods confirming AST construction ignores extraneous pipes in various contexts: trailing pipes after source/stats, complex queries, and empty pipes in middle or trailing positions.
Integration Tests
integ-test/src/test/java/org/opensearch/sql/ppl/TrailingPipeIT.java
New integration test class with eight test methods verifying trailing and empty pipes produce identical query results across multiple query types (source, fields, head, complex queries).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly and concisely summarizes the main change: adding support for trailing and empty pipes in PPL queries, which aligns perfectly with the changeset.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the motivation, specific grammar modifications, and test coverage across syntax parser, AST builder, and integration tests.

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

Comment on lines +950 to +956
@Test
public void testQueryWithMultipleTrailingPipesShouldPass() {
// Multiple consecutive trailing pipes should be handled gracefully
// by allowing one optional trailing pipe
ParseTree tree = new PPLSyntaxParser().parse("search source=t a=1 b=2 | fields a,b | |");
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

Misleading comment — the test exercises two pipes, not one.

The comment at Line 952-953 says "allowing one optional trailing pipe" but the query | fields a,b | | has an empty middle pipe AND a trailing empty pipe (two pipe tokens without intervening commands).

✏️ Suggested comment fix
-    // Multiple consecutive trailing pipes should be handled gracefully
-    // by allowing one optional trailing pipe
+    // Multiple consecutive pipes (empty middle pipe + trailing pipe) should 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 - 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.

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

2 participants