Skip to content

Commit 888361c

Browse files
fix(core/translate): handle NULL values correctly in SQLite min/max special-case
1 parent 9eb7628 commit 888361c

File tree

2 files changed

+89
-7
lines changed

2 files changed

+89
-7
lines changed

core/translate/group_by.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -520,28 +520,48 @@ fn emit_min_max_comparison(
520520
dest: update_flag_reg,
521521
});
522522

523-
// Skip comparison if aggregate argument is NULL (NULLs are ignored in MIN/MAX)
524523
let after_comparison_label = program.allocate_label();
525-
let check_first_row_label = program.allocate_label();
524+
let handle_non_null_arg_label = program.allocate_label();
525+
let set_flag_for_null_label = program.allocate_label();
526526

527+
// Check if aggregate argument is NULL
527528
program.emit_insn(Insn::NotNull {
528529
reg: agg_arg_reg,
529-
target_pc: check_first_row_label,
530+
target_pc: handle_non_null_arg_label,
531+
});
532+
533+
// Aggregate argument is NULL: check if current_min_max is also NULL
534+
// If both are NULL, we should update bare columns so last NULL row wins
535+
program.emit_insn(Insn::IsNull {
536+
reg: current_min_max_reg,
537+
target_pc: set_flag_for_null_label,
538+
});
539+
// If current_min_max is NOT NULL, we've seen a non-NULL value before
540+
// Don't update bare columns for this NULL value
541+
program.emit_insn(Insn::Goto {
542+
target_pc: after_comparison_label,
543+
});
544+
545+
// Both agg_arg and current_min_max are NULL: update bare columns
546+
program.preassign_label_to_next_insn(set_flag_for_null_label);
547+
program.emit_insn(Insn::Integer {
548+
value: 1,
549+
dest: update_flag_reg,
530550
});
531551
program.emit_insn(Insn::Goto {
532552
target_pc: after_comparison_label,
533553
});
534554

535555
// Aggregate argument is not NULL: proceed with comparison
536-
program.preassign_label_to_next_insn(check_first_row_label);
556+
program.preassign_label_to_next_insn(handle_non_null_arg_label);
537557

538-
// Check if this is the first row in the group (current_min_max_reg is NULL)
558+
// Check if this is the first non-NULL value in the group (current_min_max_reg is NULL)
539559
let compare_label = program.allocate_label();
540560
program.emit_insn(Insn::NotNull {
541561
reg: current_min_max_reg,
542562
target_pc: compare_label,
543563
});
544-
// First row: always update bare columns
564+
// First non-NULL value: always update bare columns
545565
program.emit_insn(Insn::Integer {
546566
value: 1,
547567
dest: update_flag_reg,
@@ -550,7 +570,7 @@ fn emit_min_max_comparison(
550570
target_pc: after_comparison_label,
551571
});
552572

553-
// Not first row: compare aggregate argument with current min/max
573+
// Not first non-NULL value: compare aggregate argument with current min/max
554574
program.preassign_label_to_next_insn(compare_label);
555575

556576
// Get collation for comparison

testing/groupby.test

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,24 @@ do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_with_nulls_min {
410410
SELECT a, b, min(c) FROM t GROUP BY a;
411411
} {1|a|a}
412412

413+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_all_nulls_max {
414+
CREATE TABLE t(a, b, c);
415+
INSERT INTO t VALUES (1, 'a', NULL), (1, 'b', NULL), (1, NULL, NULL);
416+
SELECT a, b, max(c) FROM t GROUP BY a;
417+
} {1||}
418+
419+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_all_nulls_min {
420+
CREATE TABLE t(a, b, c);
421+
INSERT INTO t VALUES (1, 'a', NULL), (1, 'b', NULL), (1, NULL, NULL);
422+
SELECT a, b, min(c) FROM t GROUP BY a;
423+
} {1||}
424+
425+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_all_nulls_min_2 {
426+
CREATE TABLE t(a, b, c);
427+
INSERT INTO t VALUES (1, 'a', NULL), (1, NULL, NULL), (1, 'b', NULL);
428+
SELECT a, b, min(c) FROM t GROUP BY a;
429+
} {1|b|}
430+
413431
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_ties_max {
414432
CREATE TABLE t(a, b, c);
415433
INSERT INTO t VALUES (1, 'a', 'x'), (1, 'b', 'x'), (1, 'c', 'y');
@@ -433,3 +451,47 @@ do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_multiple_bare_col
433451
INSERT INTO t VALUES (1, 'a', 'x', 10), (1, 'b', 'y', 20), (1, 'c', 'z', 30);
434452
SELECT a, b, c, min(d) FROM t GROUP BY a;
435453
} {1|a|x|10}
454+
455+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_nulls_then_value_max {
456+
CREATE TABLE t(a, b, c);
457+
INSERT INTO t VALUES (1, 'a', NULL), (1, 'b', NULL), (1, 'c', 5);
458+
SELECT a, b, max(c) FROM t GROUP BY a;
459+
} {1|c|5}
460+
461+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_nulls_then_value_min {
462+
CREATE TABLE t(a, b, c);
463+
INSERT INTO t VALUES (1, 'a', NULL), (1, 'b', NULL), (1, 'c', 5);
464+
SELECT a, b, min(c) FROM t GROUP BY a;
465+
} {1|c|5}
466+
467+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_value_then_nulls_max {
468+
CREATE TABLE t(a, b, c);
469+
INSERT INTO t VALUES (1, 'a', 5), (1, 'b', NULL), (1, 'c', NULL);
470+
SELECT a, b, max(c) FROM t GROUP BY a;
471+
} {1|a|5}
472+
473+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_value_then_nulls_min {
474+
CREATE TABLE t(a, b, c);
475+
INSERT INTO t VALUES (1, 'a', 5), (1, 'b', NULL), (1, 'c', NULL);
476+
SELECT a, b, min(c) FROM t GROUP BY a;
477+
} {1|a|5}
478+
479+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_multiple_groups_with_nulls_max {
480+
CREATE TABLE t(a, b, c);
481+
INSERT INTO t VALUES (1, 'a', NULL), (1, 'b', NULL);
482+
INSERT INTO t VALUES (2, 'x', 5), (2, 'y', NULL);
483+
INSERT INTO t VALUES (3, 'p', NULL), (3, 'q', 10);
484+
SELECT a, b, max(c) FROM t GROUP BY a ORDER BY a;
485+
} {1|b|
486+
2|x|5
487+
3|q|10}
488+
489+
do_execsql_test_on_specific_db {:memory:} min_max_bare_columns_multiple_groups_with_nulls_min {
490+
CREATE TABLE t(a, b, c);
491+
INSERT INTO t VALUES (1, 'a', NULL), (1, 'b', NULL);
492+
INSERT INTO t VALUES (2, 'x', 5), (2, 'y', NULL);
493+
INSERT INTO t VALUES (3, 'p', NULL), (3, 'q', 10);
494+
SELECT a, b, min(c) FROM t GROUP BY a ORDER BY a;
495+
} {1|b|
496+
2|x|5
497+
3|q|10}

0 commit comments

Comments
 (0)