Skip to content

fix(postgrest): escape " and \ inside quoted filter values - #2529

Open
PedroHenrique0713 wants to merge 1 commit into
supabase:masterfrom
PedroHenrique0713:fix/postgrest-filter-escape-quoted-values
Open

fix(postgrest): escape " and \ inside quoted filter values#2529
PedroHenrique0713 wants to merge 1 commit into
supabase:masterfrom
PedroHenrique0713:fix/postgrest-filter-escape-quoted-values

Conversation

@PedroHenrique0713

Copy link
Copy Markdown
Contributor

🔍 Description

What changed?

Six filter methods in PostgrestFilterBuilder now correctly escape " and \ inside quoted filter values, per PostgREST's backslash convention:

  • in() / notIn(): values that contain both a reserved char (,()``) and a "or` are now escaped as \" and \\ inside the wrapping double quotes.
  • likeAllOf() / likeAnyOf() / ilikeAllOf() / ilikeAnyOf(): patterns that contain a reserved char are now wrapped in double quotes (previously they were not quoted at all), with the same \" / \\ escaping.

The stale comment link was also updated to the current PostgREST docs (/en/stable/references/api/url_grammar.html#reserved-characters).

Why was this change needed?

PostgREST's URL grammar treats ,, ., :, () as reserved characters. When a filter value contains one of these, it must be wrapped in double quotes %22...%22. Inside those quotes, " is escaped with a backslash \" and \ with \\not CSV-style doubling "".

Bug 1 — in()/notIn(): the old code wrapped values in "..." when they contained a reserved char, but never escaped an inner ". So .in("col", ["a\"b,c"]) produced in.("a"b,c"), which PostgREST cannot parse (the quote terminates the value early). Affected any value with both a reserved char and a literal double quote.

Bug 2 — likeAllOf/likeAnyOf/ilikeAllOf/ilikeAnyOf: the old code did patterns.join(",") with no quoting at all. A pattern containing a comma was silently split by PostgREST into multiple patterns — .likeAllOf("col", ["%foo,bar%"]) produced like(all).{%foo,bar%} which PostgREST reads as two patterns (%foo and bar%), matching the wrong rows or nothing.

PostgREST docs: https://postgrest.org/en/stable/references/api/url_grammar.html#reserved-characters

📸 Screenshots/Examples

// Before: in.("a"b,c")  — broken, PostgREST 400/error
// After:  in.("a\"b,c")  — correct, matches the row
await supabase.from("users").select().in("username", ["a\"b,c"])

// Before: like(all).{%foo,bar%}  — PostgREST reads 2 patterns
// After:  like(all).{"%foo,bar%"} — 1 pattern with a literal comma
await supabase.from("users").select().likeAllOf("username", ["%foo,bar%"])

🔄 Breaking changes

  • This PR contains no breaking changes

Values without reserved characters are unchanged (no quoting added). The escaping only applies when a value already triggers the existing quoting path.

📋 Checklist

  • I have read the Contributing Guidelines
  • My PR title follows the conventional commit format: fix(postgrest): ...
  • I have run pnpm nx format to ensure consistent code formatting
  • I have added tests for new functionality (if applicable)
  • I have updated documentation (if applicable)

📝 Additional notes

Validated E2E against a real PostgREST server (Supabase CLI local, port 54321): the test suite inserts rows whose usernames contain reserved chars and double quotes, then filters by them — confirming the escaping survives the full pipeline (URL → PostgREST parser → PostgreSQL). 8/8 tests pass.

Local checks: jest test/filter-encoding.test.ts → 8 passed, tsc --noEmit → clean, prettier --check → clean.

The initial attempt used CSV-style "" doubling (inspired by PostgREST v7 docs); E2E testing against the real server caught this — PostgREST expects backslash escaping \" / \\, confirmed by the current docs and by direct curl tests.

Six filter methods in PostgrestFilterBuilder wrapped values with reserved
chars (,()) in double quotes but never escaped inner " or \ per
PostgREST's backslash convention. Two distinct symptoms:

1. in()/notIn(): a value containing both a reserved char and a double
   quote produced a broken quoted string — e.g. in.("a"b,c") — that
   PostgREST cannot parse. Now escapes " as \" and \ as \\, producing
   in.("a\\"b,c") which PostgREST parses correctly.

2. likeAllOf/likeAnyOf/ilikeAllOf/ilikeAnyOf(): patterns were joined with
   , without any quoting. A pattern containing a comma was silently split
   by PostgREST into multiple patterns — e.g. like(all).{%foo,bar%} became
   two patterns %foo and bar%. Now quotes patterns that contain reserved
   chars, with the same backslash escaping for " and \.

PostgREST docs:
https://postgrest.org/en/stable/references/api/url_grammar.html#reserved-characters

Validated E2E against a real PostgREST server (Supabase CLI local, port
54321): 8/8 tests pass. tsc clean, prettier clean.
@PedroHenrique0713
PedroHenrique0713 requested review from a team as code owners July 15, 2026 18:02
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f1427a3-9215-428c-9ba2-f0650d3f9f22

📥 Commits

Reviewing files that changed from the base of the PR and between 5c18b62 and 1f08eea.

📒 Files selected for processing (2)
  • packages/core/postgrest-js/src/PostgrestFilterBuilder.ts
  • packages/core/postgrest-js/test/filter-encoding.test.ts

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved filtering with in and notIn when values contain commas, parentheses, quotes, or backslashes.
    • Fixed like and ilike filters so patterns containing reserved characters are handled as complete values.
    • Prevented filter values from being incorrectly split or causing request errors.
    • Added coverage for matching and excluding records with special characters.

Walkthrough

PostgrestFilterBuilder now quotes and escapes reserved characters in like, ilike, in, and notIn filter values. Pattern arrays are cleaned before joining into PostgREST expressions, while membership values escape backslashes and double quotes when quoting is required. New authenticated end-to-end tests cover commas, quotes, parentheses, and case-sensitive or case-insensitive pattern matching.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

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.

1 participant