fix(query): preserve both sides of duplicate column names in JOIN
When SELECT * joins tables with overlapping column names (e.g. 'id' in both users and orders), the right-side value was being dropped because irekStar filtered out dotted keys. - Add expandStarRow() that keeps bare columns and adds qualified aliases (u.id, o.id) only for duplicated names - Fix r.columns expansion to include all joined tables, qualifying duplicates from the right side
This commit is contained in:
@@ -3000,6 +3000,25 @@ proc computeWindowValues*(rows: seq[Row], expr: IRExpr, ctx: ExecutionContext =
|
|||||||
# IR Plan Execution (with actual filter/sort/projection)
|
# IR Plan Execution (with actual filter/sort/projection)
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc expandStarRow(row: Row): Row =
|
||||||
|
result = initTable[string, Value]()
|
||||||
|
var seenCols = initTable[string, bool]()
|
||||||
|
var qualifiedCount = initTable[string, int]()
|
||||||
|
for k, v in row:
|
||||||
|
if not k.startsWith("$") and k.contains("."):
|
||||||
|
let parts = k.split(".")
|
||||||
|
if parts.len == 2:
|
||||||
|
qualifiedCount[parts[1]] = qualifiedCount.getOrDefault(parts[1], 0) + 1
|
||||||
|
for k, v in row:
|
||||||
|
if not k.startsWith("$") and not k.contains("."):
|
||||||
|
result[k] = v
|
||||||
|
seenCols[k] = true
|
||||||
|
for k, v in row:
|
||||||
|
if not k.startsWith("$") and k.contains("."):
|
||||||
|
let parts = k.split(".")
|
||||||
|
if parts.len == 2 and parts[1] in seenCols and qualifiedCount.getOrDefault(parts[1], 0) > 1:
|
||||||
|
result[k] = v
|
||||||
|
|
||||||
proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row] =
|
proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row] =
|
||||||
if plan == nil: return @[]
|
if plan == nil: return @[]
|
||||||
|
|
||||||
@@ -3056,9 +3075,7 @@ proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row] =
|
|||||||
if expr.kind == irekWindowFunc:
|
if expr.kind == irekWindowFunc:
|
||||||
newRow[alias] = winValues[i][rowIdx]
|
newRow[alias] = winValues[i][rowIdx]
|
||||||
elif expr.kind == irekStar:
|
elif expr.kind == irekStar:
|
||||||
for k, v in row:
|
newRow = expandStarRow(row)
|
||||||
if not k.startsWith("$") and not k.contains("."):
|
|
||||||
newRow[k] = v
|
|
||||||
else:
|
else:
|
||||||
let val = evalExpr(expr, row, ctx)
|
let val = evalExpr(expr, row, ctx)
|
||||||
if alias.len > 0: newRow[alias] = val
|
if alias.len > 0: newRow[alias] = val
|
||||||
@@ -3077,9 +3094,7 @@ proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row] =
|
|||||||
let expr = plan.projectExprs[i]
|
let expr = plan.projectExprs[i]
|
||||||
if expr.kind == irekStar:
|
if expr.kind == irekStar:
|
||||||
if sourceRows.len > 0:
|
if sourceRows.len > 0:
|
||||||
for k, v in sourceRows[0]:
|
newRow = expandStarRow(sourceRows[0])
|
||||||
if not k.startsWith("$") and not k.contains("."):
|
|
||||||
newRow[k] = v
|
|
||||||
elif expr.kind == irekAggregate:
|
elif expr.kind == irekAggregate:
|
||||||
# Apply FILTER (WHERE ...) if present
|
# Apply FILTER (WHERE ...) if present
|
||||||
var filteredRows = sourceRows
|
var filteredRows = sourceRows
|
||||||
@@ -3152,10 +3167,7 @@ proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row] =
|
|||||||
if i < plan.projectExprs.len:
|
if i < plan.projectExprs.len:
|
||||||
let expr = plan.projectExprs[i]
|
let expr = plan.projectExprs[i]
|
||||||
if expr.kind == irekStar:
|
if expr.kind == irekStar:
|
||||||
# Expand star to all columns in the row (excluding internal keys)
|
newRow = expandStarRow(row)
|
||||||
for k, v in row:
|
|
||||||
if not k.startsWith("$") and not k.contains("."):
|
|
||||||
newRow[k] = v
|
|
||||||
elif expr.kind == irekAggregate and sourceIsGroupBy:
|
elif expr.kind == irekAggregate and sourceIsGroupBy:
|
||||||
# Look up pre-computed aggregate from GroupBy row
|
# Look up pre-computed aggregate from GroupBy row
|
||||||
let aggKey = "$agg_" & $expr.aggOp
|
let aggKey = "$agg_" & $expr.aggOp
|
||||||
@@ -4475,11 +4487,29 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
|||||||
# Expand star to table columns
|
# Expand star to table columns
|
||||||
if "*" in cols:
|
if "*" in cols:
|
||||||
var expandedCols: seq[string] = @[]
|
var expandedCols: seq[string] = @[]
|
||||||
let tbl = ctx.getTableDef(if stmt.selFrom != nil and stmt.selFrom.kind == nkFrom: stmt.selFrom.fromTable else: "")
|
var seenColNames = initTable[string, bool]()
|
||||||
|
let fromTable = if stmt.selFrom != nil and stmt.selFrom.kind == nkFrom: stmt.selFrom.fromTable else: ""
|
||||||
|
let fromAlias = if stmt.selFrom != nil and stmt.selFrom.kind == nkFrom: stmt.selFrom.fromAlias else: ""
|
||||||
for c in cols:
|
for c in cols:
|
||||||
if c == "*":
|
if c == "*":
|
||||||
|
if fromTable.len > 0:
|
||||||
|
let tbl = ctx.getTableDef(fromTable)
|
||||||
for tc in tbl.columns:
|
for tc in tbl.columns:
|
||||||
expandedCols.add(tc.name)
|
expandedCols.add(tc.name)
|
||||||
|
seenColNames[tc.name] = true
|
||||||
|
for j in stmt.selJoins:
|
||||||
|
if j.kind == nkJoin and j.joinTarget != nil and j.joinTarget.kind == nkFrom:
|
||||||
|
let joinTbl = ctx.getTableDef(j.joinTarget.fromTable)
|
||||||
|
let alias = j.joinTarget.fromAlias
|
||||||
|
for tc in joinTbl.columns:
|
||||||
|
if tc.name in seenColNames:
|
||||||
|
if alias.len > 0:
|
||||||
|
expandedCols.add(alias & "." & tc.name)
|
||||||
|
else:
|
||||||
|
expandedCols.add(j.joinTarget.fromTable & "." & tc.name)
|
||||||
|
else:
|
||||||
|
expandedCols.add(tc.name)
|
||||||
|
seenColNames[tc.name] = true
|
||||||
else:
|
else:
|
||||||
expandedCols.add(c)
|
expandedCols.add(c)
|
||||||
cols = expandedCols
|
cols = expandedCols
|
||||||
|
|||||||
Reference in New Issue
Block a user