mirror of
https://github.com/simonw/datasette.git
synced 2026-07-08 08:34:42 +02:00
Return 400 not 500 for wrong-arity composite-PK row URLs (#2815)
Fixes #2811 Co-authored-by: Zain Dana Harper <zain@aurora-framework.dev> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
617acedd38
commit
211e70d4e1
2 changed files with 39 additions and 0 deletions
|
|
@ -134,6 +134,7 @@ from .utils import (
|
|||
from .tokens import TokenInvalid
|
||||
from .utils.asgi import (
|
||||
AsgiLifespan,
|
||||
BadRequest,
|
||||
Forbidden,
|
||||
NotFound,
|
||||
DatabaseNotFound,
|
||||
|
|
@ -2820,6 +2821,10 @@ class Datasette:
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -369,6 +369,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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue