libredb / libredb/libredb-studio

Inline grid edit builds its UPDATE from the tab name, so it can write to the wrong table

Open
#881 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.