Skip to content

fix(postgrest): escape array-literal elements in cs/cd/contains/contained_by/overlaps - #1593

Open
eeshsaxena wants to merge 1 commit into
supabase:mainfrom
eeshsaxena:fix/postgrest-array-literal-escaping
Open

fix(postgrest): escape array-literal elements in cs/cd/contains/contained_by/overlaps#1593
eeshsaxena wants to merge 1 commit into
supabase:mainfrom
eeshsaxena:fix/postgrest-array-literal-escaping

Conversation

@eeshsaxena

Copy link
Copy Markdown

What kind of change does this PR introduce?

Bug fix.

Fixes #1592.

What is the current behavior?

cs, cd, contains, contained_by, and overlaps build their PostgreSQL array literal {...} from the raw str() of each element with no escaping. Any element containing the comma delimiter (or a brace, double quote, backslash, whitespace, or the word NULL, or an empty string) is emitted bare and mis-parsed by PostgREST. The comma case is the clearest:

client.from_("things").select("*").contains("tags", ["a,b", "c"])
# sends: tags=cs.{a,b,c}   ->  PostgREST reads THREE elements, not the two passed

in_() already quotes correctly via sanitize_param; the {...} operators did not. The suite even encoded the corruption: test_contained_by_mixed_items asserted {a,["b", "c"]}, itself a malformed literal whose inner comma splits the second element.

What is the new behavior?

Added sanitize_array_element / sanitize_array_param in utils.py that quote each element following PostgreSQL array-literal rules (quote when empty, NULL, or containing a brace / comma / double quote / backslash / whitespace; backslash-escape embedded quotes and backslashes), and routed the five array operators through it.

contains("tags", ["a,b", "c"])   ->   tags=cs.{"a,b",c}   # two elements, as passed

Simple values are unchanged (["a", "b"] -> {a,b}, [1, 2, 3] -> {1,2,3}; plain-string and dict/JSON inputs untouched), so this is backward-compatible for the common case. test_contained_by_mixed_items is updated to the corrected literal, with a comment noting the old output was corrupted.

Additional context

  • Tests added for the comma, brace, embedded-quote, whitespace, empty and NULL cases across cs/cd/contains/contained_by/overlaps, in both the _async source and the generated _sync mirror. Full postgrest unit suite, ruff, and mypy pass locally.
  • Scope is intentionally limited to the {...} array operators. in_() uses a different (parenthesized) syntax and its own quoting, left untouched.

…ined_by/overlaps

The array-membership filters built their PostgreSQL array literal {...} from
the raw str() of each element with no escaping, so an element containing the
comma delimiter (or a brace, double quote, backslash, whitespace, or the word
NULL, or an empty string) was emitted bare and mis-parsed by PostgREST. A
value with a comma silently split into extra elements: contains("tags",
["a,b", "c"]) sent cs.{a,b,c}, which PostgREST reads as three elements.

Add sanitize_array_param/sanitize_array_element that quote each element
following PostgreSQL array-literal rules, and use them from the five array
operators. in_() already quoted via sanitize_param; this brings the {...}
operators in line. Simple values are unchanged.

Fixes supabase#1592
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.

Array filters (cs/cd/contains/contained_by/overlaps) don't escape elements, corrupting values containing the comma delimiter

1 participant