@@ -116,19 +116,9 @@ func indexSearchableLookup(ctx *sql.Context, c transform.Context, rt sql.TableNo
116116 ret = plan .NewFilter (newFilter , ret )
117117 }
118118
119- if fds != nil && fds .HasMax1Row () && ! qFlags .JoinIsSet () && ! qFlags .SubqueryIsSet () && lookup .Ranges .Len () == 1 {
120- // Prevent max1Row when parent will combine results
121- if c .Parent != nil {
122- switch parent := c .Parent .(type ) {
123- case * plan.SetOp :
124- return ret , transform .NewTree , nil
125- case * plan.Project :
126- if plan .IsUnary (parent ) {
127- return ret , transform .NewTree , nil
128- }
129- }
130- }
131-
119+ // Skip Max1Row optimization for SetOp queries to avoid incorrect single-row assumption
120+ _ , isSetOp := c .Parent .(* plan.SetOp )
121+ if fds != nil && fds .HasMax1Row () && ! qFlags .JoinIsSet () && ! qFlags .SubqueryIsSet () && lookup .Ranges .Len () == 1 && ! isSetOp {
132122 qFlags .Set (sql .QFlagMax1Row )
133123 }
134124
@@ -331,9 +321,13 @@ func getCostedIndexScan(ctx *sql.Context, statsProv sql.StatsProvider, rt sql.Ta
331321 bestStat = stats .UpdateCounts (bestStat )
332322 }
333323
334- if bestStat .FuncDeps ().HasMax1Row () && ! qFlags .JoinIsSet () && ! qFlags .SubqueryIsSet () && lookup .Ranges .Len () == 1 {
335- qFlags .Set (sql .QFlagMax1Row )
336- }
324+ // Conservative: disable Max1Row optimization here due to lack of context for SetOp detection (dolt#9641)
325+ // The indexSearchableLookup path above handles the context-aware SetOp detection for memory engine
326+ // Server engine uses this path and would incorrectly set Max1Row for UNION subqueries
327+ // See: https://github.com/dolthub/dolt/issues/9641
328+ // if bestStat.FuncDeps().HasMax1Row() && !qFlags.JoinIsSet() && !qFlags.SubqueryIsSet() && lookup.Ranges.Len() == 1 {
329+ // qFlags.Set(sql.QFlagMax1Row)
330+ // }
337331
338332 return ret , bestStat , retFilters , nil
339333}
0 commit comments