Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions datasette/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
)
from .utils.asgi import (
AsgiLifespan,
BadRequest,
Forbidden,
NotFound,
DatabaseNotFound,
Expand Down Expand Up @@ -2789,6 +2790,10 @@ async def resolve_row(self, request):
db, table_name, _ = await self.resolve_table(request)
pk_values = urlsafe_components(request.url_vars["pks"])
sql, params, pks = await row_sql_params_pks(db, table_name, pk_values)
if len(pk_values) != len(pks):
raise BadRequest(
"URL row identifier does not match the primary key for this table"
)
results = await db.execute(sql, params, truncate=True)
row = results.first()
if row is None:
Expand Down
34 changes: 34 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,40 @@ async def test_row(ds_client):
assert response.json()["rows"] == [{"id": 1, "content": "hello"}]


@pytest.mark.asyncio
@pytest.mark.parametrize("suffix", ("", ".json"))
@pytest.mark.parametrize(
"row_path",
(
"a", # too few components for a two-column primary key
"a,b,c", # too many components for a two-column primary key
),
)
async def test_row_pk_arity_mismatch_returns_400(ds_client, row_path, suffix):
# A row URL with the wrong number of comma-separated primary key
# components used to raise an uncaught sqlite3.ProgrammingError (HTTP 500)
# because the SQL had one bind placeholder per PK column but params were
# only bound for the supplied components. It should be a 400 instead,
# mirroring the existing guard in datasette/views/table.py.
response = await ds_client.get(
"/fixtures/compound_primary_key/{}{}".format(row_path, suffix)
)
assert response.status_code == 400
if suffix == ".json":
assert response.json()["ok"] is False
assert response.json()["status"] == 400


@pytest.mark.asyncio
async def test_row_compound_pk_correct_arity(ds_client):
# The valid two-component URL still resolves the row.
response = await ds_client.get(
"/fixtures/compound_primary_key/a,b.json?_shape=objects"
)
assert response.status_code == 200
assert response.json()["rows"] == [{"pk1": "a", "pk2": "b", "content": "c"}]


@pytest.mark.asyncio
async def test_row_strange_table_name(ds_client):
response = await ds_client.get(
Expand Down
Loading