diff --git a/runtime/vam/op/hashjoin.go b/runtime/vam/op/hashjoin.go index 69f265544..6346f6dd9 100644 --- a/runtime/vam/op/hashjoin.go +++ b/runtime/vam/op/hashjoin.go @@ -192,9 +192,12 @@ func (j *hashJoin) probeLeft() (vector.Any, error) { if keyVal.IsMissing() { continue } - key := hashKey(keyVal) + var rightVals []super.Value + var ok bool + if !keyVal.IsNull() { + rightVals, ok = j.table[hashKey(keyVal)] + } leftVal := vector.ValueAt(&sb, vec, i) - rightVals, ok := j.table[key] if !ok { if j.style != "inner" { b.Write(j.wrap(leftVal.Ptr(), nil)) @@ -235,10 +238,14 @@ func (j *hashJoin) probeRight() (vector.Any, error) { if keyVal.IsMissing() { continue } - key := hashKey(keyVal) - leftVals, ok := j.table[key] - if ok { - j.hits[key] = true + var leftVals []super.Value + if !keyVal.IsNull() { + key := hashKey(keyVal) + var ok bool + leftVals, ok = j.table[key] + if ok { + j.hits[key] = true + } } if j.style == "anti" { continue diff --git a/runtime/ztests/op/join-first-key-is-null.yaml b/runtime/ztests/op/join-first-key-is-null.yaml index f6cabd3a0..e1bd95a78 100644 --- a/runtime/ztests/op/join-first-key-is-null.yaml +++ b/runtime/ztests/op/join-first-key-is-null.yaml @@ -3,7 +3,5 @@ spq: | | join using (a) | values left -input: &input | +input: | {a:null} - -output: *input diff --git a/runtime/ztests/op/join-kinds.yaml b/runtime/ztests/op/join-kinds.yaml index d05048ef1..7f6c93895 100644 --- a/runtime/ztests/op/join-kinds.yaml +++ b/runtime/ztests/op/join-kinds.yaml @@ -30,6 +30,7 @@ inputs: {a:40,sa:"a3"} {a:45,sa:"a5"} {a:50,sa:"a4"} + {a:null,sa:"anull"} {anti_a:"Anti hash join output must not contain this record."} - name: B.sup data: | @@ -38,6 +39,7 @@ inputs: {b:40,sb:"b40"} {b:40,sb:"b40.2"} {b:60,sb:"b60"} + {b:null,sb:"bnull"} - name: C.sup data: | {c:15,sc:"c8"} @@ -46,6 +48,7 @@ inputs: {c:30,sc:"c2"} {c:35,sc:"c6"} {c:40,sc:"c3"} + {c:null,sc:"cnull"} outputs: - name: stdout @@ -58,10 +61,12 @@ outputs: {a:35,sa:"a6"} {a:45,sa:"a5"} {a:50,sa:"a4"} + {a:null,sa:"anull"} === anti nested loop {a:10,sa:"a0"} {a:15,sa:"a8"} {a:20,sa:"a1"} + {a:null,sa:"anull"} {anti_a:"Anti hash join output must not contain this record."} === left hash {a:10,sa:"a0",hit:error("missing")} @@ -75,6 +80,7 @@ outputs: {a:40,sa:"a3",hit:"b40.2"} {a:45,sa:"a5",hit:error("missing")} {a:50,sa:"a4",hit:error("missing")} + {a:null,sa:"anull",hit:error("missing")} === left nested loop {a:10,sa:"a0",hit:error("missing")} {a:15,sa:"a8",hit:error("missing")} @@ -95,6 +101,7 @@ outputs: {a:50,sa:"a4",hit:"b20.2"} {a:50,sa:"a4",hit:"b40"} {a:50,sa:"a4",hit:"b40.2"} + {a:null,sa:"anull",hit:error("missing")} {anti_a:"Anti hash join output must not contain this record.",hit:error("missing")} === inner hash {a:20,sa:"a1",hit:"b20.1"} @@ -127,6 +134,7 @@ outputs: {c:35,sc:"c6",hit:error("missing")} {c:40,sc:"c3",hit:"b40"} {c:40,sc:"c3",hit:"b40.2"} + {c:null,sc:"cnull",hit:error("missing")} === right nested loop {c:15,sc:"c8",hit:error("missing")} {c:20,sc:"c1",hit:error("missing")} @@ -138,6 +146,7 @@ outputs: {c:35,sc:"c6",hit:"b20.2"} {c:40,sc:"c3",hit:"b20.1"} {c:40,sc:"c3",hit:"b20.2"} + {c:null,sc:"cnull",hit:error("missing")} === cross {b:20,sb:"b20.1",c:15,sc:"c8"} {b:20,sb:"b20.1",c:20,sc:"c1"} @@ -145,27 +154,39 @@ outputs: {b:20,sb:"b20.1",c:30,sc:"c2"} {b:20,sb:"b20.1",c:35,sc:"c6"} {b:20,sb:"b20.1",c:40,sc:"c3"} + {b:20,sb:"b20.1",c:null,sc:"cnull"} {b:20,sb:"b20.2",c:15,sc:"c8"} {b:20,sb:"b20.2",c:20,sc:"c1"} {b:20,sb:"b20.2",c:25,sc:"c7"} {b:20,sb:"b20.2",c:30,sc:"c2"} {b:20,sb:"b20.2",c:35,sc:"c6"} {b:20,sb:"b20.2",c:40,sc:"c3"} + {b:20,sb:"b20.2",c:null,sc:"cnull"} {b:40,sb:"b40",c:15,sc:"c8"} {b:40,sb:"b40",c:20,sc:"c1"} {b:40,sb:"b40",c:25,sc:"c7"} {b:40,sb:"b40",c:30,sc:"c2"} {b:40,sb:"b40",c:35,sc:"c6"} {b:40,sb:"b40",c:40,sc:"c3"} + {b:40,sb:"b40",c:null,sc:"cnull"} {b:40,sb:"b40.2",c:15,sc:"c8"} {b:40,sb:"b40.2",c:20,sc:"c1"} {b:40,sb:"b40.2",c:25,sc:"c7"} {b:40,sb:"b40.2",c:30,sc:"c2"} {b:40,sb:"b40.2",c:35,sc:"c6"} {b:40,sb:"b40.2",c:40,sc:"c3"} + {b:40,sb:"b40.2",c:null,sc:"cnull"} {b:60,sb:"b60",c:15,sc:"c8"} {b:60,sb:"b60",c:20,sc:"c1"} {b:60,sb:"b60",c:25,sc:"c7"} {b:60,sb:"b60",c:30,sc:"c2"} {b:60,sb:"b60",c:35,sc:"c6"} {b:60,sb:"b60",c:40,sc:"c3"} + {b:60,sb:"b60",c:null,sc:"cnull"} + {b:null,sb:"bnull",c:15,sc:"c8"} + {b:null,sb:"bnull",c:20,sc:"c1"} + {b:null,sb:"bnull",c:25,sc:"c7"} + {b:null,sb:"bnull",c:30,sc:"c2"} + {b:null,sb:"bnull",c:35,sc:"c6"} + {b:null,sb:"bnull",c:40,sc:"c3"} + {b:null,sb:"bnull",c:null,sc:"cnull"}