Skip to content

#3588 fix: gRPC query and streaming query now propagate the language parameter#3589

Merged
robfrank merged 2 commits intomainfrom
feat/grpc-query-languages-support
Mar 7, 2026
Merged

#3588 fix: gRPC query and streaming query now propagate the language parameter#3589
robfrank merged 2 commits intomainfrom
feat/grpc-query-languages-support

Conversation

@robfrank
Copy link
Collaborator

@robfrank robfrank commented Mar 7, 2026

Summary

Fixes #3588

  • Added string language = 9; to ExecuteQueryRequest proto (additive, backward-compatible)
  • Server: executeQuery() and all three streamQuery modes (streamCursor, streamMaterialized, streamPaged) now use the language from the request instead of hardcoding "sql"
  • Client: query(), queryStream(), and private streamQuery() now pass the language parameter to the proto builders
  • Enabled Gremlin and Cypher e2e tests that were disabled pending this fix

Test plan

  • Proto, server, and client modules compile successfully
  • RemoteGrpcDatabaseTest.simpleGremlinQuery() enabled (was @Disabled)
  • RemoteGrpcDatabaseTest.simpleCypherQuery() enabled (was @Disabled)
  • RemoteGrpcDatabaseTest.simpleOpenCypherQuery() already passing
  • Run full e2e suite: cd e2e && mvn test -Dtest=RemoteGrpcDatabaseTest

🤖 Generated with Claude Code

…parameter

The language parameter was ignored in query() and queryStream() paths,
causing all queries to be executed as SQL regardless of the specified language.

- Added language field to ExecuteQueryRequest proto (field 9, backward-compatible)
- Server: executeQuery() and streamQuery() modes now use the language from the request
- Client: query(), queryStream(), and streamQuery() now pass language to the proto builders
- Enabled Gremlin and Cypher e2e tests that were disabled pending this fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical limitation in the gRPC client where query and streaming query operations did not correctly propagate the specified language, effectively restricting them to SQL. By introducing a new language field in the gRPC proto, and updating both the server and client implementations to utilize this parameter, the system now fully supports diverse query languages like Gremlin and Cypher through gRPC. This enhancement significantly broadens the capabilities of the gRPC interface, allowing for more flexible and powerful database interactions.

Highlights

  • gRPC Query Language Propagation: The language parameter is now correctly propagated through gRPC query and streaming query operations, resolving an issue where only command() supported non-SQL languages.
  • Protocol Buffer Update: The ExecuteQueryRequest proto message was extended with a new language field to explicitly carry the query language information.
  • Server-Side Language Handling: Server-side executeQuery() and all streamQuery modes (streamCursor, streamMaterialized, streamPaged) now dynamically use the language specified in the request, defaulting to 'sql' if not provided.
  • Client-Side Language Parameterization: Client methods query(), queryStream(), and the private streamQuery() were updated to pass the language parameter to their respective gRPC request builders.
  • Enabled E2E Tests: Previously disabled Gremlin and Cypher end-to-end tests were re-enabled and are now passing, confirming the fix for non-SQL query languages.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • docs/plans/2026-03-07-grpc-query-language-support-design.md
    • Added a new design document detailing the problem, design decisions, and proposed changes for gRPC query language support.
    • Outlined the three layers affected: Proto, Client, and Server, and their respective modifications.
    • Described the testing strategy for verifying the implementation.
  • docs/plans/2026-03-07-grpc-query-language-support-impl.md
    • Added a new implementation plan document providing a step-by-step guide for applying the changes.
    • Included instructions for modifying the proto, server, and client files, along with build commands.
    • Suggested specific e2e tests to validate the changes.
  • e2e/src/test/java/com/arcadedb/e2e/RemoteGrpcDatabaseTest.java
    • Removed the org.junit.jupiter.api.Disabled import.
    • Removed the @Disabled annotation from the simpleGremlinQuery() test method.
    • Removed the @Disabled annotation from the simpleCypherQuery() test method.
    • Added a new test method simpleOpenCypherQuery() to verify OpenCypher queries.
  • grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java
    • Modified the query() method to include .setLanguage(language) in the ExecuteQueryRequest builder.
    • Modified the queryStream() method to include .setLanguage(language) in the StreamQueryRequest builder.
    • Modified the private streamQuery() method to explicitly set the language to 'sql' in the StreamQueryRequest builder.
  • grpc/src/main/proto/arcadedb-server.proto
    • Added a new string language = 9; field to the ExecuteQueryRequest message, with a comment indicating 'sql' as the default if empty.
  • grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java
    • In the executeQuery() method, replaced the hardcoded 'sql' language with the language from the request, defaulting to 'sql' if not provided, and updated logging.
    • In the streamQuery() method, extracted the language from the request and passed it as a new parameter to streamMaterialized(), streamPaged(), and streamCursor() helper methods.
    • Updated the streamCursor() method signature to accept a String language parameter and used it in the db.query() call.
    • Updated the streamMaterialized() method signature to accept a String language parameter and used it in the db.query() call.
    • Updated the streamPaged() method signature to accept a String language parameter and used it in the db.query() call.
Activity
  • The pull request was initially generated using Claude Code.
  • The author confirmed successful compilation of proto, server, and client modules.
  • The author enabled and verified the RemoteGrpcDatabaseTest.simpleGremlinQuery() test.
  • The author enabled and verified the RemoteGrpcDatabaseTest.simpleCypherQuery() test.
  • The author confirmed that RemoteGrpcDatabaseTest.simpleOpenCypherQuery() was already passing.
  • The author plans to run the full e2e test suite.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@robfrank robfrank added this to the 26.4.1 milestone Mar 7, 2026
@mergify
Copy link
Contributor

mergify bot commented Mar 7, 2026

🧪 CI Insights

Here's what we observed from your CI run for 2ca180b.

🟢 All jobs passed!

But CI Insights is watching 👀

@claude
Copy link

claude bot commented Mar 7, 2026

Code Review

The core fix is correct and clean — adding language = 9 to ExecuteQueryRequest is additive and backward-compatible, and the propagation through server and client is consistent. Good work enabling the previously-disabled tests. A few issues worth addressing:


Bug: streamPaged breaks for non-SQL languages

wrapWithSkipLimit wraps the query in ArcadeDB SQL syntax:

// ArcadeDbGrpcService.java:1392
return "SELECT FROM (" + originalSql + ") ORDER BY @rid SKIP :_skip LIMIT :_limit";

Before this PR, streamPaged was hardcoded to "sql", so this was safe. Now that any language can reach it, a client sending language=cypher or language=gremlin with RetrievalMode=PAGED will produce a mixed-syntax query like:

SELECT FROM (MATCH(p:Beer) RETURN * LIMIT 10) ORDER BY @rid SKIP :_skip LIMIT :_limit

...which will fail at query execution. The method comment and variable name (pagedSql) already signal this is SQL-only. Options:

  • Restrict PAGED mode to SQL-only and return an error for other languages
  • Fall back to CURSOR mode when language is not SQL
  • Document the limitation prominently and protect it server-side

NPE risk: null language in client

Both query() and queryStream() call .setLanguage(language) where language comes directly from the caller. Protobuf's generated setLanguage(String) throws NullPointerException if passed null. The existing langOrDefault() helper is right there — use it:

// grpc-client/.../RemoteGrpcDatabase.java
.setLanguage(langOrDefault(language))   // instead of .setLanguage(language)

The command() path (line 620) already uses langOrDefault() correctly. Make query() and queryStream() consistent with it.


Duplicated language-defaulting logic on the server

The inline ternary:

(request.getLanguage() == null || request.getLanguage().isEmpty()) ? "sql" : request.getLanguage()

appears 3 times in ArcadeDbGrpcService.java (lines 256, 821, 1110). The client already has a langOrDefault() helper — extract the same helper on the server side to keep it DRY.


Planning documents committed to the repo

docs/plans/2026-03-07-grpc-query-language-support-design.md and docs/plans/2026-03-07-grpc-query-language-support-impl.md are Claude Code planning artifacts. These shouldn't be committed to the codebase — please remove them before merge.


Inconsistent test: simpleOpenCypherQuery wraps in a transaction

void simpleOpenCypherQuery() {
    database.transaction(() -> {
        final ResultSet result = database.query("opencypher", "MATCH(p:Beer) RETURN * LIMIT 10");
        assertThat(CollectionUtils.countEntries(result)).isEqualTo(10);
    }, false, 10);
}

simpleCypherQuery and simpleGremlinQuery don't need a transaction wrapper. If opencypher requires one for some reason, add a comment explaining why; otherwise remove the wrapper to make the tests consistent.


Missing streaming tests for non-SQL languages

The fix also corrects queryStream() on both client and server, but there are no e2e tests that exercise queryStream() with language=cypher or language=gremlin. Consider adding at least one streaming test for a non-SQL language to validate that path end-to-end.


Summary: The proto change and core propagation are solid. The streamPaged + non-SQL combination is the main correctness gap to resolve, and the two planning .md files should be dropped before merge.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses the issue of propagating the language parameter in gRPC query and streaming query operations, with well-structured changes and enabled e2e tests. However, the implementation introduces security concerns: logging unsanitized user input in executeQuery can lead to log injection, and the streamPaged pagination logic is vulnerable to a SQL injection-style bypass that could cause a denial-of-service via an infinite loop. Furthermore, the streamPaged implementation is currently SQL-specific and will fail for other query languages. A medium-severity suggestion has also been provided to simplify and deduplicate some new server-side logic.

Comment on lines 1300 to +1302
private void streamPaged(Database db, StreamQueryRequest request, int batchSize,
ServerCallStreamObserver<QueryResult> scso,
AtomicBoolean cancelled, ProjectionConfig projectionConfig) {
AtomicBoolean cancelled, ProjectionConfig projectionConfig, String language) {
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

By adding the language parameter, this method now incorrectly suggests it supports paged streaming for any language. However, the implementation calls wrapWithSkipLimit which generates SQL-specific syntax for pagination. This will cause queries in other languages like Gremlin or Cypher to fail because the wrapped query is not valid in those languages.

To fix this, you should add a check at the beginning of the method to throw an UnsupportedOperationException if the language is not SQL, making it clear that this mode is not yet supported for other languages.

Comment on lines +821 to +823
final String language = (request.getLanguage() == null || request.getLanguage().isEmpty()) ? "sql" : request.getLanguage();

ResultSet resultSet = database.query("sql", request.getQuery(),
LogManager.instance().log(this, Level.FINE, "executeQuery(): language = %s query = %s", language, request.getQuery());
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The executeQuery method logs user-supplied language and query parameters without sanitization, creating a log injection vulnerability where attackers can spoof log entries or corrupt log files. Additionally, logging the full query string may inadvertently expose sensitive information. It is crucial to sanitize input by removing or replacing newline characters and consider masking sensitive data before logging. While request.getLanguage() == null is technically redundant for proto3 string fields (which default to empty strings), the provided suggestion prioritizes robust sanitization. This pattern is also present in streamQuery; consider extracting this logic into a private helper method to avoid duplication and improve maintainability and consistent security practices.

Suggested change
final String language = (request.getLanguage() == null || request.getLanguage().isEmpty()) ? "sql" : request.getLanguage();
ResultSet resultSet = database.query("sql", request.getQuery(),
LogManager.instance().log(this, Level.FINE, "executeQuery(): language = %s query = %s", language, request.getQuery());
final String language = (request.getLanguage() == null || request.getLanguage().isEmpty()) ? "sql" : request.getLanguage();
LogManager.instance().log(this, Level.FINE, "executeQuery(): language = %s query = %s", language.replace('\n', '_').replace('\r', '_'), request.getQuery().replace('\n', '_').replace('\r', '_'));

QueryResult.Builder b = QueryResult.newBuilder();

try (ResultSet rs = db.query("sql", pagedSql, params)) {
try (ResultSet rs = db.query(language, pagedSql, params)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

In streamPaged mode, the server constructs a SQL query by concatenating the user-supplied query into a SELECT FROM (...) wrapper to apply pagination. An attacker can provide a query that escapes the subquery (e.g., by using a closing parenthesis and a comment marker like ) --) to comment out the SKIP and LIMIT clauses added by the server. This can lead to an infinite loop on the server, as it will repeatedly execute the query and receive the same full result set, causing a denial-of-service and resource exhaustion. Furthermore, the code now passes the user-supplied language to this SQL-specific wrapper, which will cause syntax errors or unpredictable behavior if a non-SQL language is specified. It is recommended to validate the query string or use a more robust method for applying pagination that does not rely on simple string concatenation.

- Guard streamPaged against non-SQL languages (falls back to CURSOR mode)
- Use langOrDefault() in client query/queryStream to prevent NPE on null language
- Extract langOrDefault() helper on server side to deduplicate language defaulting
- Add streaming query e2e tests for SQL and OpenCypher

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Mar 7, 2026

Code Review

This is a clean, well-scoped fix that correctly propagates the language parameter through the gRPC stack. A few observations:

Correctness

PAGED mode silent fallback (ArcadeDbGrpcService.java lines 1115-1119): When a client explicitly requests PAGED mode but uses a non-SQL language, the code silently switches to CURSOR mode. This is the right behaviour since wrapWithSkipLimit generates SQL-specific syntax (SELECT FROM (...) ORDER BY @rid SKIP :_skip LIMIT :_limit), but the client receives no indication that the requested mode was ignored. A Level.WARNING log entry here would aid debugging.

streamPaged still accepts a language parameter but is now only ever called with "sql" due to the guard above. The parameter is harmless but slightly misleading — minor nit.

Tests

simpleOpenCypherQuery wrapped in a transaction (RemoteGrpcDatabaseTest.java lines 72-75): The SQL and Gremlin/Cypher query tests run without a transaction, but this new test wraps the call in database.transaction(…, false, 10). If this wrapper is genuinely required for OpenCypher, a short comment explaining why would be helpful; if it is a copy-paste artefact it should be removed for consistency.

Missing stream tests for Gremlin and Cypher: streamQueryWithSQL and streamQueryWithOpenCypher are present, but there are no streamQueryWithGremlin / streamQueryWithCypher tests. Given that enabling those two query styles was the primary motivation of the PR, covering them in the streaming path too would be valuable.

Design / Proto

Proto field placement: adding language as field 9 at the bottom of ExecuteQueryRequest is correct and backward-compatible. Consistent with StreamQueryRequest where language was already field 7.

langOrDefault is duplicated: the same one-liner exists in both RemoteGrpcDatabase (client) and ArcadeDbGrpcService (server). Acceptable since the modules compile independently, but both must stay in sync if the default ever changes.

Documentation artefacts

docs/plans/2026-03-07-grpc-query-language-support-design.md and docs/plans/2026-03-07-grpc-query-language-support-impl.md are planning/scratchpad documents that do not belong in the permanent repository history. Please remove these files before merging.

Summary

The core fix (proto field + server + client plumbing) is correct and backward-compatible. Actionable items before merging:

  1. Remove the two docs/plans/ files.
  2. Add a Level.WARNING log when PAGED mode falls back to CURSOR for non-SQL languages.
  3. Clarify or remove the database.transaction() wrapper in simpleOpenCypherQuery.
  4. Consider adding streamQueryWithGremlin and streamQueryWithCypher e2e tests.

@codacy-production
Copy link

codacy-production bot commented Mar 7, 2026

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
-9.70% 94.12%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (43f1922) 103672 77234 74.50%
Head commit (9ab1d79) 134238 (+30566) 86980 (+9746) 64.80% (-9.70%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3589) 17 16 94.12%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@codecov
Copy link

codecov bot commented Mar 7, 2026

Codecov Report

❌ Patch coverage is 81.81818% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.75%. Comparing base (43f1922) to head (2ca180b).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../com/arcadedb/server/grpc/ArcadeDbGrpcService.java 75.00% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3589      +/-   ##
==========================================
+ Coverage   65.55%   65.75%   +0.19%     
==========================================
  Files        1514     1514              
  Lines      103672   103683      +11     
  Branches    21457    21457              
==========================================
+ Hits        67967    68178     +211     
+ Misses      26467    26282     -185     
+ Partials     9238     9223      -15     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@robfrank robfrank merged commit ac3f5c0 into main Mar 7, 2026
25 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gRPC query and streaming query ignore the language parameter (always use SQL)

1 participant