diff --git a/backend/windmill-worker/src/pg_executor.rs b/backend/windmill-worker/src/pg_executor.rs index e6cfff84b1..4ec70712c5 100644 --- a/backend/windmill-worker/src/pg_executor.rs +++ b/backend/windmill-worker/src/pg_executor.rs @@ -2005,6 +2005,29 @@ impl FromSql<'_> for StringCollector { mod tests { use super::*; + #[test] + fn test_statement_requires_no_transaction() { + // CONCURRENTLY index DDL must bypass the implicit transaction. + assert!(statement_requires_no_transaction( + "CREATE INDEX CONCURRENTLY idx ON t (a)" + )); + assert!(statement_requires_no_transaction( + "create unique index concurrently idx on t (a)" + )); + assert!(statement_requires_no_transaction( + "DROP INDEX CONCURRENTLY IF EXISTS idx" + )); + assert!(statement_requires_no_transaction( + " REINDEX INDEX CONCURRENTLY idx" + )); + // Plain DDL and unrelated statements stay on the normal path. + assert!(!statement_requires_no_transaction( + "CREATE INDEX idx ON t (a)" + )); + assert!(!statement_requires_no_transaction("DROP INDEX idx")); + assert!(!statement_requires_no_transaction("SELECT 1")); + } + #[test] fn test_parse_naive_date() { // chrono's NaiveDate::to_string() format diff --git a/frontend/src/lib/components/DBIndexManager.svelte b/frontend/src/lib/components/DBIndexManager.svelte index 8aa313dc27..36723e42f7 100644 --- a/frontend/src/lib/components/DBIndexManager.svelte +++ b/frontend/src/lib/components/DBIndexManager.svelte @@ -109,17 +109,29 @@ } } - function openDropConfirm(idx: DbIndex) { - confirm = { - open: true, - title: `Drop index "${idx.name}"?`, - confirmationText: 'Drop index', - code: idx.definition, - onConfirm: async () => { - await dbIndexOps.dropIndex({ name: idx.name, schema, concurrent: true }) - sendUserToast('Index dropped') - indexes.refetch() + async function openDropConfirm(idx: DbIndex) { + busy = true + try { + const sql = await dbIndexOps.previewDropIndexSql({ + name: idx.name, + schema, + concurrent: true + }) + confirm = { + open: true, + title: `Drop index "${idx.name}"?`, + confirmationText: 'Drop index', + code: sql, + onConfirm: async () => { + await dbIndexOps.dropIndex({ name: idx.name, schema, concurrent: true }) + sendUserToast('Index dropped') + indexes.refetch() + } } + } catch (e) { + toastErr(e) + } finally { + busy = false } } diff --git a/frontend/src/lib/components/dbOps.ts b/frontend/src/lib/components/dbOps.ts index fca84a83f0..5791fae84b 100644 --- a/frontend/src/lib/components/dbOps.ts +++ b/frontend/src/lib/components/dbOps.ts @@ -301,6 +301,11 @@ export type IDbIndexOps = { values: CreateIndexInput }) => Promise dropIndex: (params: { name: string; schema?: string; concurrent?: boolean }) => Promise + previewDropIndexSql: (params: { + name: string + schema?: string + concurrent?: boolean + }) => Promise } export function dbIndexOpsWithPreviewScripts({ @@ -366,6 +371,10 @@ export function dbIndexOpsWithPreviewScripts({ workspace, requestBody: { args: { ...dbArg }, language, content } }) + }, + previewDropIndexSql: async ({ name, schema, concurrent }) => { + const content = makeMarker('DROP_INDEX', { name, schema, concurrent: concurrent ?? false }) + return expandMarker(workspace, language, content) } } }