Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions controlplane/admin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ func managedWarehouseUpsertColumns() []string {
// DB column name. Mismatching this against the actual column makes
// the ON CONFLICT … DO UPDATE clause throw 42703.
"duck_lake_version",
"duckling_name",
"warehouse_database_endpoint",
"warehouse_database_port",
"metadata_store_kind",
Expand Down
19 changes: 13 additions & 6 deletions controlplane/admin/api_postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ func TestUpsertManagedWarehousePreservesCreatedAt(t *testing.T) {

createdAt := time.Date(2024, time.January, 2, 3, 4, 5, 0, time.UTC)
original := &configstore.ManagedWarehouse{
OrgID: "analytics",
State: configstore.ManagedWarehouseStatePending,
CreatedAt: createdAt,
UpdatedAt: createdAt,
OrgID: "analytics",
DucklingName: "analytics",
State: configstore.ManagedWarehouseStatePending,
CreatedAt: createdAt,
UpdatedAt: createdAt,
WarehouseDatabase: configstore.ManagedWarehouseDatabase{
Endpoint: "analytics-wh.cluster.example",
},
Expand All @@ -124,8 +125,11 @@ func TestUpsertManagedWarehousePreservesCreatedAt(t *testing.T) {

replacementCreatedAt := time.Date(2030, time.January, 2, 3, 4, 5, 0, time.UTC)
stored, ok, err := apiStore.UpsertManagedWarehouse("analytics", &configstore.ManagedWarehouse{
CreatedAt: replacementCreatedAt,
UpdatedAt: replacementCreatedAt,
CreatedAt: replacementCreatedAt,
UpdatedAt: replacementCreatedAt,
// Changed vs the seeded row: the ON CONFLICT assignment-column list
// must carry duckling_name or this change is silently dropped.
DucklingName: "analytics-renamed",
State: configstore.ManagedWarehouseStateReady,
StatusMessage: "ready",
WarehouseDatabase: configstore.ManagedWarehouseDatabase{
Expand All @@ -152,6 +156,9 @@ func TestUpsertManagedWarehousePreservesCreatedAt(t *testing.T) {
if stored.MetadataStore.DatabaseName != "ducklake_metadata" {
t.Fatalf("expected updated metadata db name, got %q", stored.MetadataStore.DatabaseName)
}
if stored.DucklingName != "analytics-renamed" {
t.Fatalf("expected updated duckling_name analytics-renamed, got %q", stored.DucklingName)
}
}

func TestMutateManagedWarehouseSerializesConcurrentWriters(t *testing.T) {
Expand Down
19 changes: 14 additions & 5 deletions controlplane/admin/ui/src/pages/OrgDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,12 +331,21 @@ function WarehousePanel({
setMsg({ kind: "err", text: "Duckling name is required." });
return;
}
// Send only fields that actually changed: the PUT is a merge-patch and
// the audit log records the body's keys as "changed", so carrying
// untouched fields would log phantom changes.
const body: Partial<ManagedWarehouse> = {};
if (image !== (data?.image ?? "")) body.image = image;
if (version !== (data?.ducklake_version ?? "")) body.ducklake_version = version;
if (ducklingNameInput !== (data?.duckling_name || ducklingName(orgId))) {
body.duckling_name = ducklingNameInput;
}
if (Object.keys(body).length === 0) {
setMsg({ kind: "ok", text: "No changes." });
return;
}
try {
await update.mutateAsync({
image,
ducklake_version: version,
duckling_name: ducklingNameInput,
});
await update.mutateAsync(body);
setMsg({ kind: "ok", text: "Saved." });
} catch (e) {
setMsg({ kind: "err", text: e instanceof Error ? e.message : "Save failed" });
Expand Down
Loading