fix(datatables): a workspace-id change leaves one owner for each login

The rename copies the whole config, so the archived id and the live one both
named the same generated logins — and deleting the archive planned drops for
logins the renamed workspace was still using, destroying their grants.

The archived copy stops naming them: it is kept for reference, and a
reference needs no credentials. The renamed workspace keeps working, since
resolution uses the stored `pg_rolename` rather than the generated one, and
its next permissions save finds the stored name no longer matches what this
workspace id generates and renames the login to match — under the ownership
proof that rename already carries.

Also: a pending row action now has to name the same data table the manager
is open on, not merely fail to differ from it. `undefined` — a plain
postgres resource, or a DuckLake — is somewhere else too, and a matching
schema and table name there would have run it. Both drawers drop what is
pending when they close.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
This commit is contained in:
Diego Imbert
2026-09-05 21:06:36 +02:00
co-authored by Claude Opus 5
parent 6eb63fa58b
commit af0477c16f
4 changed files with 24 additions and 1 deletions
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE workspace_settings\n SET datatable = jsonb_set(datatable, '{datatables}', COALESCE((\n SELECT jsonb_object_agg(key, value - 'permissions')\n FROM jsonb_each(datatable->'datatables')\n ), '{}'::jsonb))\n WHERE workspace_id = $1 AND jsonb_typeof(datatable->'datatables') = 'object'",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "f42f40af69449bdd47103c63139318a1d1a61af5ec7032353acd5e89027ae8dd"
}
+3 -1
View File
@@ -458,7 +458,9 @@
// Landed somewhere else: the target may have failed to load, or the user
// may have moved on. Either way this action was asked for on another
// database, and dropping a schema is not a thing to do by approximation.
if (currentDatatable !== undefined && req.datatable !== currentDatatable) {
// `undefined` is somewhere else too — a plain postgres resource or a
// DuckLake — so the two must be equal, not merely not-known-to-differ.
if (req.datatable !== currentDatatable) {
pendingAction = undefined
return
}
@@ -131,6 +131,10 @@
if (!open) {
expand = false
uriState.closeDrawer()
// An action asked for on one data table must not be waiting when the
// drawer is next opened on another database — or on no data table at
// all, where nothing would recognise it as foreign.
pendingAction = undefined
}
})
@@ -160,6 +160,9 @@
export function closeDrawer() {
open = false
dbManagerContent?.clearReplResult()
// Same reason as its sibling: an action outlives the data table it was
// asked for otherwise.
pendingAction = undefined
}
function handleAddTables() {