Skip to content

fix(qc): switch JSON_TYPE gate to ARRAY for wildcard MySQL paths#5813

Open
truffle-dev wants to merge 1 commit into
prisma:mainfrom
truffle-dev:fix/json-string-filter-wildcard-path-29571
Open

fix(qc): switch JSON_TYPE gate to ARRAY for wildcard MySQL paths#5813
truffle-dev wants to merge 1 commit into
prisma:mainfrom
truffle-dev:fix/json-string-filter-wildcard-path-29571

Conversation

@truffle-dev
Copy link
Copy Markdown

On MySQL, JSON_EXTRACT(col, '$.items[*].name') returns a JSON array of matched values rather than a scalar. The existing JSON_TYPE = 'STRING' gate added around string_contains, string_starts_with, and string_ends_with always evaluated to false in that case, so the filter quietly returned 0 rows even when matching strings existed in the array elements.

This PR detects the three MySQL path wildcards ([*], .*, **) at the orchestration site in convert_json_filter, threads a path_returns_array: bool through JsonFilterExt, and switches the gate to JSON_TYPE = 'ARRAY' when a wildcard is present. The produced SQL matches the expected query in the issue:

JSON_UNQUOTE(JSON_EXTRACT(`t`.`metadata`, '$.items[*].name')) LIKE CONCAT('%', 'Widget', '%')
AND JSON_TYPE(JSON_EXTRACT(`t`.`metadata`, '$.items[*].name')) = 'ARRAY'

Postgres' array-form path (JsonFilterPath::Array) carries literal segments only and cannot encode a wildcard, so it remains on the STRING gate.

Regression test in query-engine-tests/tests/queries/filters/json_filters.rs::string_contains_wildcard_path covers the issue case (MySQL 5.7 and 8). The connector test kit needs a live MySQL instance, which isn't available in my dev environment, so the test compiles cleanly but I haven't run it against a database — happy to retarget the regression in a sql-query-builder unit test (visitor SQL snapshot) if you'd prefer.

Closes prisma/prisma#29571

`JSON_EXTRACT(col, '$.items[*].name')` returns a JSON array of matched
values, so the existing `JSON_TYPE = 'STRING'` gate around
`string_contains`, `string_starts_with`, and `string_ends_with` always
evaluated to false and the filter returned 0 rows.

Detect the three MySQL path wildcards (`[*]`, `.*`, `**`) and expect
`JSON_TYPE = 'ARRAY'` in that case. Postgres' array-form path
(`JsonFilterPath::Array`) carries literal segments only and cannot
encode a wildcard, so it is unaffected.

Closes prisma/prisma#29571
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Review Change Stack

Summary by CodeRabbit

  • Bug Fixes
    • Fixed JSON string filtering operations (contains, starts with, ends with) to correctly handle wildcard paths that return arrays, improving compatibility with complex JSON query patterns.

Walkthrough

This PR fixes JSON path wildcard filtering in MySQL by detecting when a path like $.items[*].name returns an array and applying the correct JSON_TYPE check. Instead of always checking JSON_TYPE = 'STRING', the code now checks JSON_TYPE = 'ARRAY' for wildcard paths, preventing queries from returning zero results when they should match array elements.

Changes

JSON wildcard path type checking for string filters

Layer / File(s) Summary
Wildcard path detection and type mapping helpers
query-compiler/query-builders/sql-query-builder/src/filter/visitor.rs
Added path_returns_array() helper to detect [*] wildcards in JSON paths and string_target_expected_type() to map the wildcard flag to the correct JsonType for type gating.
Entry point path detection and trait threading
query-compiler/query-builders/sql-query-builder/src/filter/visitor.rs
convert_json_filter computes path_returns_array and threads it into ScalarCondition branches for Contains, StartsWith, and EndsWith when calling JSON helper methods.
String filter implementations with flexible type checking
query-compiler/query-builders/sql-query-builder/src/filter/visitor.rs
Updated json_contains, json_starts_with, and json_ends_with trait methods and implementations to accept path_returns_array and use the computed expected type instead of hard-coded JsonType::String in all value-based and field-ref-based branches.
Regression test for wildcard path string containment
query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/json_filters.rs
New string_contains_wildcard_path test for MySQL 5.7/8 verifies that $.items[*].name filters correctly match string values in array elements and return expected records.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: detecting wildcard MySQL paths and switching the JSON_TYPE gate from STRING to ARRAY for string filters.
Description check ✅ Passed The description is well-related to the changeset, explaining the bug fix, the approach taken, the SQL output, and noting that a regression test was added.
Linked Issues check ✅ Passed The PR fully addresses the root problem in #29571 by detecting wildcard paths and switching the JSON_TYPE gate to ARRAY, matching the expected query shown in the issue.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to fixing the wildcard JSON path issue for string filters on MySQL; no unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

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
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/json_filters.rs`:
- Around line 557-597: Add additional tests mirroring
string_contains_wildcard_path to cover the other wildcard patterns and string
operations: create new async test functions (e.g.,
string_contains_member_wildcard, string_contains_recursive_descent,
string_starts_with_wildcard_path, string_ends_with_wildcard_path) that use
create_row to seed rows and run_query!/jsonq with path values "$.items.*.name"
and "$.items**.name" (and with string_starts_with / string_ends_with payloads)
and assert the expected snapshots similarly to string_contains_wildcard_path;
ensure tests are annotated with the same connector_test capabilities
(JsonFilteringJsonPath) and MySql versions so they exercise the same fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a623da30-9365-4776-8efd-47e7388dd138

📥 Commits

Reviewing files that changed from the base of the PR and between 3c6e192 and eb31f25.

📒 Files selected for processing (2)
  • query-compiler/query-builders/sql-query-builder/src/filter/visitor.rs
  • query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/json_filters.rs

Comment on lines +557 to +597
// Regression for https://github.com/prisma/prisma/issues/29571.
//
// MySQL JSON path wildcards (`[*]`, `.*`, `**`) make JSON_EXTRACT return an
// array even when each matched value is a scalar string, so the
// `JSON_TYPE = 'STRING'` gate around `string_contains` always evaluated to
// false and the filter returned 0 rows. With the fix, wildcard-path filters
// expect `JSON_TYPE = 'ARRAY'` and `LIKE` matches against the JSON-serialized
// array text.
#[connector_test(capabilities(JsonFilteringJsonPath), only(MySql(5.7), MySql(8)))]
async fn string_contains_wildcard_path(runner: Runner) -> TestResult<()> {
create_row(
&runner,
1,
r#"{ \"items\": [{ \"name\": \"Widget A\" }, { \"name\": \"Gadget B\" }] }"#,
false,
)
.await?;
create_row(
&runner,
2,
r#"{ \"items\": [{ \"name\": \"Gadget X\" }, { \"name\": \"Gadget Y\" }] }"#,
false,
)
.await?;
create_row(&runner, 3, r#"{ \"items\": [] }"#, false).await?;

let res = run_query!(
runner,
jsonq(
&runner,
r#"path: "$.items[*].name", string_contains: "Widget" "#,
Some("")
)
);
insta::assert_snapshot!(
res,
@r###"{"data":{"findManyTestModel":[{"id":1}]}}"###
);

Ok(())
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider adding tests for other wildcard patterns and string operations.

The test validates the [*] wildcard with string_contains, which covers the reported issue. However, the implementation also handles .* (object member wildcard) and ** (recursive descent), and applies the fix to string_starts_with and string_ends_with as well. Adding tests for these variations would strengthen confidence in the fix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/json_filters.rs`
around lines 557 - 597, Add additional tests mirroring
string_contains_wildcard_path to cover the other wildcard patterns and string
operations: create new async test functions (e.g.,
string_contains_member_wildcard, string_contains_recursive_descent,
string_starts_with_wildcard_path, string_ends_with_wildcard_path) that use
create_row to seed rows and run_query!/jsonq with path values "$.items.*.name"
and "$.items**.name" (and with string_starts_with / string_ends_with payloads)
and assert the expected snapshots similarly to string_contains_wildcard_path;
ensure tests are annotated with the same connector_test capabilities
(JsonFilteringJsonPath) and MySql versions so they exercise the same fix.

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.

JSON_TYPE check causes string_contains to fail with wildcard JSON path ([*]) on MySQL

2 participants