-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
MDEV-21879 GROUP_CONCAT(DISTINCT ORDER BY) is wrong when Unique spills #5574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
arcivanov
wants to merge
1
commit into
MariaDB:bb-blob-main-monty
Choose a base branch
from
arcivanov:MDEV-21879
base: bb-blob-main-monty
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # | ||
| # Each block records the answer computed with memory to spare, then | ||
| # recomputes it with the duplicate filter starved, and compares. | ||
| # | ||
| CREATE TABLE t1 (pk INT AUTO_INCREMENT PRIMARY KEY, a VARCHAR(100) NOT NULL); | ||
| INSERT INTO t1 (a) SELECT LPAD(seq, 4, '0') FROM seq_1_to_50; | ||
| INSERT INTO t1 (a) SELECT a FROM t1 ORDER BY pk; | ||
| SELECT COUNT(*) AS rows_in_table, COUNT(DISTINCT a) AS distinct_values FROM t1; | ||
| rows_in_table distinct_values | ||
| 100 50 | ||
| SELECT GROUP_CONCAT(DISTINCT a) INTO @gc FROM t1; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) INTO @gc_order FROM t1; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a) INTO @ja FROM t1; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a ORDER BY a) INTO @ja_order FROM t1; | ||
| SET @@tmp_memory_table_size=0; | ||
| SELECT GROUP_CONCAT(DISTINCT a) = @gc AS gc_unchanged FROM t1; | ||
| gc_unchanged | ||
| 1 | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) = @gc_order AS gc_order_unchanged FROM t1; | ||
| gc_order_unchanged | ||
| 1 | ||
| SELECT JSON_ARRAYAGG(DISTINCT a) = @ja AS ja_unchanged FROM t1; | ||
| ja_unchanged | ||
| 1 | ||
| SELECT JSON_ARRAYAGG(DISTINCT a ORDER BY a) = @ja_order AS ja_order_unchanged FROM t1; | ||
| ja_order_unchanged | ||
| 1 | ||
| SET @@tmp_memory_table_size=DEFAULT; | ||
| DROP TABLE t1; | ||
| # | ||
| # Values wide enough that the filter flushes on nearly every row. | ||
| # Here the ORDER BY case used to return a single value out of 30. | ||
| # | ||
| CREATE TABLE t2 (a VARCHAR(2000)) AS | ||
| SELECT CONCAT(seq, REPEAT('.', 1990)) AS a FROM seq_1_to_30; | ||
| SELECT COUNT(*) AS rows_in_table, COUNT(DISTINCT a) AS distinct_values FROM t2; | ||
| rows_in_table distinct_values | ||
| 30 30 | ||
| SELECT GROUP_CONCAT(DISTINCT a) INTO @gc FROM t2; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) INTO @gc_order FROM t2; | ||
| SET @@tmp_memory_table_size=1000, @@max_heap_table_size=1000; | ||
| Warnings: | ||
| Warning 1292 Truncated incorrect tmp_memory_table_size value: '1000' | ||
| Warning 1292 Truncated incorrect max_heap_table_size value: '1000' | ||
| SELECT GROUP_CONCAT(DISTINCT a) = @gc AS gc_unchanged FROM t2; | ||
| gc_unchanged | ||
| 1 | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) = @gc_order AS gc_order_unchanged FROM t2; | ||
| gc_order_unchanged | ||
| 1 | ||
| SET @@tmp_memory_table_size=DEFAULT, @@max_heap_table_size=DEFAULT; | ||
| DROP TABLE t2; | ||
| # | ||
| # ORDER BY does not order the rows that tie on the ordering | ||
| # expression. Which of them comes first is not specified, but it | ||
| # must not depend on the memory available or on the order the rows | ||
| # are read in. | ||
| # | ||
| CREATE TABLE t3 (a BIT(2), b VARCHAR(10), c BIT); | ||
| INSERT INTO t3 VALUES (1, 'a', 0), (0, 'b', 1), (0, 'c', 0), (3, 'd', 1), | ||
| (1, 'e', 1), (3, 'f', 1), (0, 'g', 1); | ||
| SELECT GROUP_CONCAT(DISTINCT a, c ORDER BY a) AS at_default FROM t3; | ||
| at_default | ||
| 01,00,11,10,31 | ||
| SET @@tmp_memory_table_size=0; | ||
| SELECT GROUP_CONCAT(DISTINCT a, c ORDER BY a) AS at_zero FROM t3; | ||
| at_zero | ||
| 01,00,11,10,31 | ||
| SET @@tmp_memory_table_size=DEFAULT; | ||
| DELETE FROM t3; | ||
| INSERT INTO t3 VALUES (0, 'c', 0), (0, 'b', 1), (1, 'a', 0), (3, 'd', 1), | ||
| (1, 'e', 1), (3, 'f', 1), (0, 'g', 1); | ||
| SELECT GROUP_CONCAT(DISTINCT a, c ORDER BY a) AS reversed_scan_order FROM t3; | ||
| reversed_scan_order | ||
| 01,00,11,10,31 | ||
| DROP TABLE t3; | ||
| # | ||
| # The sort tree can overflow too. Starving it makes repack_tree() | ||
| # cut rows out of the group, which is expected, but the rows that | ||
| # do come back must still be deduplicated and still be in order. | ||
| # | ||
| CREATE TABLE t4 (pk INT AUTO_INCREMENT PRIMARY KEY, a VARCHAR(20)); | ||
| INSERT INTO t4 (a) SELECT LPAD(seq, 6, '0') FROM seq_1_to_200; | ||
| INSERT INTO t4 (a) SELECT a FROM t4 ORDER BY pk; | ||
| SET @@tmp_memory_table_size=0, @@group_concat_max_len=4000; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) INTO @gc FROM t4; | ||
| Warnings: | ||
| Warning 1260 Row 32 was cut by group_concat() | ||
| SELECT JSON_ARRAYAGG(DISTINCT a ORDER BY a) INTO @ja FROM t4; | ||
| SET @@tmp_memory_table_size=DEFAULT, @@group_concat_max_len=DEFAULT; | ||
| SELECT COUNT(*) = COUNT(DISTINCT val) AS gc_no_duplicates, | ||
| GROUP_CONCAT(val ORDER BY val) = @gc AS gc_ascending | ||
| FROM (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@gc, ',', seq), ',', -1) AS val | ||
| FROM seq_1_to_500 | ||
| WHERE seq <= 1 + LENGTH(@gc) - LENGTH(REPLACE(@gc, ',', ''))) split; | ||
| gc_no_duplicates gc_ascending | ||
| 1 1 | ||
| SELECT JSON_VALID(@ja) AS ja_valid, | ||
| COUNT(*) = COUNT(DISTINCT val) AS ja_no_duplicates, | ||
| JSON_ARRAYAGG(val ORDER BY val) = @ja AS ja_ascending | ||
| FROM (SELECT JSON_UNQUOTE(JSON_EXTRACT(@ja, CONCAT('$[', seq - 1, ']'))) AS val | ||
| FROM seq_1_to_500 WHERE seq <= JSON_LENGTH(@ja)) split; | ||
| ja_valid ja_no_duplicates ja_ascending | ||
| 1 1 1 | ||
| DROP TABLE t4; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| # | ||
| # GROUP_CONCAT(DISTINCT ...) and JSON_ARRAYAGG(DISTINCT ...) filter | ||
| # duplicates with a Unique object, which flushes to disk when it runs out | ||
| # of memory. The answer must not depend on whether that flush happened. | ||
| # | ||
| --source include/have_sequence.inc | ||
|
|
||
| --echo # | ||
| --echo # Each block records the answer computed with memory to spare, then | ||
| --echo # recomputes it with the duplicate filter starved, and compares. | ||
| --echo # | ||
|
|
||
| CREATE TABLE t1 (pk INT AUTO_INCREMENT PRIMARY KEY, a VARCHAR(100) NOT NULL); | ||
| INSERT INTO t1 (a) SELECT LPAD(seq, 4, '0') FROM seq_1_to_50; | ||
| INSERT INTO t1 (a) SELECT a FROM t1 ORDER BY pk; | ||
| SELECT COUNT(*) AS rows_in_table, COUNT(DISTINCT a) AS distinct_values FROM t1; | ||
|
|
||
| SELECT GROUP_CONCAT(DISTINCT a) INTO @gc FROM t1; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) INTO @gc_order FROM t1; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a) INTO @ja FROM t1; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a ORDER BY a) INTO @ja_order FROM t1; | ||
|
|
||
| SET @@tmp_memory_table_size=0; | ||
| SELECT GROUP_CONCAT(DISTINCT a) = @gc AS gc_unchanged FROM t1; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) = @gc_order AS gc_order_unchanged FROM t1; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a) = @ja AS ja_unchanged FROM t1; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a ORDER BY a) = @ja_order AS ja_order_unchanged FROM t1; | ||
| SET @@tmp_memory_table_size=DEFAULT; | ||
| DROP TABLE t1; | ||
|
|
||
| --echo # | ||
| --echo # Values wide enough that the filter flushes on nearly every row. | ||
| --echo # Here the ORDER BY case used to return a single value out of 30. | ||
| --echo # | ||
| CREATE TABLE t2 (a VARCHAR(2000)) AS | ||
| SELECT CONCAT(seq, REPEAT('.', 1990)) AS a FROM seq_1_to_30; | ||
| SELECT COUNT(*) AS rows_in_table, COUNT(DISTINCT a) AS distinct_values FROM t2; | ||
|
|
||
| SELECT GROUP_CONCAT(DISTINCT a) INTO @gc FROM t2; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) INTO @gc_order FROM t2; | ||
|
|
||
| SET @@tmp_memory_table_size=1000, @@max_heap_table_size=1000; | ||
| SELECT GROUP_CONCAT(DISTINCT a) = @gc AS gc_unchanged FROM t2; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) = @gc_order AS gc_order_unchanged FROM t2; | ||
| SET @@tmp_memory_table_size=DEFAULT, @@max_heap_table_size=DEFAULT; | ||
| DROP TABLE t2; | ||
|
|
||
| --echo # | ||
| --echo # ORDER BY does not order the rows that tie on the ordering | ||
| --echo # expression. Which of them comes first is not specified, but it | ||
| --echo # must not depend on the memory available or on the order the rows | ||
| --echo # are read in. | ||
| --echo # | ||
| CREATE TABLE t3 (a BIT(2), b VARCHAR(10), c BIT); | ||
| INSERT INTO t3 VALUES (1, 'a', 0), (0, 'b', 1), (0, 'c', 0), (3, 'd', 1), | ||
| (1, 'e', 1), (3, 'f', 1), (0, 'g', 1); | ||
| SELECT GROUP_CONCAT(DISTINCT a, c ORDER BY a) AS at_default FROM t3; | ||
| SET @@tmp_memory_table_size=0; | ||
| SELECT GROUP_CONCAT(DISTINCT a, c ORDER BY a) AS at_zero FROM t3; | ||
| SET @@tmp_memory_table_size=DEFAULT; | ||
|
|
||
| DELETE FROM t3; | ||
| INSERT INTO t3 VALUES (0, 'c', 0), (0, 'b', 1), (1, 'a', 0), (3, 'd', 1), | ||
| (1, 'e', 1), (3, 'f', 1), (0, 'g', 1); | ||
| SELECT GROUP_CONCAT(DISTINCT a, c ORDER BY a) AS reversed_scan_order FROM t3; | ||
| DROP TABLE t3; | ||
|
|
||
| --echo # | ||
| --echo # The sort tree can overflow too. Starving it makes repack_tree() | ||
| --echo # cut rows out of the group, which is expected, but the rows that | ||
| --echo # do come back must still be deduplicated and still be in order. | ||
| --echo # | ||
| CREATE TABLE t4 (pk INT AUTO_INCREMENT PRIMARY KEY, a VARCHAR(20)); | ||
| INSERT INTO t4 (a) SELECT LPAD(seq, 6, '0') FROM seq_1_to_200; | ||
| INSERT INTO t4 (a) SELECT a FROM t4 ORDER BY pk; | ||
|
|
||
| SET @@tmp_memory_table_size=0, @@group_concat_max_len=4000; | ||
| SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) INTO @gc FROM t4; | ||
| SELECT JSON_ARRAYAGG(DISTINCT a ORDER BY a) INTO @ja FROM t4; | ||
| SET @@tmp_memory_table_size=DEFAULT, @@group_concat_max_len=DEFAULT; | ||
|
|
||
| SELECT COUNT(*) = COUNT(DISTINCT val) AS gc_no_duplicates, | ||
| GROUP_CONCAT(val ORDER BY val) = @gc AS gc_ascending | ||
| FROM (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@gc, ',', seq), ',', -1) AS val | ||
| FROM seq_1_to_500 | ||
| WHERE seq <= 1 + LENGTH(@gc) - LENGTH(REPLACE(@gc, ',', ''))) split; | ||
|
|
||
| SELECT JSON_VALID(@ja) AS ja_valid, | ||
| COUNT(*) = COUNT(DISTINCT val) AS ja_no_duplicates, | ||
| JSON_ARRAYAGG(val ORDER BY val) = @ja AS ja_ascending | ||
| FROM (SELECT JSON_UNQUOTE(JSON_EXTRACT(@ja, CONCAT('$[', seq - 1, ']'))) AS val | ||
| FROM seq_1_to_500 WHERE seq <= JSON_LENGTH(@ja)) split; | ||
| DROP TABLE t4; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd try to stabilize this test instead of re-recording the new undeterministic order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is deterministic, it's just that determinism has changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See Behavior Change section please