Skip to content

Commit 8c3a382

Browse files
committed
fix: preserve XPath path source spacing in retrieve clauses
Symptom: retrieve WHERE clauses using compact XPath paths could roundtrip as canonicalized paths with spaces around '/', producing cosmetic drift. Root cause: source-expression preservation did not treat retrieve XPath paths as source-sensitive, and XPath constraints were built directly from normalized XPath AST nodes. Fix: preserve original source text for XPath constraints and retrieve WHERE expressions that contain path separators without changing normal expression parsing. Tests: added a retrieve XPath path-spacing regression and ran make test.
1 parent 904362b commit 8c3a382

2 files changed

Lines changed: 55 additions & 3 deletions

File tree

mdl/visitor/visitor_microflow_statements.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -934,15 +934,15 @@ func buildRetrieveStatement(ctx parser.IRetrieveStatementContext) *ast.RetrieveS
934934
if len(xpathConstraints) == 1 {
935935
xcCtx := xpathConstraints[0].(*parser.XpathConstraintContext)
936936
if xpathExpr := xcCtx.XpathExpr(); xpathExpr != nil {
937-
stmt.Where = buildXPathExpr(xpathExpr)
937+
stmt.Where = buildXPathSourceExpression(xpathExpr)
938938
}
939939
} else if len(xpathConstraints) > 1 {
940940
// Multiple predicates [cond1][cond2] — combine with AND
941941
var andExprs []ast.Expression
942942
for _, xc := range xpathConstraints {
943943
xcCtx := xc.(*parser.XpathConstraintContext)
944944
if xpathExpr := xcCtx.XpathExpr(); xpathExpr != nil {
945-
andExprs = append(andExprs, buildXPathExpr(xpathExpr))
945+
andExprs = append(andExprs, buildXPathSourceExpression(xpathExpr))
946946
}
947947
}
948948
if len(andExprs) == 1 {
@@ -956,7 +956,7 @@ func buildRetrieveStatement(ctx parser.IRetrieveStatementContext) *ast.RetrieveS
956956
stmt.Where = result
957957
}
958958
} else if expr := retrCtx.Expression(0); expr != nil {
959-
stmt.Where = buildSourceExpression(expr)
959+
stmt.Where = buildRetrieveWhereExpression(expr)
960960
}
961961
}
962962

@@ -1172,6 +1172,34 @@ func buildSourceExpression(ctx parser.IExpressionContext) ast.Expression {
11721172
return expr
11731173
}
11741174

1175+
func buildXPathSourceExpression(ctx parser.IXpathExprContext) ast.Expression {
1176+
if ctx == nil {
1177+
return nil
1178+
}
1179+
expr := buildXPathExpr(ctx)
1180+
if prc, ok := ctx.(antlr.ParserRuleContext); ok {
1181+
if source := strings.TrimSpace(extractOriginalText(prc)); source != "" {
1182+
return &ast.SourceExpr{Expression: expr, Source: source}
1183+
}
1184+
}
1185+
return expr
1186+
}
1187+
1188+
func buildRetrieveWhereExpression(ctx parser.IExpressionContext) ast.Expression {
1189+
if ctx == nil {
1190+
return nil
1191+
}
1192+
expr := buildExpression(ctx)
1193+
if prc, ok := ctx.(antlr.ParserRuleContext); ok {
1194+
if source := strings.TrimSpace(extractOriginalText(prc)); source != "" {
1195+
if shouldPreserveExpressionSource(source) || strings.Contains(source, "/") {
1196+
return &ast.SourceExpr{Expression: expr, Source: source}
1197+
}
1198+
}
1199+
}
1200+
return expr
1201+
}
1202+
11751203
func shouldPreserveExpressionSource(source string) bool {
11761204
if strings.ContainsAny(source, "\r\n") {
11771205
return true

mdl/visitor/visitor_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,6 +1714,30 @@ END;`
17141714
}
17151715
}
17161716

1717+
func TestRetrieveXPathPreservesOriginalPathSpacing(t *testing.T) {
1718+
input := `CREATE MICROFLOW Synthetic.PreserveXPathPathSpacing ()
1719+
BEGIN
1720+
RETRIEVE $Items FROM Synthetic.Item
1721+
WHERE Synthetic.Item_Group/Synthetic.Group/Synthetic.Group_Company = $Company;
1722+
END;`
1723+
1724+
prog, errs := Build(input)
1725+
if len(errs) > 0 {
1726+
t.Fatalf("unexpected parse errors: %v", errs)
1727+
}
1728+
1729+
mf := prog.Statements[0].(*ast.CreateMicroflowStmt)
1730+
retrieveStmt := mf.Body[0].(*ast.RetrieveStmt)
1731+
source, ok := retrieveStmt.Where.(*ast.SourceExpr)
1732+
if !ok {
1733+
t.Fatalf("expected retrieve XPath SourceExpr, got %T", retrieveStmt.Where)
1734+
}
1735+
want := "Synthetic.Item_Group/Synthetic.Group/Synthetic.Group_Company = $Company"
1736+
if source.Source != want {
1737+
t.Fatalf("XPath source = %q, want %q", source.Source, want)
1738+
}
1739+
}
1740+
17171741
func TestTrailingExpressionWhitespacePreservedForRoundtripSlots(t *testing.T) {
17181742
input := `CREATE MICROFLOW Synthetic.PreserveTrailingExpressionWhitespace (
17191743
$Object: Synthetic.Entity

0 commit comments

Comments
 (0)