Skip to content

Commit 0490201

Browse files
fix(postgrest): align rpc maybe_single with select (#1555)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e9f1141 commit 0490201

5 files changed

Lines changed: 130 additions & 5 deletions

File tree

src/postgrest/src/postgrest/_async/request_builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ def __init__(self, request: ReqConfig) -> None:
197197
BaseFilterRequestBuilder.__init__(self, request)
198198
AsyncSingleRequestBuilder.__init__(self, request)
199199

200+
def maybe_single(self) -> AsyncMaybeSingleRequestBuilder:
201+
"""Retrieves at most one row from the result. Result must be at most one row (e.g. using `eq` on a UNIQUE column), otherwise this will result in an error."""
202+
return AsyncMaybeSingleRequestBuilder(self.request)
203+
200204

201205
class AsyncSelectRequestBuilder(
202206
AsyncQueryRequestBuilder, BaseSelectRequestBuilder[AsyncClient]

src/postgrest/src/postgrest/_sync/request_builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ def __init__(self, request: ReqConfig) -> None:
197197
BaseFilterRequestBuilder.__init__(self, request)
198198
SyncSingleRequestBuilder.__init__(self, request)
199199

200+
def maybe_single(self) -> SyncMaybeSingleRequestBuilder:
201+
"""Retrieves at most one row from the result. Result must be at most one row (e.g. using `eq` on a UNIQUE column), otherwise this will result in an error."""
202+
return SyncMaybeSingleRequestBuilder(self.request)
203+
200204

201205
class SyncSelectRequestBuilder(
202206
SyncQueryRequestBuilder, BaseSelectRequestBuilder[Client]

src/postgrest/src/postgrest/base_request_builder.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -668,11 +668,6 @@ def single(self) -> Self:
668668
self.request.headers["Accept"] = "application/vnd.pgrst.object+json"
669669
return self
670670

671-
def maybe_single(self) -> Self:
672-
"""Retrieves at most one row from the result. Result must be at most one row (e.g. using `eq` on a UNIQUE column), otherwise this will result in an error."""
673-
self.request.headers["Accept"] = "application/vnd.pgrst.object+json"
674-
return self
675-
676671
def csv(self) -> Self:
677672
"""Specify that the query must retrieve data as a single CSV string."""
678673
self.request.headers["Accept"] = "text/csv"

src/postgrest/tests/_async/test_filter_request_builder_integration.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import pytest
2+
13
from postgrest import CountMethod
4+
from postgrest.exceptions import APIError
25

36
from .client import rest_client, rest_client_httpx
47

@@ -76,6 +79,23 @@ async def test_no_match_maybe_single():
7679
assert res is None
7780

7881

82+
async def test_maybe_single_multiple_rows():
83+
with pytest.raises(APIError) as exc_info:
84+
await (
85+
rest_client()
86+
.from_("countries")
87+
.select("country_name, iso")
88+
.lte("numcode", 8)
89+
.gte("numcode", 4)
90+
.maybe_single()
91+
.execute()
92+
)
93+
94+
assert exc_info.value.code == "406"
95+
assert exc_info.value.message == "Cannot coerce the result to a single JSON object"
96+
assert exc_info.value.details == "The result contains more than one row."
97+
98+
7999
async def test_equals():
80100
res = (
81101
await rest_client()
@@ -500,6 +520,47 @@ async def test_rpc_with_single():
500520
assert res.data == {"nicename": "Albania", "country_name": "ALBANIA", "iso": "AL"}
501521

502522

523+
async def test_rpc_with_maybe_single():
524+
res = (
525+
await rest_client()
526+
.rpc("list_stored_countries", {})
527+
.select("nicename, country_name, iso")
528+
.eq("nicename", "Albania")
529+
.maybe_single()
530+
.execute()
531+
)
532+
533+
assert res.data == {"nicename": "Albania", "country_name": "ALBANIA", "iso": "AL"}
534+
535+
536+
async def test_rpc_with_maybe_single_no_match():
537+
res = (
538+
await rest_client()
539+
.rpc("list_stored_countries", {})
540+
.select("nicename, country_name, iso")
541+
.eq("nicename", "Wonderland")
542+
.maybe_single()
543+
.execute()
544+
)
545+
546+
assert res is None
547+
548+
549+
async def test_rpc_with_maybe_single_multiple_rows():
550+
with pytest.raises(APIError) as exc_info:
551+
await (
552+
rest_client()
553+
.rpc("list_stored_countries", {})
554+
.select("nicename, country_name, iso")
555+
.maybe_single()
556+
.execute()
557+
)
558+
559+
assert exc_info.value.code == "406"
560+
assert exc_info.value.message == "Cannot coerce the result to a single JSON object"
561+
assert exc_info.value.details == "The result contains more than one row."
562+
563+
503564
async def test_rpc_with_limit():
504565
res = (
505566
await rest_client()

src/postgrest/tests/_sync/test_filter_request_builder_integration.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import pytest
2+
13
from postgrest import CountMethod
4+
from postgrest.exceptions import APIError
25

36
from .client import rest_client, rest_client_httpx
47

@@ -76,6 +79,23 @@ def test_no_match_maybe_single():
7679
assert res is None
7780

7881

82+
def test_maybe_single_multiple_rows():
83+
with pytest.raises(APIError) as exc_info:
84+
(
85+
rest_client()
86+
.from_("countries")
87+
.select("country_name, iso")
88+
.lte("numcode", 8)
89+
.gte("numcode", 4)
90+
.maybe_single()
91+
.execute()
92+
)
93+
94+
assert exc_info.value.code == "406"
95+
assert exc_info.value.message == "Cannot coerce the result to a single JSON object"
96+
assert exc_info.value.details == "The result contains more than one row."
97+
98+
7999
def test_equals():
80100
res = (
81101
rest_client()
@@ -493,6 +513,47 @@ def test_rpc_with_single():
493513
assert res.data == {"nicename": "Albania", "country_name": "ALBANIA", "iso": "AL"}
494514

495515

516+
def test_rpc_with_maybe_single():
517+
res = (
518+
rest_client()
519+
.rpc("list_stored_countries", {})
520+
.select("nicename, country_name, iso")
521+
.eq("nicename", "Albania")
522+
.maybe_single()
523+
.execute()
524+
)
525+
526+
assert res.data == {"nicename": "Albania", "country_name": "ALBANIA", "iso": "AL"}
527+
528+
529+
def test_rpc_with_maybe_single_no_match():
530+
res = (
531+
rest_client()
532+
.rpc("list_stored_countries", {})
533+
.select("nicename, country_name, iso")
534+
.eq("nicename", "Wonderland")
535+
.maybe_single()
536+
.execute()
537+
)
538+
539+
assert res is None
540+
541+
542+
def test_rpc_with_maybe_single_multiple_rows():
543+
with pytest.raises(APIError) as exc_info:
544+
(
545+
rest_client()
546+
.rpc("list_stored_countries", {})
547+
.select("nicename, country_name, iso")
548+
.maybe_single()
549+
.execute()
550+
)
551+
552+
assert exc_info.value.code == "406"
553+
assert exc_info.value.message == "Cannot coerce the result to a single JSON object"
554+
assert exc_info.value.details == "The result contains more than one row."
555+
556+
496557
def test_rpc_with_limit():
497558
res = (
498559
rest_client()

0 commit comments

Comments
 (0)