MDEV-24943: Implement FILTER clause support for aggregate functions - #4439
MDEV-24943: Implement FILTER clause support for aggregate functions#4439KhaledR57 wants to merge 1 commit into
Conversation
9f3c0ff to
cb7d38d
Compare
|
Thank for taking the effort to work on this. Indeed, the LIMIT clause is a part of (at least) SQL 2011 and, as such, is a very good feature to have. However, the diff seems very incomplete. Can you please make sure that you:
Please re-submit when you have the above. |
|
I don't see how the diff is incomplete. It applies fine. It compiles fine. It passes tests on buildbot, not every builder, but it passes on many builders, this doesn't look like a non-working diff. |
|
Hi @KhaledR57 , I'm experimenting with the changes and I see a difference between what should otherwise be equivalent statements. What was once written using However, on MariaDB, the Why is this the case? |
|
If we store the sequence engine result to an InnoDB-backed table and run the FILTER query against that instead we get the correct result: |
cb7d38d to
aaea60a
Compare
|
Hi @DaveGosselin-MariaDB , Sorry for the delayed response! The issue was with the sequence storage engine's I've added a if (item->type() != Item::SUM_FUNC_ITEM ||
(((Item_sum*) item)->sum_func() != Item_sum::SUM_FUNC &&
((Item_sum*) item)->sum_func() != Item_sum::COUNT_FUNC) ||
((Item_sum*) item)->has_filter()) // NEW CHECK
return 0; // Fall back to normal aggregationStoring the sequence data to an InnoDB table gave the correct result because InnoDB goes through the standard I'll add sequence-specific tests in the next commit after I finish the stored aggregates (almost done with those). |
34ffec1 to
06b76ca
Compare
2e31328 to
0d88cba
Compare
|
Hi @KhaledR57 ,
Will such a change be required for every storage engine? If so, is there a way to generalize this for every storage engine? |
DaveGosselin-MariaDB
left a comment
There was a problem hiding this comment.
Hi @KhaledR57 ,
Here are the places I found in your patch which are not exercised by your new tests. Please add test cases that exercise them. Feel free to reach out to me on Zulip if you need help. I still have more review work to do and will update again soon.
Thanks,
Dave
Hi @DaveGosselin-MariaDB, |
|
Hi @KhaledR57 , |
Sorry, I wasn't following the ticket. I believe these are already handled. Or the comment meant something else. If I misunderstood something, please let me know. |
408db34 to
c6e3c69
Compare
DaveGosselin-MariaDB
left a comment
There was a problem hiding this comment.
Hi @KhaledR57 , thanks for your patience. I'm still working through some test cases and looking at the code. Please see my latest comments here.
c6e3c69 to
3341d48
Compare
DaveGosselin-MariaDB
left a comment
There was a problem hiding this comment.
Things are looking pretty good, just a couple of observations and questions for you to look into.
I think it's time to add the HTON_ flag (see handler.h) support that you, Sergei, and I discussed over on Zulip. We don't want to introduce silent 'wrong result' issues for unsupported engines. You can look at how existing HTON_ flags are used and feel free to ping me with any specific questions.
| m_group != 0, not_all_columns, | ||
| distinct_record_structure, false); | ||
| if (!new_field) | ||
| goto err; |
There was a problem hiding this comment.
If we force new_field to be NULL here (with a debugger) and let the program continue, then we trigger the assertion DBUG_ASSERT(tab->join || current_thd->is_error()); at sql_select.cc:12956. The stack trace is
* frame #4: 0x0000000100642d08 mariadbd`next_breadth_first_tab(first_top_tab=0x000000012002a718, n_top_tabs_count=2, tab=0x000000012002b008) at sql_select.cc:12956:3
frame #5: 0x00000001006353f0 mariadbd`JOIN::cleanup(this=0x000000013535dd28, full=true) at sql_select.cc:17289:19
frame #6: 0x0000000100635008 mariadbd`JOIN::destroy(this=0x000000013535dd28) at sql_select.cc:5123:3
frame #7: 0x000000010070dd6c mariadbd`st_select_lex::cleanup(this=0x0000000135358410) at sql_union.cc:2975:18
frame #8: 0x000000010060cec4 mariadbd`mysql_select(thd=0x00000001401d8088, tables=0x000000013535b610, fields=0x00000001353586c8, conds=0x0000000000000000, og_num=1, order=0x0000000000000000, group=0x000000013535d350, having=0x0000000000000000, proc_param=0x0000000000000000, select_options=2164525824, result=0x000000013535dd00, unit=0x00000001401dc6a8, select_lex=0x0000000135358410) at sql_select.cc:5428:29
This is likely a pre-existing problem because other places in the same function also goto err;. Building as a Release build instead of Debug doesn't result in a crash, but some test results are different if the error is forced (when I think it would be better to emit an error).
There was a problem hiding this comment.
Noting that, with this commit (01f25f9059f5) rebased to the latest main (df64bf8114a7b), this crash still occurs (if we force the new_field = nullptr case with a debugger) with the same assertion and stack trace. This isn't strictly due to the current patch, but deserves its own Jira ticket. We claim to handle the OOM case originating from Create_tmp_table::add_fields but this demonstrates that we don't (at least in Debug builds).
Place a breakpoint in the following, found in Create_tmp_table::add_fields:
Field *new_field=
create_tmp_field(table, arg, ©_func,
tmp_from_field, &m_default_field[fieldnr],
m_group != 0, not_all_columns,
distinct_record_structure , false);
if (!new_field) /// <--- breakpoint here, set new_field to NULL, then continue
goto err; // Should be OOM
The repro case with FILTER is (I haven't taken the time to simplify it):
CREATE TABLE test_aggregates (
id INT PRIMARY KEY,
category VARCHAR(50),
status VARCHAR(20),
value INT,
price DECIMAL(10,2),
amount DECIMAL(10,2) unique NOT NULL,
name VARCHAR(50),
key_name VARCHAR(50),
value_col VARCHAR(50),
bit_value INT,
extra_value float(10,2),
geom GEOMETRY
);
CREATE TABLE test_aggregates2 (
id INT PRIMARY KEY,
ref_id INT,
extra_value INT
);
delimiter |
CREATE AGGREGATE FUNCTION weighted_avg(val INT, weight INT) RETURNS DOUBLE
BEGIN
DECLARE sum_val_weight DOUBLE DEFAULT 0;
DECLARE sum_weight DOUBLE DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND
RETURN IF(sum_weight > 0, sum_val_weight / sum_weight, NULL);
LOOP
FETCH GROUP NEXT ROW;
SET sum_val_weight = sum_val_weight + val * weight;
SET sum_weight = sum_weight + weight;
END LOOP;
END|
delimiter ;
INSERT INTO test_aggregates2 VALUES
(1, 1, 10),
(2, 2, 20),
(3, 3, 30),
(4, 4, 40);
SELECT
t1.category,
AVG(t1.value) FILTER (WHERE t2.extra_value > 15) as avg_result,
weighted_avg(t1.value, t1.amount) FILTER (WHERE t2.extra_value > 15) as weighted_avg_result,
SUM(t1.value) FILTER (WHERE t2.extra_value > 20) as sum_result,
COUNT(*) FILTER (WHERE t2.extra_value > 25) as count_result
FROM test_aggregates t1
JOIN test_aggregates2 t2 ON t1.id = t2.ref_id
GROUP BY t1.category;
|
|
||
| thd->mem_root= mem_root_save; | ||
| if (!(tmp_item= new (thd->mem_root) Item_field(thd, new_field))) | ||
| goto err; |
There was a problem hiding this comment.
(same if we force this branch)
gkodinov
left a comment
There was a problem hiding this comment.
This is a preliminary review to get some of the basics right.
Please squash your commits in a single one and update the commit message.
Otherwise, nothing to add to David's comments. Please keep working with him.
456e478 to
01f25f9
Compare
|
Hi @gkodinov Sorry for the delay, I was away for a while. I am back now and have updated the PR |
|
@mariadb-YuchenPei I have looked through the latest squashed commit and am currently looking through the Claude review feedback. The Spider change looks simple, can you please review it, it appears confined to |
|
@KhaledR57 below is feedback from an automated review by Claude. It appears to me that these are legitimate issues that we need to address. Please look over them and let me know what you think. Claude refers to itself as "I" below, so those remarks are from it rather than from me. Separately, in the GitHub description, why is subqueries crossed-out? If the patch now supports subqueries, then please update the description accordingly. Begin Claude Review of: MDEV-24943 FILTER clause for aggregate functionsI found a number of issues that need to be resolved before this can go in. The Everything below was reproduced against a debug build of this branch unless Wrong results1.
|
bf22edf to
a62e1ff
Compare
gkodinov
left a comment
There was a problem hiding this comment.
LGTM. Please keep working with the final reviewer.
a62e1ff to
68a9138
Compare
| DBUG_ENTER("spider_db_mbase_util::open_item_sum_func"); | ||
| DBUG_PRINT("info",("spider Sumfunctype = %d", item_sum->sum_func())); | ||
| if (item_sum->has_filter()) | ||
| DBUG_RETURN(ER_SPIDER_COND_SKIP_NUM); |
There was a problem hiding this comment.
Nice, but please add test coverage for this, like so:
new file storage/spider/mysql-test/spider/feature/r/pushdown_aggregate.result
@@ -0,0 +1,21 @@
+for master_1
+for child2
+for child3
+set spider_same_server_link= 1;
+CREATE SERVER srv FOREIGN DATA WRAPPER mysql
+OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root');
+create table t2 (c int);
+create table t1 (c int) ENGINE=Spider REMOTE_SERVER=srv REMOTE_TABLE=t2;
+insert into t1 values (3), (7), (4), (1), (5);
+explain
+select sum(c) filter (where c < 5) from t1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 2
+select sum(c) filter (where c < 5) from t1;
+sum(c) filter (where c < 5)
+8
+drop table t1, t2;
+drop server srv;
+for master_1
+for child2
+for child3
new file storage/spider/mysql-test/spider/feature/t/pushdown_aggregate.test
@@ -0,0 +1,26 @@
+--disable_query_log
+--disable_result_log
+--source ../../t/test_init.inc
+--enable_result_log
+--enable_query_log
+--source ../../include/have_group_by_handler.inc
+
+set spider_same_server_link= 1;
+evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql
+OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root');
+create table t2 (c int);
+create table t1 (c int) ENGINE=Spider REMOTE_SERVER=srv REMOTE_TABLE=t2;
+
+insert into t1 values (3), (7), (4), (1), (5);
+
+explain
+select sum(c) filter (where c < 5) from t1;
+select sum(c) filter (where c < 5) from t1;
+
+drop table t1, t2;
+drop server srv;
+--disable_query_log
+--disable_result_log
+--source ../../t/test_deinit.inc
+--enable_result_log
+--enable_query_log
There was a problem hiding this comment.
@mariadb-YuchenPei what about columnstore, should I add the same has_filter() guard to columnstore, mirroring the spider fix?
There was a problem hiding this comment.
Good question. I've asked the columnstore team. Let's wait and see what they say
mariadb-YuchenPei
left a comment
There was a problem hiding this comment.
Please add test coverage for the select handler, as mentioned in my previous comment #4439 (comment)
Something like this:
modified mysql-test/suite/federated/federatedx_create_handlers.result
@@ -1577,6 +1577,38 @@ id op op_count
drop table federated.t1, federated.t2;
connection slave;
drop table federated.t1, federated.t2;
+# End of 11.4 tests
+#
+# MDEV-24943: Implement FILTER clause support for aggregate functions
+#
+connection slave;
+CREATE TABLE federated.t1 (
+id int(20) NOT NULL,
+name varchar(16) NOT NULL default ''
+);
+INSERT INTO federated.t1 VALUES
+(3,'xxx'), (7,'yyy'), (4,'xxx'), (1,'zzz'), (5,'yyy');
+connection master;
+CREATE TABLE federated.t1 (
+id int(20) NOT NULL,
+name varchar(16) NOT NULL default ''
+)
+ENGINE="FEDERATED" DEFAULT CHARSET=latin1
+CONNECTION='mysql://root@127.0.0.1:19001/federated/t1';
+select sum(id) from federated.t1;
+sum(id)
+20
+explain
+select sum(id) filter (where name = 'yyy') from federated.t1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PUSHED SELECT NULL NULL NULL NULL NULL NULL NULL NULL
+select sum(id) filter (where name = 'yyy') from federated.t1;
+sum(id) filter (where name = 'yyy')
+12
+drop table federated.t1;
+connection slave;
+drop table federated.t1;
+# End of 13.2 tests
connection default;
set global federated_pushdown=0;
connection master;
@@ -1585,4 +1617,3 @@ DROP DATABASE IF EXISTS federated;
connection slave;
DROP TABLE IF EXISTS federated.t1;
DROP DATABASE IF EXISTS federated;
-# End of 11.4 tests
modified mysql-test/suite/federated/federatedx_create_handlers.test
@@ -1024,9 +1024,43 @@ drop table federated.t1, federated.t2;
connection slave;
drop table federated.t1, federated.t2;
+--echo # End of 11.4 tests
+
+--echo #
+--echo # MDEV-24943: Implement FILTER clause support for aggregate functions
+--echo #
+
+--connection slave
+CREATE TABLE federated.t1 (
+ id int(20) NOT NULL,
+ name varchar(16) NOT NULL default ''
+);
+
+INSERT INTO federated.t1 VALUES
+ (3,'xxx'), (7,'yyy'), (4,'xxx'), (1,'zzz'), (5,'yyy');
+
+--connection master
+eval
+CREATE TABLE federated.t1 (
+ id int(20) NOT NULL,
+ name varchar(16) NOT NULL default ''
+)
+ENGINE="FEDERATED" DEFAULT CHARSET=latin1
+CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1';
+
+select sum(id) from federated.t1;
+explain
+select sum(id) filter (where name = 'yyy') from federated.t1;
+select sum(id) filter (where name = 'yyy') from federated.t1;
+
+drop table federated.t1;
+
+connection slave;
+drop table federated.t1;
+
+--echo # End of 13.2 tests
+
# cleanup
connection default;
set global federated_pushdown=0;
source include/federated_cleanup.inc;
-
---echo # End of 11.4 testsbe4c166 to
2c70b63
Compare
DaveGosselin-MariaDB
left a comment
There was a problem hiding this comment.
(these changes actually go in the .test files, not the .result files but when you record the test they will then appear in both).
| @@ -1585,4 +1617,3 @@ DROP DATABASE IF EXISTS federated; | |||
| connection slave; | |||
| DROP TABLE IF EXISTS federated.t1; | |||
| DROP DATABASE IF EXISTS federated; | |||
There was a problem hiding this comment.
Please add a line to indicate the end of the tests for the version into which this will be added.
--echo End of AB.C tests
where AB.C is the version (e.g. 12.1 or something)
| for master_1 | ||
| for child2 | ||
| for child3 | ||
| # |
There was a problem hiding this comment.
Please add a line to indicate the end of the tests for the version into which this will be added.
--echo End of AB.C tests
where AB.C is the version (e.g. 12.1 or something)
Aggregates lacked the SQL-standard FILTER clause, forcing CASE-based workarounds that reduced readability across (sum, avg, count, …). This update introduces the ability to specify a FILTER clause for aggregate functions, allowing for more granular control over which rows are included in the aggregation. Also, improves standards compliance and makes queries clearer and more readable. The FILTER(WHERE ...) condition may contain any expression allowed in regular WHERE clauses, except window functions, and outer references.
2c70b63 to
88d9da0
Compare
mariadb-YuchenPei
left a comment
There was a problem hiding this comment.
Thanks for the update
| @@ -0,0 +1,35 @@ | |||
| --echo # | |||
| --echo # MDEV-24943: Implement FILTER clause support for aggregate functions | |||
| --echo # | |||
There was a problem hiding this comment.
Please move the header part to below the setup "--source ../../include/have_group_by_handler.inc"
|
|
||
| --echo # | ||
| --echo # end of test pushdown_aggregate | ||
| --echo # |
There was a problem hiding this comment.
The "end of test ..." block is optional. If you must have it, please place it after the end of the test case "drop server srv;" above
| --enable_result_log | ||
| --enable_query_log | ||
|
|
||
| --echo # End of 13.2 tests |
There was a problem hiding this comment.
This should be placed before the teardown part, i.e. before the "--disable_query_log" above. If you keep the "end of test part ..." this should be after it that block because of the hierarchy of test > version > testcase
| DBUG_ENTER("spider_db_mbase_util::open_item_sum_func"); | ||
| DBUG_PRINT("info",("spider Sumfunctype = %d", item_sum->sum_func())); | ||
| if (item_sum->has_filter()) | ||
| DBUG_RETURN(ER_SPIDER_COND_SKIP_NUM); |
There was a problem hiding this comment.
Good question. I've asked the columnstore team. Let's wait and see what they say
Description
Aggregates lacked the SQL-standard
FILTERclause, forcing CASE-based workarounds that reduced readability.This update introduces the ability to specify a
FILTERclause for aggregate functions, allowing for more granular control over which rows are included in the aggregation. Also, improves standards compliance and makes queries clearer and more readable.The
<condition>may contain any expression allowed in regularWHEREclauses, exceptsubqueries, window functions, and outer references.Example:
How can this PR be tested?
Running the Test Suite
Basing the PR against the correct MariaDB version
mainbranch.PR quality check