libredb / libredb/libredb-studio
Inline grid edit builds its UPDATE from the tab name, so it can write to the wrong table
- Dominant language
- TypeScript
- Stars
- 726
- Forks
- 119
- Avg merge
- 7h 47m
- Merged PRs (30d)
- 265
Description
## What
Inline grid editing builds its UPDATE from the editor tab's name, not from the query that produced the rows. When the tab name and the queried table differ, the UPDATE is sent to the wrong table.
## Where
src/hooks/use-inline-editing.ts:72-74
```ts
// Detect table name from current tab or query
const tableName =
currentTab.name.replace(/^Query[: ]*/, "") || currentTab.query.match(/FROM\s+(\S+)/i)?.[1] || "table_name";
```
The tab name is read first. The query is only parsed when the tab name is empty. The statement is built at line 130:
```ts
sql: `UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE ${quote(pkColumn)} = ${pkVal}`
```
The only guard is `isBareIdentifier(tableName)` at line 81, which checks that the name is a usable identifier. It does not check that the name is the table the rows came from.
The code comment at line 76 already states the problem: "The table name is a GUESS (a tab title, or the first word after FROM)".
## How to reproduce
1. Open a tab and rename it `orders`.
2. Run `SELECT * FROM products` in that tab.
3. Turn on EDIT, change a cell, apply.
Observed: the client sends `UPDATE orders SET "sku" = $1 WHERE "product_id" = $2`.
In this case it failed, because `orders` has no `product_id` column. Had the tab name matched a table that does carry the same primary key column name, the write would have landed on the wrong table with no error.
## Why this matters
A tab name is free text. It is not tied to the query, it survives when the query is replaced, and a user editing a result has no reason to expect it to select the write target. The failure mode is a silent write to the wrong table, which is data loss in the general case.
## Suggested direction
Derive the target from the result rather than the tab title. The query that produced the rows is on the tab, and the result carries field metadata; either is a stronger source than the title. Where the target cannot be determined with confidence (joins, computed columns, subqueries), refuse to build the statement and say so, the way line 81 already refuses an unusable identifier.
## Related
The failure is also silent in the UI, and the grid is not refreshed after a successful save. Filed separately.
Contributor guide
Research direction
Start in src/hooks/use-inline-editing.ts at lines 72-81 and trace how the edited result and field metadata reach the UPDATE built around line 130. Confirm the target comes from the query or result rather than the tab title, and identify how uncertain targets such as joins or computed columns are rejected. Done means a renamed tab cannot redirect the write, and indeterminate targets do not produce an UPDATE.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- sql, typescript
- Domain
- databases, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100