diff --git a/crates/nu-command/src/filters/join.rs b/crates/nu-command/src/filters/join.rs index 7f3bbae238..1dea038347 100644 --- a/crates/nu-command/src/filters/join.rs +++ b/crates/nu-command/src/filters/join.rs @@ -255,6 +255,16 @@ fn join_rows( config: &Config, span: Span, ) { + if !this + .iter() + .any(|this_record| match this_record.as_record() { + Ok(record) => record.contains(this_join_key), + Err(_) => false, + }) + { + // `this` table does not contain the join column; do nothing + return; + } for this_row in this { if let Value::Record { val: this_record, .. @@ -281,39 +291,40 @@ fn join_rows( result.push(Value::record(record, span)) } } - } else if !matches!(join_type, JoinType::Inner) { - // `other` table did not contain any rows matching - // `this` row on the join column; emit a single joined - // row with null values for columns not present, - let other_record = other_keys - .iter() - .map(|&key| { - let val = if Some(key.as_ref()) == shared_join_key { - this_record - .get(key) - .cloned() - .unwrap_or_else(|| Value::nothing(span)) - } else { - Value::nothing(span) - }; - - (key.clone(), val) - }) - .collect(); - - let record = match join_type { - JoinType::Inner | JoinType::Right => { - merge_records(&other_record, this_record, shared_join_key) - } - JoinType::Left => { - merge_records(this_record, &other_record, shared_join_key) - } - _ => panic!("not implemented"), - }; - - result.push(Value::record(record, span)) + continue; } - } // else { a row is missing a value for the join column } + } + if !matches!(join_type, JoinType::Inner) { + // Either `this` row is missing a value for the join column or + // `other` table did not contain any rows matching + // `this` row on the join column; emit a single joined + // row with null values for columns not present + let other_record = other_keys + .iter() + .map(|&key| { + let val = if Some(key.as_ref()) == shared_join_key { + this_record + .get(key) + .cloned() + .unwrap_or_else(|| Value::nothing(span)) + } else { + Value::nothing(span) + }; + + (key.clone(), val) + }) + .collect(); + + let record = match join_type { + JoinType::Inner | JoinType::Right => { + merge_records(&other_record, this_record, shared_join_key) + } + JoinType::Left => merge_records(this_record, &other_record, shared_join_key), + _ => panic!("not implemented"), + }; + + result.push(Value::record(record, span)) + } }; } } diff --git a/crates/nu-command/tests/commands/join.rs b/crates/nu-command/tests/commands/join.rs index a3c2bad11e..4483debce7 100644 --- a/crates/nu-command/tests/commands/join.rs +++ b/crates/nu-command/tests/commands/join.rs @@ -195,6 +195,52 @@ fn do_cases_where_result_differs_between_join_types(join_type: &str) { ), ], ), + ( + // a row in the left table does not have the join column + ( + "[{a: 1 ref: 1} {a: 2 ref: 2} {a: 3}]", + "[{ref: 1 b: 1} {ref: 2 b: 2} {ref: 3 b: 3}]", + "ref", + ), + [ + ("--inner", "[[a, ref, b]; [1, 1, 1], [2, 2, 2]]"), + ( + "--left", + "[[a, ref, b]; [1, 1, 1], [2, 2, 2], [3, null, null]]", + ), + ( + "--right", + "[[a, ref, b]; [1, 1, 1], [2, 2, 2], [null, 3, 3]]", + ), + ( + "--outer", + "[[a, ref, b]; [1, 1, 1], [2, 2, 2], [3, null, null], [null, 3, 3]]", + ), + ], + ), + ( + // a row in the right table does not have the join column + ( + "[{a: 1 ref: 1} {a: 2 ref: 2} {a: 3 ref: 3}]", + "[{ref: 1 b: 1} {ref: 2 b: 2} {b: 3}]", + "ref", + ), + [ + ("--inner", "[[a, ref, b]; [1, 1, 1], [2, 2, 2]]"), + ( + "--left", + "[[a, ref, b]; [1, 1, 1], [2, 2, 2], [3, 3, null]]", + ), + ( + "--right", + "[[a, ref, b]; [1, 1, 1], [2, 2, 2], [null, null, 3]]", + ), + ( + "--outer", + "[[a, ref, b]; [1, 1, 1], [2, 2, 2], [3, 3, null], [null, null, 3]]", + ), + ], + ), ] { for (join_type_, expected) in join_types { if join_type_ == join_type {