From 7953ce965b573f472b3a05cb88b20d4d88878f72 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sat, 18 Jul 2026 05:38:57 -0700 Subject: [PATCH] fix(semantic): avoid duplicate-field panic when a deduped column alias collides with an explicit one --- compiler/semantic/sql.go | 17 ++++++++++++++--- .../ztests/sql/select-dup-alias-collision.yaml | 9 +++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 compiler/ztests/sql/select-dup-alias-collision.yaml diff --git a/compiler/semantic/sql.go b/compiler/semantic/sql.go index acc3c0471..eecf7eaa9 100644 --- a/compiler/semantic/sql.go +++ b/compiler/semantic/sql.go @@ -720,10 +720,21 @@ func (t *translator) resolveOrdinalOuter(ts tableScope, n ast.Node, prefix strin func dedup(scores map[string]int, s string) string { cnt := scores[s] scores[s] = cnt + 1 - if cnt != 0 { - s = fmt.Sprintf("%s_%d", s, cnt) + if cnt == 0 { + return s + } + // Suffix duplicate names with "_", but skip any candidate that is + // already taken by another column (explicit or previously generated) so + // the projected record cannot end up with two identical field names, which + // would panic in MustLookupTypeRecord (e.g. "SELECT 1 AS x, 2 AS x, 3 AS x_1"). + for { + candidate := fmt.Sprintf("%s_%d", s, cnt) + if scores[candidate] == 0 { + scores[candidate] = 1 + return candidate + } + cnt++ } - return s } func valuesExpr(e sem.Expr, seq sem.Seq) sem.Seq { diff --git a/compiler/ztests/sql/select-dup-alias-collision.yaml b/compiler/ztests/sql/select-dup-alias-collision.yaml new file mode 100644 index 000000000..557e4f6bb --- /dev/null +++ b/compiler/ztests/sql/select-dup-alias-collision.yaml @@ -0,0 +1,9 @@ +script: | + super compile -C -O "select 1 as x, 2 as x, 3 as x_1" + +outputs: + - name: stdout + data: | + null + | values {x:1,x_1:2,x_1_1:3} + | output main