@@ -285,7 +285,6 @@ func isCacheValid(ctx *ExecContext, cachePath string, requiredMode string) (bool
285285
286286// loadCachedCatalog loads a catalog from the cache file.
287287func loadCachedCatalog (ctx * ExecContext , cachePath string ) error {
288- e := ctx .executor
289288 cat , err := catalog .NewFromFile (cachePath )
290289 if err != nil {
291290 return err
@@ -298,7 +297,6 @@ func loadCachedCatalog(ctx *ExecContext, cachePath string) error {
298297 }
299298
300299 ctx .Catalog = cat
301- e .catalog = cat
302300 if ! ctx .Quiet {
303301 age := time .Since (info .BuildTime )
304302 fmt .Fprintf (ctx .Output , "Loading cached catalog (built %s ago, %s mode)...\n " ,
@@ -324,7 +322,6 @@ func formatDuration(d time.Duration) string {
324322
325323// buildCatalog builds the catalog from the project.
326324func buildCatalog (ctx * ExecContext , full bool , source ... bool ) error {
327- e := ctx .executor
328325 isSource := len (source ) > 0 && source [0 ]
329326 if isSource {
330327 full = true // source implies full
@@ -382,7 +379,6 @@ func buildCatalog(ctx *ExecContext, full bool, source ...bool) error {
382379
383380 cat .SetBuilt (true )
384381 ctx .Catalog = cat
385- e .catalog = cat
386382
387383 if ! ctx .Quiet {
388384 fmt .Fprintf (ctx .Output , "✓ Catalog ready (%.1fs)\n " , elapsed .Seconds ())
@@ -408,7 +404,6 @@ func buildCatalog(ctx *ExecContext, full bool, source ...bool) error {
408404
409405// execRefreshCatalogStmt handles REFRESH CATALOG [FULL] [SOURCE] [FORCE] [BACKGROUND] command.
410406func execRefreshCatalogStmt (ctx * ExecContext , stmt * ast.RefreshCatalogStmt ) error {
411- e := ctx .executor
412407 if ! ctx .Connected () {
413408 return mdlerrors .NewNotConnected ()
414409 }
@@ -431,7 +426,6 @@ func execRefreshCatalogStmt(ctx *ExecContext, stmt *ast.RefreshCatalogStmt) erro
431426 if ctx .Catalog != nil {
432427 ctx .Catalog .Close ()
433428 ctx .Catalog = nil
434- e .catalog = nil
435429 }
436430 return loadCachedCatalog (ctx , cachePath )
437431 }
@@ -451,14 +445,20 @@ func execRefreshCatalogStmt(ctx *ExecContext, stmt *ast.RefreshCatalogStmt) erro
451445 if ctx .Catalog != nil {
452446 ctx .Catalog .Close ()
453447 ctx .Catalog = nil
454- e .catalog = nil
455448 }
456449
457- // Handle background mode
450+ // Handle background mode — clone ctx so the goroutine doesn't race
451+ // with the main dispatch loop (which may syncBack and mutate fields).
452+ // NOTE: bgCtx.Output still shares the underlying writer with the main
453+ // goroutine. This is a pre-existing limitation — the original code also
454+ // wrote to ctx.Output from the goroutine. A synchronized writer would
455+ // fix this but is out of scope for the executor cleanup.
458456 if stmt .Background {
457+ bgCtx := * ctx // shallow copy — isolates scalar fields
458+ bgCtx .Cache = nil // detach shared cache so preWarmCache writes stay local
459459 go func () {
460- if err := buildCatalog (ctx , stmt .Full , stmt .Source ); err != nil {
461- fmt .Fprintf (ctx .Output , "Background catalog build failed: %v\n " , err )
460+ if err := buildCatalog (& bgCtx , stmt .Full , stmt .Source ); err != nil {
461+ fmt .Fprintf (bgCtx .Output , "Background catalog build failed: %v\n " , err )
462462 }
463463 }()
464464 fmt .Fprintln (ctx .Output , "Catalog build started in background..." )
@@ -701,16 +701,6 @@ func captureDescribeParallel(ctx *ExecContext, objectType string, qualifiedName
701701 Cache : ctx .Cache ,
702702 MprPath : ctx .MprPath ,
703703 }
704- // If a backing Executor exists, create a local one for handlers that still
705- // need e.backend/e.output (e.g., describeMicroflow via writeDescribeJSON).
706- if ctx .executor != nil {
707- local := & Executor {
708- backend : ctx .executor .backend ,
709- output : & buf ,
710- cache : ctx .Cache ,
711- }
712- localCtx .executor = local
713- }
714704
715705 var err error
716706 switch strings .ToLower (objectType ) {
0 commit comments