Skip to content

fix(editor): write an inline cell edit to the table its rows came from, and ten more reported defects - #954

Open
yusuf-gundogdu wants to merge 10 commits into
mainfrom
fix/inline-edit-target
Open

yusuf-gundogdu wants to merge 10 commits into
mainfrom
fix/inline-edit-target

Conversation

@yusuf-gundogdu

Copy link
Copy Markdown
Member

Eleven reported defects, one commit each. Every fix carries a test that was proven to fail without it, and the whole suite plus the 100% coverage gate is green locally.

The one that matters most

An inline cell edit chose its table from the tab title, falling back to a regex over the editor buffer. Both outlive the rows on screen: rename a tab, or type a new query without running it, and the UPDATE went to a different table than the one being edited. The write landed there.

A tab now records the statement that produced the result it is showing, and the edit resolves its table from that statement, read under the connection's own dialect by src/lib/sql/update-target.ts. A query whose rows have no single base table is refused with a reason instead of guessed at.

That reader is where most of the review effort should go. It is built on the folder's existing span scanner rather than a second hand-rolled one, and its refusals are deliberately wide: it turned out that nine separate constructs read as one table under one engine and another under the next, and each one of those was a measured wrong-table write before it was a refusal. The cases are pinned in tests/unit/sql/update-target.test.ts with the engine each was measured on.

Three further defects on that same path are fixed in the same commit, because they are only reachable through it:

  • the grid passed row.index, the index into the filtered array, so a column filter built the UPDATE from one row's key and another row's value
  • executeQuery returned void, so a refused UPDATE was reported as saved and its edit discarded with no way to retry
  • the post-apply refresh re-ran the last UPDATE rather than the SELECT, leaving an empty grid

Closing

Closes #881
Closes #882
Closes #883
Closes #856
Closes #880
Closes #884

Five more were closed as not-planned but are real and are fixed here, so they cannot be Closes lines: #919 (icon-only buttons with no accessible name), #943 (the Koyeb button published a working JWT secret and admin password), #909 (/health and /api/health now answer, so a probe pointed at the obvious path does not report a serving container unhealthy), #913 (checksums beside the snap packages and the SBOM), #908 (no JWT secret in production now refuses to boot instead of booting healthy and answering every login 503).

What I deliberately did not fix

pendingChanges is not scoped to a tab. Edit a cell in tab A, switch to tab B, apply, and the edit is attributed to B's rows. That is reachable on main today with none of this branch's changes, it is a state-shape problem rather than a fix on this path, and a partial guard I tried made it worse, so it is named rather than patched. Happy to open it as its own issue.

Notes on #880 and #884

Both had an outside contributor assigned. The 0.16.0 follow-up needed them sooner than expected, so I picked them up rather than leave them queued, and said so on each issue. That is a scheduling call, not a comment on their work.

An inline cell edit picked its table from the tab title, falling back to a
regex over the editor buffer. Both outlive the rows on screen: renaming a tab,
or typing a new query without running it, pointed the UPDATE at a different
table than the one being edited and the write landed there.

A tab now records the statement that produced the result it is showing
(`resultQuery`), and the edit resolves its table from that statement, read
under the connection's own dialect by a new reader in `src/lib/sql/`. A query
whose rows have no single base table - a join, a comma-separated FROM, a
subquery in FROM or in the select list, a CTE, a set operation - is refused
with a reason instead of guessed at. The reader uses the folder's existing
span scanner, so quoting and comment rules follow the dialect rather than a
second hand-rolled one.

Three further defects on the same path:

- The grid passed `row.index` to the update. That is the index into the
  filtered array, so with a column filter active the UPDATE was built from one
  row's primary key and another row's value.
- `executeQuery` returned void, so the apply loop counted every statement as
  applied. A refused UPDATE was reported as saved and its edit discarded, with
  no way to retry it.
- The post-apply refresh re-ran the last UPDATE rather than the SELECT, so the
  grid went empty after a successful save.

Closes #881
Closes #882
Closes #883
Recharts writes the radial background and the tooltip surface as inline
styles, so a hardcoded white track survives the theme switch. In the light
theme the gauge's unfilled arc was white on white and the reading was
impossible to place.

Both gauges now take the track colour from the active chart theme, the same
source the rest of the chart already uses.

Closes #856
…lbars

An icon-only button with no text and no label reaches a screen reader as
"button", which is all a keyboard-only user gets to tell them apart. The
sidebar's collapse, refresh, settings and connection controls, the schema
explorer, and the toolbars above the results grid were all in that state.

Each now carries an accessible name and a matching tooltip, so the pointer and
the screen reader read the same thing.

Refs #919
…word

The one-click Koyeb deploy passed a real JWT_SECRET and a real admin password
as default values. Every deployment made from that button shared them, and
both are readable by anyone who opens the README.

They are placeholders now, and the JWT_SECRET placeholder is deliberately
shorter than the 32-character minimum the server enforces, so a deployment
that leaves it as it stands refuses to boot rather than running on a published
secret. A test pins both, so a future edit cannot quietly put a usable value
back.

Refs #943
The liveness path is /api/db/health, which is not where an operator looks
first. Our own Azure guide, install.sh and the Helm chart each hard-code it
precisely because the obvious paths returned the login redirect, and a probe
pointed at /health reported the container unhealthy while it was serving.

Both paths now answer with the same fixed body as /api/db/health, from one
shared handler, and both are on the proxy's public list. They depend on
nothing and disclose nothing beyond liveness.

Refs #909
Every other release artifact ships a .sha256 sidecar. The two snap packages
and the CycloneDX SBOM did not, so the one file a downstream packager is meant
to verify a build against had nothing to verify it with.

The release workflow now writes a sidecar for each, and the SBOM sidecar joins
the required-asset list so a release missing it fails rather than ships.

Refs #913
With AUTH_BOOTSTRAP off and no JWT_SECRET set, the server started, reported
itself healthy, and answered every single login with a 503. The health probe
said the deployment was fine while nobody could get in, which is the hardest
shape of this to diagnose.

In production, that combination now prints what is missing and exits instead
of starting. A container that cannot serve a login says so at boot.

Refs #908
The Data Profiler drew the same hash icon beside every column heading,
including text and date columns, so the one signal telling column types apart
said "number" for all of them. The type is already named in the line below.

Closes #880
Schema Diff read the schema held in memory from the last explorer load. Run a
DDL statement and diff, and it compared the snapshot against a copy of the
database as it was before the change: no differences, which is the one answer
that tool must never give wrongly.

It now reads the schema from the connection when it opens, and uses that for
both the diff's current side and any new snapshot.

Closes #884
/health is a route at the app root rather than under src/app/api/, which is
the one exception to that rule, and the public list in src/proxy.ts now has
two more entries. Both are facts the next person reading this file needs.
@codecov

codecov Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud

Copy link
Copy Markdown

@cevheri
cevheri self-requested a review September 18, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment