refactor: remove original_dbname/original_resource from forked_from, resolve from parent

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-03-27 15:01:50 +01:00
parent e0654e3b9a
commit f6978bbca5
5 changed files with 39 additions and 32 deletions
@@ -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<PgDatabase> {
pub(crate) async fn resolve_pg_source(db: &DB, w_id: &str, source: &str) -> Result<PgDatabase> {
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:") {
@@ -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::<PgDatabase>(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 });
-7
View File
@@ -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
@@ -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<String>,
/// Original resource value before fork (resource datatables only)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub original_resource: Option<serde_json::Value>,
/// Schema snapshot at fork time
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schema: Option<serde_json::Value>,
@@ -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<string, any> | undefined
try {
const res = await ResourceService.getResource({
workspace: forkWorkspaceId,
path: resourcePath
})
originalResource = (res.value as Record<string, any>) ?? {}
const updatedValue = { ...originalResource, dbname: job._newDbName }
const value = (res.value as Record<string, any>) ?? {}
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 ?? {}
}
}