fix(postgrest): escape reserved characters in array filter values - #1126
fix(postgrest): escape reserved characters in array filter values#1126AndroidPoet wants to merge 2 commits into
Conversation
Array's PostgrestFilterValue.rawValue joined elements with commas inside
{...} without escaping, so an element containing a comma, brace, quote,
backslash, surrounding whitespace, or the literal NULL corrupted the array
literal — e.g. ["a,b"] became {a,b}, which PostgREST reads as two separate
values. Quote and backslash-escape elements that need it, mirroring the
existing in() filter escaping. Nested array literals ({...}) pass through
unchanged so arrays of arrays keep working.
Port the array-literal escaping tests to Swift Testing, which main now requires.
📝 WalkthroughSummary by CodeRabbit
WalkthroughPostgREST array filter serialization now escapes elements containing reserved characters, quotes, backslashes, whitespace, empty values, or 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. Comment |
There was a problem hiding this comment.
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 `@Sources/PostgREST/PostgrestFilterValue.swift`:
- Around line 71-76: Update the array-element encoding flow around the map
closure to use a typed array-member encoding contract or marker before
converting values to raw strings; do not infer nested-array or NULL semantics
from rawValue alone. Ensure scalar brace-delimited strings remain escaped as
scalar elements, while Optional<Int>.none and AnyJSON.null encode as actual NULL
array members, and add regressions covering both cases.
🪄 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: CHILL
Plan: Pro Plus
Run ID: bd8c80a7-7c02-4206-9012-b9b0477f62a9
📒 Files selected for processing (3)
Sources/Helpers/PostgRESTFilterValue.swiftSources/PostgREST/PostgrestFilterValue.swiftTests/PostgRESTTests/PostgrestFilterValueTests.swift
| let elements = map { element -> String in | ||
| let raw = element.rawValue | ||
| if raw.hasPrefix("{"), raw.hasSuffix("}") { | ||
| return raw | ||
| } | ||
| return escapePostgRESTArrayLiteralElement(raw) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Do not infer array-member semantics from rawValue.
A scalar ["{a,b}"] becomes {{a,b}} (a nested array), while [Optional<Int>.none] and [AnyJSON.null] become {"NULL"} (a string rather than a NULL element). Add a typed array-member encoding contract/marker before stringification, and regressions for both cases.
🤖 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 `@Sources/PostgREST/PostgrestFilterValue.swift` around lines 71 - 76, Update
the array-element encoding flow around the map closure to use a typed
array-member encoding contract or marker before converting values to raw
strings; do not infer nested-array or NULL semantics from rawValue alone. Ensure
scalar brace-delimited strings remain escaped as scalar elements, while
Optional<Int>.none and AnyJSON.null encode as actual NULL array members, and add
regressions covering both cases.
What
Array'sPostgrestFilterValueconformance builds a PostgreSQL array literal by joining the elements' raw values with commas, with no escaping:So any element that contains a character with structural meaning inside an array literal is silently corrupted. For example, filtering an array/
text[]column with["a,b"]produces{a,b}, which PostgREST parses as the two valuesaandbinstead of the single valuea,b. The same happens for elements containing{,},",\, surrounding whitespace, an empty string, or the literalNULL.This is the array-literal counterpart of the
in()filter escaping fixed in #1061.Fix
Quote and backslash-escape each element that needs it, reusing the same escaping approach as the existing
in()filter helper (added alongside it inHelpers):Elements that are already safe (
admin,9:00, numbers) are emitted verbatim, so existing valid usage is unchanged.Note on nested arrays
An element whose raw value both starts with
{and ends with}is treated as an already-formed nested array literal and passed through unquoted, so arrays of arrays ([[1, 2], [3, 4]]->{{1,2},{3,4}}) keep working. The trade-off is that aStringelement that is literally{...}will not be quoted — this is inherent to the string-basedrawValuedesign, which can't distinguish a nested[Int](whoserawValueis{1,2}) from the string"{1,2}". Elements with partial braces (e.g.a{b) are still quoted.Testing
Added tests covering reserved characters (comma, brace), quote/backslash escaping, whitespace/empty/
NULL, nested array literals, theAnyJSON.arraypath, and that safe/numeric values stay unquoted. They fail onmainand pass with this change.swift test --filter PostgRESTTests— 105 passing (existingin()and snapshot tests unchanged)./scripts/format.sh— clean