From f6978bbca5dfd265344a5813db9b1a20fd3e4c42 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Fri, 27 Mar 2026 15:01:50 +0100 Subject: [PATCH] refactor: remove original_dbname/original_resource from forked_from, resolve from parent Co-Authored-By: Claude Opus 4.5 --- .../windmill-api-workspaces/src/workspaces.rs | 2 +- .../src/workspaces_extra.rs | 46 ++++++++++++++----- backend/windmill-api/openapi.yaml | 7 --- backend/windmill-common/src/workspaces.rs | 6 --- .../CreateWorkspaceInner.svelte | 10 ++-- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/backend/windmill-api-workspaces/src/workspaces.rs b/backend/windmill-api-workspaces/src/workspaces.rs index ff44398958..2387f63895 100644 --- a/backend/windmill-api-workspaces/src/workspaces.rs +++ b/backend/windmill-api-workspaces/src/workspaces.rs @@ -1348,7 +1348,7 @@ async fn get_datatable_schema(db: &DB, w_id: &str, datatable_name: &str) -> Resu /// Resolve a source string to PgDatabase credentials. /// Supports `datatable://name` (resolves via workspace datatable config) /// and `$res:path` (resolves via resource table). -async fn resolve_pg_source(db: &DB, w_id: &str, source: &str) -> Result { +pub(crate) async fn resolve_pg_source(db: &DB, w_id: &str, source: &str) -> Result { let db_resource = if let Some(name) = source.strip_prefix("datatable://") { get_datatable_resource_from_db_unchecked(db, w_id, name).await? } else if source.starts_with("$res:") { diff --git a/backend/windmill-api-workspaces/src/workspaces_extra.rs b/backend/windmill-api-workspaces/src/workspaces_extra.rs index f350bdd730..86ccbdc084 100644 --- a/backend/windmill-api-workspaces/src/workspaces_extra.rs +++ b/backend/windmill-api-workspaces/src/workspaces_extra.rs @@ -26,7 +26,6 @@ use windmill_common::{ error::{Error, Result}, utils::require_admin, workspaces::DataTable, - PgDatabase, }; use windmill_queue::schedule::{get_schedule_opt, push_scheduled_job}; @@ -871,6 +870,24 @@ async fn drop_forked_datatable_databases( w_id: &str, datatable_names: &[String], ) { + // Get parent workspace ID + let parent_w_id = match sqlx::query_scalar!( + "SELECT parent_workspace_id FROM workspace WHERE id = $1", + w_id + ) + .fetch_optional(&mut **tx) + .await + { + Ok(Some(Some(parent))) => parent, + _ => { + tracing::error!( + "Cannot drop forked databases: no parent workspace for '{}'", + w_id + ); + return; + } + }; + let datatable_config = match sqlx::query_scalar!( "SELECT datatable->'datatables' FROM workspace_settings WHERE workspace_id = $1", w_id @@ -887,12 +904,8 @@ async fn drop_forked_datatable_databases( for dt_name in datatable_names { let dt = match datatables.get(dt_name) { - Some(dt) => dt, - None => continue, - }; - let forked_from = match &dt.forked_from { - Some(v) => v, - None => continue, + Some(dt) if dt.forked_from.is_some() => dt, + _ => continue, }; let db_to_drop = &dt.database.resource_path; @@ -902,15 +915,26 @@ async fn drop_forked_datatable_databases( if let Err(e) = windmill_common::drop_custom_instance_database(db, db_to_drop).await { tracing::error!("Failed to drop instance database '{}': {}", db_to_drop, e); } - } else if let Some(original_resource) = &forked_from.original_resource { - // Connect to the original resource's database to run DROP on the forked db - let pg = match serde_json::from_value::(original_resource.clone()) { + } else { + // Resource DB: resolve the resource from the parent workspace to get connection info + let pg = match crate::workspaces::resolve_pg_source( + db, + &parent_w_id, + &format!("datatable://{}", dt_name), + ) + .await + { Ok(pg) => pg, Err(e) => { - tracing::error!("Failed to parse original_resource for '{}': {}", dt_name, e); + tracing::error!( + "Failed to resolve parent resource for datatable '{}': {}", + dt_name, + e + ); continue; } }; + // Connect to the parent's database and DROP the forked one match pg.connect().await { Ok((client, connection)) => { let join_handle = tokio::spawn(async move { connection.await }); diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index f7fa19a73d..e59a2a52b8 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -23859,13 +23859,6 @@ components: type: object description: Fork origin info with schema snapshot properties: - original_dbname: - type: string - description: Original instance database name (instance datatables only) - original_resource: - type: object - description: Original resource value before fork (resource datatables only) - additionalProperties: true schema: type: object description: Schema snapshot at fork time diff --git a/backend/windmill-common/src/workspaces.rs b/backend/windmill-common/src/workspaces.rs index 947664b50a..b7b4ca3b63 100644 --- a/backend/windmill-common/src/workspaces.rs +++ b/backend/windmill-common/src/workspaces.rs @@ -412,12 +412,6 @@ pub struct DataTable { #[derive(Deserialize, Serialize, Debug)] pub struct DataTableForkedFrom { - /// Original instance database name (instance datatables only) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub original_dbname: Option, - /// Original resource value before fork (resource datatables only) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub original_resource: Option, /// Schema snapshot at fork time #[serde(default, skip_serializing_if = "Option::is_none")] pub schema: Option, diff --git a/frontend/src/lib/components/workspaceSettings/CreateWorkspaceInner.svelte b/frontend/src/lib/components/workspaceSettings/CreateWorkspaceInner.svelte index 3b2e72fb3f..6bdce2ab47 100644 --- a/frontend/src/lib/components/workspaceSettings/CreateWorkspaceInner.svelte +++ b/frontend/src/lib/components/workspaceSettings/CreateWorkspaceInner.svelte @@ -243,24 +243,21 @@ if (job._isInstance) { // Instance: update resource_path and set forked_from with schema snapshot if (datatableConfig.datatables[job.name]) { - const originalDbname = datatableConfig.datatables[job.name].database.resource_path datatableConfig.datatables[job.name].database.resource_path = job._newDbName datatableConfig.datatables[job.name].forked_from = { - original_dbname: originalDbname, schema: job._schema ?? {} } } } else { // Resource: update the resource's dbname and set non_diffable const resourcePath = job._resourcePath - let originalResource: Record | undefined try { const res = await ResourceService.getResource({ workspace: forkWorkspaceId, path: resourcePath }) - originalResource = (res.value as Record) ?? {} - const updatedValue = { ...originalResource, dbname: job._newDbName } + const value = (res.value as Record) ?? {} + const updatedValue = { ...value, dbname: job._newDbName } await ResourceService.updateResource({ workspace: forkWorkspaceId, path: resourcePath, @@ -273,10 +270,9 @@ console.error(`Failed to update resource ${resourcePath}:`, e) } - // Also set forked_from on the datatable config + // Set forked_from on the datatable config if (datatableConfig.datatables[job.name]) { datatableConfig.datatables[job.name].forked_from = { - original_resource: originalResource ?? {}, schema: job._schema ?? {} } }