Skip to content

Commit e250af0

Browse files
authored
Merge pull request #274 from retran/feature/phase2-executor-cleanup
refactor: remove ExecContext.executor back-pointer
2 parents 20a3b64 + 9613ad3 commit e250af0

19 files changed

Lines changed: 251 additions & 287 deletions

mdl/executor/cmd_associations.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616

1717
// execCreateAssociation handles CREATE ASSOCIATION statements.
1818
func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error {
19-
e := ctx.executor
2019
if !ctx.Connected() {
2120
return mdlerrors.NewNotConnected()
2221
}
@@ -138,7 +137,7 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error
138137
}
139138
}
140139

141-
e.trackModifiedDomainModel(module.ID, module.Name)
140+
ctx.trackModifiedDomainModel(module.ID, module.Name)
142141
fmt.Fprintf(ctx.Output, "Created association: %s\n", s.Name)
143142
return nil
144143
}

mdl/executor/cmd_catalog.go

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ func isCacheValid(ctx *ExecContext, cachePath string, requiredMode string) (bool
285285

286286
// loadCachedCatalog loads a catalog from the cache file.
287287
func 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.
326324
func 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.
410406
func 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) {

mdl/executor/cmd_entities.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ func buildEventHandlers(ctx *ExecContext, defs []ast.EventHandlerDef) ([]*domain
4646
}
4747

4848
func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error {
49-
e := ctx.executor
5049
if !ctx.Connected() {
5150
return mdlerrors.NewNotConnected()
5251
}
@@ -288,7 +287,7 @@ func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error {
288287
fmt.Fprintf(ctx.Output, "Created entity: %s\n", s.Name)
289288
}
290289

291-
e.trackModifiedDomainModel(module.ID, module.Name)
290+
ctx.trackModifiedDomainModel(module.ID, module.Name)
292291
return nil
293292
}
294293

@@ -463,7 +462,6 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error {
463462

464463
// execAlterEntity handles ALTER ENTITY statements.
465464
func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error {
466-
e := ctx.executor
467465
if !ctx.Connected() {
468466
return mdlerrors.NewNotConnected()
469467
}
@@ -911,7 +909,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error {
911909
return mdlerrors.NewUnsupported("unsupported alter entity operation")
912910
}
913911

914-
e.trackModifiedDomainModel(module.ID, module.Name)
912+
ctx.trackModifiedDomainModel(module.ID, module.Name)
915913
return nil
916914
}
917915

mdl/executor/cmd_fragments.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import (
1717
func execDefineFragment(ctx *ExecContext, s *ast.DefineFragmentStmt) error {
1818
if ctx.Fragments == nil {
1919
ctx.Fragments = make(map[string]*ast.DefineFragmentStmt)
20-
// Also update the executor's fragments map so newExecContext picks it up.
21-
ctx.executor.fragments = ctx.Fragments
2220
}
2321
if _, exists := ctx.Fragments[s.Name]; exists {
2422
return mdlerrors.NewAlreadyExists("fragment", s.Name)

mdl/executor/cmd_lint.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515

1616
// execLint executes a LINT statement.
1717
func execLint(ctx *ExecContext, s *ast.LintStmt) error {
18-
e := ctx.executor
1918
if !ctx.Connected() {
2019
return mdlerrors.NewNotConnected()
2120
}
@@ -35,7 +34,7 @@ func execLint(ctx *ExecContext, s *ast.LintStmt) error {
3534

3635
// Create lint context
3736
lintCtx := linter.NewLintContext(ctx.Catalog)
38-
lintCtx.SetReader(e.Reader())
37+
lintCtx.SetReader(ctx.Reader())
3938

4039
// Load configuration
4140
projectDir := filepath.Dir(ctx.MprPath)

mdl/executor/cmd_lint_mock_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ func TestLint_ShowRules(t *testing.T) {
5252
// ---------------------------------------------------------------------------
5353
// execLint — full lint path
5454
//
55-
// NOTE: The full lint path (ShowRules=false) requires ctx.executor,
55+
// NOTE: The full lint path (ShowRules=false) requires ctx.Reader(),
5656
// ctx.Catalog, buildCatalog, and the linter package pipeline. The current
57-
// mock infrastructure does not expose executor or catalog mocks, so only
57+
// mock infrastructure does not expose catalog mocks, so only
5858
// the ShowRules branch can be exercised. Expanding coverage requires
59-
// building executor/catalog mock support (tracked separately).
59+
// building catalog mock support (tracked separately).
6060
// ---------------------------------------------------------------------------

mdl/executor/cmd_microflows_create.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ func loadRestServices(ctx *ExecContext) ([]*model.ConsumedRestService, error) {
3333
}
3434

3535
func execCreateMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) error {
36-
e := ctx.executor
3736
if !ctx.ConnectedForWrite() {
3837
return mdlerrors.NewNotConnectedWrite()
3938
}
@@ -270,7 +269,7 @@ func execCreateMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) error {
270269

271270
// Track the created microflow so it can be resolved by subsequent page creations
272271
returnEntityName := extractEntityFromReturnType(mf.ReturnType)
273-
e.trackCreatedMicroflow(s.Name.Module, s.Name.Name, mf.ID, containerID, returnEntityName)
272+
ctx.trackCreatedMicroflow(s.Name.Module, s.Name.Name, mf.ID, containerID, returnEntityName)
274273

275274
// Invalidate hierarchy cache so the new microflow's container is visible
276275
invalidateHierarchy(ctx)

mdl/executor/cmd_misc.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ func execRefresh(ctx *ExecContext) error {
4141
func execSet(ctx *ExecContext, s *ast.SetStmt) error {
4242
if ctx.Settings == nil {
4343
ctx.Settings = make(map[string]any)
44-
// Persist back to Executor so subsequent statements see the map.
45-
if ctx.executor != nil {
46-
ctx.executor.settings = ctx.Settings
47-
}
4844
}
4945
ctx.Settings[s.Key] = s.Value
5046
fmt.Fprintf(ctx.Output, "Set %s = %v\n", s.Key, s.Value)
@@ -396,7 +392,6 @@ func execExit(ctx *ExecContext) error {
396392

397393
// execExecuteScript handles EXECUTE SCRIPT statements.
398394
func execExecuteScript(ctx *ExecContext, s *ast.ExecuteScriptStmt) error {
399-
e := ctx.executor
400395
// Resolve path relative to current working directory
401396
scriptPath := s.Path
402397
if !filepath.IsAbs(scriptPath) {
@@ -428,8 +423,11 @@ func execExecuteScript(ctx *ExecContext, s *ast.ExecuteScriptStmt) error {
428423

429424
// Execute all statements in the script
430425
fmt.Fprintf(ctx.Output, "Executing script: %s\n", s.Path)
426+
if ctx.ExecuteFn == nil {
427+
return mdlerrors.NewBackend("execute script", errors.New("ExecuteFn not set — ExecContext was not created via Executor dispatch"))
428+
}
431429
for _, stmt := range prog.Statements {
432-
if err := e.Execute(stmt); err != nil {
430+
if err := ctx.ExecuteFn(stmt); err != nil {
433431
// Exit within a script just stops the script, doesn't exit mxcli
434432
if errors.Is(err, ErrExit) {
435433
fmt.Fprintf(ctx.Output, "Script exited: %s\n", s.Path)

mdl/executor/cmd_pages_create_v3.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616

1717
// execCreatePageV3 handles CREATE PAGE statement with V3 syntax.
1818
func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error {
19-
e := ctx.executor
2019
if !ctx.ConnectedForWrite() {
2120
return mdlerrors.NewNotConnectedWrite()
2221
}
@@ -101,7 +100,7 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error {
101100
}
102101

103102
// Track the created page so it can be resolved by subsequent page references
104-
e.trackCreatedPage(s.Name.Module, s.Name.Name, page.ID, moduleID)
103+
ctx.trackCreatedPage(s.Name.Module, s.Name.Name, page.ID, moduleID)
105104

106105
// Invalidate hierarchy cache so the new page's container is visible
107106
invalidateHierarchy(ctx)
@@ -112,7 +111,6 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error {
112111

113112
// execCreateSnippetV3 handles CREATE SNIPPET statement with V3 syntax.
114113
func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error {
115-
e := ctx.executor
116114
if !ctx.ConnectedForWrite() {
117115
return mdlerrors.NewNotConnectedWrite()
118116
}
@@ -170,7 +168,7 @@ func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error {
170168
}
171169

172170
// Track the created snippet so it can be resolved by subsequent snippet references
173-
e.trackCreatedSnippet(s.Name.Module, s.Name.Name, snippet.ID, moduleID)
171+
ctx.trackCreatedSnippet(s.Name.Module, s.Name.Name, snippet.ID, moduleID)
174172

175173
// Invalidate hierarchy cache so the new snippet's container is visible
176174
invalidateHierarchy(ctx)

mdl/executor/cmd_security_write.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ func execDropUserRole(ctx *ExecContext, s *ast.DropUserRoleStmt) error {
304304

305305
// execGrantEntityAccess handles GRANT roles ON Module.Entity (rights) [WHERE '...'].
306306
func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error {
307-
e := ctx.executor
308307
if !ctx.ConnectedForWrite() {
309308
return mdlerrors.NewNotConnectedWrite()
310309
}
@@ -458,7 +457,7 @@ func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error
458457
fmt.Fprintf(ctx.Output, "Reconciled %d access rule(s) in module %s\n", count, module.Name)
459458
}
460459

461-
e.trackModifiedDomainModel(module.ID, module.Name)
460+
ctx.trackModifiedDomainModel(module.ID, module.Name)
462461
fmt.Fprintf(ctx.Output, "Granted access on %s.%s to %s\n", s.Entity.Module, s.Entity.Name, strings.Join(roleNames, ", "))
463462
if !ctx.Quiet {
464463
fmt.Fprint(ctx.Output, formatAccessRuleResult(ctx, s.Entity.Module, s.Entity.Name, roleNames))
@@ -468,7 +467,6 @@ func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error
468467

469468
// execRevokeEntityAccess handles REVOKE roles ON Module.Entity [(rights...)].
470469
func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) error {
471-
e := ctx.executor
472470
if !ctx.ConnectedForWrite() {
473471
return mdlerrors.NewNotConnectedWrite()
474472
}
@@ -550,7 +548,7 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err
550548
}
551549
}
552550
}
553-
e.trackModifiedDomainModel(module.ID, module.Name)
551+
ctx.trackModifiedDomainModel(module.ID, module.Name)
554552
return nil
555553
}
556554

0 commit comments

Comments
 (0)