From 5ef44f662c06e91d0adc6a8c0831088890df0f1f Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Wed, 9 Mar 2022 16:04:43 +0100 Subject: [PATCH 01/67] Added some necessary files and set up the project so it runs --- .../suite/gis/r/st_shortest_path.result | 1169 +++++++++++++++++ mysql-test/suite/gis/t/st_shortest_path.test | 497 +++++++ sql/CMakeLists.txt | 1 + sql/a_star.cc | 199 +++ sql/a_star.h | 38 + sql/gen_lex_token.cc | 2 +- sql/lex.h | 1 + sql/sql_yacc.yy | 12 + 8 files changed, 1918 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/gis/r/st_shortest_path.result create mode 100644 mysql-test/suite/gis/t/st_shortest_path.test create mode 100644 sql/a_star.cc create mode 100644 sql/a_star.h diff --git a/mysql-test/suite/gis/r/st_shortest_path.result b/mysql-test/suite/gis/r/st_shortest_path.result new file mode 100644 index 000000000000..31ec925a6d43 --- /dev/null +++ b/mysql-test/suite/gis/r/st_shortest_path.result @@ -0,0 +1,1169 @@ +# +# Setup test. +# +CREATE TABLE t1 (a int, k int, b VARCHAR(10)) CHARSET utf8mb4; +INSERT INTO t1 VALUES +(1, 1, "alfa"), +(1, 2, null), +(2, 3, "doi"), +(1, 4, "unu"), +(3, 5, "trei"), +(4, 6, null), +(4, 7, null), +(1, 8, "one"); +# +# Test JSON_ARRAYAGG. +# +FLUSH STATUS; +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT JSON_ARRAYAGG(b) FROM t1; +JSON_ARRAYAGG(b) +["alfa", null, "doi", "unu", "trei", null, null, "one"] +SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; +JSON_ARRAYAGG(b) +["alfa", null, "doi", "unu", "trei", null, null, "one"] +SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; +JSON_ARRAYAGG(b) +["alfa", null, "doi", "unu", "trei", null, null, "one"] +SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; +JSON_ARRAYAGG(b) +["alfa", null, "doi", "unu", "trei", null, null, "one"] +PREPARE p1 FROM "SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; +EXECUTE p1; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +EXECUTE p1; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +deallocate prepare p1; +PREPARE p3 FROM +"SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; +EXECUTE p3; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +EXECUTE p3; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +deallocate prepare p3; +PREPARE p4 FROM "SELECT JSON_ARRAYAGG(b) FROM t1"; +EXECUTE p4; +JSON_ARRAYAGG(b) +["alfa", null, "doi", "unu", "trei", null, null, "one"] +EXECUTE p4; +JSON_ARRAYAGG(b) +["alfa", null, "doi", "unu", "trei", null, null, "one"] +deallocate prepare p4; +SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1; +JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') +["alfa", null, "doi", "unu", "trei", null, null, "one", true, false] +PREPARE p1 FROM +"SELECT a, JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1 GROUP BY a"; +EXECUTE p1; +a JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') +1 ["alfa", null, "unu", "one", true, false] +2 ["doi", true, false] +3 ["trei", true, false] +4 [null, null, true, false] +EXECUTE p1; +a JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') +1 ["alfa", null, "unu", "one", true, false] +2 ["doi", true, false] +3 ["trei", true, false] +4 [null, null, true, false] +deallocate prepare p1; +PREPARE p4 FROM +"SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1"; +EXECUTE p4; +JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') +["alfa", null, "doi", "unu", "trei", null, null, "one", true, false] +EXECUTE p4; +JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') +["alfa", null, "doi", "unu", "trei", null, null, "one", true, false] +deallocate prepare p4; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +EXPLAIN SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 Using filesort +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN SELECT JSON_ARRAYAGG(b) FROM t1; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 NULL +Warnings: +Note 1003 /* select#1 */ select json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` +EXPLAIN FORMAT=json SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "9.05" + }, + "grouping_operation": { + "using_filesort": true, + "cost_info": { + "sort_cost": "8.00" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "a", + "b" + ] + } + } + } +} +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) +FROM t1 +GROUP BY a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "9.05" + }, + "grouping_operation": { + "using_filesort": true, + "cost_info": { + "sort_cost": "8.00" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "a", + "b" + ] + } + } + } +} +Warnings: +Note 1003 /* select#1 */ select sql_big_result `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) +FROM t1 +GROUP BY a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "9.05" + }, + "grouping_operation": { + "using_filesort": true, + "cost_info": { + "sort_cost": "8.00" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "a", + "b" + ] + } + } + } +} +Warnings: +Note 1003 /* select#1 */ select sql_small_result `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) +FROM t1 +GROUP BY a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "9.05" + }, + "grouping_operation": { + "using_filesort": true, + "cost_info": { + "sort_cost": "8.00" + }, + "buffer_result": { + "using_temporary_table": true, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "a", + "b" + ] + } + } + } + } +} +Warnings: +Note 1003 /* select#1 */ select sql_buffer_result `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN FORMAT=json SELECT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "1.05" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "b" + ] + } + } +} +Warnings: +Note 1003 /* select#1 */ select json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` +EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "1.05" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "b" + ] + } + } +} +Warnings: +Note 1003 /* select#1 */ select sql_big_result json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` +EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "1.05" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "b" + ] + } + } +} +Warnings: +Note 1003 /* select#1 */ select sql_small_result json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` +EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "1.05" + }, + "buffer_result": { + "using_temporary_table": true, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "b" + ] + } + } + } +} +Warnings: +Note 1003 /* select#1 */ select sql_buffer_result json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a WITH ROLLUP; +a JSON_ARRAYAGG(b) +1 ["alfa", null, "unu", "one"] +2 ["doi"] +3 ["trei"] +4 [null, null] +NULL ["alfa", null, "unu", "one", "doi", "trei", null, null] +SELECT a, JSON_ARRAYAGG(b) as jarray +FROM t1 +GROUP BY a +HAVING jarray= JSON_ARRAY("trei"); +a jarray +3 ["trei"] +# +# Test JSON_OBJECTAGG. +# +FLUSH STATUS; +SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +a JSON_OBJECTAGG(k, b) +1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} +2 {"3": "doi"} +3 {"5": "trei"} +4 {"6": null, "7": null} +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT JSON_OBJECTAGG(k, b) FROM t1; +JSON_OBJECTAGG(k, b) +{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +PREPARE p1 FROM "SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a"; +EXECUTE p1; +a JSON_OBJECTAGG(k, b) +1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} +2 {"3": "doi"} +3 {"5": "trei"} +4 {"6": null, "7": null} +EXECUTE p1; +a JSON_OBJECTAGG(k, b) +1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} +2 {"3": "doi"} +3 {"5": "trei"} +4 {"6": null, "7": null} +deallocate prepare p1; +PREPARE p4 FROM "SELECT JSON_OBJECTAGG(k, b) FROM t1"; +EXECUTE p4; +JSON_OBJECTAGG(k, b) +{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} +EXECUTE p4; +JSON_OBJECTAGG(k, b) +{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} +deallocate prepare p4; +SELECT a, JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') FROM t1 GROUP BY a; +a JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +1 [{"1": "alfa", "2": null, "4": "unu", "8": "one"}, true, false] +2 [{"3": "doi"}, true, false] +3 [{"5": "trei"}, true, false] +4 [{"6": null, "7": null}, true, false] +SELECT JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') FROM t1; +JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +[{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"}, true, false] +PREPARE p1 FROM +"SELECT a, JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +FROM t1 +GROUP BY a"; +EXECUTE p1; +a JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +1 [{"1": "alfa", "2": null, "4": "unu", "8": "one"}, true, false] +2 [{"3": "doi"}, true, false] +3 [{"5": "trei"}, true, false] +4 [{"6": null, "7": null}, true, false] +EXECUTE p1; +a JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +1 [{"1": "alfa", "2": null, "4": "unu", "8": "one"}, true, false] +2 [{"3": "doi"}, true, false] +3 [{"5": "trei"}, true, false] +4 [{"6": null, "7": null}, true, false] +deallocate prepare p1; +PREPARE p4 FROM +"SELECT JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') FROM t1"; +EXECUTE p4; +JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +[{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"}, true, false] +EXECUTE p4; +JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') +[{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"}, true, false] +deallocate prepare p4; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +EXPLAIN SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 Using filesort +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN SELECT JSON_OBJECTAGG(k, b) FROM t1; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 NULL +Warnings: +Note 1003 /* select#1 */ select json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` +EXPLAIN FORMAT=json SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "9.05" + }, + "grouping_operation": { + "using_filesort": true, + "cost_info": { + "sort_cost": "8.00" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "a", + "k", + "b" + ] + } + } + } +} +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN FORMAT=json SELECT JSON_OBJECTAGG(k, b) FROM t1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost_info": { + "query_cost": "1.05" + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows_examined_per_scan": 8, + "rows_produced_per_join": 8, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.80", + "prefix_cost": "1.05", + "data_read_per_join": "448" + }, + "used_columns": [ + "k", + "b" + ] + } + } +} +Warnings: +Note 1003 /* select#1 */ select json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` +SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a WITH ROLLUP; +a JSON_OBJECTAGG(k, b) +1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} +2 {"3": "doi"} +3 {"5": "trei"} +4 {"6": null, "7": null} +NULL {"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} +SELECT a, JSON_OBJECTAGG(k, b) as jobject +FROM t1 +GROUP BY a +HAVING jobject = JSON_OBJECT(3, "doi"); +a jobject +2 {"3": "doi"} +# +# NULL values. +# +SELECT a, JSON_ARRAYAGG(null) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(null) +1 [null, null, null, null] +2 [null] +3 [null] +4 [null, null] +SELECT JSON_ARRAYAGG(null) FROM t1; +JSON_ARRAYAGG(null) +[null, null, null, null, null, null, null, null] +SELECT a, JSON_OBJECTAGG(k, null) FROM t1 GROUP BY a; +a JSON_OBJECTAGG(k, null) +1 {"1": null, "2": null, "4": null, "8": null} +2 {"3": null} +3 {"5": null} +4 {"6": null, "7": null} +SELECT JSON_OBJECTAGG(k, null) FROM t1; +JSON_OBJECTAGG(k, null) +{"1": null, "2": null, "3": null, "4": null, "5": null, "6": null, "7": null, "8": null} +SELECT a, JSON_OBJECTAGG(null, b) FROM t1 GROUP BY a; +ERROR 22032: JSON documents may not contain NULL member names. +SELECT JSON_OBJECTAGG(null, b) FROM t1; +ERROR 22032: JSON documents may not contain NULL member names. +# +# Coverage test for fix_fields: Disable_semijoin_flattening. +# +CREATE TABLE t(a INT); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +EXPLAIN format=json SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "message": "No tables used", + "optimized_away_subqueries": [ + { + "dependent": true, + "cacheable": false, + "query_block": { + "select_id": 2, + "cost_info": { + "query_cost": "0.35" + }, + "table": { + "table_name": "t", + "access_type": "ALL", + "rows_examined_per_scan": 1, + "rows_produced_per_join": 1, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.10", + "prefix_cost": "0.35", + "data_read_per_join": "8" + }, + "attached_condition": "(0 <> json_arrayagg(((1,(/* select#3 */ select 1 from `test`.`t` where true)))))" + }, + "optimized_away_subqueries": [ + { + "dependent": false, + "cacheable": true, + "query_block": { + "select_id": 3, + "cost_info": { + "query_cost": "0.35" + }, + "table": { + "table_name": "t", + "access_type": "ALL", + "rows_examined_per_scan": 1, + "rows_produced_per_join": 1, + "filtered": "100.00", + "cost_info": { + "read_cost": "0.25", + "eval_cost": "0.10", + "prefix_cost": "0.35", + "data_read_per_join": "8" + } + } + } + } + ] + } + } + ] + } +} +Warnings: +Warning 3986 Evaluating a JSON value in SQL boolean context does an implicit comparison against JSON integer 0; if this is not what you want, consider converting JSON to a SQL numeric type with JSON_VALUE RETURNING +Note 1003 /* select#1 */ select (/* select#2 */ select 1 from `test`.`t` where (0 <> json_arrayagg(((1,(/* select#3 */ select 1 from `test`.`t` where true)))))) AS `(SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t)))` +SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); +(SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))) +NULL +Warnings: +Warning 3986 Evaluating a JSON value in SQL boolean context does an implicit comparison against JSON integer 0; if this is not what you want, consider converting JSON to a SQL numeric type with JSON_VALUE RETURNING +DROP TABLE t; +# +# Coverage test for fix_fields: check_cols. +# +SELECT JSON_ARRAYAGG((SELECT 1, 1)); +ERROR 21000: Operand should contain 1 column(s) +# +# Coverage test for fix_fields: resolve_type. +# +CREATE TABLE t2(gid int, a int); +SELECT JSON_ARRAYAGG(ST_PointFromText('POINT(10 10)')) FROM t2; +ERROR HY000: Incorrect arguments to json_arrayagg +# +# Coverage test for fix_fields: check_sum_func. +# +SELECT (SELECT JSON_ARRAYAGG(COUNT(a)) FROM t2) FROM t1; +ERROR HY000: Invalid use of group function +DROP TABLE t2; +# +# Empty table. +# +TRUNCATE TABLE t1; +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +SELECT JSON_ARRAYAGG(b) FROM t1; +JSON_ARRAYAGG(b) +NULL +SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +a JSON_OBJECTAGG(k, b) +SELECT JSON_OBJECTAGG(k, b) FROM t1; +JSON_OBJECTAGG(k, b) +NULL +# +# Tests for max_allowed_packet. +# +CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, x INT); +INSERT INTO t(x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +INSERT INTO t(x) SELECT t1.x from t t1, t t2, t t3; +SET GLOBAL net_buffer_length = 1024; +SET GLOBAL max_allowed_packet = 1024; +SELECT JSON_ARRAYAGG(x) FROM t; +JSON_ARRAYAGG(x) +NULL +Warnings: +Warning 1301 Result of json_arrayagg() was larger than max_allowed_packet (1024) - truncated +SELECT JSON_OBJECTAGG(id, x) FROM t; +JSON_OBJECTAGG(id, x) +NULL +Warnings: +Warning 1301 Result of json_objectagg() was larger than max_allowed_packet (1024) - truncated +SELECT id % 2 AS i, JSON_ARRAYAGG(x) FROM t GROUP BY i; +i JSON_ARRAYAGG(x) +0 NULL +1 NULL +Warnings: +Warning 1301 Result of json_arrayagg() was larger than max_allowed_packet (1024) - truncated +Warning 1301 Result of json_arrayagg() was larger than max_allowed_packet (1024) - truncated +SELECT id % 2 AS i, JSON_OBJECTAGG(id, x) FROM t GROUP BY i; +i JSON_OBJECTAGG(id, x) +0 NULL +1 NULL +Warnings: +Warning 1301 Result of json_objectagg() was larger than max_allowed_packet (1024) - truncated +Warning 1301 Result of json_objectagg() was larger than max_allowed_packet (1024) - truncated +SET GLOBAL max_allowed_packet = default; +SET GLOBAL net_buffer_length = default; +DROP TABLE t; +# +# Cleanup test. +# +DROP TABLE t1; +# +# Bug #24368053 +# WL#7987: ASSERTION `!TABLE || (!TABLE->WRITE_SET || BITMAP_IS_SET(TABLE-> ... +# +CREATE TABLE C(col_int int); +CREATE TABLE CC(col_int int); +INSERT INTO CC VALUES (1),(2),(3); +SELECT JSON_OBJECTAGG(table1.`col_int` ,table1.`col_int`) AS field2, +(SELECT JSON_ARRAYAGG(SUBQUERY2_t1.`col_int`) +FROM CC AS SUBQUERY2_t1 +WHERE SUBQUERY2_t1.`col_int` <> table1.`col_int`) AS field5 +FROM (CC AS table1) +WHERE (table1.`col_int` <> ALL (SELECT SUBQUERY4_t1.`col_int` + FROM (CC AS SUBQUERY4_t1 STRAIGHT_JOIN C))) +GROUP BY +field5; +field2 field5 +{"3": 3} [1, 2] +{"2": 2} [1, 3] +{"1": 1} [2, 3] +Warnings: +Warning 1235 This version of MySQL doesn't yet support 'sorting of non-scalar JSON values' +DROP TABLE C; +DROP TABLE CC; +# +# Bug #24367384 +# WL#7987: INNODB: ASSERTION FAILURE: ROW0SEL.CC:2558:FIELD->PREFIX_LEN > 0 ... +# +CREATE TABLE BB (pk INT AUTO_INCREMENT PRIMARY KEY, col_varchar_key VARCHAR(1)); +INSERT INTO BB VALUES(1,'a'); +SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 +WHERE t1.`col_varchar_key` <> t2.`col_varchar_key`) AS field2 +FROM BB as t2 +GROUP BY field2; +field2 +NULL +SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 +WHERE t1.`col_varchar_key` = t2.`col_varchar_key`) AS field2 +FROM BB as t2 +GROUP BY field2; +field2 +[1] +DROP TABLE BB; +# +# Bug #24365264 +# WL#7987: SIG 11 IN ITEM::MARK_FIELD_IN_MAP|SQL/ITEM.H +# +CREATE TABLE C (col_int int); +INSERT INTO C VALUES (1); +SELECT * +FROM C WHERE col_int < (SELECT JSON_ARRAYAGG(col_int) FROM C ) +ORDER BY col_int ; +col_int +1 +DROP TABLE C; +# +# Bug #24366341 +# WL#7987: SIG 6 IN JSON_WRAPPER::TYPE|SQL/JSON_DOM.CC +# +CREATE TABLE CC(col_varchar_key varchar(1)); +INSERT INTO CC VALUES ('a'); +SELECT JSON_ARRAYAGG(col_varchar_key) AS field1 FROM CC HAVING field1 > 9; +field1 +["a"] +SELECT JSON_OBJECTAGG(col_varchar_key, col_varchar_key) AS field1 FROM CC +HAVING (field1 <> 'a' AND field1 != 'e'); +field1 +{"a": "a"} +DROP TABLE CC; +# +# with ROLLUP + two/three groups +# +CREATE TABLE tg (g1 int, g2 int, k int, b VARCHAR(10)); +INSERT INTO tg VALUES +(1, 1, 1, "alfa"), +(1, 2, 2, null), +(2, 3, 3, "doi"), +(1, 1, 4, "unu"), +(3, 2, 5, "trei"), +(4, 3, 6, null), +(4, 1, 7, null), +(1, 2, 8, "one"); +SELECT g1, g2, JSON_ARRAYAGG(g2) FROM tg GROUP BY g1, g2 with rollup; +g1 g2 JSON_ARRAYAGG(g2) +1 1 [1, 1] +1 2 [2, 2] +1 NULL [1, 1, 2, 2] +2 3 [3] +2 NULL [3] +3 2 [2] +3 NULL [2] +4 1 [1] +4 3 [3] +4 NULL [1, 3] +NULL NULL [1, 1, 2, 2, 3, 2, 1, 3] +SELECT g1, g2, JSON_OBJECTAGG(k, g1) FROM tg GROUP BY g1, g2 with rollup; +g1 g2 JSON_OBJECTAGG(k, g1) +1 1 {"1": 1, "4": 1} +1 2 {"2": 1, "8": 1} +1 NULL {"1": 1, "2": 1, "4": 1, "8": 1} +2 3 {"3": 2} +2 NULL {"3": 2} +3 2 {"5": 3} +3 NULL {"5": 3} +4 1 {"7": 4} +4 3 {"6": 4} +4 NULL {"6": 4, "7": 4} +NULL NULL {"1": 1, "2": 1, "3": 2, "4": 1, "5": 3, "6": 4, "7": 4, "8": 1} +CREATE TABLE tg3 (g1 int, g2 int, g3 int, k int, b VARCHAR(10)); +INSERT INTO tg3 VALUES +(1, 1, 1, 1, "1.1.1"), +(1, 1, 2, 2, "1.1.2"), +(1, 1, 3, 3, "1.1.3"), +(1, 2, 1, 4, "1.2.1"), +(1, 2, 2, 5, "1.2.2"), +(1, 2, 3, 6, "1.2.3"), +(1, 3, 1, 7, "1.3.1"), +(1, 3, 2, 8, "1.3.2"), +(1, 3, 3, 9, "1.3.3"), +(2, 1, 1, 10, "2.1.1"), +(2, 1, 2, 11, "2.1.2"), +(2, 1, 3, 12, "2.1.3"), +(2, 2, 1, 13, "2.2.1"), +(2, 2, 2, 14, "2.2.2"), +(2, 2, 3, 15, "2.2.3"), +(2, 3, 1, 16, "2.3.1"), +(2, 3, 2, 17, "2.3.2"), +(2, 3, 3, 18, "2.3.3"), +(3, 1, 1, 19, "3.1.1"), +(3, 1, 2, 20, "3.1.2"), +(3, 1, 3, 21, "3.1.3"), +(3, 2, 1, 22, "3.2.1"), +(3, 2, 2, 23, "3.2.2"), +(3, 2, 3, 24, "3.2.3"), +(3, 3, 1, 25, "3.3.1"), +(3, 3, 2, 26, "3.3.2"), +(3, 3, 3, 27, "3.3.3"); +SELECT g1, g2, g3, JSON_ARRAYAGG(b) FROM tg3 GROUP BY g1, g2, g3 with rollup; +g1 g2 g3 JSON_ARRAYAGG(b) +1 1 1 ["1.1.1"] +1 1 2 ["1.1.2"] +1 1 3 ["1.1.3"] +1 1 NULL ["1.1.1", "1.1.2", "1.1.3"] +1 2 1 ["1.2.1"] +1 2 2 ["1.2.2"] +1 2 3 ["1.2.3"] +1 2 NULL ["1.2.1", "1.2.2", "1.2.3"] +1 3 1 ["1.3.1"] +1 3 2 ["1.3.2"] +1 3 3 ["1.3.3"] +1 3 NULL ["1.3.1", "1.3.2", "1.3.3"] +1 NULL NULL ["1.1.1", "1.1.2", "1.1.3", "1.2.1", "1.2.2", "1.2.3", "1.3.1", "1.3.2", "1.3.3"] +2 1 1 ["2.1.1"] +2 1 2 ["2.1.2"] +2 1 3 ["2.1.3"] +2 1 NULL ["2.1.1", "2.1.2", "2.1.3"] +2 2 1 ["2.2.1"] +2 2 2 ["2.2.2"] +2 2 3 ["2.2.3"] +2 2 NULL ["2.2.1", "2.2.2", "2.2.3"] +2 3 1 ["2.3.1"] +2 3 2 ["2.3.2"] +2 3 3 ["2.3.3"] +2 3 NULL ["2.3.1", "2.3.2", "2.3.3"] +2 NULL NULL ["2.1.1", "2.1.2", "2.1.3", "2.2.1", "2.2.2", "2.2.3", "2.3.1", "2.3.2", "2.3.3"] +3 1 1 ["3.1.1"] +3 1 2 ["3.1.2"] +3 1 3 ["3.1.3"] +3 1 NULL ["3.1.1", "3.1.2", "3.1.3"] +3 2 1 ["3.2.1"] +3 2 2 ["3.2.2"] +3 2 3 ["3.2.3"] +3 2 NULL ["3.2.1", "3.2.2", "3.2.3"] +3 3 1 ["3.3.1"] +3 3 2 ["3.3.2"] +3 3 3 ["3.3.3"] +3 3 NULL ["3.3.1", "3.3.2", "3.3.3"] +3 NULL NULL ["3.1.1", "3.1.2", "3.1.3", "3.2.1", "3.2.2", "3.2.3", "3.3.1", "3.3.2", "3.3.3"] +NULL NULL NULL ["1.1.1", "1.1.2", "1.1.3", "1.2.1", "1.2.2", "1.2.3", "1.3.1", "1.3.2", "1.3.3", "2.1.1", "2.1.2", "2.1.3", "2.2.1", "2.2.2", "2.2.3", "2.3.1", "2.3.2", "2.3.3", "3.1.1", "3.1.2", "3.1.3", "3.2.1", "3.2.2", "3.2.3", "3.3.1", "3.3.2", "3.3.3"] +SELECT g1, g2, g3, JSON_OBJECTAGG(k, b) FROM tg3 GROUP BY g1, g2, g3 with rollup; +g1 g2 g3 JSON_OBJECTAGG(k, b) +1 1 1 {"1": "1.1.1"} +1 1 2 {"2": "1.1.2"} +1 1 3 {"3": "1.1.3"} +1 1 NULL {"1": "1.1.1", "2": "1.1.2", "3": "1.1.3"} +1 2 1 {"4": "1.2.1"} +1 2 2 {"5": "1.2.2"} +1 2 3 {"6": "1.2.3"} +1 2 NULL {"4": "1.2.1", "5": "1.2.2", "6": "1.2.3"} +1 3 1 {"7": "1.3.1"} +1 3 2 {"8": "1.3.2"} +1 3 3 {"9": "1.3.3"} +1 3 NULL {"7": "1.3.1", "8": "1.3.2", "9": "1.3.3"} +1 NULL NULL {"1": "1.1.1", "2": "1.1.2", "3": "1.1.3", "4": "1.2.1", "5": "1.2.2", "6": "1.2.3", "7": "1.3.1", "8": "1.3.2", "9": "1.3.3"} +2 1 1 {"10": "2.1.1"} +2 1 2 {"11": "2.1.2"} +2 1 3 {"12": "2.1.3"} +2 1 NULL {"10": "2.1.1", "11": "2.1.2", "12": "2.1.3"} +2 2 1 {"13": "2.2.1"} +2 2 2 {"14": "2.2.2"} +2 2 3 {"15": "2.2.3"} +2 2 NULL {"13": "2.2.1", "14": "2.2.2", "15": "2.2.3"} +2 3 1 {"16": "2.3.1"} +2 3 2 {"17": "2.3.2"} +2 3 3 {"18": "2.3.3"} +2 3 NULL {"16": "2.3.1", "17": "2.3.2", "18": "2.3.3"} +2 NULL NULL {"10": "2.1.1", "11": "2.1.2", "12": "2.1.3", "13": "2.2.1", "14": "2.2.2", "15": "2.2.3", "16": "2.3.1", "17": "2.3.2", "18": "2.3.3"} +3 1 1 {"19": "3.1.1"} +3 1 2 {"20": "3.1.2"} +3 1 3 {"21": "3.1.3"} +3 1 NULL {"19": "3.1.1", "20": "3.1.2", "21": "3.1.3"} +3 2 1 {"22": "3.2.1"} +3 2 2 {"23": "3.2.2"} +3 2 3 {"24": "3.2.3"} +3 2 NULL {"22": "3.2.1", "23": "3.2.2", "24": "3.2.3"} +3 3 1 {"25": "3.3.1"} +3 3 2 {"26": "3.3.2"} +3 3 3 {"27": "3.3.3"} +3 3 NULL {"25": "3.3.1", "26": "3.3.2", "27": "3.3.3"} +3 NULL NULL {"19": "3.1.1", "20": "3.1.2", "21": "3.1.3", "22": "3.2.1", "23": "3.2.2", "24": "3.2.3", "25": "3.3.1", "26": "3.3.2", "27": "3.3.3"} +NULL NULL NULL {"1": "1.1.1", "2": "1.1.2", "3": "1.1.3", "4": "1.2.1", "5": "1.2.2", "6": "1.2.3", "7": "1.3.1", "8": "1.3.2", "9": "1.3.3", "10": "2.1.1", "11": "2.1.2", "12": "2.1.3", "13": "2.2.1", "14": "2.2.2", "15": "2.2.3", "16": "2.3.1", "17": "2.3.2", "18": "2.3.3", "19": "3.1.1", "20": "3.1.2", "21": "3.1.3", "22": "3.2.1", "23": "3.2.2", "24": "3.2.3", "25": "3.3.1", "26": "3.3.2", "27": "3.3.3"} +DROP TABLE tg; +DROP TABLE tg3; +# +# Tests duplicates for JSON_OBJECTAGG +# +CREATE TABLE t1 (a int, k int, b VARCHAR(10)); +INSERT INTO t1 VALUES +(1, 1, "1.1"), +(1, 1, "1.2"), +(1, 1, "1.3"), +(2, 2, "2.1"), +(2, 2, "2.2"), +(2, 2, "2.3"); +SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +a JSON_OBJECTAGG(k, b) +1 {"1": "1.3"} +2 {"2": "2.3"} +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["1.1", "1.2", "1.3"] +2 ["2.1", "2.2", "2.3"] +DROP TABLE t1; +# +# Tests with ORDER BY, DISTINCT +# +CREATE TABLE t1 (a int, k int, b VARCHAR(10)); +INSERT INTO t1 VALUES +(2, 8, "2.3"), +(1, 7, "1.1"), +(3, 6, "3.2"), +(2, 5, "2.2"), +(3, 9, "3.1"), +(1, 4, "1.2"), +(3, 3, "3.3"), +(2, 2, "2.1"), +(1, 1, "1.3"); +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["1.1", "1.2", "1.3"] +2 ["2.3", "2.2", "2.1"] +3 ["3.2", "3.1", "3.3"] +SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +a JSON_ARRAYAGG(b) +1 ["1.1", "1.2", "1.3"] +2 ["2.3", "2.2", "2.1"] +3 ["3.2", "3.1", "3.3"] +SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a ASC; +a JSON_ARRAYAGG(b) +1 ["1.1", "1.2", "1.3"] +2 ["2.3", "2.2", "2.1"] +3 ["3.2", "3.1", "3.3"] +SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a DESC; +a JSON_ARRAYAGG(b) +3 ["3.2", "3.1", "3.3"] +2 ["2.3", "2.2", "2.1"] +1 ["1.1", "1.2", "1.3"] +SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +a JSON_OBJECTAGG(k, b) +1 {"1": "1.3", "4": "1.2", "7": "1.1"} +2 {"2": "2.1", "5": "2.2", "8": "2.3"} +3 {"3": "3.3", "6": "3.2", "9": "3.1"} +SELECT DISTINCT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; +a JSON_OBJECTAGG(k, b) +1 {"1": "1.3", "4": "1.2", "7": "1.1"} +2 {"2": "2.1", "5": "2.2", "8": "2.3"} +3 {"3": "3.3", "6": "3.2", "9": "3.1"} +SELECT DISTINCT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a ORDER BY a ASC; +a JSON_OBJECTAGG(k, b) +1 {"1": "1.3", "4": "1.2", "7": "1.1"} +2 {"2": "2.1", "5": "2.2", "8": "2.3"} +3 {"3": "3.3", "6": "3.2", "9": "3.1"} +SELECT DISTINCT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a ORDER BY a DESC; +a JSON_OBJECTAGG(k, b) +3 {"3": "3.3", "6": "3.2", "9": "3.1"} +2 {"2": "2.1", "5": "2.2", "8": "2.3"} +1 {"1": "1.3", "4": "1.2", "7": "1.1"} +DROP TABLE t1; +# +# Tests with joins +# +CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, t1 INT, t2 INT); +CREATE TABLE p(id INT PRIMARY KEY AUTO_INCREMENT, p1 INT, p2 INT); +INSERT INTO t(t1, t2) VALUES (1, 1), (2, 1), (3,3), (1, 4); +INSERT INTO p(p1, p2) VALUES (2, 1), (1, 1), (3,3), (2, 4); +FLUSH STATUS; +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1; +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT t1, JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1 group by t1; +SHOW SESSION STATUS LIKE 'Handler_update%'; +Variable_name Value +Handler_update 0 +SELECT (SELECT 1 AS foo ORDER BY JSON_ARRAYAGG(t2)) AS x FROM t; +x +1 +SELECT t1 FROM t ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); +t1 +1 +2 +3 +1 +SELECT JSON_ARRAYAGG(t1) FROM t +ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); +JSON_ARRAYAGG(t1) +[1, 2, 3, 1] +SELECT (SELECT JSON_ARRAYAGG(t1_outer.t1) FROM t AS t1_inner LIMIT 1) as f +FROM t AS t1_outer GROUP BY t1_outer.t2; +f +[1, 2] +[3] +[1] +DROP TABLE t; +DROP TABLE p; +# +# Tests with JSON +# +CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, k INT, j JSON); +INSERT INTO t(k, j) VALUES +(1, '[1,2,3,4]'), +(2, '{"prop1": 1}'), +(1, '[3]'), +(2, '{"prop2": 2, "prop10": 10}'), +(1, '[99]'); +SELECT k, JSON_ARRAYAGG(j) FROM t GROUP BY k; +k JSON_ARRAYAGG(j) +1 [[1, 2, 3, 4], [3], [99]] +2 [{"prop1": 1}, {"prop2": 2, "prop10": 10}] +SELECT k, JSON_OBJECTAGG(id, j) FROM t GROUP BY k; +k JSON_OBJECTAGG(id, j) +1 {"1": [1, 2, 3, 4], "3": [3], "5": [99]} +2 {"2": {"prop1": 1}, "4": {"prop2": 2, "prop10": 10}} +DROP TABLE t; +# +# Coverage tests for val_* functions +# +CREATE TABLE t2(gid int, a int); +INSERT INTO t2(gid, a) VALUES (1, 1), (1, 2), (2, 4), (2, 8); +SELECT gid, 1.0 * JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; +gid 1.0 * JSON_ARRAYAGG(a) +1 0 +2 0 +Warnings: +Warning 3156 Invalid JSON value for CAST to DOUBLE from column json_arrayagg at row 1 +Warning 3156 Invalid JSON value for CAST to DOUBLE from column json_arrayagg at row 2 +SELECT gid, 0x30 << JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; +gid 0x30 << JSON_ARRAYAGG(a) +1 48 +2 48 +Warnings: +Warning 3156 Invalid JSON value for CAST to INTEGER from column json_arrayagg at row 1 +Warning 3156 Invalid JSON value for CAST to INTEGER from column json_arrayagg at row 2 +SELECT gid, DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) +FROM t2 GROUP BY gid; +gid DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) +1 NULL +2 NULL +Warnings: +Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 1 +Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 2 +SELECT gid, ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') +FROM t2 GROUP BY gid; +gid ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') +1 NULL +2 NULL +Warnings: +Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 1 +Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 2 +SELECT gid, SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2 GROUP BY gid; +gid SEC_TO_TIME(JSON_ARRAYAGG(a)) +1 00:00:00.000000 +2 00:00:00.000000 +Warnings: +Warning 3156 Invalid JSON value for CAST to DECIMAL from column json_arrayagg at row 1 +Warning 3156 Invalid JSON value for CAST to DECIMAL from column json_arrayagg at row 2 +TRUNCATE TABLE t2; +SELECT 1.0 * JSON_ARRAYAGG(a) FROM t2; +1.0 * JSON_ARRAYAGG(a) +NULL +SELECT 0x30 << JSON_ARRAYAGG(a) FROM t2; +0x30 << JSON_ARRAYAGG(a) +NULL +SELECT DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) FROM t2; +DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) +NULL +SELECT ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') FROM t2; +ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') +NULL +SELECT SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2; +SEC_TO_TIME(JSON_ARRAYAGG(a)) +NULL +DROP TABLE t2; diff --git a/mysql-test/suite/gis/t/st_shortest_path.test b/mysql-test/suite/gis/t/st_shortest_path.test new file mode 100644 index 000000000000..3dcdc1871db4 --- /dev/null +++ b/mysql-test/suite/gis/t/st_shortest_path.test @@ -0,0 +1,497 @@ +############################################################################### +# # +# Tests JSON aggregation functions added in WL#7987 # +# # +############################################################################### + + +--echo # +--echo # Setup test. +--echo # + +CREATE TABLE t1 (a int, k int, b VARCHAR(10)) CHARSET utf8mb4; +INSERT INTO t1 VALUES +(1, 1, "alfa"), +(1, 2, null), +(2, 3, "doi"), +(1, 4, "unu"), +(3, 5, "trei"), +(4, 6, null), +(4, 7, null), +(1, 8, "one"); + +--echo # +--echo # Test JSON_ARRAYAGG. +--echo # + +FLUSH STATUS; +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SHOW SESSION STATUS LIKE 'Handler_update%'; +SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SHOW SESSION STATUS LIKE 'Handler_update%'; +SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SHOW SESSION STATUS LIKE 'Handler_update%'; + +SELECT JSON_ARRAYAGG(b) FROM t1; +SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; +SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; +SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; + +PREPARE p1 FROM "SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; +EXECUTE p1; +EXECUTE p1; +deallocate prepare p1; + +PREPARE p3 FROM +"SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; +EXECUTE p3; +EXECUTE p3; +deallocate prepare p3; + +PREPARE p4 FROM "SELECT JSON_ARRAYAGG(b) FROM t1"; +EXECUTE p4; +EXECUTE p4; +deallocate prepare p4; + + +SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1; + +PREPARE p1 FROM +"SELECT a, JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1 GROUP BY a"; +EXECUTE p1; +EXECUTE p1; +deallocate prepare p1; + + +PREPARE p4 FROM +"SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1"; +EXECUTE p4; +EXECUTE p4; +deallocate prepare p4; + + +ANALYZE TABLE t1; + +EXPLAIN SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +EXPLAIN SELECT JSON_ARRAYAGG(b) FROM t1; + +EXPLAIN FORMAT=json SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) +FROM t1 +GROUP BY a; +EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) +FROM t1 +GROUP BY a; +EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) +FROM t1 +GROUP BY a; + +EXPLAIN FORMAT=json SELECT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; +EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; + +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a WITH ROLLUP; +SELECT a, JSON_ARRAYAGG(b) as jarray +FROM t1 +GROUP BY a +HAVING jarray= JSON_ARRAY("trei"); + +--echo # +--echo # Test ST_SHORTEST_DIR_PATH. +--echo # + +FLUSH STATUS; +SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +SHOW SESSION STATUS LIKE 'Handler_update%'; +SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; +SHOW SESSION STATUS LIKE 'Handler_update%'; + +PREPARE p1 FROM "SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a"; +EXECUTE p1; +EXECUTE p1; +deallocate prepare p1; + +PREPARE p4 FROM "SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1"; +EXECUTE p4; +EXECUTE p4; +deallocate prepare p4; + +SELECT a, JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') FROM t1 GROUP BY a; +SELECT JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') FROM t1; + +PREPARE p1 FROM +"SELECT a, JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') +FROM t1 +GROUP BY a"; +EXECUTE p1; +EXECUTE p1; +deallocate prepare p1; + + +PREPARE p4 FROM +"SELECT JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') FROM t1"; +EXECUTE p4; +EXECUTE p4; +deallocate prepare p4; + +ANALYZE TABLE t1; + +EXPLAIN SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +EXPLAIN SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; + +EXPLAIN FORMAT=json SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +EXPLAIN FORMAT=json SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; + +SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a WITH ROLLUP; +SELECT a, ST_SHORTEST_DIR_PATH(k, b) as jobject +FROM t1 +GROUP BY a +HAVING jobject = JSON_OBJECT(3, "doi"); + +--echo # +--echo # NULL values. +--echo # + +SELECT a, JSON_ARRAYAGG(null) FROM t1 GROUP BY a; +SELECT JSON_ARRAYAGG(null) FROM t1; + +SELECT a, ST_SHORTEST_DIR_PATH(k, null) FROM t1 GROUP BY a; +SELECT ST_SHORTEST_DIR_PATH(k, null) FROM t1; + + +--error ER_JSON_DOCUMENT_NULL_KEY +SELECT a, ST_SHORTEST_DIR_PATH(null, b) FROM t1 GROUP BY a; + +--error ER_JSON_DOCUMENT_NULL_KEY +SELECT ST_SHORTEST_DIR_PATH(null, b) FROM t1; + +--echo # +--echo # Coverage test for fix_fields: Disable_semijoin_flattening. +--echo # + +CREATE TABLE t(a INT); +ANALYZE TABLE t1; +EXPLAIN format=json SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); +SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); +DROP TABLE t; + +--echo # +--echo # Coverage test for fix_fields: check_cols. +--echo # + +--error ER_OPERAND_COLUMNS +SELECT JSON_ARRAYAGG((SELECT 1, 1)); + +--echo # +--echo # Coverage test for fix_fields: resolve_type. +--echo # + +CREATE TABLE t2(gid int, a int); +--error ER_WRONG_ARGUMENTS +SELECT JSON_ARRAYAGG(ST_PointFromText('POINT(10 10)')) FROM t2; + +--echo # +--echo # Coverage test for fix_fields: check_sum_func. +--echo # + +--error ER_INVALID_GROUP_FUNC_USE +SELECT (SELECT JSON_ARRAYAGG(COUNT(a)) FROM t2) FROM t1; + +DROP TABLE t2; + +--echo # +--echo # Empty table. +--echo # + +TRUNCATE TABLE t1; + +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SELECT JSON_ARRAYAGG(b) FROM t1; +SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; + +--echo # +--echo # Tests for max_allowed_packet. +--echo # + +CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, x INT); +INSERT INTO t(x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +INSERT INTO t(x) SELECT t1.x from t t1, t t2, t t3; + +SET GLOBAL net_buffer_length = 1024; +SET GLOBAL max_allowed_packet = 1024; +CONNECT (con1,localhost,root,,); +SELECT JSON_ARRAYAGG(x) FROM t; +SELECT ST_SHORTEST_DIR_PATH(id, x) FROM t; +SELECT id % 2 AS i, JSON_ARRAYAGG(x) FROM t GROUP BY i; +SELECT id % 2 AS i, ST_SHORTEST_DIR_PATH(id, x) FROM t GROUP BY i; +CONNECTION default; +DISCONNECT con1; +SET GLOBAL max_allowed_packet = default; +SET GLOBAL net_buffer_length = default; + +DROP TABLE t; + +--echo # +--echo # Cleanup test. +--echo # + +DROP TABLE t1; + + +--echo # +--echo # Bug #24368053 +--echo # WL#7987: ASSERTION `!TABLE || (!TABLE->WRITE_SET || BITMAP_IS_SET(TABLE-> ... +--echo # + +CREATE TABLE C(col_int int); +CREATE TABLE CC(col_int int); +INSERT INTO CC VALUES (1),(2),(3); + +SELECT ST_SHORTEST_DIR_PATH(table1.`col_int` ,table1.`col_int`) AS field2, +(SELECT JSON_ARRAYAGG(SUBQUERY2_t1.`col_int`) + FROM CC AS SUBQUERY2_t1 + WHERE SUBQUERY2_t1.`col_int` <> table1.`col_int`) AS field5 +FROM (CC AS table1) +WHERE (table1.`col_int` <> ALL (SELECT SUBQUERY4_t1.`col_int` + FROM (CC AS SUBQUERY4_t1 STRAIGHT_JOIN C))) +GROUP BY +field5; + +DROP TABLE C; +DROP TABLE CC; + +--echo # +--echo # Bug #24367384 +--echo # WL#7987: INNODB: ASSERTION FAILURE: ROW0SEL.CC:2558:FIELD->PREFIX_LEN > 0 ... +--echo # +CREATE TABLE BB (pk INT AUTO_INCREMENT PRIMARY KEY, col_varchar_key VARCHAR(1)); +INSERT INTO BB VALUES(1,'a'); + +--skip_if_hypergraph # Different warnings. +SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 + WHERE t1.`col_varchar_key` <> t2.`col_varchar_key`) AS field2 +FROM BB as t2 +GROUP BY field2; + +--skip_if_hypergraph # Different warnings. +SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 + WHERE t1.`col_varchar_key` = t2.`col_varchar_key`) AS field2 +FROM BB as t2 +GROUP BY field2; + +DROP TABLE BB; + +--echo # +--echo # Bug #24365264 +--echo # WL#7987: SIG 11 IN ITEM::MARK_FIELD_IN_MAP|SQL/ITEM.H +--echo # + +CREATE TABLE C (col_int int); +INSERT INTO C VALUES (1); + +SELECT * +FROM C WHERE col_int < (SELECT JSON_ARRAYAGG(col_int) FROM C ) +ORDER BY col_int ; + +DROP TABLE C; + + +--echo # +--echo # Bug #24366341 +--echo # WL#7987: SIG 6 IN JSON_WRAPPER::TYPE|SQL/JSON_DOM.CC +--echo # + +CREATE TABLE CC(col_varchar_key varchar(1)); +INSERT INTO CC VALUES ('a'); + +SELECT JSON_ARRAYAGG(col_varchar_key) AS field1 FROM CC HAVING field1 > 9; + +SELECT ST_SHORTEST_DIR_PATH(col_varchar_key, col_varchar_key) AS field1 FROM CC +HAVING (field1 <> 'a' AND field1 != 'e'); + +DROP TABLE CC; + +--echo # +--echo # with ROLLUP + two/three groups +--echo # + +CREATE TABLE tg (g1 int, g2 int, k int, b VARCHAR(10)); +INSERT INTO tg VALUES +(1, 1, 1, "alfa"), +(1, 2, 2, null), +(2, 3, 3, "doi"), +(1, 1, 4, "unu"), +(3, 2, 5, "trei"), +(4, 3, 6, null), +(4, 1, 7, null), +(1, 2, 8, "one"); + + +SELECT g1, g2, JSON_ARRAYAGG(g2) FROM tg GROUP BY g1, g2 with rollup; +SELECT g1, g2, ST_SHORTEST_DIR_PATH(k, g1) FROM tg GROUP BY g1, g2 with rollup; + + +CREATE TABLE tg3 (g1 int, g2 int, g3 int, k int, b VARCHAR(10)); +INSERT INTO tg3 VALUES +(1, 1, 1, 1, "1.1.1"), +(1, 1, 2, 2, "1.1.2"), +(1, 1, 3, 3, "1.1.3"), +(1, 2, 1, 4, "1.2.1"), +(1, 2, 2, 5, "1.2.2"), +(1, 2, 3, 6, "1.2.3"), +(1, 3, 1, 7, "1.3.1"), +(1, 3, 2, 8, "1.3.2"), +(1, 3, 3, 9, "1.3.3"), +(2, 1, 1, 10, "2.1.1"), +(2, 1, 2, 11, "2.1.2"), +(2, 1, 3, 12, "2.1.3"), +(2, 2, 1, 13, "2.2.1"), +(2, 2, 2, 14, "2.2.2"), +(2, 2, 3, 15, "2.2.3"), +(2, 3, 1, 16, "2.3.1"), +(2, 3, 2, 17, "2.3.2"), +(2, 3, 3, 18, "2.3.3"), +(3, 1, 1, 19, "3.1.1"), +(3, 1, 2, 20, "3.1.2"), +(3, 1, 3, 21, "3.1.3"), +(3, 2, 1, 22, "3.2.1"), +(3, 2, 2, 23, "3.2.2"), +(3, 2, 3, 24, "3.2.3"), +(3, 3, 1, 25, "3.3.1"), +(3, 3, 2, 26, "3.3.2"), +(3, 3, 3, 27, "3.3.3"); + + +SELECT g1, g2, g3, JSON_ARRAYAGG(b) FROM tg3 GROUP BY g1, g2, g3 with rollup; +SELECT g1, g2, g3, ST_SHORTEST_DIR_PATH(k, b) FROM tg3 GROUP BY g1, g2, g3 with rollup; + + +DROP TABLE tg; +DROP TABLE tg3; + +--echo # +--echo # Tests duplicates for ST_SHORTEST_DIR_PATH +--echo # + +CREATE TABLE t1 (a int, k int, b VARCHAR(10)); +INSERT INTO t1 VALUES +(1, 1, "1.1"), +(1, 1, "1.2"), +(1, 1, "1.3"), +(2, 2, "2.1"), +(2, 2, "2.2"), +(2, 2, "2.3"); + +SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; + +DROP TABLE t1; + +--echo # +--echo # Tests with ORDER BY, DISTINCT +--echo # + +CREATE TABLE t1 (a int, k int, b VARCHAR(10)); +INSERT INTO t1 VALUES +(2, 8, "2.3"), +(1, 7, "1.1"), +(3, 6, "3.2"), +(2, 5, "2.2"), +(3, 9, "3.1"), +(1, 4, "1.2"), +(3, 3, "3.3"), +(2, 2, "2.1"), +(1, 1, "1.3"); + +SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; +SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a ASC; +SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a DESC; + +SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +SELECT DISTINCT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; +SELECT DISTINCT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a ORDER BY a ASC; +SELECT DISTINCT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a ORDER BY a DESC; + +DROP TABLE t1; + +--echo # +--echo # Tests with joins +--echo # + +CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, t1 INT, t2 INT); +CREATE TABLE p(id INT PRIMARY KEY AUTO_INCREMENT, p1 INT, p2 INT); + +INSERT INTO t(t1, t2) VALUES (1, 1), (2, 1), (3,3), (1, 4); +INSERT INTO p(p1, p2) VALUES (2, 1), (1, 1), (3,3), (2, 4); + +FLUSH STATUS; +SHOW SESSION STATUS LIKE 'Handler_update%'; +#Since the JSON_*AGG does not have the ORDER BY clause order is not predictable +disable_result_log; +SELECT JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1; +enable_result_log; +SHOW SESSION STATUS LIKE 'Handler_update%'; +disable_result_log; +SELECT t1, JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1 group by t1; +enable_result_log; +SHOW SESSION STATUS LIKE 'Handler_update%'; + +# Subquery in ORDER BY with outer reference +SELECT (SELECT 1 AS foo ORDER BY JSON_ARRAYAGG(t2)) AS x FROM t; +--skip_if_hypergraph # Different warnings. +SELECT t1 FROM t ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); +SELECT JSON_ARRAYAGG(t1) FROM t +ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); + +SELECT (SELECT JSON_ARRAYAGG(t1_outer.t1) FROM t AS t1_inner LIMIT 1) as f +FROM t AS t1_outer GROUP BY t1_outer.t2; + +DROP TABLE t; +DROP TABLE p; + +--echo # +--echo # Tests with JSON +--echo # + +CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, k INT, j JSON); +INSERT INTO t(k, j) VALUES +(1, '[1,2,3,4]'), +(2, '{"prop1": 1}'), +(1, '[3]'), +(2, '{"prop2": 2, "prop10": 10}'), +(1, '[99]'); + + +SELECT k, JSON_ARRAYAGG(j) FROM t GROUP BY k; +SELECT k, ST_SHORTEST_DIR_PATH(id, j) FROM t GROUP BY k; + +DROP TABLE t; + +--echo # +--echo # Coverage tests for val_* functions +--echo # + +CREATE TABLE t2(gid int, a int); +INSERT INTO t2(gid, a) VALUES (1, 1), (1, 2), (2, 4), (2, 8); + +SELECT gid, 1.0 * JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; +SELECT gid, 0x30 << JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; +SELECT gid, DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) +FROM t2 GROUP BY gid; +SELECT gid, ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') +FROM t2 GROUP BY gid; +SELECT gid, SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2 GROUP BY gid; + +TRUNCATE TABLE t2; + +SELECT 1.0 * JSON_ARRAYAGG(a) FROM t2; +SELECT 0x30 << JSON_ARRAYAGG(a) FROM t2; +SELECT DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) FROM t2; +SELECT ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') FROM t2; +SELECT SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2; + +DROP TABLE t2; \ No newline at end of file diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 96b5133386a4..90c133f12486 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -405,6 +405,7 @@ SET(SQL_SHARED_SOURCES item_strfunc.cc item_subselect.cc item_sum.cc + a_star.cc window.cc item_timefunc.cc item_xmlfunc.cc diff --git a/sql/a_star.cc b/sql/a_star.cc new file mode 100644 index 000000000000..4fba4cd92189 --- /dev/null +++ b/sql/a_star.cc @@ -0,0 +1,199 @@ +#include "sql/a_star.h" + +#include +#include +#include +#include +#include +#include +#include // std::forward + +#include "decimal.h" +#include "my_alloc.h" +#include "my_base.h" +#include "my_byteorder.h" +#include "my_compare.h" +#include "my_dbug.h" +#include "my_double2ulonglong.h" +#include "my_sys.h" +#include "mysql_com.h" +#include "mysqld_error.h" +#include "sql/aggregate_check.h" // Distinct_check +#include "sql/create_field.h" +#include "sql/current_thd.h" // current_thd +#include "sql/dd/cache/dictionary_client.h" +#include "sql/derror.h" // ER_THD +#include "sql/field.h" +#include "sql/gis/gc_utils.h" +#include "sql/gis/geometries.h" +#include "sql/gis/geometry_extraction.h" +#include "sql/gis/relops.h" +#include "sql/handler.h" +#include "sql/item_cmpfunc.h" +#include "sql/item_func.h" +#include "sql/item_json_func.h" +#include "sql/item_subselect.h" +#include "sql/json_dom.h" +#include "sql/key_spec.h" +#include "sql/mysqld.h" +#include "sql/parse_tree_helpers.h" // PT_item_list +#include "sql/parse_tree_node_base.h" // Parse_context +#include "sql/parse_tree_nodes.h" // PT_order_list +#include "sql/parser_yystype.h" +#include "sql/sql_array.h" +#include "sql/sql_class.h" // THD +#include "sql/sql_const.h" +#include "sql/sql_error.h" +#include "sql/sql_exception_handler.h" // handle_std_exception +#include "sql/sql_executor.h" +#include "sql/sql_lex.h" +#include "sql/sql_list.h" +#include "sql/sql_resolver.h" // setup_order +#include "sql/sql_select.h" +#include "sql/sql_tmp_table.h" // create_tmp_table +#include "sql/srs_fetcher.h" // Srs_fetcher +#include "sql/system_variables.h" +#include "sql/table.h" +#include "sql/temp_table_param.h" // Temp_table_param +#include "sql/uniques.h" // Unique +#include "sql/window.h" + +a_star_ting::a_star_ting( + THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper, + unique_ptr_destroy_only object) + : Item_sum_json(std::move(wrapper), thd, item), + m_json_object(std::move(object)) {} + +a_star_ting::a_star_ting( + const POS &pos, Item *a, Item *b, PT_window *w, + unique_ptr_destroy_only wrapper, + unique_ptr_destroy_only object) + : Item_sum_json(std::move(wrapper), pos, a, b, w), + m_json_object(std::move(object)) {} + +a_star_ting::~a_star_ting() = default; + +void a_star_ting::clear() { + null_value = true; + m_json_object->clear(); + + // Set the object to the m_wrapper, but let a_star_ting keep the + // ownership. + *m_wrapper = Json_wrapper(m_json_object.get(), true); + m_key_map.clear(); +} + +bool a_star_ting::add() { + assert(fixed == 1); + assert(arg_count == 2); + + const THD *thd = base_query_block->parent_lex->thd; + /* + Checking if an error happened inside one of the functions that have no + way of returning an error status. (reset_field(), update_field() or + clear()) + */ + if (thd->is_error()) return error_json(); + + try { + // key + Item *key_item = args[0]; + const char *safep; // contents of key_item, possibly converted + size_t safe_length; // length of safep + + if (get_json_object_member_name(thd, key_item, &m_tmp_key_value, + &m_conversion_buffer, &safep, &safe_length)) + return error_json(); + + std::string key(safep, safe_length); + if (m_is_window_function) { + /* + When a row is leaving a frame, we have two options: + 1. If rows are ordered according to the "key", then remove + the key/value pair from Json_object if this row is the + last row in peerset for that key. + 2. If unordered, reduce the count in the key map for this key. + If the count is 0, remove the key/value pair from the Json_object. + */ + if (m_window->do_inverse()) { + auto object = down_cast(m_wrapper->to_dom(thd)); + if (m_optimize) // Option 1 + { + if (m_window->is_last_row_in_peerset_within_frame()) + object->remove(key); + } else // Option 2 + { + auto it = m_key_map.find(key); + if (it != m_key_map.end()) { + int count = it->second - 1; + if (count > 0) { + it->second = count; + } else { + m_key_map.erase(it); + object->remove(key); + } + } + } + object->cardinality() == 0 ? null_value = true : null_value = false; + return false; + } + } + // value + Json_wrapper value_wrapper; + if (get_atom_null_as_null(args, 1, func_name(), &m_value, + &m_conversion_buffer, &value_wrapper)) + return error_json(); + + /* + The m_wrapper always points to m_json_object or the result of + deserializing the result_field in reset/update_field. + */ + Json_object *object = down_cast(m_wrapper->to_dom(thd)); + if (object->add_alias(key, value_wrapper.to_dom(thd))) + return error_json(); /* purecov: inspected */ + /* + If rows in the window are not ordered based on "key", add this key + to the key map. + */ + if (m_is_window_function && !m_optimize) { + int count = 1; + auto it = m_key_map.find(key); + if (it != m_key_map.end()) { + count = count + it->second; + it->second = count; + } else + m_key_map.emplace(std::make_pair(key, count)); + } + + null_value = false; + // object will take ownership of the value + value_wrapper.set_alias(); + } catch (...) { + /* purecov: begin inspected */ + handle_std_exception(func_name()); + return error_json(); + /* purecov: end */ + } + + return false; +} + +Item *a_star_ting::copy_or_same(THD *thd) { + if (m_is_window_function) return this; + + auto wrapper = make_unique_destroy_only(thd->mem_root); + if (wrapper == nullptr) return nullptr; + + unique_ptr_destroy_only object{::new (thd->mem_root) + Json_object}; + if (object == nullptr) return nullptr; + + return new (thd->mem_root) + a_star_ting(thd, this, std::move(wrapper), std::move(object)); +} + +bool a_star_ting::check_wf_semantics1(THD *thd, Query_block *select, + Window_evaluation_requirements *reqs) { + return Item_sum::check_wf_semantics1(thd, select, reqs); +} + diff --git a/sql/a_star.h b/sql/a_star.h new file mode 100644 index 000000000000..2ec560dfbf09 --- /dev/null +++ b/sql/a_star.h @@ -0,0 +1,38 @@ +#include "sql/item_sum.h" + +class a_star_ting final : public Item_sum_json { + /// Accumulates the final value. + unique_ptr_destroy_only m_json_object; + /// Buffer used to get the value of the key. + String m_tmp_key_value; + /** + Map of keys in Json_object and the count for each key + within a window frame. It is used in handling rows + leaving a window frame when rows are not sorted + according to the key in Json_object. + */ + std::map m_key_map; + /** + If window provides ordering on the key in Json_object, + a key_map is not needed to handle rows leaving a window + frame. In this case, process_buffered_windowing_record() + will set flags when a key/value pair can be removed from + the Json_object. + */ + bool m_optimize{false}; + + public: + a_star_ting(THD *thd, Item_sum *item, + unique_ptr_destroy_only wrapper, + unique_ptr_destroy_only object); + a_star_ting(const POS &pos, Item *a, Item *b, PT_window *w, + unique_ptr_destroy_only wrapper, + unique_ptr_destroy_only object); + ~a_star_ting() override; + const char *func_name() const override { return "json_objectagg"; } + void clear() override; + bool add() override; + Item *copy_or_same(THD *thd) override; + bool check_wf_semantics1(THD *thd, Query_block *select, + Window_evaluation_requirements *reqs) override; +}; \ No newline at end of file diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc index 05cd93d0bd83..06cc29eb0a3c 100644 --- a/sql/gen_lex_token.cc +++ b/sql/gen_lex_token.cc @@ -144,7 +144,7 @@ struct gen_lex_token_string { - mode named tokens from bison (sql_yacc.yy). See also YYMAXUTOK. */ -const int MY_MAX_TOKEN = 1200; +const int MY_MAX_TOKEN = 1201; gen_lex_token_string compiled_token_array[MY_MAX_TOKEN]; diff --git a/sql/lex.h b/sql/lex.h index aa69731bdf22..ed232c45b341 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -855,6 +855,7 @@ static const SYMBOL symbols[] = { {SYM_FN("STDDEV_POP", STD_SYM)}, {SYM_FN("STDDEV_SAMP", STDDEV_SAMP_SYM)}, {SYM_FN("ST_COLLECT", ST_COLLECT_SYM)}, + {SYM_FN("ST_SHORTEST_DIR_PATH", ST_SHORTEST_DIR_PATH_SYM)}, {SYM_FN("SUBDATE", SUBDATE_SYM)}, {SYM_FN("SUBSTR", SUBSTRING)}, {SYM_FN("SUBSTRING", SUBSTRING)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5b72e68f645d..5117b824a942 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -103,6 +103,7 @@ Note: YYTHD is passed as an argument to yyparse(), and subsequently to yylex(). #include "sql/item_strfunc.h" #include "sql/item_subselect.h" #include "sql/item_sum.h" +#include "sql/a_star.h" #include "sql/item_timefunc.h" #include "sql/json_dom.h" #include "sql/json_syntax_check.h" // is_valid_json_syntax @@ -1367,6 +1368,7 @@ void warn_about_deprecated_binary(THD *thd) %token CHALLENGE_RESPONSE_SYM 1198 /* MYSQL */ %token GTID_ONLY_SYM 1199 /* MYSQL */ +%token ST_SHORTEST_DIR_PATH_SYM 1200 /*MYSQL*/ /* Precedence rules used to resolve the ambiguity when using keywords as idents @@ -11102,6 +11104,16 @@ sum_expr: $$ = NEW_PTN Item_sum_json_object( @$, $3, $5, $7, std::move(wrapper), std::move(object)); } + | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ')' opt_windowing_clause + { + auto wrapper = make_unique_destroy_only(YYMEM_ROOT); + if (wrapper == nullptr) YYABORT; + unique_ptr_destroy_only object{::new (YYMEM_ROOT) + Json_object}; + if (object == nullptr) YYABORT; + $$ = NEW_PTN a_star_ting ( + @$, $3, $5, $7, std::move(wrapper), std::move(object)); + } | ST_COLLECT_SYM '(' in_sum_expr ')' opt_windowing_clause { $$= NEW_PTN Item_sum_collect(@$, $3, $5, false); From 89c7830682ba2bcba42898da411a65a8362468e6 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 15 Mar 2022 00:03:46 +0100 Subject: [PATCH 02/67] merge & st_sdp now takes 6 args --- mysql-test/suite/gis/t/st_shortest_path.test | 505 +------------------ sql/a_star.cc | 24 +- sql/a_star.h | 39 +- sql/item_sum.cc | 8 +- sql/item_sum.h | 8 +- sql/sql_yacc.yy | 14 +- 6 files changed, 71 insertions(+), 527 deletions(-) diff --git a/mysql-test/suite/gis/t/st_shortest_path.test b/mysql-test/suite/gis/t/st_shortest_path.test index 3dcdc1871db4..4ac833a6450b 100644 --- a/mysql-test/suite/gis/t/st_shortest_path.test +++ b/mysql-test/suite/gis/t/st_shortest_path.test @@ -1,497 +1,14 @@ -############################################################################### -# # -# Tests JSON aggregation functions added in WL#7987 # -# # -############################################################################### +create table paths( + id int primary key, + from_id int, + to_id int, + cost double +); +insert into paths values(0, 0, 1, 5.0); +insert into paths values(1, 1, 2, 5.0); +insert into paths values(2, 0, 2, 15.0); ---echo # ---echo # Setup test. ---echo # +select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; -CREATE TABLE t1 (a int, k int, b VARCHAR(10)) CHARSET utf8mb4; -INSERT INTO t1 VALUES -(1, 1, "alfa"), -(1, 2, null), -(2, 3, "doi"), -(1, 4, "unu"), -(3, 5, "trei"), -(4, 6, null), -(4, 7, null), -(1, 8, "one"); - ---echo # ---echo # Test JSON_ARRAYAGG. ---echo # - -FLUSH STATUS; -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SHOW SESSION STATUS LIKE 'Handler_update%'; -SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SHOW SESSION STATUS LIKE 'Handler_update%'; -SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SHOW SESSION STATUS LIKE 'Handler_update%'; - -SELECT JSON_ARRAYAGG(b) FROM t1; -SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; -SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; -SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; - -PREPARE p1 FROM "SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; -EXECUTE p1; -EXECUTE p1; -deallocate prepare p1; - -PREPARE p3 FROM -"SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; -EXECUTE p3; -EXECUTE p3; -deallocate prepare p3; - -PREPARE p4 FROM "SELECT JSON_ARRAYAGG(b) FROM t1"; -EXECUTE p4; -EXECUTE p4; -deallocate prepare p4; - - -SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1; - -PREPARE p1 FROM -"SELECT a, JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1 GROUP BY a"; -EXECUTE p1; -EXECUTE p1; -deallocate prepare p1; - - -PREPARE p4 FROM -"SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1"; -EXECUTE p4; -EXECUTE p4; -deallocate prepare p4; - - -ANALYZE TABLE t1; - -EXPLAIN SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -EXPLAIN SELECT JSON_ARRAYAGG(b) FROM t1; - -EXPLAIN FORMAT=json SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) -FROM t1 -GROUP BY a; -EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) -FROM t1 -GROUP BY a; -EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) -FROM t1 -GROUP BY a; - -EXPLAIN FORMAT=json SELECT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; - -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a WITH ROLLUP; -SELECT a, JSON_ARRAYAGG(b) as jarray -FROM t1 -GROUP BY a -HAVING jarray= JSON_ARRAY("trei"); - ---echo # ---echo # Test ST_SHORTEST_DIR_PATH. ---echo # - -FLUSH STATUS; -SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -SHOW SESSION STATUS LIKE 'Handler_update%'; -SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; -SHOW SESSION STATUS LIKE 'Handler_update%'; - -PREPARE p1 FROM "SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a"; -EXECUTE p1; -EXECUTE p1; -deallocate prepare p1; - -PREPARE p4 FROM "SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1"; -EXECUTE p4; -EXECUTE p4; -deallocate prepare p4; - -SELECT a, JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') FROM t1 GROUP BY a; -SELECT JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') FROM t1; - -PREPARE p1 FROM -"SELECT a, JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') -FROM t1 -GROUP BY a"; -EXECUTE p1; -EXECUTE p1; -deallocate prepare p1; - - -PREPARE p4 FROM -"SELECT JSON_MERGE_PRESERVE(ST_SHORTEST_DIR_PATH(k, b), '[true, false]') FROM t1"; -EXECUTE p4; -EXECUTE p4; -deallocate prepare p4; - -ANALYZE TABLE t1; - -EXPLAIN SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -EXPLAIN SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; - -EXPLAIN FORMAT=json SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -EXPLAIN FORMAT=json SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; - -SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a WITH ROLLUP; -SELECT a, ST_SHORTEST_DIR_PATH(k, b) as jobject -FROM t1 -GROUP BY a -HAVING jobject = JSON_OBJECT(3, "doi"); - ---echo # ---echo # NULL values. ---echo # - -SELECT a, JSON_ARRAYAGG(null) FROM t1 GROUP BY a; -SELECT JSON_ARRAYAGG(null) FROM t1; - -SELECT a, ST_SHORTEST_DIR_PATH(k, null) FROM t1 GROUP BY a; -SELECT ST_SHORTEST_DIR_PATH(k, null) FROM t1; - - ---error ER_JSON_DOCUMENT_NULL_KEY -SELECT a, ST_SHORTEST_DIR_PATH(null, b) FROM t1 GROUP BY a; - ---error ER_JSON_DOCUMENT_NULL_KEY -SELECT ST_SHORTEST_DIR_PATH(null, b) FROM t1; - ---echo # ---echo # Coverage test for fix_fields: Disable_semijoin_flattening. ---echo # - -CREATE TABLE t(a INT); -ANALYZE TABLE t1; -EXPLAIN format=json SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); -SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); -DROP TABLE t; - ---echo # ---echo # Coverage test for fix_fields: check_cols. ---echo # - ---error ER_OPERAND_COLUMNS -SELECT JSON_ARRAYAGG((SELECT 1, 1)); - ---echo # ---echo # Coverage test for fix_fields: resolve_type. ---echo # - -CREATE TABLE t2(gid int, a int); ---error ER_WRONG_ARGUMENTS -SELECT JSON_ARRAYAGG(ST_PointFromText('POINT(10 10)')) FROM t2; - ---echo # ---echo # Coverage test for fix_fields: check_sum_func. ---echo # - ---error ER_INVALID_GROUP_FUNC_USE -SELECT (SELECT JSON_ARRAYAGG(COUNT(a)) FROM t2) FROM t1; - -DROP TABLE t2; - ---echo # ---echo # Empty table. ---echo # - -TRUNCATE TABLE t1; - -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SELECT JSON_ARRAYAGG(b) FROM t1; -SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -SELECT ST_SHORTEST_DIR_PATH(k, b) FROM t1; - ---echo # ---echo # Tests for max_allowed_packet. ---echo # - -CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, x INT); -INSERT INTO t(x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); -INSERT INTO t(x) SELECT t1.x from t t1, t t2, t t3; - -SET GLOBAL net_buffer_length = 1024; -SET GLOBAL max_allowed_packet = 1024; -CONNECT (con1,localhost,root,,); -SELECT JSON_ARRAYAGG(x) FROM t; -SELECT ST_SHORTEST_DIR_PATH(id, x) FROM t; -SELECT id % 2 AS i, JSON_ARRAYAGG(x) FROM t GROUP BY i; -SELECT id % 2 AS i, ST_SHORTEST_DIR_PATH(id, x) FROM t GROUP BY i; -CONNECTION default; -DISCONNECT con1; -SET GLOBAL max_allowed_packet = default; -SET GLOBAL net_buffer_length = default; - -DROP TABLE t; - ---echo # ---echo # Cleanup test. ---echo # - -DROP TABLE t1; - - ---echo # ---echo # Bug #24368053 ---echo # WL#7987: ASSERTION `!TABLE || (!TABLE->WRITE_SET || BITMAP_IS_SET(TABLE-> ... ---echo # - -CREATE TABLE C(col_int int); -CREATE TABLE CC(col_int int); -INSERT INTO CC VALUES (1),(2),(3); - -SELECT ST_SHORTEST_DIR_PATH(table1.`col_int` ,table1.`col_int`) AS field2, -(SELECT JSON_ARRAYAGG(SUBQUERY2_t1.`col_int`) - FROM CC AS SUBQUERY2_t1 - WHERE SUBQUERY2_t1.`col_int` <> table1.`col_int`) AS field5 -FROM (CC AS table1) -WHERE (table1.`col_int` <> ALL (SELECT SUBQUERY4_t1.`col_int` - FROM (CC AS SUBQUERY4_t1 STRAIGHT_JOIN C))) -GROUP BY -field5; - -DROP TABLE C; -DROP TABLE CC; - ---echo # ---echo # Bug #24367384 ---echo # WL#7987: INNODB: ASSERTION FAILURE: ROW0SEL.CC:2558:FIELD->PREFIX_LEN > 0 ... ---echo # -CREATE TABLE BB (pk INT AUTO_INCREMENT PRIMARY KEY, col_varchar_key VARCHAR(1)); -INSERT INTO BB VALUES(1,'a'); - ---skip_if_hypergraph # Different warnings. -SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 - WHERE t1.`col_varchar_key` <> t2.`col_varchar_key`) AS field2 -FROM BB as t2 -GROUP BY field2; - ---skip_if_hypergraph # Different warnings. -SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 - WHERE t1.`col_varchar_key` = t2.`col_varchar_key`) AS field2 -FROM BB as t2 -GROUP BY field2; - -DROP TABLE BB; - ---echo # ---echo # Bug #24365264 ---echo # WL#7987: SIG 11 IN ITEM::MARK_FIELD_IN_MAP|SQL/ITEM.H ---echo # - -CREATE TABLE C (col_int int); -INSERT INTO C VALUES (1); - -SELECT * -FROM C WHERE col_int < (SELECT JSON_ARRAYAGG(col_int) FROM C ) -ORDER BY col_int ; - -DROP TABLE C; - - ---echo # ---echo # Bug #24366341 ---echo # WL#7987: SIG 6 IN JSON_WRAPPER::TYPE|SQL/JSON_DOM.CC ---echo # - -CREATE TABLE CC(col_varchar_key varchar(1)); -INSERT INTO CC VALUES ('a'); - -SELECT JSON_ARRAYAGG(col_varchar_key) AS field1 FROM CC HAVING field1 > 9; - -SELECT ST_SHORTEST_DIR_PATH(col_varchar_key, col_varchar_key) AS field1 FROM CC -HAVING (field1 <> 'a' AND field1 != 'e'); - -DROP TABLE CC; - ---echo # ---echo # with ROLLUP + two/three groups ---echo # - -CREATE TABLE tg (g1 int, g2 int, k int, b VARCHAR(10)); -INSERT INTO tg VALUES -(1, 1, 1, "alfa"), -(1, 2, 2, null), -(2, 3, 3, "doi"), -(1, 1, 4, "unu"), -(3, 2, 5, "trei"), -(4, 3, 6, null), -(4, 1, 7, null), -(1, 2, 8, "one"); - - -SELECT g1, g2, JSON_ARRAYAGG(g2) FROM tg GROUP BY g1, g2 with rollup; -SELECT g1, g2, ST_SHORTEST_DIR_PATH(k, g1) FROM tg GROUP BY g1, g2 with rollup; - - -CREATE TABLE tg3 (g1 int, g2 int, g3 int, k int, b VARCHAR(10)); -INSERT INTO tg3 VALUES -(1, 1, 1, 1, "1.1.1"), -(1, 1, 2, 2, "1.1.2"), -(1, 1, 3, 3, "1.1.3"), -(1, 2, 1, 4, "1.2.1"), -(1, 2, 2, 5, "1.2.2"), -(1, 2, 3, 6, "1.2.3"), -(1, 3, 1, 7, "1.3.1"), -(1, 3, 2, 8, "1.3.2"), -(1, 3, 3, 9, "1.3.3"), -(2, 1, 1, 10, "2.1.1"), -(2, 1, 2, 11, "2.1.2"), -(2, 1, 3, 12, "2.1.3"), -(2, 2, 1, 13, "2.2.1"), -(2, 2, 2, 14, "2.2.2"), -(2, 2, 3, 15, "2.2.3"), -(2, 3, 1, 16, "2.3.1"), -(2, 3, 2, 17, "2.3.2"), -(2, 3, 3, 18, "2.3.3"), -(3, 1, 1, 19, "3.1.1"), -(3, 1, 2, 20, "3.1.2"), -(3, 1, 3, 21, "3.1.3"), -(3, 2, 1, 22, "3.2.1"), -(3, 2, 2, 23, "3.2.2"), -(3, 2, 3, 24, "3.2.3"), -(3, 3, 1, 25, "3.3.1"), -(3, 3, 2, 26, "3.3.2"), -(3, 3, 3, 27, "3.3.3"); - - -SELECT g1, g2, g3, JSON_ARRAYAGG(b) FROM tg3 GROUP BY g1, g2, g3 with rollup; -SELECT g1, g2, g3, ST_SHORTEST_DIR_PATH(k, b) FROM tg3 GROUP BY g1, g2, g3 with rollup; - - -DROP TABLE tg; -DROP TABLE tg3; - ---echo # ---echo # Tests duplicates for ST_SHORTEST_DIR_PATH ---echo # - -CREATE TABLE t1 (a int, k int, b VARCHAR(10)); -INSERT INTO t1 VALUES -(1, 1, "1.1"), -(1, 1, "1.2"), -(1, 1, "1.3"), -(2, 2, "2.1"), -(2, 2, "2.2"), -(2, 2, "2.3"); - -SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; - -DROP TABLE t1; - ---echo # ---echo # Tests with ORDER BY, DISTINCT ---echo # - -CREATE TABLE t1 (a int, k int, b VARCHAR(10)); -INSERT INTO t1 VALUES -(2, 8, "2.3"), -(1, 7, "1.1"), -(3, 6, "3.2"), -(2, 5, "2.2"), -(3, 9, "3.1"), -(1, 4, "1.2"), -(3, 3, "3.3"), -(2, 2, "2.1"), -(1, 1, "1.3"); - -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a ASC; -SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a DESC; - -SELECT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -SELECT DISTINCT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a; -SELECT DISTINCT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a ORDER BY a ASC; -SELECT DISTINCT a, ST_SHORTEST_DIR_PATH(k, b) FROM t1 GROUP BY a ORDER BY a DESC; - -DROP TABLE t1; - ---echo # ---echo # Tests with joins ---echo # - -CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, t1 INT, t2 INT); -CREATE TABLE p(id INT PRIMARY KEY AUTO_INCREMENT, p1 INT, p2 INT); - -INSERT INTO t(t1, t2) VALUES (1, 1), (2, 1), (3,3), (1, 4); -INSERT INTO p(p1, p2) VALUES (2, 1), (1, 1), (3,3), (2, 4); - -FLUSH STATUS; -SHOW SESSION STATUS LIKE 'Handler_update%'; -#Since the JSON_*AGG does not have the ORDER BY clause order is not predictable -disable_result_log; -SELECT JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1; -enable_result_log; -SHOW SESSION STATUS LIKE 'Handler_update%'; -disable_result_log; -SELECT t1, JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1 group by t1; -enable_result_log; -SHOW SESSION STATUS LIKE 'Handler_update%'; - -# Subquery in ORDER BY with outer reference -SELECT (SELECT 1 AS foo ORDER BY JSON_ARRAYAGG(t2)) AS x FROM t; ---skip_if_hypergraph # Different warnings. -SELECT t1 FROM t ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); -SELECT JSON_ARRAYAGG(t1) FROM t -ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); - -SELECT (SELECT JSON_ARRAYAGG(t1_outer.t1) FROM t AS t1_inner LIMIT 1) as f -FROM t AS t1_outer GROUP BY t1_outer.t2; - -DROP TABLE t; -DROP TABLE p; - ---echo # ---echo # Tests with JSON ---echo # - -CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, k INT, j JSON); -INSERT INTO t(k, j) VALUES -(1, '[1,2,3,4]'), -(2, '{"prop1": 1}'), -(1, '[3]'), -(2, '{"prop2": 2, "prop10": 10}'), -(1, '[99]'); - - -SELECT k, JSON_ARRAYAGG(j) FROM t GROUP BY k; -SELECT k, ST_SHORTEST_DIR_PATH(id, j) FROM t GROUP BY k; - -DROP TABLE t; - ---echo # ---echo # Coverage tests for val_* functions ---echo # - -CREATE TABLE t2(gid int, a int); -INSERT INTO t2(gid, a) VALUES (1, 1), (1, 2), (2, 4), (2, 8); - -SELECT gid, 1.0 * JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; -SELECT gid, 0x30 << JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; -SELECT gid, DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) -FROM t2 GROUP BY gid; -SELECT gid, ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') -FROM t2 GROUP BY gid; -SELECT gid, SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2 GROUP BY gid; - -TRUNCATE TABLE t2; - -SELECT 1.0 * JSON_ARRAYAGG(a) FROM t2; -SELECT 0x30 << JSON_ARRAYAGG(a) FROM t2; -SELECT DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) FROM t2; -SELECT ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') FROM t2; -SELECT SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2; - -DROP TABLE t2; \ No newline at end of file +drop table paths; diff --git a/sql/a_star.cc b/sql/a_star.cc index 4fba4cd92189..726c4ca8958e 100644 --- a/sql/a_star.cc +++ b/sql/a_star.cc @@ -58,22 +58,20 @@ #include "sql/uniques.h" // Unique #include "sql/window.h" -a_star_ting::a_star_ting( +Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper, unique_ptr_destroy_only object) : Item_sum_json(std::move(wrapper), thd, item), m_json_object(std::move(object)) {} -a_star_ting::a_star_ting( - const POS &pos, Item *a, Item *b, PT_window *w, +Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( + const POS &pos, PT_item_list *args, PT_window *w, unique_ptr_destroy_only wrapper, unique_ptr_destroy_only object) - : Item_sum_json(std::move(wrapper), pos, a, b, w), + : Item_sum_json(std::move(wrapper), pos, args, w), m_json_object(std::move(object)) {} -a_star_ting::~a_star_ting() = default; - -void a_star_ting::clear() { +void Item_sum_shortest_dir_path::clear() { null_value = true; m_json_object->clear(); @@ -83,9 +81,9 @@ void a_star_ting::clear() { m_key_map.clear(); } -bool a_star_ting::add() { - assert(fixed == 1); - assert(arg_count == 2); +bool Item_sum_shortest_dir_path::add() { + assert(fixed); + assert(arg_count == 6); const THD *thd = base_query_block->parent_lex->thd; /* @@ -178,7 +176,7 @@ bool a_star_ting::add() { return false; } -Item *a_star_ting::copy_or_same(THD *thd) { +Item *Item_sum_shortest_dir_path::copy_or_same(THD *thd) { if (m_is_window_function) return this; auto wrapper = make_unique_destroy_only(thd->mem_root); @@ -189,10 +187,10 @@ Item *a_star_ting::copy_or_same(THD *thd) { if (object == nullptr) return nullptr; return new (thd->mem_root) - a_star_ting(thd, this, std::move(wrapper), std::move(object)); + Item_sum_shortest_dir_path(thd, this, std::move(wrapper), std::move(object)); } -bool a_star_ting::check_wf_semantics1(THD *thd, Query_block *select, +bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *select, Window_evaluation_requirements *reqs) { return Item_sum::check_wf_semantics1(thd, select, reqs); } diff --git a/sql/a_star.h b/sql/a_star.h index 2ec560dfbf09..dce66ba87cce 100644 --- a/sql/a_star.h +++ b/sql/a_star.h @@ -1,6 +1,26 @@ +#ifndef ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED +#define ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED + #include "sql/item_sum.h" -class a_star_ting final : public Item_sum_json { + +struct Edge { + int id, from, to; + double cost; +}; + +class Dijkstra { + int heu_coeff; +public: + Dijkstra(const int& heu_coeff = 1): heu_coeff(heu_coeff) {} + template + std::vector operator()(const std::unordered_multimap& edge_map, int start_id, int end_id){ + return {}; + } +}; + +class Item_sum_shortest_dir_path final : public Item_sum_json { + std::unordered_multimap edge_map; /// Accumulates the final value. unique_ptr_destroy_only m_json_object; /// Buffer used to get the value of the key. @@ -22,17 +42,24 @@ class a_star_ting final : public Item_sum_json { bool m_optimize{false}; public: - a_star_ting(THD *thd, Item_sum *item, + Item_sum_shortest_dir_path(THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper, unique_ptr_destroy_only object); - a_star_ting(const POS &pos, Item *a, Item *b, PT_window *w, + Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, PT_window *w, unique_ptr_destroy_only wrapper, unique_ptr_destroy_only object); - ~a_star_ting() override; - const char *func_name() const override { return "json_objectagg"; } + + ~Item_sum_shortest_dir_path() override = default; + + enum Sumfunctype sum_func() const override { return SHORTEST_DIR_PATH_FUNC; } + const char *func_name() const override { return "st_shortest_dir_path"; } + void clear() override; bool add() override; Item *copy_or_same(THD *thd) override; bool check_wf_semantics1(THD *thd, Query_block *select, Window_evaluation_requirements *reqs) override; -}; \ No newline at end of file + +}; + +#endif /* ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED */ diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 37030731223e..9f7721840d2c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -5635,13 +5635,6 @@ bool Item_lead_lag::compute() { return null_value || current_thd->is_error(); } -template -Item_sum_json::Item_sum_json(unique_ptr_destroy_only wrapper, - Args &&... parent_args) - : Item_sum(std::forward(parent_args)...), - m_wrapper(std::move(wrapper)) { - set_data_type_json(); -} Item_sum_json::~Item_sum_json() = default; @@ -6573,3 +6566,4 @@ void Item_sum_collect::reset_field() { add(); store_result_field(); } + diff --git a/sql/item_sum.h b/sql/item_sum.h index 0baa64935967..b34b9cb70fb0 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -461,7 +461,8 @@ class Item_sum : public Item_func { FIRST_LAST_VALUE_FUNC, NTH_VALUE_FUNC, ROLLUP_SUM_SWITCHER_FUNC, - GEOMETRY_AGGREGATE_FUNC + GEOMETRY_AGGREGATE_FUNC, + SHORTEST_DIR_PATH_FUNC }; /** @@ -1210,7 +1211,10 @@ class Item_sum_json : public Item_sum { */ template explicit Item_sum_json(unique_ptr_destroy_only wrapper, - Args &&... parent_args); + Args &&... parent_args) + : Item_sum(std::forward(parent_args)...), + m_wrapper(std::move(wrapper)) { set_data_type_json(); } + public: ~Item_sum_json() override; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5117b824a942..2d4498b3a803 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1368,6 +1368,7 @@ void warn_about_deprecated_binary(THD *thd) %token CHALLENGE_RESPONSE_SYM 1198 /* MYSQL */ %token GTID_ONLY_SYM 1199 /* MYSQL */ + %token ST_SHORTEST_DIR_PATH_SYM 1200 /*MYSQL*/ /* @@ -11104,15 +11105,17 @@ sum_expr: $$ = NEW_PTN Item_sum_json_object( @$, $3, $5, $7, std::move(wrapper), std::move(object)); } - | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ')' opt_windowing_clause + | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' opt_windowing_clause { auto wrapper = make_unique_destroy_only(YYMEM_ROOT); if (wrapper == nullptr) YYABORT; - unique_ptr_destroy_only object{::new (YYMEM_ROOT) - Json_object}; + unique_ptr_destroy_only object{::new (YYMEM_ROOT) Json_object}; if (object == nullptr) YYABORT; - $$ = NEW_PTN a_star_ting ( - @$, $3, $5, $7, std::move(wrapper), std::move(object)); + + PT_item_list *args= NEW_PTN PT_item_list; + args->push_back($3);args->push_back($5);args->push_back($7);args->push_back($9); + args->push_back($11);args->push_back($13); + $$ = NEW_PTN Item_sum_shortest_dir_path(@$, args, $15, std::move(wrapper), std::move(object)); } | ST_COLLECT_SYM '(' in_sum_expr ')' opt_windowing_clause { @@ -15571,6 +15574,7 @@ ident_keywords_unambiguous: | STREAM_SYM | STRING_SYM | ST_COLLECT_SYM + | ST_SHORTEST_DIR_PATH_SYM | SUBCLASS_ORIGIN_SYM | SUBDATE_SYM | SUBJECT_SYM From 700977f9cdf4a82d7bd79b1ca9c6a35b478b2bb1 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 15 Mar 2022 00:05:05 +0100 Subject: [PATCH 03/67] rename --- .../gis/t/{st_shortest_path.test => st_shortest_dir_path.test} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mysql-test/suite/gis/t/{st_shortest_path.test => st_shortest_dir_path.test} (100%) diff --git a/mysql-test/suite/gis/t/st_shortest_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test similarity index 100% rename from mysql-test/suite/gis/t/st_shortest_path.test rename to mysql-test/suite/gis/t/st_shortest_dir_path.test From 33a5d5e0d801632a34e7b1bbdc834c6a19618262 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 15 Mar 2022 15:24:07 +0100 Subject: [PATCH 04/67] st_sdp val_str --- .../suite/gis/r/st_shortest_dir_path.result | 13 + .../suite/gis/r/st_shortest_path.result | 1169 ----------------- sql/a_star.cc | 96 +- sql/a_star.h | 3 + 4 files changed, 32 insertions(+), 1249 deletions(-) create mode 100644 mysql-test/suite/gis/r/st_shortest_dir_path.result delete mode 100644 mysql-test/suite/gis/r/st_shortest_path.result diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result new file mode 100644 index 000000000000..ee6df8cbd4e3 --- /dev/null +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -0,0 +1,13 @@ +create table paths( +id int primary key, +from_id int, +to_id int, +cost double +); +insert into paths values(0, 0, 1, 5.0); +insert into paths values(1, 1, 2, 5.0); +insert into paths values(2, 0, 2, 15.0); +select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; +st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) +mip mop +drop table paths; diff --git a/mysql-test/suite/gis/r/st_shortest_path.result b/mysql-test/suite/gis/r/st_shortest_path.result deleted file mode 100644 index 31ec925a6d43..000000000000 --- a/mysql-test/suite/gis/r/st_shortest_path.result +++ /dev/null @@ -1,1169 +0,0 @@ -# -# Setup test. -# -CREATE TABLE t1 (a int, k int, b VARCHAR(10)) CHARSET utf8mb4; -INSERT INTO t1 VALUES -(1, 1, "alfa"), -(1, 2, null), -(2, 3, "doi"), -(1, 4, "unu"), -(3, 5, "trei"), -(4, 6, null), -(4, 7, null), -(1, 8, "one"); -# -# Test JSON_ARRAYAGG. -# -FLUSH STATUS; -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT JSON_ARRAYAGG(b) FROM t1; -JSON_ARRAYAGG(b) -["alfa", null, "doi", "unu", "trei", null, null, "one"] -SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; -JSON_ARRAYAGG(b) -["alfa", null, "doi", "unu", "trei", null, null, "one"] -SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; -JSON_ARRAYAGG(b) -["alfa", null, "doi", "unu", "trei", null, null, "one"] -SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; -JSON_ARRAYAGG(b) -["alfa", null, "doi", "unu", "trei", null, null, "one"] -PREPARE p1 FROM "SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; -EXECUTE p1; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -EXECUTE p1; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -deallocate prepare p1; -PREPARE p3 FROM -"SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a"; -EXECUTE p3; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -EXECUTE p3; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -deallocate prepare p3; -PREPARE p4 FROM "SELECT JSON_ARRAYAGG(b) FROM t1"; -EXECUTE p4; -JSON_ARRAYAGG(b) -["alfa", null, "doi", "unu", "trei", null, null, "one"] -EXECUTE p4; -JSON_ARRAYAGG(b) -["alfa", null, "doi", "unu", "trei", null, null, "one"] -deallocate prepare p4; -SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1; -JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') -["alfa", null, "doi", "unu", "trei", null, null, "one", true, false] -PREPARE p1 FROM -"SELECT a, JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1 GROUP BY a"; -EXECUTE p1; -a JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') -1 ["alfa", null, "unu", "one", true, false] -2 ["doi", true, false] -3 ["trei", true, false] -4 [null, null, true, false] -EXECUTE p1; -a JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') -1 ["alfa", null, "unu", "one", true, false] -2 ["doi", true, false] -3 ["trei", true, false] -4 [null, null, true, false] -deallocate prepare p1; -PREPARE p4 FROM -"SELECT JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') FROM t1"; -EXECUTE p4; -JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') -["alfa", null, "doi", "unu", "trei", null, null, "one", true, false] -EXECUTE p4; -JSON_MERGE_PRESERVE(JSON_ARRAYAGG(b), '[true, false]') -["alfa", null, "doi", "unu", "trei", null, null, "one", true, false] -deallocate prepare p4; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -EXPLAIN SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 Using filesort -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN SELECT JSON_ARRAYAGG(b) FROM t1; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 NULL -Warnings: -Note 1003 /* select#1 */ select json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` -EXPLAIN FORMAT=json SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "9.05" - }, - "grouping_operation": { - "using_filesort": true, - "cost_info": { - "sort_cost": "8.00" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "a", - "b" - ] - } - } - } -} -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT a, JSON_ARRAYAGG(b) -FROM t1 -GROUP BY a; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "9.05" - }, - "grouping_operation": { - "using_filesort": true, - "cost_info": { - "sort_cost": "8.00" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "a", - "b" - ] - } - } - } -} -Warnings: -Note 1003 /* select#1 */ select sql_big_result `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT a, JSON_ARRAYAGG(b) -FROM t1 -GROUP BY a; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "9.05" - }, - "grouping_operation": { - "using_filesort": true, - "cost_info": { - "sort_cost": "8.00" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "a", - "b" - ] - } - } - } -} -Warnings: -Note 1003 /* select#1 */ select sql_small_result `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT a, JSON_ARRAYAGG(b) -FROM t1 -GROUP BY a; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "9.05" - }, - "grouping_operation": { - "using_filesort": true, - "cost_info": { - "sort_cost": "8.00" - }, - "buffer_result": { - "using_temporary_table": true, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "a", - "b" - ] - } - } - } - } -} -Warnings: -Note 1003 /* select#1 */ select sql_buffer_result `test`.`t1`.`a` AS `a`,json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN FORMAT=json SELECT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "1.05" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "b" - ] - } - } -} -Warnings: -Note 1003 /* select#1 */ select json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` -EXPLAIN FORMAT=json SELECT SQL_BIG_RESULT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "1.05" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "b" - ] - } - } -} -Warnings: -Note 1003 /* select#1 */ select sql_big_result json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` -EXPLAIN FORMAT=json SELECT SQL_SMALL_RESULT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "1.05" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "b" - ] - } - } -} -Warnings: -Note 1003 /* select#1 */ select sql_small_result json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` -EXPLAIN FORMAT=json SELECT SQL_BUFFER_RESULT JSON_ARRAYAGG(b) FROM t1; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "1.05" - }, - "buffer_result": { - "using_temporary_table": true, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "b" - ] - } - } - } -} -Warnings: -Note 1003 /* select#1 */ select sql_buffer_result json_arrayagg(`test`.`t1`.`b`) AS `JSON_ARRAYAGG(b)` from `test`.`t1` -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a WITH ROLLUP; -a JSON_ARRAYAGG(b) -1 ["alfa", null, "unu", "one"] -2 ["doi"] -3 ["trei"] -4 [null, null] -NULL ["alfa", null, "unu", "one", "doi", "trei", null, null] -SELECT a, JSON_ARRAYAGG(b) as jarray -FROM t1 -GROUP BY a -HAVING jarray= JSON_ARRAY("trei"); -a jarray -3 ["trei"] -# -# Test JSON_OBJECTAGG. -# -FLUSH STATUS; -SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -a JSON_OBJECTAGG(k, b) -1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} -2 {"3": "doi"} -3 {"5": "trei"} -4 {"6": null, "7": null} -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT JSON_OBJECTAGG(k, b) FROM t1; -JSON_OBJECTAGG(k, b) -{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -PREPARE p1 FROM "SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a"; -EXECUTE p1; -a JSON_OBJECTAGG(k, b) -1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} -2 {"3": "doi"} -3 {"5": "trei"} -4 {"6": null, "7": null} -EXECUTE p1; -a JSON_OBJECTAGG(k, b) -1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} -2 {"3": "doi"} -3 {"5": "trei"} -4 {"6": null, "7": null} -deallocate prepare p1; -PREPARE p4 FROM "SELECT JSON_OBJECTAGG(k, b) FROM t1"; -EXECUTE p4; -JSON_OBJECTAGG(k, b) -{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} -EXECUTE p4; -JSON_OBJECTAGG(k, b) -{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} -deallocate prepare p4; -SELECT a, JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') FROM t1 GROUP BY a; -a JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -1 [{"1": "alfa", "2": null, "4": "unu", "8": "one"}, true, false] -2 [{"3": "doi"}, true, false] -3 [{"5": "trei"}, true, false] -4 [{"6": null, "7": null}, true, false] -SELECT JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') FROM t1; -JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -[{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"}, true, false] -PREPARE p1 FROM -"SELECT a, JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -FROM t1 -GROUP BY a"; -EXECUTE p1; -a JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -1 [{"1": "alfa", "2": null, "4": "unu", "8": "one"}, true, false] -2 [{"3": "doi"}, true, false] -3 [{"5": "trei"}, true, false] -4 [{"6": null, "7": null}, true, false] -EXECUTE p1; -a JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -1 [{"1": "alfa", "2": null, "4": "unu", "8": "one"}, true, false] -2 [{"3": "doi"}, true, false] -3 [{"5": "trei"}, true, false] -4 [{"6": null, "7": null}, true, false] -deallocate prepare p1; -PREPARE p4 FROM -"SELECT JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') FROM t1"; -EXECUTE p4; -JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -[{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"}, true, false] -EXECUTE p4; -JSON_MERGE_PRESERVE(JSON_OBJECTAGG(k, b), '[true, false]') -[{"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"}, true, false] -deallocate prepare p4; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -EXPLAIN SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 Using filesort -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN SELECT JSON_OBJECTAGG(k, b) FROM t1; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 8 100.00 NULL -Warnings: -Note 1003 /* select#1 */ select json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` -EXPLAIN FORMAT=json SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "9.05" - }, - "grouping_operation": { - "using_filesort": true, - "cost_info": { - "sort_cost": "8.00" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "a", - "k", - "b" - ] - } - } - } -} -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` group by `test`.`t1`.`a` -EXPLAIN FORMAT=json SELECT JSON_OBJECTAGG(k, b) FROM t1; -EXPLAIN -{ - "query_block": { - "select_id": 1, - "cost_info": { - "query_cost": "1.05" - }, - "table": { - "table_name": "t1", - "access_type": "ALL", - "rows_examined_per_scan": 8, - "rows_produced_per_join": 8, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.80", - "prefix_cost": "1.05", - "data_read_per_join": "448" - }, - "used_columns": [ - "k", - "b" - ] - } - } -} -Warnings: -Note 1003 /* select#1 */ select json_objectagg(`test`.`t1`.`k`,`test`.`t1`.`b`) AS `JSON_OBJECTAGG(k, b)` from `test`.`t1` -SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a WITH ROLLUP; -a JSON_OBJECTAGG(k, b) -1 {"1": "alfa", "2": null, "4": "unu", "8": "one"} -2 {"3": "doi"} -3 {"5": "trei"} -4 {"6": null, "7": null} -NULL {"1": "alfa", "2": null, "3": "doi", "4": "unu", "5": "trei", "6": null, "7": null, "8": "one"} -SELECT a, JSON_OBJECTAGG(k, b) as jobject -FROM t1 -GROUP BY a -HAVING jobject = JSON_OBJECT(3, "doi"); -a jobject -2 {"3": "doi"} -# -# NULL values. -# -SELECT a, JSON_ARRAYAGG(null) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(null) -1 [null, null, null, null] -2 [null] -3 [null] -4 [null, null] -SELECT JSON_ARRAYAGG(null) FROM t1; -JSON_ARRAYAGG(null) -[null, null, null, null, null, null, null, null] -SELECT a, JSON_OBJECTAGG(k, null) FROM t1 GROUP BY a; -a JSON_OBJECTAGG(k, null) -1 {"1": null, "2": null, "4": null, "8": null} -2 {"3": null} -3 {"5": null} -4 {"6": null, "7": null} -SELECT JSON_OBJECTAGG(k, null) FROM t1; -JSON_OBJECTAGG(k, null) -{"1": null, "2": null, "3": null, "4": null, "5": null, "6": null, "7": null, "8": null} -SELECT a, JSON_OBJECTAGG(null, b) FROM t1 GROUP BY a; -ERROR 22032: JSON documents may not contain NULL member names. -SELECT JSON_OBJECTAGG(null, b) FROM t1; -ERROR 22032: JSON documents may not contain NULL member names. -# -# Coverage test for fix_fields: Disable_semijoin_flattening. -# -CREATE TABLE t(a INT); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -EXPLAIN format=json SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); -EXPLAIN -{ - "query_block": { - "select_id": 1, - "message": "No tables used", - "optimized_away_subqueries": [ - { - "dependent": true, - "cacheable": false, - "query_block": { - "select_id": 2, - "cost_info": { - "query_cost": "0.35" - }, - "table": { - "table_name": "t", - "access_type": "ALL", - "rows_examined_per_scan": 1, - "rows_produced_per_join": 1, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.10", - "prefix_cost": "0.35", - "data_read_per_join": "8" - }, - "attached_condition": "(0 <> json_arrayagg(((1,(/* select#3 */ select 1 from `test`.`t` where true)))))" - }, - "optimized_away_subqueries": [ - { - "dependent": false, - "cacheable": true, - "query_block": { - "select_id": 3, - "cost_info": { - "query_cost": "0.35" - }, - "table": { - "table_name": "t", - "access_type": "ALL", - "rows_examined_per_scan": 1, - "rows_produced_per_join": 1, - "filtered": "100.00", - "cost_info": { - "read_cost": "0.25", - "eval_cost": "0.10", - "prefix_cost": "0.35", - "data_read_per_join": "8" - } - } - } - } - ] - } - } - ] - } -} -Warnings: -Warning 3986 Evaluating a JSON value in SQL boolean context does an implicit comparison against JSON integer 0; if this is not what you want, consider converting JSON to a SQL numeric type with JSON_VALUE RETURNING -Note 1003 /* select#1 */ select (/* select#2 */ select 1 from `test`.`t` where (0 <> json_arrayagg(((1,(/* select#3 */ select 1 from `test`.`t` where true)))))) AS `(SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t)))` -SELECT (SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))); -(SELECT 1 FROM t WHERE JSON_ARRAYAGG(1 IN (SELECT 1 FROM t))) -NULL -Warnings: -Warning 3986 Evaluating a JSON value in SQL boolean context does an implicit comparison against JSON integer 0; if this is not what you want, consider converting JSON to a SQL numeric type with JSON_VALUE RETURNING -DROP TABLE t; -# -# Coverage test for fix_fields: check_cols. -# -SELECT JSON_ARRAYAGG((SELECT 1, 1)); -ERROR 21000: Operand should contain 1 column(s) -# -# Coverage test for fix_fields: resolve_type. -# -CREATE TABLE t2(gid int, a int); -SELECT JSON_ARRAYAGG(ST_PointFromText('POINT(10 10)')) FROM t2; -ERROR HY000: Incorrect arguments to json_arrayagg -# -# Coverage test for fix_fields: check_sum_func. -# -SELECT (SELECT JSON_ARRAYAGG(COUNT(a)) FROM t2) FROM t1; -ERROR HY000: Invalid use of group function -DROP TABLE t2; -# -# Empty table. -# -TRUNCATE TABLE t1; -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -SELECT JSON_ARRAYAGG(b) FROM t1; -JSON_ARRAYAGG(b) -NULL -SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -a JSON_OBJECTAGG(k, b) -SELECT JSON_OBJECTAGG(k, b) FROM t1; -JSON_OBJECTAGG(k, b) -NULL -# -# Tests for max_allowed_packet. -# -CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, x INT); -INSERT INTO t(x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); -INSERT INTO t(x) SELECT t1.x from t t1, t t2, t t3; -SET GLOBAL net_buffer_length = 1024; -SET GLOBAL max_allowed_packet = 1024; -SELECT JSON_ARRAYAGG(x) FROM t; -JSON_ARRAYAGG(x) -NULL -Warnings: -Warning 1301 Result of json_arrayagg() was larger than max_allowed_packet (1024) - truncated -SELECT JSON_OBJECTAGG(id, x) FROM t; -JSON_OBJECTAGG(id, x) -NULL -Warnings: -Warning 1301 Result of json_objectagg() was larger than max_allowed_packet (1024) - truncated -SELECT id % 2 AS i, JSON_ARRAYAGG(x) FROM t GROUP BY i; -i JSON_ARRAYAGG(x) -0 NULL -1 NULL -Warnings: -Warning 1301 Result of json_arrayagg() was larger than max_allowed_packet (1024) - truncated -Warning 1301 Result of json_arrayagg() was larger than max_allowed_packet (1024) - truncated -SELECT id % 2 AS i, JSON_OBJECTAGG(id, x) FROM t GROUP BY i; -i JSON_OBJECTAGG(id, x) -0 NULL -1 NULL -Warnings: -Warning 1301 Result of json_objectagg() was larger than max_allowed_packet (1024) - truncated -Warning 1301 Result of json_objectagg() was larger than max_allowed_packet (1024) - truncated -SET GLOBAL max_allowed_packet = default; -SET GLOBAL net_buffer_length = default; -DROP TABLE t; -# -# Cleanup test. -# -DROP TABLE t1; -# -# Bug #24368053 -# WL#7987: ASSERTION `!TABLE || (!TABLE->WRITE_SET || BITMAP_IS_SET(TABLE-> ... -# -CREATE TABLE C(col_int int); -CREATE TABLE CC(col_int int); -INSERT INTO CC VALUES (1),(2),(3); -SELECT JSON_OBJECTAGG(table1.`col_int` ,table1.`col_int`) AS field2, -(SELECT JSON_ARRAYAGG(SUBQUERY2_t1.`col_int`) -FROM CC AS SUBQUERY2_t1 -WHERE SUBQUERY2_t1.`col_int` <> table1.`col_int`) AS field5 -FROM (CC AS table1) -WHERE (table1.`col_int` <> ALL (SELECT SUBQUERY4_t1.`col_int` - FROM (CC AS SUBQUERY4_t1 STRAIGHT_JOIN C))) -GROUP BY -field5; -field2 field5 -{"3": 3} [1, 2] -{"2": 2} [1, 3] -{"1": 1} [2, 3] -Warnings: -Warning 1235 This version of MySQL doesn't yet support 'sorting of non-scalar JSON values' -DROP TABLE C; -DROP TABLE CC; -# -# Bug #24367384 -# WL#7987: INNODB: ASSERTION FAILURE: ROW0SEL.CC:2558:FIELD->PREFIX_LEN > 0 ... -# -CREATE TABLE BB (pk INT AUTO_INCREMENT PRIMARY KEY, col_varchar_key VARCHAR(1)); -INSERT INTO BB VALUES(1,'a'); -SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 -WHERE t1.`col_varchar_key` <> t2.`col_varchar_key`) AS field2 -FROM BB as t2 -GROUP BY field2; -field2 -NULL -SELECT (SELECT JSON_ARRAYAGG(`pk`) FROM BB as t1 -WHERE t1.`col_varchar_key` = t2.`col_varchar_key`) AS field2 -FROM BB as t2 -GROUP BY field2; -field2 -[1] -DROP TABLE BB; -# -# Bug #24365264 -# WL#7987: SIG 11 IN ITEM::MARK_FIELD_IN_MAP|SQL/ITEM.H -# -CREATE TABLE C (col_int int); -INSERT INTO C VALUES (1); -SELECT * -FROM C WHERE col_int < (SELECT JSON_ARRAYAGG(col_int) FROM C ) -ORDER BY col_int ; -col_int -1 -DROP TABLE C; -# -# Bug #24366341 -# WL#7987: SIG 6 IN JSON_WRAPPER::TYPE|SQL/JSON_DOM.CC -# -CREATE TABLE CC(col_varchar_key varchar(1)); -INSERT INTO CC VALUES ('a'); -SELECT JSON_ARRAYAGG(col_varchar_key) AS field1 FROM CC HAVING field1 > 9; -field1 -["a"] -SELECT JSON_OBJECTAGG(col_varchar_key, col_varchar_key) AS field1 FROM CC -HAVING (field1 <> 'a' AND field1 != 'e'); -field1 -{"a": "a"} -DROP TABLE CC; -# -# with ROLLUP + two/three groups -# -CREATE TABLE tg (g1 int, g2 int, k int, b VARCHAR(10)); -INSERT INTO tg VALUES -(1, 1, 1, "alfa"), -(1, 2, 2, null), -(2, 3, 3, "doi"), -(1, 1, 4, "unu"), -(3, 2, 5, "trei"), -(4, 3, 6, null), -(4, 1, 7, null), -(1, 2, 8, "one"); -SELECT g1, g2, JSON_ARRAYAGG(g2) FROM tg GROUP BY g1, g2 with rollup; -g1 g2 JSON_ARRAYAGG(g2) -1 1 [1, 1] -1 2 [2, 2] -1 NULL [1, 1, 2, 2] -2 3 [3] -2 NULL [3] -3 2 [2] -3 NULL [2] -4 1 [1] -4 3 [3] -4 NULL [1, 3] -NULL NULL [1, 1, 2, 2, 3, 2, 1, 3] -SELECT g1, g2, JSON_OBJECTAGG(k, g1) FROM tg GROUP BY g1, g2 with rollup; -g1 g2 JSON_OBJECTAGG(k, g1) -1 1 {"1": 1, "4": 1} -1 2 {"2": 1, "8": 1} -1 NULL {"1": 1, "2": 1, "4": 1, "8": 1} -2 3 {"3": 2} -2 NULL {"3": 2} -3 2 {"5": 3} -3 NULL {"5": 3} -4 1 {"7": 4} -4 3 {"6": 4} -4 NULL {"6": 4, "7": 4} -NULL NULL {"1": 1, "2": 1, "3": 2, "4": 1, "5": 3, "6": 4, "7": 4, "8": 1} -CREATE TABLE tg3 (g1 int, g2 int, g3 int, k int, b VARCHAR(10)); -INSERT INTO tg3 VALUES -(1, 1, 1, 1, "1.1.1"), -(1, 1, 2, 2, "1.1.2"), -(1, 1, 3, 3, "1.1.3"), -(1, 2, 1, 4, "1.2.1"), -(1, 2, 2, 5, "1.2.2"), -(1, 2, 3, 6, "1.2.3"), -(1, 3, 1, 7, "1.3.1"), -(1, 3, 2, 8, "1.3.2"), -(1, 3, 3, 9, "1.3.3"), -(2, 1, 1, 10, "2.1.1"), -(2, 1, 2, 11, "2.1.2"), -(2, 1, 3, 12, "2.1.3"), -(2, 2, 1, 13, "2.2.1"), -(2, 2, 2, 14, "2.2.2"), -(2, 2, 3, 15, "2.2.3"), -(2, 3, 1, 16, "2.3.1"), -(2, 3, 2, 17, "2.3.2"), -(2, 3, 3, 18, "2.3.3"), -(3, 1, 1, 19, "3.1.1"), -(3, 1, 2, 20, "3.1.2"), -(3, 1, 3, 21, "3.1.3"), -(3, 2, 1, 22, "3.2.1"), -(3, 2, 2, 23, "3.2.2"), -(3, 2, 3, 24, "3.2.3"), -(3, 3, 1, 25, "3.3.1"), -(3, 3, 2, 26, "3.3.2"), -(3, 3, 3, 27, "3.3.3"); -SELECT g1, g2, g3, JSON_ARRAYAGG(b) FROM tg3 GROUP BY g1, g2, g3 with rollup; -g1 g2 g3 JSON_ARRAYAGG(b) -1 1 1 ["1.1.1"] -1 1 2 ["1.1.2"] -1 1 3 ["1.1.3"] -1 1 NULL ["1.1.1", "1.1.2", "1.1.3"] -1 2 1 ["1.2.1"] -1 2 2 ["1.2.2"] -1 2 3 ["1.2.3"] -1 2 NULL ["1.2.1", "1.2.2", "1.2.3"] -1 3 1 ["1.3.1"] -1 3 2 ["1.3.2"] -1 3 3 ["1.3.3"] -1 3 NULL ["1.3.1", "1.3.2", "1.3.3"] -1 NULL NULL ["1.1.1", "1.1.2", "1.1.3", "1.2.1", "1.2.2", "1.2.3", "1.3.1", "1.3.2", "1.3.3"] -2 1 1 ["2.1.1"] -2 1 2 ["2.1.2"] -2 1 3 ["2.1.3"] -2 1 NULL ["2.1.1", "2.1.2", "2.1.3"] -2 2 1 ["2.2.1"] -2 2 2 ["2.2.2"] -2 2 3 ["2.2.3"] -2 2 NULL ["2.2.1", "2.2.2", "2.2.3"] -2 3 1 ["2.3.1"] -2 3 2 ["2.3.2"] -2 3 3 ["2.3.3"] -2 3 NULL ["2.3.1", "2.3.2", "2.3.3"] -2 NULL NULL ["2.1.1", "2.1.2", "2.1.3", "2.2.1", "2.2.2", "2.2.3", "2.3.1", "2.3.2", "2.3.3"] -3 1 1 ["3.1.1"] -3 1 2 ["3.1.2"] -3 1 3 ["3.1.3"] -3 1 NULL ["3.1.1", "3.1.2", "3.1.3"] -3 2 1 ["3.2.1"] -3 2 2 ["3.2.2"] -3 2 3 ["3.2.3"] -3 2 NULL ["3.2.1", "3.2.2", "3.2.3"] -3 3 1 ["3.3.1"] -3 3 2 ["3.3.2"] -3 3 3 ["3.3.3"] -3 3 NULL ["3.3.1", "3.3.2", "3.3.3"] -3 NULL NULL ["3.1.1", "3.1.2", "3.1.3", "3.2.1", "3.2.2", "3.2.3", "3.3.1", "3.3.2", "3.3.3"] -NULL NULL NULL ["1.1.1", "1.1.2", "1.1.3", "1.2.1", "1.2.2", "1.2.3", "1.3.1", "1.3.2", "1.3.3", "2.1.1", "2.1.2", "2.1.3", "2.2.1", "2.2.2", "2.2.3", "2.3.1", "2.3.2", "2.3.3", "3.1.1", "3.1.2", "3.1.3", "3.2.1", "3.2.2", "3.2.3", "3.3.1", "3.3.2", "3.3.3"] -SELECT g1, g2, g3, JSON_OBJECTAGG(k, b) FROM tg3 GROUP BY g1, g2, g3 with rollup; -g1 g2 g3 JSON_OBJECTAGG(k, b) -1 1 1 {"1": "1.1.1"} -1 1 2 {"2": "1.1.2"} -1 1 3 {"3": "1.1.3"} -1 1 NULL {"1": "1.1.1", "2": "1.1.2", "3": "1.1.3"} -1 2 1 {"4": "1.2.1"} -1 2 2 {"5": "1.2.2"} -1 2 3 {"6": "1.2.3"} -1 2 NULL {"4": "1.2.1", "5": "1.2.2", "6": "1.2.3"} -1 3 1 {"7": "1.3.1"} -1 3 2 {"8": "1.3.2"} -1 3 3 {"9": "1.3.3"} -1 3 NULL {"7": "1.3.1", "8": "1.3.2", "9": "1.3.3"} -1 NULL NULL {"1": "1.1.1", "2": "1.1.2", "3": "1.1.3", "4": "1.2.1", "5": "1.2.2", "6": "1.2.3", "7": "1.3.1", "8": "1.3.2", "9": "1.3.3"} -2 1 1 {"10": "2.1.1"} -2 1 2 {"11": "2.1.2"} -2 1 3 {"12": "2.1.3"} -2 1 NULL {"10": "2.1.1", "11": "2.1.2", "12": "2.1.3"} -2 2 1 {"13": "2.2.1"} -2 2 2 {"14": "2.2.2"} -2 2 3 {"15": "2.2.3"} -2 2 NULL {"13": "2.2.1", "14": "2.2.2", "15": "2.2.3"} -2 3 1 {"16": "2.3.1"} -2 3 2 {"17": "2.3.2"} -2 3 3 {"18": "2.3.3"} -2 3 NULL {"16": "2.3.1", "17": "2.3.2", "18": "2.3.3"} -2 NULL NULL {"10": "2.1.1", "11": "2.1.2", "12": "2.1.3", "13": "2.2.1", "14": "2.2.2", "15": "2.2.3", "16": "2.3.1", "17": "2.3.2", "18": "2.3.3"} -3 1 1 {"19": "3.1.1"} -3 1 2 {"20": "3.1.2"} -3 1 3 {"21": "3.1.3"} -3 1 NULL {"19": "3.1.1", "20": "3.1.2", "21": "3.1.3"} -3 2 1 {"22": "3.2.1"} -3 2 2 {"23": "3.2.2"} -3 2 3 {"24": "3.2.3"} -3 2 NULL {"22": "3.2.1", "23": "3.2.2", "24": "3.2.3"} -3 3 1 {"25": "3.3.1"} -3 3 2 {"26": "3.3.2"} -3 3 3 {"27": "3.3.3"} -3 3 NULL {"25": "3.3.1", "26": "3.3.2", "27": "3.3.3"} -3 NULL NULL {"19": "3.1.1", "20": "3.1.2", "21": "3.1.3", "22": "3.2.1", "23": "3.2.2", "24": "3.2.3", "25": "3.3.1", "26": "3.3.2", "27": "3.3.3"} -NULL NULL NULL {"1": "1.1.1", "2": "1.1.2", "3": "1.1.3", "4": "1.2.1", "5": "1.2.2", "6": "1.2.3", "7": "1.3.1", "8": "1.3.2", "9": "1.3.3", "10": "2.1.1", "11": "2.1.2", "12": "2.1.3", "13": "2.2.1", "14": "2.2.2", "15": "2.2.3", "16": "2.3.1", "17": "2.3.2", "18": "2.3.3", "19": "3.1.1", "20": "3.1.2", "21": "3.1.3", "22": "3.2.1", "23": "3.2.2", "24": "3.2.3", "25": "3.3.1", "26": "3.3.2", "27": "3.3.3"} -DROP TABLE tg; -DROP TABLE tg3; -# -# Tests duplicates for JSON_OBJECTAGG -# -CREATE TABLE t1 (a int, k int, b VARCHAR(10)); -INSERT INTO t1 VALUES -(1, 1, "1.1"), -(1, 1, "1.2"), -(1, 1, "1.3"), -(2, 2, "2.1"), -(2, 2, "2.2"), -(2, 2, "2.3"); -SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -a JSON_OBJECTAGG(k, b) -1 {"1": "1.3"} -2 {"2": "2.3"} -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["1.1", "1.2", "1.3"] -2 ["2.1", "2.2", "2.3"] -DROP TABLE t1; -# -# Tests with ORDER BY, DISTINCT -# -CREATE TABLE t1 (a int, k int, b VARCHAR(10)); -INSERT INTO t1 VALUES -(2, 8, "2.3"), -(1, 7, "1.1"), -(3, 6, "3.2"), -(2, 5, "2.2"), -(3, 9, "3.1"), -(1, 4, "1.2"), -(3, 3, "3.3"), -(2, 2, "2.1"), -(1, 1, "1.3"); -SELECT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["1.1", "1.2", "1.3"] -2 ["2.3", "2.2", "2.1"] -3 ["3.2", "3.1", "3.3"] -SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a; -a JSON_ARRAYAGG(b) -1 ["1.1", "1.2", "1.3"] -2 ["2.3", "2.2", "2.1"] -3 ["3.2", "3.1", "3.3"] -SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a ASC; -a JSON_ARRAYAGG(b) -1 ["1.1", "1.2", "1.3"] -2 ["2.3", "2.2", "2.1"] -3 ["3.2", "3.1", "3.3"] -SELECT DISTINCT a, JSON_ARRAYAGG(b) FROM t1 GROUP BY a ORDER BY a DESC; -a JSON_ARRAYAGG(b) -3 ["3.2", "3.1", "3.3"] -2 ["2.3", "2.2", "2.1"] -1 ["1.1", "1.2", "1.3"] -SELECT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -a JSON_OBJECTAGG(k, b) -1 {"1": "1.3", "4": "1.2", "7": "1.1"} -2 {"2": "2.1", "5": "2.2", "8": "2.3"} -3 {"3": "3.3", "6": "3.2", "9": "3.1"} -SELECT DISTINCT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a; -a JSON_OBJECTAGG(k, b) -1 {"1": "1.3", "4": "1.2", "7": "1.1"} -2 {"2": "2.1", "5": "2.2", "8": "2.3"} -3 {"3": "3.3", "6": "3.2", "9": "3.1"} -SELECT DISTINCT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a ORDER BY a ASC; -a JSON_OBJECTAGG(k, b) -1 {"1": "1.3", "4": "1.2", "7": "1.1"} -2 {"2": "2.1", "5": "2.2", "8": "2.3"} -3 {"3": "3.3", "6": "3.2", "9": "3.1"} -SELECT DISTINCT a, JSON_OBJECTAGG(k, b) FROM t1 GROUP BY a ORDER BY a DESC; -a JSON_OBJECTAGG(k, b) -3 {"3": "3.3", "6": "3.2", "9": "3.1"} -2 {"2": "2.1", "5": "2.2", "8": "2.3"} -1 {"1": "1.3", "4": "1.2", "7": "1.1"} -DROP TABLE t1; -# -# Tests with joins -# -CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, t1 INT, t2 INT); -CREATE TABLE p(id INT PRIMARY KEY AUTO_INCREMENT, p1 INT, p2 INT); -INSERT INTO t(t1, t2) VALUES (1, 1), (2, 1), (3,3), (1, 4); -INSERT INTO p(p1, p2) VALUES (2, 1), (1, 1), (3,3), (2, 4); -FLUSH STATUS; -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1; -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT t1, JSON_ARRAYAGG(t2) FROM t join p on t.t1=p.p1 group by t1; -SHOW SESSION STATUS LIKE 'Handler_update%'; -Variable_name Value -Handler_update 0 -SELECT (SELECT 1 AS foo ORDER BY JSON_ARRAYAGG(t2)) AS x FROM t; -x -1 -SELECT t1 FROM t ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); -t1 -1 -2 -3 -1 -SELECT JSON_ARRAYAGG(t1) FROM t -ORDER BY (SELECT JSON_ARRAYAGG(t1) FROM t AS t2); -JSON_ARRAYAGG(t1) -[1, 2, 3, 1] -SELECT (SELECT JSON_ARRAYAGG(t1_outer.t1) FROM t AS t1_inner LIMIT 1) as f -FROM t AS t1_outer GROUP BY t1_outer.t2; -f -[1, 2] -[3] -[1] -DROP TABLE t; -DROP TABLE p; -# -# Tests with JSON -# -CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, k INT, j JSON); -INSERT INTO t(k, j) VALUES -(1, '[1,2,3,4]'), -(2, '{"prop1": 1}'), -(1, '[3]'), -(2, '{"prop2": 2, "prop10": 10}'), -(1, '[99]'); -SELECT k, JSON_ARRAYAGG(j) FROM t GROUP BY k; -k JSON_ARRAYAGG(j) -1 [[1, 2, 3, 4], [3], [99]] -2 [{"prop1": 1}, {"prop2": 2, "prop10": 10}] -SELECT k, JSON_OBJECTAGG(id, j) FROM t GROUP BY k; -k JSON_OBJECTAGG(id, j) -1 {"1": [1, 2, 3, 4], "3": [3], "5": [99]} -2 {"2": {"prop1": 1}, "4": {"prop2": 2, "prop10": 10}} -DROP TABLE t; -# -# Coverage tests for val_* functions -# -CREATE TABLE t2(gid int, a int); -INSERT INTO t2(gid, a) VALUES (1, 1), (1, 2), (2, 4), (2, 8); -SELECT gid, 1.0 * JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; -gid 1.0 * JSON_ARRAYAGG(a) -1 0 -2 0 -Warnings: -Warning 3156 Invalid JSON value for CAST to DOUBLE from column json_arrayagg at row 1 -Warning 3156 Invalid JSON value for CAST to DOUBLE from column json_arrayagg at row 2 -SELECT gid, 0x30 << JSON_ARRAYAGG(a) FROM t2 GROUP BY gid; -gid 0x30 << JSON_ARRAYAGG(a) -1 48 -2 48 -Warnings: -Warning 3156 Invalid JSON value for CAST to INTEGER from column json_arrayagg at row 1 -Warning 3156 Invalid JSON value for CAST to INTEGER from column json_arrayagg at row 2 -SELECT gid, DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) -FROM t2 GROUP BY gid; -gid DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) -1 NULL -2 NULL -Warnings: -Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 1 -Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 2 -SELECT gid, ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') -FROM t2 GROUP BY gid; -gid ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') -1 NULL -2 NULL -Warnings: -Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 1 -Warning 3156 Invalid JSON value for CAST to DATE/TIME/DATETIME/TIMESTAMP from column json_arrayagg at row 2 -SELECT gid, SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2 GROUP BY gid; -gid SEC_TO_TIME(JSON_ARRAYAGG(a)) -1 00:00:00.000000 -2 00:00:00.000000 -Warnings: -Warning 3156 Invalid JSON value for CAST to DECIMAL from column json_arrayagg at row 1 -Warning 3156 Invalid JSON value for CAST to DECIMAL from column json_arrayagg at row 2 -TRUNCATE TABLE t2; -SELECT 1.0 * JSON_ARRAYAGG(a) FROM t2; -1.0 * JSON_ARRAYAGG(a) -NULL -SELECT 0x30 << JSON_ARRAYAGG(a) FROM t2; -0x30 << JSON_ARRAYAGG(a) -NULL -SELECT DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) FROM t2; -DATE_ADD(JSON_ARRAYAGG(a), INTERVAL 31 DAY) -NULL -SELECT ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') FROM t2; -ADDTIME(JSON_ARRAYAGG(a), '02:00:00.999998') -NULL -SELECT SEC_TO_TIME(JSON_ARRAYAGG(a)) FROM t2; -SEC_TO_TIME(JSON_ARRAYAGG(a)) -NULL -DROP TABLE t2; diff --git a/sql/a_star.cc b/sql/a_star.cc index 726c4ca8958e..13e5f975b8c6 100644 --- a/sql/a_star.cc +++ b/sql/a_star.cc @@ -71,6 +71,20 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( : Item_sum_json(std::move(wrapper), pos, args, w), m_json_object(std::move(object)) {} +bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { + + + return Item_sum_json::val_json(wr); +} +String *Item_sum_shortest_dir_path::val_str(String *str) { + //const THD *thd = base_query_block->parent_lex->thd; + + str->length(0); + str->append("mip mop"); + + return str; //Item_sum_json::val_str(str); +} + void Item_sum_shortest_dir_path::clear() { null_value = true; m_json_object->clear(); @@ -92,86 +106,8 @@ bool Item_sum_shortest_dir_path::add() { clear()) */ if (thd->is_error()) return error_json(); - - try { - // key - Item *key_item = args[0]; - const char *safep; // contents of key_item, possibly converted - size_t safe_length; // length of safep - - if (get_json_object_member_name(thd, key_item, &m_tmp_key_value, - &m_conversion_buffer, &safep, &safe_length)) - return error_json(); - - std::string key(safep, safe_length); - if (m_is_window_function) { - /* - When a row is leaving a frame, we have two options: - 1. If rows are ordered according to the "key", then remove - the key/value pair from Json_object if this row is the - last row in peerset for that key. - 2. If unordered, reduce the count in the key map for this key. - If the count is 0, remove the key/value pair from the Json_object. - */ - if (m_window->do_inverse()) { - auto object = down_cast(m_wrapper->to_dom(thd)); - if (m_optimize) // Option 1 - { - if (m_window->is_last_row_in_peerset_within_frame()) - object->remove(key); - } else // Option 2 - { - auto it = m_key_map.find(key); - if (it != m_key_map.end()) { - int count = it->second - 1; - if (count > 0) { - it->second = count; - } else { - m_key_map.erase(it); - object->remove(key); - } - } - } - object->cardinality() == 0 ? null_value = true : null_value = false; - return false; - } - } - // value - Json_wrapper value_wrapper; - if (get_atom_null_as_null(args, 1, func_name(), &m_value, - &m_conversion_buffer, &value_wrapper)) - return error_json(); - - /* - The m_wrapper always points to m_json_object or the result of - deserializing the result_field in reset/update_field. - */ - Json_object *object = down_cast(m_wrapper->to_dom(thd)); - if (object->add_alias(key, value_wrapper.to_dom(thd))) - return error_json(); /* purecov: inspected */ - /* - If rows in the window are not ordered based on "key", add this key - to the key map. - */ - if (m_is_window_function && !m_optimize) { - int count = 1; - auto it = m_key_map.find(key); - if (it != m_key_map.end()) { - count = count + it->second; - it->second = count; - } else - m_key_map.emplace(std::make_pair(key, count)); - } - - null_value = false; - // object will take ownership of the value - value_wrapper.set_alias(); - } catch (...) { - /* purecov: begin inspected */ - handle_std_exception(func_name()); - return error_json(); - /* purecov: end */ - } + + return false; } diff --git a/sql/a_star.h b/sql/a_star.h index dce66ba87cce..e6c2e6900373 100644 --- a/sql/a_star.h +++ b/sql/a_star.h @@ -54,6 +54,9 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { enum Sumfunctype sum_func() const override { return SHORTEST_DIR_PATH_FUNC; } const char *func_name() const override { return "st_shortest_dir_path"; } + bool val_json(Json_wrapper *wr) override; + String *val_str(String *str) override; + void clear() override; bool add() override; Item *copy_or_same(THD *thd) override; From dd451d1e2f0cb63f34c7fdb4b41466f72e6cf936 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 15 Mar 2022 21:20:22 +0100 Subject: [PATCH 05/67] st_sdp input output framework --- .../suite/gis/r/st_shortest_dir_path.result | 2 +- sql/a_star.cc | 23 ++++++++++++++++--- sql/a_star.h | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index ee6df8cbd4e3..c9263e3ac0c5 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -9,5 +9,5 @@ insert into paths values(1, 1, 2, 5.0); insert into paths values(2, 0, 2, 15.0); select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) -mip mop +{"0": 5.0, "1": 5.0, "2": 15.0} drop table paths; diff --git a/sql/a_star.cc b/sql/a_star.cc index 13e5f975b8c6..d52128da9827 100644 --- a/sql/a_star.cc +++ b/sql/a_star.cc @@ -77,10 +77,17 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { return Item_sum_json::val_json(wr); } String *Item_sum_shortest_dir_path::val_str(String *str) { - //const THD *thd = base_query_block->parent_lex->thd; + const THD *thd = base_query_block->parent_lex->thd; + Json_object *object = down_cast(m_wrapper->to_dom(thd)); + + for (auto& edge_pair : m_edge_map) { + Json_double *num = new (std::nothrow) Json_double(edge_pair.second.cost); + if (object->add_alias(std::to_string(edge_pair.first), (Json_dom*)num)) + return error_str(); + } str->length(0); - str->append("mip mop"); + if (m_wrapper->to_string(str, true, func_name())) return error_str(); return str; //Item_sum_json::val_str(str); } @@ -107,7 +114,17 @@ bool Item_sum_shortest_dir_path::add() { */ if (thd->is_error()) return error_json(); - + int id, from_id, to_id; + double cost; + id = args[0]->val_real(); + from_id = args[1]->val_real(); + to_id = args[2]->val_real(); + cost = args[3]->val_real(); + if (thd->is_error()) return true; + for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; + + m_edge_map.insert(std::pair(id, Edge{id, from_id, to_id, cost})); + return false; } diff --git a/sql/a_star.h b/sql/a_star.h index e6c2e6900373..c1c7c8935049 100644 --- a/sql/a_star.h +++ b/sql/a_star.h @@ -20,7 +20,7 @@ class Dijkstra { }; class Item_sum_shortest_dir_path final : public Item_sum_json { - std::unordered_multimap edge_map; + std::unordered_multimap m_edge_map; /// Accumulates the final value. unique_ptr_destroy_only m_json_object; /// Buffer used to get the value of the key. From 6f046572d49e11c191fbaabf0b50c928915faa13 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 16 Mar 2022 12:47:07 +0100 Subject: [PATCH 06/67] st_sdp dijkstra & file rename --- .../suite/gis/r/st_shortest_dir_path.result | 2 +- sql/CMakeLists.txt | 3 +- sql/Dijkstras_functor.cc | 46 ++++++++++++++++ sql/Dijkstras_functor.h | 53 +++++++++++++++++++ ..._star.cc => Item_sum_shortest_dir_path.cc} | 13 +++-- ...{a_star.h => Item_sum_shortest_dir_path.h} | 19 +------ sql/sql_yacc.yy | 2 +- 7 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 sql/Dijkstras_functor.cc create mode 100644 sql/Dijkstras_functor.h rename sql/{a_star.cc => Item_sum_shortest_dir_path.cc} (91%) rename sql/{a_star.h => Item_sum_shortest_dir_path.h} (83%) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index c9263e3ac0c5..fecdd3a56f41 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -9,5 +9,5 @@ insert into paths values(1, 1, 2, 5.0); insert into paths values(2, 0, 2, 15.0); select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) -{"0": 5.0, "1": 5.0, "2": 15.0} +{"0": 5.0, "1": 5.0} drop table paths; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 90c133f12486..3f5fda8220a3 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -405,7 +405,7 @@ SET(SQL_SHARED_SOURCES item_strfunc.cc item_subselect.cc item_sum.cc - a_star.cc + Item_sum_shortest_dir_path.cc window.cc item_timefunc.cc item_xmlfunc.cc @@ -625,6 +625,7 @@ SET(SQL_SHARED_SOURCES uniques.cc xa.cc daemon_proxy_keyring/daemon_proxy_keyring.cc + Dijkstras_functor.cc ) IF(BUILD_IS_SINGLE_CONFIG) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc new file mode 100644 index 000000000000..c6b9ac598443 --- /dev/null +++ b/sql/Dijkstras_functor.cc @@ -0,0 +1,46 @@ +#include "sql/Dijkstras_functor.h" + +std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { + empty_path_maps(); + int point = start_point_id; + set_point(point, 0, heu(point), nullptr); + popped_map[point] = true; + // A* + while (point != end_point_id) { + const double point_cost = cost_map[point]; + const std::pair edge_range_it = edges_lookup_from.equal_range(point); + for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { + const Edge* edge = edge_it->second; + if (contains(popped_map, edge->to)) continue; + double old_cost, new_cost = point_cost + edge->cost; + const bool found = extract(cost_map, edge->to, old_cost); + if (found && new_cost > old_cost) continue; + set_point(edge->to, new_cost, new_cost + heu(edge->to), edge); + point_heap.push_back(edge->to); + std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); + } + if (point_heap.empty()) return {}; + point = point_heap.front(); + point_heap.pop_front(); + popped_map[point] = true; + } + total_cost = cost_map[point]; + return retrace(point, start_point_id); +} +void Dijkstra::set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path) { + cost_map[id] = cost; heu_cost_map[id] = heu_cost; path_map[id] = path; +} +std::vector Dijkstra::retrace(int from_point, int to_point) { + std::vector path; + while (from_point != to_point) { + const Edge* path_ = path_map[from_point]; + path.push_back(path_); + from_point = path_->from; + } + std::reverse(path.begin(), path.end()); + return path; +} +void Dijkstra::empty_path_maps(){ + popped_map = {}; cost_map = {}; heu_cost_map = {}; path_map = {}; + point_heap = {}; +} diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h new file mode 100644 index 000000000000..44b2e17b5f23 --- /dev/null +++ b/sql/Dijkstras_functor.h @@ -0,0 +1,53 @@ +#ifndef DIJKSTRAS_FUNCTOR_INCLUDED +#define DIJKSTRAS_FUNCTOR_INCLUDED + +#include +#include +#include +#include + +struct Edge { + int id; + int from, to; + double cost; +}; + +class Dijkstra { + typedef std::unordered_multimap::iterator edge_iterator; + + std::unordered_multimap edges_lookup_from; + std::function heu = [](const int&) -> double { return 0.0; }; + std::unordered_map popped_map; + std::unordered_map cost_map, heu_cost_map; // heu_cost = real_cost + heuristic + std::unordered_map path_map; + + struct greater_point_heuristic_comparator { + bool operator()(const int& a, const int& b) { return val.at(a) > val.at(b); } + const std::unordered_map& val; + } heap_cmp{ heu_cost_map }; + std::deque point_heap; + + public: + Dijkstra(std::unordered_multimap edges_lookup_from, + std::function heu_func = [](const int&) -> double { return 0.0; }) + : edges_lookup_from(edges_lookup_from), heu(heu_func) {} + + std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost); + private: + template + inline static bool contains(std::unordered_map map, Key key) { + return map.find(key) != map.end(); + } + template + bool extract(std::unordered_map map, Key key, T& val) { + auto pair = map.find(key); + bool found = pair != map.end(); + if (found) val = pair->second; + return found; + } + void set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path); + std::vector retrace(int from_point, int to_point); + void empty_path_maps(); +}; + +#endif diff --git a/sql/a_star.cc b/sql/Item_sum_shortest_dir_path.cc similarity index 91% rename from sql/a_star.cc rename to sql/Item_sum_shortest_dir_path.cc index d52128da9827..ac16c1b517e2 100644 --- a/sql/a_star.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -1,4 +1,4 @@ -#include "sql/a_star.h" +#include "sql/Item_sum_shortest_dir_path.h" #include #include @@ -57,6 +57,7 @@ #include "sql/temp_table_param.h" // Temp_table_param #include "sql/uniques.h" // Unique #include "sql/window.h" +#include "sql/Dijkstras_functor.h" Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper, @@ -80,9 +81,11 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { const THD *thd = base_query_block->parent_lex->thd; Json_object *object = down_cast(m_wrapper->to_dom(thd)); - for (auto& edge_pair : m_edge_map) { - Json_double *num = new (std::nothrow) Json_double(edge_pair.second.cost); - if (object->add_alias(std::to_string(edge_pair.first), (Json_dom*)num)) + Dijkstra dijkstra(m_edge_map); + double cost; + for (const Edge* edge : dijkstra(0, 2, cost)) { + Json_double *num = new (std::nothrow) Json_double(edge->cost); + if (object->add_alias(std::to_string(edge->id), (Json_dom*)num)) return error_str(); } @@ -123,7 +126,7 @@ bool Item_sum_shortest_dir_path::add() { if (thd->is_error()) return true; for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; - m_edge_map.insert(std::pair(id, Edge{id, from_id, to_id, cost})); + m_edge_map.insert(std::pair(from_id, new Edge{id, from_id, to_id, cost})); return false; diff --git a/sql/a_star.h b/sql/Item_sum_shortest_dir_path.h similarity index 83% rename from sql/a_star.h rename to sql/Item_sum_shortest_dir_path.h index c1c7c8935049..be6a6796f34d 100644 --- a/sql/a_star.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -2,25 +2,10 @@ #define ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED #include "sql/item_sum.h" - - -struct Edge { - int id, from, to; - double cost; -}; - -class Dijkstra { - int heu_coeff; -public: - Dijkstra(const int& heu_coeff = 1): heu_coeff(heu_coeff) {} - template - std::vector operator()(const std::unordered_multimap& edge_map, int start_id, int end_id){ - return {}; - } -}; +#include "sql/Dijkstras_functor.h" class Item_sum_shortest_dir_path final : public Item_sum_json { - std::unordered_multimap m_edge_map; + std::unordered_multimap m_edge_map; /// Accumulates the final value. unique_ptr_destroy_only m_json_object; /// Buffer used to get the value of the key. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 2d4498b3a803..eff7fb6dd0bb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -103,7 +103,7 @@ Note: YYTHD is passed as an argument to yyparse(), and subsequently to yylex(). #include "sql/item_strfunc.h" #include "sql/item_subselect.h" #include "sql/item_sum.h" -#include "sql/a_star.h" +#include "sql/Item_sum_shortest_dir_path.h" #include "sql/item_timefunc.h" #include "sql/json_dom.h" #include "sql/json_syntax_check.h" // is_valid_json_syntax From 362191d1bdc7e25db4a62c67f1128877235a4a00 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 17 Mar 2022 11:30:55 +0100 Subject: [PATCH 07/67] st_sdp json path array from val_str --- .../suite/gis/r/st_shortest_dir_path.result | 12 ++++--- .../suite/gis/t/st_shortest_dir_path.test | 10 ++++-- sql/Item_sum_shortest_dir_path.cc | 34 +++++++++++++++---- sql/Item_sum_shortest_dir_path.h | 3 ++ 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index fecdd3a56f41..809c9d026522 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -4,10 +4,14 @@ from_id int, to_id int, cost double ); -insert into paths values(0, 0, 1, 5.0); -insert into paths values(1, 1, 2, 5.0); -insert into paths values(2, 0, 2, 15.0); +insert into paths values +(0, 0, 1, 5.0), +(1, 1, 2, 5.0), +(2, 0, 2, 15.0), +(3, 1, 3, 2.0), +(4, 3, 2, 2.0) +; select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) -{"0": 5.0, "1": 5.0} +{"cost": 9.0, "path": [{"id": 0, "cost": 5.0}, {"id": 3, "cost": 2.0}, {"id": 4, "cost": 2.0}]} drop table paths; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 4ac833a6450b..395b25d9960b 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -5,9 +5,13 @@ create table paths( cost double ); -insert into paths values(0, 0, 1, 5.0); -insert into paths values(1, 1, 2, 5.0); -insert into paths values(2, 0, 2, 15.0); +insert into paths values + (0, 0, 1, 5.0), + (1, 1, 2, 5.0), + (2, 0, 2, 15.0), + (3, 1, 3, 2.0), + (4, 3, 2, 2.0) + ; select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index ac16c1b517e2..816ff7b610df 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -57,7 +57,6 @@ #include "sql/temp_table_param.h" // Temp_table_param #include "sql/uniques.h" // Unique #include "sql/window.h" -#include "sql/Dijkstras_functor.h" Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper, @@ -73,25 +72,37 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( m_json_object(std::move(object)) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { - + assert(false); return Item_sum_json::val_json(wr); } String *Item_sum_shortest_dir_path::val_str(String *str) { - const THD *thd = base_query_block->parent_lex->thd; - Json_object *object = down_cast(m_wrapper->to_dom(thd)); + assert(!m_is_window_function); + const THD *thd = base_query_block->parent_lex->thd; + if (thd->is_error()) return error_str(); + + Json_array *arr = new (std::nothrow) Json_array(); Dijkstra dijkstra(m_edge_map); double cost; for (const Edge* edge : dijkstra(0, 2, cost)) { - Json_double *num = new (std::nothrow) Json_double(edge->cost); - if (object->add_alias(std::to_string(edge->id), (Json_dom*)num)) - return error_str(); + Json_object *json_edge = new (std::nothrow) Json_object(); + if (json_edge->add_alias("id", jsonify_to_heap(edge->id)) || + json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || + arr->append_alias(json_edge)) + return error_str(); } + Json_object *object = down_cast(m_wrapper->to_dom(thd)); + object->clear(); + if( object->add_alias("path", arr) || + object->add_alias("cost", jsonify_to_heap(cost))) + return error_str(); str->length(0); if (m_wrapper->to_string(str, true, func_name())) return error_str(); + if(aggr) aggr->endup(); + return str; //Item_sum_json::val_str(str); } @@ -99,6 +110,7 @@ void Item_sum_shortest_dir_path::clear() { null_value = true; m_json_object->clear(); + for (auto& pair : m_edge_map) delete pair.second; // Set the object to the m_wrapper, but let a_star_ting keep the // ownership. *m_wrapper = Json_wrapper(m_json_object.get(), true); @@ -108,6 +120,7 @@ void Item_sum_shortest_dir_path::clear() { bool Item_sum_shortest_dir_path::add() { assert(fixed); assert(arg_count == 6); + assert(!m_is_window_function); const THD *thd = base_query_block->parent_lex->thd; /* @@ -151,3 +164,10 @@ bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *sele return Item_sum::check_wf_semantics1(thd, select, reqs); } +Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(int i) { + return new (std::nothrow) Json_int(i); +} +Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(double d) { + return new (std::nothrow) Json_double(d); +} + diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index be6a6796f34d..89d0d93f342a 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -48,6 +48,9 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { bool check_wf_semantics1(THD *thd, Query_block *select, Window_evaluation_requirements *reqs) override; + private: + Json_dom *jsonify_to_heap(int i); + Json_dom *jsonify_to_heap(double d); }; #endif /* ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED */ From 7e588d619e7c34626272618666c95d123f46d9b0 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 17 Mar 2022 11:50:15 +0100 Subject: [PATCH 08/67] added begin and end node --- sql/Item_sum_shortest_dir_path.cc | 13 ++++++++----- sql/Item_sum_shortest_dir_path.h | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index ac16c1b517e2..6dcbd769e563 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -83,7 +83,7 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { Dijkstra dijkstra(m_edge_map); double cost; - for (const Edge* edge : dijkstra(0, 2, cost)) { + for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { Json_double *num = new (std::nothrow) Json_double(edge->cost); if (object->add_alias(std::to_string(edge->id), (Json_dom*)num)) return error_str(); @@ -116,13 +116,16 @@ bool Item_sum_shortest_dir_path::add() { clear()) */ if (thd->is_error()) return error_json(); - + int id, from_id, to_id; double cost; - id = args[0]->val_real(); - from_id = args[1]->val_real(); - to_id = args[2]->val_real(); + + id = args[0]->val_int(); + from_id = args[1]->val_int(); + to_id = args[2]->val_int(); cost = args[3]->val_real(); + m_begin_node = args[4]->val_real(); + m_end_node = args[5]->val_real(); if (thd->is_error()) return true; for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index be6a6796f34d..883d97bc9914 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -5,6 +5,8 @@ #include "sql/Dijkstras_functor.h" class Item_sum_shortest_dir_path final : public Item_sum_json { + + int m_begin_node, m_end_node; std::unordered_multimap m_edge_map; /// Accumulates the final value. unique_ptr_destroy_only m_json_object; From e0db1dd04e20113e0fe3e7b4c1b0b495187824c6 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 17 Mar 2022 13:12:06 +0100 Subject: [PATCH 09/67] st_sdp clear --- .../suite/gis/r/st_shortest_dir_path.result | 19 ++++++++------ .../suite/gis/t/st_shortest_dir_path.test | 25 +++++++++++-------- sql/Item_sum_shortest_dir_path.cc | 1 + 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 809c9d026522..c36e64118bba 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -2,16 +2,19 @@ create table paths( id int primary key, from_id int, to_id int, -cost double +cost double, +type varchar(50) ); insert into paths values -(0, 0, 1, 5.0), -(1, 1, 2, 5.0), -(2, 0, 2, 15.0), -(3, 1, 3, 2.0), -(4, 3, 2, 2.0) +(10, 0, 1, 2.0, "car"), +(11, 1, 2, 3.0, "car"), +(12, 0, 2, 8.0, "car"), +(20, 0, 1, 20.0, "bike"), +(21, 1, 2, 25.0, "bike"), +(22, 0, 2, 40.0, "bike") ; -select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; +select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) -{"cost": 9.0, "path": [{"id": 0, "cost": 5.0}, {"id": 3, "cost": 2.0}, {"id": 4, "cost": 2.0}]} +{"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} +{"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} drop table paths; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 395b25d9960b..ef113f2b6cf9 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -1,18 +1,21 @@ create table paths( - id int primary key, - from_id int, - to_id int, - cost double +id int primary key, +from_id int, +to_id int, +cost double, +type varchar(50) ); insert into paths values - (0, 0, 1, 5.0), - (1, 1, 2, 5.0), - (2, 0, 2, 15.0), - (3, 1, 3, 2.0), - (4, 3, 2, 2.0) - ; +(10, 0, 1, 2.0, "car"), +(11, 1, 2, 3.0, "car"), +(12, 0, 2, 8.0, "car"), -select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; +(20, 0, 1, 20.0, "bike"), +(21, 1, 2, 25.0, "bike"), +(22, 0, 2, 40.0, "bike") +; + +select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; drop table paths; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 64558562c0bd..113587de64ac 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -113,6 +113,7 @@ void Item_sum_shortest_dir_path::clear() { m_json_object->clear(); for (auto& pair : m_edge_map) delete pair.second; + m_edge_map.clear(); // Set the object to the m_wrapper, but let a_star_ting keep the // ownership. *m_wrapper = Json_wrapper(m_json_object.get(), true); From 4af0086f577d469756b4e6b006a489fb1b853a14 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 17 Mar 2022 13:18:17 +0100 Subject: [PATCH 10/67] st_sdp added argument verifacation --- .../suite/gis/t/st_shortest_dir_path.test | 2 +- sql/Item_sum_shortest_dir_path.cc | 45 ++++++++++++++++++- sql/Item_sum_shortest_dir_path.h | 3 ++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 395b25d9960b..ceae149d53d7 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -13,6 +13,6 @@ insert into paths values (4, 3, 2, 2.0) ; -select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths; +select st_shortest_dir_path(id, from_id, to_id, cost, id, 2) from paths; drop table paths; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 64558562c0bd..f87cdcbb5689 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -135,12 +135,24 @@ bool Item_sum_shortest_dir_path::add() { int id, from_id, to_id; double cost; + for (size_t i = 0; i < 2; i++) + { + verify_id_argument(i); + } + + verify_cost_argument(3); + + for (size_t i = 4; i < 5; i++) + { + verify_const_id_argument(i); + } + id = args[0]->val_int(); from_id = args[1]->val_int(); to_id = args[2]->val_int(); cost = args[3]->val_real(); - m_begin_node = args[4]->val_real(); - m_end_node = args[5]->val_real(); + m_begin_node = args[4]->val_int(); + m_end_node = args[5]->val_int(); if (thd->is_error()) return true; for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; @@ -169,6 +181,35 @@ bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *sele return Item_sum::check_wf_semantics1(thd, select, reqs); } +bool Item_sum_shortest_dir_path::verify_const_id_argument(int i) { + + if (!args[i]->const_item() || + (!args[i]->is_null() && + (args[i]->result_type() != INT_RESULT))) { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return true; + } + return false; +} + +bool Item_sum_shortest_dir_path::verify_id_argument(int i) { + + if (!args[i]->is_null() && (args[i]->result_type() != INT_RESULT)) { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return true; + } + return false; +} + +bool Item_sum_shortest_dir_path::verify_cost_argument(int i) { + + if (!args[i]->is_null() && (args[i]->result_type() != REAL_RESULT)) { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return true; + } + return false; +} + Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(int i) { return new (std::nothrow) Json_int(i); } diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 8715409967dd..5afd27f9b24b 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -51,6 +51,9 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { Window_evaluation_requirements *reqs) override; private: + bool verify_const_id_argument(int i); + bool verify_id_argument(int i); + bool verify_cost_argument(int i); Json_dom *jsonify_to_heap(int i); Json_dom *jsonify_to_heap(double d); }; From 7da484b8ea2830bf503f57291658f8c45e67798b Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 17 Mar 2022 13:23:19 +0100 Subject: [PATCH 11/67] st_sdb fixed one off errors --- sql/Item_sum_shortest_dir_path.cc | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 1eccfe625111..a76cb11cdf04 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -136,17 +136,9 @@ bool Item_sum_shortest_dir_path::add() { int id, from_id, to_id; double cost; - for (size_t i = 0; i < 2; i++) - { - verify_id_argument(i); - } - + for (size_t i = 0; i <= 2; i++) verify_id_argument(i); verify_cost_argument(3); - - for (size_t i = 4; i < 5; i++) - { - verify_const_id_argument(i); - } + for (size_t i = 4; i <= 5; i++) verify_const_id_argument(i); id = args[0]->val_int(); from_id = args[1]->val_int(); From 998132241b49644b9aaa86d9aea156a9df7921e9 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 18 Mar 2022 12:51:55 +0100 Subject: [PATCH 12/67] st_sdp inlining & Dijkstra doc --- sql/Dijkstras_functor.cc | 45 +++++++++----- sql/Dijkstras_functor.h | 99 ++++++++++++++++++++++++------- sql/Item_sum_shortest_dir_path.cc | 14 ++--- sql/Item_sum_shortest_dir_path.h | 10 ++-- 4 files changed, 119 insertions(+), 49 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index c6b9ac598443..7ade3bf347ad 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -3,37 +3,52 @@ std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { empty_path_maps(); int point = start_point_id; - set_point(point, 0, heu(point), nullptr); - popped_map[point] = true; + set_point(point, 0, m_heu(point), nullptr); + m_popped_map[point] = true; // A* while (point != end_point_id) { - const double point_cost = cost_map[point]; - const std::pair edge_range_it = edges_lookup_from.equal_range(point); + const double point_cost = m_cost_map[point]; + const std::pair edge_range_it = m_edges_lookup_from.equal_range(point); + // checks all edges from point (i.e. current point) for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { - const Edge* edge = edge_it->second; - if (contains(popped_map, edge->to)) continue; + const Edge *edge = edge_it->second; + // ignore edges to popped(visited) nodes + if (contains(m_popped_map, edge->to)) continue; + // ignore edge if new path cost > prev path cost to same node double old_cost, new_cost = point_cost + edge->cost; - const bool found = extract(cost_map, edge->to, old_cost); - if (found && new_cost > old_cost) continue; - set_point(edge->to, new_cost, new_cost + heu(edge->to), edge); + if (extract(m_cost_map, edge->to, old_cost) && new_cost > old_cost) continue; + + set_point(edge->to, new_cost, new_cost + m_heu(edge->to), edge); point_heap.push_back(edge->to); std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); } if (point_heap.empty()) return {}; point = point_heap.front(); point_heap.pop_front(); - popped_map[point] = true; + m_popped_map[point] = true; } - total_cost = cost_map[point]; + total_cost = m_cost_map[point]; return retrace(point, start_point_id); } + +template +inline bool Dijkstra::contains(std::unordered_map map, Key key) { + return map.find(key) != map.end(); +} +template +inline bool Dijkstra::extract(std::unordered_map map, Key key, T& val) { + auto pair = map.find(key); + bool found = pair != map.end(); + if (found) val = pair->second; + return found; +} void Dijkstra::set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path) { - cost_map[id] = cost; heu_cost_map[id] = heu_cost; path_map[id] = path; + m_cost_map[id] = cost; m_heu_cost_map[id] = heu_cost; m_path_map[id] = path; } std::vector Dijkstra::retrace(int from_point, int to_point) { std::vector path; while (from_point != to_point) { - const Edge* path_ = path_map[from_point]; + const Edge* path_ = m_path_map[from_point]; path.push_back(path_); from_point = path_->from; } @@ -41,6 +56,6 @@ std::vector Dijkstra::retrace(int from_point, int to_point) { return path; } void Dijkstra::empty_path_maps(){ - popped_map = {}; cost_map = {}; heu_cost_map = {}; path_map = {}; - point_heap = {}; + m_popped_map.clear(); m_cost_map.clear(); m_heu_cost_map.clear(); m_path_map.clear(); + point_heap.clear(); } diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 44b2e17b5f23..1a1f7b9a2e8d 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -6,48 +6,107 @@ #include #include +/** + * @brief Edge data for Dijkstra functor + * + */ struct Edge { int id; int from, to; double cost; }; +/** + * @brief functor for A* (finds shortest path) + * + */ class Dijkstra { - typedef std::unordered_multimap::iterator edge_iterator; + typedef std::unordered_multimap::const_iterator edge_iterator; - std::unordered_multimap edges_lookup_from; - std::function heu = [](const int&) -> double { return 0.0; }; - std::unordered_map popped_map; - std::unordered_map cost_map, heu_cost_map; // heu_cost = real_cost + heuristic - std::unordered_map path_map; + // key = Edge.from + const std::unordered_multimap m_edges_lookup_from; + const std::function m_heu = [](const int&) -> double { return 0.0; }; + std::unordered_map m_popped_map; + std::unordered_map m_cost_map, m_heu_cost_map; // heu_cost = real_cost + heuristic + std::unordered_map m_path_map; + + // comparator used for point_heap sorting to make min heap based on m_heu_cost_map struct greater_point_heuristic_comparator { bool operator()(const int& a, const int& b) { return val.at(a) > val.at(b); } const std::unordered_map& val; - } heap_cmp{ heu_cost_map }; + } heap_cmp{ m_heu_cost_map }; std::deque point_heap; public: + /** + * @brief Construct a new Dijkstra object + * + * @param edges_lookup_from key must equal edge start node id (i.e. Edge.from) + * @param heu_func A* heuristic. If not supplied normal dijkstra will be used + */ Dijkstra(std::unordered_multimap edges_lookup_from, std::function heu_func = [](const int&) -> double { return 0.0; }) - : edges_lookup_from(edges_lookup_from), heu(heu_func) {} - + : m_edges_lookup_from(edges_lookup_from), m_heu(heu_func) {} + /** + * @brief runs A* to find shortest path through m_edges_lookup_from + * + * @param start_point_id node id of path start + * @param end_point_id node if of path end + * @param total_cost l-val-ref returns total cost of found path (if path exists) + * @return std::vector vector of pointers pointing to edges in + * edges_lookup_from representing found path + */ std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost); private: + /** + * @brief checks if map contains key + * + * @tparam Key type + * @tparam T value type + * @param map + * @param key + * @return true if map contains key + * @return false if map doesn't contain key + */ template - inline static bool contains(std::unordered_map map, Key key) { - return map.find(key) != map.end(); - } + static inline bool contains(std::unordered_map map, Key key); + /** + * @brief extracts value from map with given key + * + * @tparam Key type + * @tparam T value type + * @param map + * @param key + * @param val + * @return true if map contains key + * @return false if map doesn't contain key + */ template - bool extract(std::unordered_map map, Key key, T& val) { - auto pair = map.find(key); - bool found = pair != map.end(); - if (found) val = pair->second; - return found; - } - void set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path); + static inline bool extract(std::unordered_map map, Key key, T& val); + /** + * @brief stores point info in node maps (i.e. m_xxx_map) + * + * @param id node id + * @param cost dijkstra path cost + * @param heu_cost cost + heuristic_cost (e.g. euclidean dist) + * @param path ptr to edge leading to node + */ + inline void set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path); + /** + * @brief finds path by accumulating edge_ptrs from m_path_map and reverting their order + * NB: will deref invalid ptr if path doesn't exist + * @param from_point node id of path start + * @param to_point node id of path end + * @return std::vector path found in m_path_map + */ std::vector retrace(int from_point, int to_point); - void empty_path_maps(); + + /** + * @brief empties all node maps (i.e. m_xxx_map) to remove prev path data + * + */ + inline void empty_path_maps(); }; #endif diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index a76cb11cdf04..9be2c1d0e225 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -174,8 +174,7 @@ bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *sele return Item_sum::check_wf_semantics1(thd, select, reqs); } -bool Item_sum_shortest_dir_path::verify_const_id_argument(int i) { - +inline bool Item_sum_shortest_dir_path::verify_const_id_argument(int i) { if (!args[i]->const_item() || (!args[i]->is_null() && (args[i]->result_type() != INT_RESULT))) { @@ -185,8 +184,7 @@ bool Item_sum_shortest_dir_path::verify_const_id_argument(int i) { return false; } -bool Item_sum_shortest_dir_path::verify_id_argument(int i) { - +inline bool Item_sum_shortest_dir_path::verify_id_argument(int i) { if (!args[i]->is_null() && (args[i]->result_type() != INT_RESULT)) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; @@ -194,8 +192,7 @@ bool Item_sum_shortest_dir_path::verify_id_argument(int i) { return false; } -bool Item_sum_shortest_dir_path::verify_cost_argument(int i) { - +inline bool Item_sum_shortest_dir_path::verify_cost_argument(int i) { if (!args[i]->is_null() && (args[i]->result_type() != REAL_RESULT)) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; @@ -203,10 +200,9 @@ bool Item_sum_shortest_dir_path::verify_cost_argument(int i) { return false; } -Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(int i) { +inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(int i) { return new (std::nothrow) Json_int(i); } -Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(double d) { +inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(double d) { return new (std::nothrow) Json_double(d); } - diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 5afd27f9b24b..31b2ef96549e 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -51,11 +51,11 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { Window_evaluation_requirements *reqs) override; private: - bool verify_const_id_argument(int i); - bool verify_id_argument(int i); - bool verify_cost_argument(int i); - Json_dom *jsonify_to_heap(int i); - Json_dom *jsonify_to_heap(double d); + inline bool verify_const_id_argument(int i); + inline bool verify_id_argument(int i); + inline bool verify_cost_argument(int i); + inline Json_dom *jsonify_to_heap(int i); + inline Json_dom *jsonify_to_heap(double d); }; #endif /* ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED */ From 175563aef9eb1e644bfb8617588900f039c60df1 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 18 Mar 2022 15:06:45 +0100 Subject: [PATCH 13/67] st_sdp rm redundant & alloc edges on thd->mem_root --- .../suite/gis/r/st_shortest_dir_path.result | 8 +++- .../suite/gis/t/st_shortest_dir_path.test | 8 +++- sql/Item_sum_shortest_dir_path.cc | 43 ++++++------------- sql/Item_sum_shortest_dir_path.h | 27 +----------- sql/sql_yacc.yy | 10 +++-- 5 files changed, 36 insertions(+), 60 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index c36e64118bba..a48cd9d59fa4 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -11,10 +11,16 @@ insert into paths values (12, 0, 2, 8.0, "car"), (20, 0, 1, 20.0, "bike"), (21, 1, 2, 25.0, "bike"), -(22, 0, 2, 40.0, "bike") +(22, 0, 2, 40.0, "bike"), +(30, 0, 1, 80.0, "pedestrian"), +(31, 1, 2, 100.0, "pedestrian"), +(32, 0, 2, 200.0, "pedestrian"), +(33, 1, 3, 40.0, "pedestrian"), +(34, 3, 2, 20.0, "pedestrian") ; select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) {"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} {"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} +{"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}]} drop table paths; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index ef113f2b6cf9..5fab6148216f 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -13,7 +13,13 @@ insert into paths values (20, 0, 1, 20.0, "bike"), (21, 1, 2, 25.0, "bike"), -(22, 0, 2, 40.0, "bike") +(22, 0, 2, 40.0, "bike"), + +(30, 0, 1, 80.0, "pedestrian"), +(31, 1, 2, 100.0, "pedestrian"), +(32, 0, 2, 200.0, "pedestrian"), +(33, 1, 3, 40.0, "pedestrian"), +(34, 3, 2, 20.0, "pedestrian") ; select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 9be2c1d0e225..bdc2fc872830 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -59,17 +59,13 @@ #include "sql/window.h" Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( - THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper, - unique_ptr_destroy_only object) - : Item_sum_json(std::move(wrapper), thd, item), - m_json_object(std::move(object)) {} + THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper) + : Item_sum_json(std::move(wrapper), thd, item) {} Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( const POS &pos, PT_item_list *args, PT_window *w, - unique_ptr_destroy_only wrapper, - unique_ptr_destroy_only object) - : Item_sum_json(std::move(wrapper), pos, args, w), - m_json_object(std::move(object)) {} + unique_ptr_destroy_only wrapper) + : Item_sum_json(std::move(wrapper), pos, args, w) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { assert(false); @@ -79,7 +75,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { String *Item_sum_shortest_dir_path::val_str(String *str) { assert(!m_is_window_function); - const THD *thd = base_query_block->parent_lex->thd; + const THD *thd = base_query_block->parent_lex->thd; if (thd->is_error()) return error_str(); Json_array *arr = new (std::nothrow) Json_array(); @@ -95,7 +91,6 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { } Json_object *object = down_cast(m_wrapper->to_dom(thd)); - object->clear(); if( object->add_alias("path", arr) || object->add_alias("cost", jsonify_to_heap(cost))) return error_str(); @@ -105,19 +100,17 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { if(aggr) aggr->endup(); - return str; //Item_sum_json::val_str(str); + return str; } void Item_sum_shortest_dir_path::clear() { + const THD *thd = base_query_block->parent_lex->thd; + null_value = true; - m_json_object->clear(); - for (auto& pair : m_edge_map) delete pair.second; m_edge_map.clear(); - // Set the object to the m_wrapper, but let a_star_ting keep the - // ownership. - *m_wrapper = Json_wrapper(m_json_object.get(), true); - m_key_map.clear(); + Json_object *object = down_cast(m_wrapper->to_dom(thd)); + object->clear(); } bool Item_sum_shortest_dir_path::add() { @@ -149,24 +142,16 @@ bool Item_sum_shortest_dir_path::add() { if (thd->is_error()) return true; for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; - m_edge_map.insert(std::pair(from_id, new Edge{id, from_id, to_id, cost})); - + m_edge_map.insert(std::pair(from_id, new (thd->mem_root) Edge{id, from_id, to_id, cost})); return false; } Item *Item_sum_shortest_dir_path::copy_or_same(THD *thd) { - if (m_is_window_function) return this; - + assert(!m_is_window_function); auto wrapper = make_unique_destroy_only(thd->mem_root); if (wrapper == nullptr) return nullptr; - - unique_ptr_destroy_only object{::new (thd->mem_root) - Json_object}; - if (object == nullptr) return nullptr; - - return new (thd->mem_root) - Item_sum_shortest_dir_path(thd, this, std::move(wrapper), std::move(object)); + return new (thd->mem_root) Item_sum_shortest_dir_path(thd, this, std::move(wrapper)); } bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *select, @@ -180,7 +165,7 @@ inline bool Item_sum_shortest_dir_path::verify_const_id_argument(int i) { (args[i]->result_type() != INT_RESULT))) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; - } + } return false; } diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 31b2ef96549e..03f4d877eabf 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -5,36 +5,13 @@ #include "sql/Dijkstras_functor.h" class Item_sum_shortest_dir_path final : public Item_sum_json { - int m_begin_node, m_end_node; std::unordered_multimap m_edge_map; - /// Accumulates the final value. - unique_ptr_destroy_only m_json_object; - /// Buffer used to get the value of the key. - String m_tmp_key_value; - /** - Map of keys in Json_object and the count for each key - within a window frame. It is used in handling rows - leaving a window frame when rows are not sorted - according to the key in Json_object. - */ - std::map m_key_map; - /** - If window provides ordering on the key in Json_object, - a key_map is not needed to handle rows leaving a window - frame. In this case, process_buffered_windowing_record() - will set flags when a key/value pair can be removed from - the Json_object. - */ - bool m_optimize{false}; - public: Item_sum_shortest_dir_path(THD *thd, Item_sum *item, - unique_ptr_destroy_only wrapper, - unique_ptr_destroy_only object); + unique_ptr_destroy_only wrapper); Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, PT_window *w, - unique_ptr_destroy_only wrapper, - unique_ptr_destroy_only object); + unique_ptr_destroy_only wrapper); ~Item_sum_shortest_dir_path() override = default; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index eff7fb6dd0bb..9a86cab9f135 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11107,15 +11107,17 @@ sum_expr: } | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' opt_windowing_clause { - auto wrapper = make_unique_destroy_only(YYMEM_ROOT); - if (wrapper == nullptr) YYABORT; - unique_ptr_destroy_only object{::new (YYMEM_ROOT) Json_object}; + Json_object *object = ::new (YYMEM_ROOT) Json_object; if (object == nullptr) YYABORT; + // object not owned (deallocated) by wrapper + auto wrapper = make_unique_destroy_only(YYMEM_ROOT, object, true); + if (wrapper == nullptr) YYABORT; PT_item_list *args= NEW_PTN PT_item_list; args->push_back($3);args->push_back($5);args->push_back($7);args->push_back($9); args->push_back($11);args->push_back($13); - $$ = NEW_PTN Item_sum_shortest_dir_path(@$, args, $15, std::move(wrapper), std::move(object)); + + $$ = NEW_PTN Item_sum_shortest_dir_path(@$, args, $15, std::move(wrapper)); } | ST_COLLECT_SYM '(' in_sum_expr ')' opt_windowing_clause { From da88b470602b8221f16eb6f9810bcac6829b41f3 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 18 Mar 2022 15:28:11 +0100 Subject: [PATCH 14/67] st_sdp doc --- sql/Dijkstras_functor.h | 2 +- sql/Item_sum_shortest_dir_path.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 1a1f7b9a2e8d..31a36cdb38b4 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -109,4 +109,4 @@ class Dijkstra { inline void empty_path_maps(); }; -#endif +#endif /* DIJKSTRAS_FUNCTOR_INCLUDED */ diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 03f4d877eabf..a9843b8c286b 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -6,10 +6,26 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; + // accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) std::unordered_multimap m_edge_map; public: + /** + * @brief Construct a new Item_sum_shortest_dir_path object + * + * @param thd + * @param item + * @param wrapper must contain Json_object + */ Item_sum_shortest_dir_path(THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper); + /** + * @brief Construct a new Item_sum_shortest_dir_path object + * + * @param pos + * @param args + * @param w + * @param wrapper must contain Json_object + */ Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, PT_window *w, unique_ptr_destroy_only wrapper); @@ -31,7 +47,19 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { inline bool verify_const_id_argument(int i); inline bool verify_id_argument(int i); inline bool verify_cost_argument(int i); + /** + * @brief allocates Json_int on heap with given value + * + * @param i value + * @return Json_dom* ptr to Json_int + */ inline Json_dom *jsonify_to_heap(int i); + /** + * @brief allocates Json_double on heap with given value + * + * @param d value + * @return Json_dom* ptr to Json_double + */ inline Json_dom *jsonify_to_heap(double d); }; From 9df5fe73e0b9c58cd5ef5ac38d63dd07b5d29d10 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 18 Mar 2022 16:05:46 +0100 Subject: [PATCH 15/67] st_sdp ::copy_or_same & cleanup & doc --- sql/Item_sum_shortest_dir_path.cc | 36 +++++++++++++++++-------------- sql/Item_sum_shortest_dir_path.h | 31 +++++++++++++++++++++----- sql/sql_yacc.yy | 2 +- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index bdc2fc872830..687a1b8e0a45 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -74,14 +74,14 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { } String *Item_sum_shortest_dir_path::val_str(String *str) { assert(!m_is_window_function); - + // could be redundant const THD *thd = base_query_block->parent_lex->thd; if (thd->is_error()) return error_str(); Json_array *arr = new (std::nothrow) Json_array(); Dijkstra dijkstra(m_edge_map); double cost; - + // jsonifying path from dijkstra into arr for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { Json_object *json_edge = new (std::nothrow) Json_object(); if (json_edge->add_alias("id", jsonify_to_heap(edge->id)) || @@ -90,6 +90,7 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { return error_str(); } + // inserting path and path cost into m_wrapper Json_object *object = down_cast(m_wrapper->to_dom(thd)); if( object->add_alias("path", arr) || object->add_alias("cost", jsonify_to_heap(cost))) @@ -129,9 +130,9 @@ bool Item_sum_shortest_dir_path::add() { int id, from_id, to_id; double cost; - for (size_t i = 0; i <= 2; i++) verify_id_argument(i); - verify_cost_argument(3); - for (size_t i = 4; i <= 5; i++) verify_const_id_argument(i); + for (size_t i = 0; i <= 2; i++) verify_id_argument(args[i]); + verify_cost_argument(args[3]); + for (size_t i = 4; i <= 5; i++) verify_const_id_argument(args[i]); id = args[0]->val_int(); from_id = args[1]->val_int(); @@ -149,7 +150,10 @@ bool Item_sum_shortest_dir_path::add() { Item *Item_sum_shortest_dir_path::copy_or_same(THD *thd) { assert(!m_is_window_function); - auto wrapper = make_unique_destroy_only(thd->mem_root); + Json_object *object = ::new (thd->mem_root, std::nothrow) Json_object; + if (object == nullptr) return nullptr; + // object not owned (deallocated) by wrapper + auto wrapper = make_unique_destroy_only(thd->mem_root, object, true); if (wrapper == nullptr) return nullptr; return new (thd->mem_root) Item_sum_shortest_dir_path(thd, this, std::move(wrapper)); } @@ -159,35 +163,35 @@ bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *sele return Item_sum::check_wf_semantics1(thd, select, reqs); } -inline bool Item_sum_shortest_dir_path::verify_const_id_argument(int i) { - if (!args[i]->const_item() || - (!args[i]->is_null() && - (args[i]->result_type() != INT_RESULT))) { +inline bool Item_sum_shortest_dir_path::verify_const_id_argument(Item *item) { + if (!item->const_item() || + (!item->is_null() && + (item->result_type() != INT_RESULT))) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; } return false; } -inline bool Item_sum_shortest_dir_path::verify_id_argument(int i) { - if (!args[i]->is_null() && (args[i]->result_type() != INT_RESULT)) { +inline bool Item_sum_shortest_dir_path::verify_id_argument(Item *item) { + if (!item->is_null() && (item->result_type() != INT_RESULT)) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; } return false; } -inline bool Item_sum_shortest_dir_path::verify_cost_argument(int i) { - if (!args[i]->is_null() && (args[i]->result_type() != REAL_RESULT)) { +inline bool Item_sum_shortest_dir_path::verify_cost_argument(Item *item) { + if (!item->is_null() && (item->result_type() != REAL_RESULT)) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; } return false; } -inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(int i) { +inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(const int& i) { return new (std::nothrow) Json_int(i); } -inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(double d) { +inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(const double& d) { return new (std::nothrow) Json_double(d); } diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index a9843b8c286b..04866e49b0dc 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -44,23 +44,44 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { Window_evaluation_requirements *reqs) override; private: - inline bool verify_const_id_argument(int i); - inline bool verify_id_argument(int i); - inline bool verify_cost_argument(int i); + /** + * @brief verifies that item is a valid const id + * + * @param item + * @return true if valid + * @return false if invalid + */ + inline bool verify_const_id_argument(Item *item); + /** + * @brief verifies that item is a valid id + * + * @param item + * @return true if valid + * @return false if invalid + */ + inline bool verify_id_argument(Item *item); + /** + * @brief verifies that item is a valid dijkstra weight (cost) + * + * @param item + * @return true if valid + * @return false if invalid + */ + inline bool verify_cost_argument(Item *item); /** * @brief allocates Json_int on heap with given value * * @param i value * @return Json_dom* ptr to Json_int */ - inline Json_dom *jsonify_to_heap(int i); + inline Json_dom *jsonify_to_heap(const int& i); /** * @brief allocates Json_double on heap with given value * * @param d value * @return Json_dom* ptr to Json_double */ - inline Json_dom *jsonify_to_heap(double d); + inline Json_dom *jsonify_to_heap(const double& d); }; #endif /* ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9a86cab9f135..a01340ba466f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11107,7 +11107,7 @@ sum_expr: } | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' opt_windowing_clause { - Json_object *object = ::new (YYMEM_ROOT) Json_object; + Json_object *object = ::new (YYMEM_ROOT, std::nothrow) Json_object; if (object == nullptr) YYABORT; // object not owned (deallocated) by wrapper auto wrapper = make_unique_destroy_only(YYMEM_ROOT, object, true); From edc67e64fdcd336bb7745ebd322fac789b6ba230 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 18 Mar 2022 16:36:36 +0100 Subject: [PATCH 16/67] st_sdp .test --- .../suite/gis/r/st_shortest_dir_path.result | 35 +++++++++++++------ .../suite/gis/t/st_shortest_dir_path.test | 31 +++++++++++----- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index a48cd9d59fa4..7b531c686aa4 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -1,11 +1,11 @@ -create table paths( -id int primary key, -from_id int, -to_id int, -cost double, -type varchar(50) +CREATE TABLE paths ( +id INT PRIMARY KEY, +from_id INT, +to_id INT, +cost DOUBLE, +type ENUM("undefined", "car", "bike", "pedestrian") ); -insert into paths values +INSERT INTO paths VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), (12, 0, 2, 8.0, "car"), @@ -18,9 +18,22 @@ insert into paths values (33, 1, 3, 40.0, "pedestrian"), (34, 3, 2, 20.0, "pedestrian") ; -select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; -st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) -{"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) FROM paths GROUP BY type; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) {"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} +{"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} {"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}]} -drop table paths; +INSERT INTO paths VALUES +(50, 0, 1, 3.0, "undefined"), +(51, 0, 2, 5.0, "undefined"), +(52, 0, 3, 8.0, "undefined"), +(53, 1, 4, 1.0, "undefined"), +(54, 4, 3, 2.0, "undefined"), +(55, 3, 5, 6.0, "undefined"), +(56, 2, 5, 12.0, "undefined"), +(57, 4, 5, 10.0, "undefined") +; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths WHERE type = "undefined"; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) +{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}]} +DROP TABLE paths; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 5fab6148216f..22a997c4153a 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -1,12 +1,12 @@ -create table paths( -id int primary key, -from_id int, -to_id int, -cost double, -type varchar(50) +CREATE TABLE paths ( + id INT PRIMARY KEY, + from_id INT, + to_id INT, + cost DOUBLE, + type ENUM("undefined", "car", "bike", "pedestrian") ); -insert into paths values +INSERT INTO paths VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), (12, 0, 2, 8.0, "car"), @@ -22,6 +22,19 @@ insert into paths values (34, 3, 2, 20.0, "pedestrian") ; -select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) FROM paths GROUP BY type; -drop table paths; +INSERT INTO paths VALUES +(50, 0, 1, 3.0, "undefined"), +(51, 0, 2, 5.0, "undefined"), +(52, 0, 3, 8.0, "undefined"), +(53, 1, 4, 1.0, "undefined"), +(54, 4, 3, 2.0, "undefined"), +(55, 3, 5, 6.0, "undefined"), +(56, 2, 5, 12.0, "undefined"), +(57, 4, 5, 10.0, "undefined") +; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths WHERE type = "undefined"; + +DROP TABLE paths; From 57e2a7abb40de85e6d1fae224a31ad843da7ffb5 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 18 Mar 2022 16:47:30 +0100 Subject: [PATCH 17/67] st_sdp .test doc --- .../suite/gis/r/st_shortest_dir_path.result | 36 +++++++++++------ .../suite/gis/t/st_shortest_dir_path.test | 40 +++++++++++++------ 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 7b531c686aa4..b1869505c018 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -1,3 +1,6 @@ +# --------------------- +# |[0] creating tables| +# --------------------- CREATE TABLE paths ( id INT PRIMARY KEY, from_id INT, @@ -5,6 +8,22 @@ to_id INT, cost DOUBLE, type ENUM("undefined", "car", "bike", "pedestrian") ); +# -------------------------------------------- +# |[1] testing Dijkstra i.e. without geometry| +# -------------------------------------------- +INSERT INTO paths VALUES +(50, 0, 1, 3.0, "undefined"), +(51, 0, 2, 5.0, "undefined"), +(52, 0, 3, 8.0, "undefined"), +(53, 1, 4, 1.0, "undefined"), +(54, 4, 3, 2.0, "undefined"), +(55, 3, 5, 6.0, "undefined"), +(56, 2, 5, 12.0, "undefined"), +(57, 4, 5, 10.0, "undefined") +; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) +{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}]} INSERT INTO paths VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), @@ -20,20 +39,11 @@ INSERT INTO paths VALUES ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) FROM paths GROUP BY type; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) +{"cost": 5.0, "path": [{"id": 51, "cost": 5.0}]} {"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} {"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} {"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}]} -INSERT INTO paths VALUES -(50, 0, 1, 3.0, "undefined"), -(51, 0, 2, 5.0, "undefined"), -(52, 0, 3, 8.0, "undefined"), -(53, 1, 4, 1.0, "undefined"), -(54, 4, 3, 2.0, "undefined"), -(55, 3, 5, 6.0, "undefined"), -(56, 2, 5, 12.0, "undefined"), -(57, 4, 5, 10.0, "undefined") -; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths WHERE type = "undefined"; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) -{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}]} DROP TABLE paths; +# ----------------------------------- +# |[2] testing A* i.e. with geometry| +# ----------------------------------- diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 22a997c4153a..bcde131aaa46 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -1,3 +1,9 @@ +# This test tests the ST_SHORTEST_DIR_PATH function. + +--echo # --------------------- +--echo # |[0] creating tables| +--echo # --------------------- + CREATE TABLE paths ( id INT PRIMARY KEY, from_id INT, @@ -6,6 +12,23 @@ CREATE TABLE paths ( type ENUM("undefined", "car", "bike", "pedestrian") ); +--echo # -------------------------------------------- +--echo # |[1] testing Dijkstra i.e. without geometry| +--echo # -------------------------------------------- + +INSERT INTO paths VALUES +(50, 0, 1, 3.0, "undefined"), +(51, 0, 2, 5.0, "undefined"), +(52, 0, 3, 8.0, "undefined"), +(53, 1, 4, 1.0, "undefined"), +(54, 4, 3, 2.0, "undefined"), +(55, 3, 5, 6.0, "undefined"), +(56, 2, 5, 12.0, "undefined"), +(57, 4, 5, 10.0, "undefined") +; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths; + INSERT INTO paths VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), @@ -24,17 +47,8 @@ INSERT INTO paths VALUES SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) FROM paths GROUP BY type; -INSERT INTO paths VALUES -(50, 0, 1, 3.0, "undefined"), -(51, 0, 2, 5.0, "undefined"), -(52, 0, 3, 8.0, "undefined"), -(53, 1, 4, 1.0, "undefined"), -(54, 4, 3, 2.0, "undefined"), -(55, 3, 5, 6.0, "undefined"), -(56, 2, 5, 12.0, "undefined"), -(57, 4, 5, 10.0, "undefined") -; - -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths WHERE type = "undefined"; - DROP TABLE paths; + +--echo # ----------------------------------- +--echo # |[2] testing A* i.e. with geometry| +--echo # ----------------------------------- From 6ea03504bfb9a750f3bbd2f16b21cb18072b0e62 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Mon, 21 Mar 2022 21:54:13 +0100 Subject: [PATCH 18/67] st_sdp use const ref and const when possible --- sql/Dijkstras_functor.cc | 4 ++-- sql/Dijkstras_functor.h | 10 +++++----- sql/Item_sum_shortest_dir_path.cc | 2 +- sql/Item_sum_shortest_dir_path.h | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 7ade3bf347ad..aecc6e7cceba 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -32,11 +32,11 @@ std::vector Dijkstra::operator()(const int& start_point_id, const i } template -inline bool Dijkstra::contains(std::unordered_map map, Key key) { +inline bool Dijkstra::contains(const std::unordered_map& map, const Key& key) { return map.find(key) != map.end(); } template -inline bool Dijkstra::extract(std::unordered_map map, Key key, T& val) { +inline bool Dijkstra::extract(const std::unordered_map& map, const Key& key, T& val) { auto pair = map.find(key); bool found = pair != map.end(); if (found) val = pair->second; diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 31a36cdb38b4..000af2cfa04f 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -21,10 +21,10 @@ struct Edge { * */ class Dijkstra { - typedef std::unordered_multimap::const_iterator edge_iterator; + typedef std::unordered_multimap::const_iterator edge_iterator; // key = Edge.from - const std::unordered_multimap m_edges_lookup_from; + const std::unordered_multimap m_edges_lookup_from; const std::function m_heu = [](const int&) -> double { return 0.0; }; std::unordered_map m_popped_map; @@ -45,7 +45,7 @@ class Dijkstra { * @param edges_lookup_from key must equal edge start node id (i.e. Edge.from) * @param heu_func A* heuristic. If not supplied normal dijkstra will be used */ - Dijkstra(std::unordered_multimap edges_lookup_from, + Dijkstra(std::unordered_multimap edges_lookup_from, std::function heu_func = [](const int&) -> double { return 0.0; }) : m_edges_lookup_from(edges_lookup_from), m_heu(heu_func) {} /** @@ -70,7 +70,7 @@ class Dijkstra { * @return false if map doesn't contain key */ template - static inline bool contains(std::unordered_map map, Key key); + static inline bool contains(const std::unordered_map& map, const Key& key); /** * @brief extracts value from map with given key * @@ -83,7 +83,7 @@ class Dijkstra { * @return false if map doesn't contain key */ template - static inline bool extract(std::unordered_map map, Key key, T& val); + static inline bool extract(const std::unordered_map& map, const Key& key, T& val); /** * @brief stores point info in node maps (i.e. m_xxx_map) * diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 687a1b8e0a45..6b7727e785ef 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -143,7 +143,7 @@ bool Item_sum_shortest_dir_path::add() { if (thd->is_error()) return true; for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; - m_edge_map.insert(std::pair(from_id, new (thd->mem_root) Edge{id, from_id, to_id, cost})); + m_edge_map.insert(std::pair(from_id, new (thd->mem_root) Edge{id, from_id, to_id, cost})); return false; } diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 04866e49b0dc..894d46d73e6e 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -7,7 +7,7 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; // accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) - std::unordered_multimap m_edge_map; + std::unordered_multimap m_edge_map; public: /** * @brief Construct a new Item_sum_shortest_dir_path object From 445b4f645c3ecee993557fb85d339a912a7cd58b Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Mon, 21 Mar 2022 22:03:10 +0100 Subject: [PATCH 19/67] st_sdp fewer copies --- sql/Dijkstras_functor.cc | 4 ++-- sql/Dijkstras_functor.h | 8 ++++---- sql/Item_sum_shortest_dir_path.cc | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index aecc6e7cceba..3f8e3ca12de2 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -8,7 +8,7 @@ std::vector Dijkstra::operator()(const int& start_point_id, const i // A* while (point != end_point_id) { const double point_cost = m_cost_map[point]; - const std::pair edge_range_it = m_edges_lookup_from.equal_range(point); + const std::pair edge_range_it = m_edges_lookup_from->equal_range(point); // checks all edges from point (i.e. current point) for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { const Edge *edge = edge_it->second; @@ -45,7 +45,7 @@ inline bool Dijkstra::extract(const std::unordered_map& map, const Key& void Dijkstra::set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path) { m_cost_map[id] = cost; m_heu_cost_map[id] = heu_cost; m_path_map[id] = path; } -std::vector Dijkstra::retrace(int from_point, int to_point) { +std::vector Dijkstra::retrace(int from_point, const int& to_point) { std::vector path; while (from_point != to_point) { const Edge* path_ = m_path_map[from_point]; diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 000af2cfa04f..a9f8b59e9bd6 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -24,7 +24,7 @@ class Dijkstra { typedef std::unordered_multimap::const_iterator edge_iterator; // key = Edge.from - const std::unordered_multimap m_edges_lookup_from; + const std::unordered_multimap* m_edges_lookup_from; const std::function m_heu = [](const int&) -> double { return 0.0; }; std::unordered_map m_popped_map; @@ -45,8 +45,8 @@ class Dijkstra { * @param edges_lookup_from key must equal edge start node id (i.e. Edge.from) * @param heu_func A* heuristic. If not supplied normal dijkstra will be used */ - Dijkstra(std::unordered_multimap edges_lookup_from, - std::function heu_func = [](const int&) -> double { return 0.0; }) + Dijkstra(const std::unordered_multimap* edges_lookup_from, + const std::function& heu_func = [](const int&) -> double { return 0.0; }) : m_edges_lookup_from(edges_lookup_from), m_heu(heu_func) {} /** * @brief runs A* to find shortest path through m_edges_lookup_from @@ -100,7 +100,7 @@ class Dijkstra { * @param to_point node id of path end * @return std::vector path found in m_path_map */ - std::vector retrace(int from_point, int to_point); + std::vector retrace(int from_point, const int& to_point); /** * @brief empties all node maps (i.e. m_xxx_map) to remove prev path data diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 6b7727e785ef..9ecb03630cae 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -79,7 +79,7 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { if (thd->is_error()) return error_str(); Json_array *arr = new (std::nothrow) Json_array(); - Dijkstra dijkstra(m_edge_map); + Dijkstra dijkstra(&m_edge_map); double cost; // jsonifying path from dijkstra into arr for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { From ea04ae05c80381608a65f6f41c0061a49d4738eb Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 12:36:05 +0100 Subject: [PATCH 20/67] Dijkstras_functor fewer hash lookups --- sql/Dijkstras_functor.cc | 50 ++++++++++------------------ sql/Dijkstras_functor.h | 71 ++++++++++------------------------------ 2 files changed, 34 insertions(+), 87 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 3f8e3ca12de2..ce19eed011aa 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -1,61 +1,45 @@ #include "sql/Dijkstras_functor.h" std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { - empty_path_maps(); + m_point_map.clear(); int point = start_point_id; - set_point(point, 0, m_heu(point), nullptr); - m_popped_map[point] = true; + Point& node = m_point_map[point] = Point{ 0, m_heu(point), nullptr }; // A* while (point != end_point_id) { - const double point_cost = m_cost_map[point]; - const std::pair edge_range_it = m_edges_lookup_from->equal_range(point); + const std::pair edge_range_it = m_edges->equal_range(point); // checks all edges from point (i.e. current point) for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { const Edge *edge = edge_it->second; - // ignore edges to popped(visited) nodes - if (contains(m_popped_map, edge->to)) continue; - // ignore edge if new path cost > prev path cost to same node - double old_cost, new_cost = point_cost + edge->cost; - if (extract(m_cost_map, edge->to, old_cost) && new_cost > old_cost) continue; + // grabs existing node or creates new with cost of INFINITY + Point& node_to = m_point_map[edge->to]; - set_point(edge->to, new_cost, new_cost + m_heu(edge->to), edge); + // ignore longer paths + double new_cost = node.cost + edge->cost; + if (new_cost >= node_to.cost) + continue; + + node_to.cost = new_cost; + node_to.cost_heu = new_cost + m_heu(edge->to); + node_to.path = edge; + point_heap.push_back(edge->to); std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); } if (point_heap.empty()) return {}; point = point_heap.front(); point_heap.pop_front(); - m_popped_map[point] = true; + node = m_point_map[point]; } - total_cost = m_cost_map[point]; + total_cost = node.cost; return retrace(point, start_point_id); } - -template -inline bool Dijkstra::contains(const std::unordered_map& map, const Key& key) { - return map.find(key) != map.end(); -} -template -inline bool Dijkstra::extract(const std::unordered_map& map, const Key& key, T& val) { - auto pair = map.find(key); - bool found = pair != map.end(); - if (found) val = pair->second; - return found; -} -void Dijkstra::set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path) { - m_cost_map[id] = cost; m_heu_cost_map[id] = heu_cost; m_path_map[id] = path; -} std::vector Dijkstra::retrace(int from_point, const int& to_point) { std::vector path; while (from_point != to_point) { - const Edge* path_ = m_path_map[from_point]; + const Edge* path_ = m_point_map[from_point].path; path.push_back(path_); from_point = path_->from; } std::reverse(path.begin(), path.end()); return path; } -void Dijkstra::empty_path_maps(){ - m_popped_map.clear(); m_cost_map.clear(); m_heu_cost_map.clear(); m_path_map.clear(); - point_heap.clear(); -} diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index a9f8b59e9bd6..bb46f929e779 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -1,10 +1,11 @@ #ifndef DIJKSTRAS_FUNCTOR_INCLUDED #define DIJKSTRAS_FUNCTOR_INCLUDED -#include -#include -#include -#include +#include // std::deque +#include // std::unordered_map & std::unordered_multimap +#include // std::function +#include // std::push_heap +#include // INFINITY /** * @brief Edge data for Dijkstra functor @@ -24,18 +25,20 @@ class Dijkstra { typedef std::unordered_multimap::const_iterator edge_iterator; // key = Edge.from - const std::unordered_multimap* m_edges_lookup_from; + const std::unordered_multimap* m_edges; const std::function m_heu = [](const int&) -> double { return 0.0; }; - std::unordered_map m_popped_map; - std::unordered_map m_cost_map, m_heu_cost_map; // heu_cost = real_cost + heuristic - std::unordered_map m_path_map; + struct Point { + double cost = INFINITY, cost_heu = INFINITY; // heu_cost = real_cost + heuristic + const Edge* path = nullptr; + }; + std::unordered_map m_point_map; // comparator used for point_heap sorting to make min heap based on m_heu_cost_map struct greater_point_heuristic_comparator { - bool operator()(const int& a, const int& b) { return val.at(a) > val.at(b); } - const std::unordered_map& val; - } heap_cmp{ m_heu_cost_map }; + bool operator()(const int& a, const int& b) { return point_map.at(a).cost_heu > point_map.at(b).cost_heu; } + const std::unordered_map& point_map; + } heap_cmp{ m_point_map }; std::deque point_heap; public: @@ -45,9 +48,9 @@ class Dijkstra { * @param edges_lookup_from key must equal edge start node id (i.e. Edge.from) * @param heu_func A* heuristic. If not supplied normal dijkstra will be used */ - Dijkstra(const std::unordered_multimap* edges_lookup_from, + Dijkstra(const std::unordered_multimap* edges, const std::function& heu_func = [](const int&) -> double { return 0.0; }) - : m_edges_lookup_from(edges_lookup_from), m_heu(heu_func) {} + : m_edges(edges), m_heu(heu_func) {} /** * @brief runs A* to find shortest path through m_edges_lookup_from * @@ -60,53 +63,13 @@ class Dijkstra { std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost); private: /** - * @brief checks if map contains key - * - * @tparam Key type - * @tparam T value type - * @param map - * @param key - * @return true if map contains key - * @return false if map doesn't contain key - */ - template - static inline bool contains(const std::unordered_map& map, const Key& key); - /** - * @brief extracts value from map with given key - * - * @tparam Key type - * @tparam T value type - * @param map - * @param key - * @param val - * @return true if map contains key - * @return false if map doesn't contain key - */ - template - static inline bool extract(const std::unordered_map& map, const Key& key, T& val); - /** - * @brief stores point info in node maps (i.e. m_xxx_map) - * - * @param id node id - * @param cost dijkstra path cost - * @param heu_cost cost + heuristic_cost (e.g. euclidean dist) - * @param path ptr to edge leading to node - */ - inline void set_point(const int& id, const double& cost, const double& heu_cost, const Edge *const path); - /** - * @brief finds path by accumulating edge_ptrs from m_path_map and reverting their order + * @brief finds path by accumulating Point.path from m_point_map and reverting their order * NB: will deref invalid ptr if path doesn't exist * @param from_point node id of path start * @param to_point node id of path end * @return std::vector path found in m_path_map */ std::vector retrace(int from_point, const int& to_point); - - /** - * @brief empties all node maps (i.e. m_xxx_map) to remove prev path data - * - */ - inline void empty_path_maps(); }; #endif /* DIJKSTRAS_FUNCTOR_INCLUDED */ From 8e0f971147dee4ed14b19a06485e5439acfafb0f Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 12:39:51 +0100 Subject: [PATCH 21/67] Dijkstras_functor doc update --- sql/Dijkstras_functor.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index bb46f929e779..4afd2f197ae1 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -28,13 +28,17 @@ class Dijkstra { const std::unordered_multimap* m_edges; const std::function m_heu = [](const int&) -> double { return 0.0; }; + /** + * @brief Node data (internal use) + * + */ struct Point { double cost = INFINITY, cost_heu = INFINITY; // heu_cost = real_cost + heuristic const Edge* path = nullptr; }; std::unordered_map m_point_map; - // comparator used for point_heap sorting to make min heap based on m_heu_cost_map + // comparator used for point_heap sorting to make min heap based on m_point_map.cost_heu struct greater_point_heuristic_comparator { bool operator()(const int& a, const int& b) { return point_map.at(a).cost_heu > point_map.at(b).cost_heu; } const std::unordered_map& point_map; @@ -52,13 +56,13 @@ class Dijkstra { const std::function& heu_func = [](const int&) -> double { return 0.0; }) : m_edges(edges), m_heu(heu_func) {} /** - * @brief runs A* to find shortest path through m_edges_lookup_from + * @brief runs A* to find shortest path through m_edges * * @param start_point_id node id of path start * @param end_point_id node if of path end * @param total_cost l-val-ref returns total cost of found path (if path exists) - * @return std::vector vector of pointers pointing to edges in - * edges_lookup_from representing found path + * @return std::vector vector of pointers, pointing to edges in + * m_edges, representing found path */ std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost); private: From c16e024d71bf6c155b597133bef033eba23138cd Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 12:41:40 +0100 Subject: [PATCH 22/67] typo --- sql/Dijkstras_functor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 4afd2f197ae1..7d001442093f 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -71,7 +71,7 @@ class Dijkstra { * NB: will deref invalid ptr if path doesn't exist * @param from_point node id of path start * @param to_point node id of path end - * @return std::vector path found in m_path_map + * @return std::vector path found in m_point_map.path */ std::vector retrace(int from_point, const int& to_point); }; From 83371091d1b14a6288605af1b47dec126622aba4 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 13:09:07 +0100 Subject: [PATCH 23/67] Dijkstras_functor doc --- sql/Dijkstras_functor.cc | 4 ++-- sql/Dijkstras_functor.h | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index ce19eed011aa..7201da55d979 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -2,7 +2,7 @@ std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { m_point_map.clear(); - int point = start_point_id; + int point = start_point_id; // node id Point& node = m_point_map[point] = Point{ 0, m_heu(point), nullptr }; // A* while (point != end_point_id) { @@ -10,7 +10,7 @@ std::vector Dijkstra::operator()(const int& start_point_id, const i // checks all edges from point (i.e. current point) for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { const Edge *edge = edge_it->second; - // grabs existing node or creates new with cost of INFINITY + // grabs existing node or creates new with cost of INFINITY inside map Point& node_to = m_point_map[edge->to]; // ignore longer paths diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 7d001442093f..b5b0313c7c5d 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -4,7 +4,7 @@ #include // std::deque #include // std::unordered_map & std::unordered_multimap #include // std::function -#include // std::push_heap +#include // std::push_heap & std::reverse #include // INFINITY /** @@ -13,7 +13,9 @@ */ struct Edge { int id; + // node id int from, to; + // weight double cost; }; @@ -33,7 +35,12 @@ class Dijkstra { * */ struct Point { - double cost = INFINITY, cost_heu = INFINITY; // heu_cost = real_cost + heuristic + // sum of edge.cost in path + double cost = INFINITY; + // cost_heu = real_cost + heuristic + double cost_heu = INFINITY; + // used in retrace() to return path + // linked list in Edge would speed up retrace(), but also mutate m_edges const Edge* path = nullptr; }; std::unordered_map m_point_map; From 4ed453bdbb1b905db1b18c814c9e250d2e2810a6 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 13:10:29 +0100 Subject: [PATCH 24/67] rm redundant --- sql/Dijkstras_functor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index b5b0313c7c5d..4a96ded8d013 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -28,7 +28,7 @@ class Dijkstra { // key = Edge.from const std::unordered_multimap* m_edges; - const std::function m_heu = [](const int&) -> double { return 0.0; }; + const std::function m_heu; /** * @brief Node data (internal use) From ce53fea309782df881e3a75e625fc45ef9ea6b40 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 16:30:05 +0100 Subject: [PATCH 25/67] st_sdp cleanup --- sql/Item_sum_shortest_dir_path.cc | 86 ++++++++++++++++++------------- sql/Item_sum_shortest_dir_path.h | 15 +++--- sql/sql_yacc.yy | 5 +- 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 9ecb03630cae..e99923358691 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -33,7 +33,6 @@ #include "sql/item_func.h" #include "sql/item_json_func.h" #include "sql/item_subselect.h" -#include "sql/json_dom.h" #include "sql/key_spec.h" #include "sql/mysqld.h" #include "sql/parse_tree_helpers.h" // PT_item_list @@ -83,18 +82,21 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { double cost; // jsonifying path from dijkstra into arr for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { - Json_object *json_edge = new (std::nothrow) Json_object(); - if (json_edge->add_alias("id", jsonify_to_heap(edge->id)) || - json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || - arr->append_alias(json_edge)) - return error_str(); - - } - // inserting path and path cost into m_wrapper - Json_object *object = down_cast(m_wrapper->to_dom(thd)); - if( object->add_alias("path", arr) || - object->add_alias("cost", jsonify_to_heap(cost))) + Json_object_ptr json_edge(new (std::nothrow) Json_object()); + if ( + json_edge == nullptr || + json_edge->add_alias("id", jsonify_to_heap(edge->id)) || + json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || + //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || + //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || + arr->append_alias(Json_dom_ptr(std::move(json_edge)))) return error_str(); + } + // inserting path and path cost into m_json_obj (which is wrapped into m_wrapper in clear()) + if ( + m_json_obj.add_alias("path", arr) || + m_json_obj.add_alias("cost", jsonify_to_heap(cost))) + return error_str(); str->length(0); if (m_wrapper->to_string(str, true, func_name())) return error_str(); @@ -105,13 +107,13 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { } void Item_sum_shortest_dir_path::clear() { - const THD *thd = base_query_block->parent_lex->thd; - null_value = true; + m_json_obj.clear(); m_edge_map.clear(); - Json_object *object = down_cast(m_wrapper->to_dom(thd)); - object->clear(); + + // insert m_json_obj into m_wrapper, but keep the ownership + *m_wrapper = Json_wrapper(&m_json_obj, true); } bool Item_sum_shortest_dir_path::add() { @@ -130,30 +132,44 @@ bool Item_sum_shortest_dir_path::add() { int id, from_id, to_id; double cost; - for (size_t i = 0; i <= 2; i++) verify_id_argument(args[i]); - verify_cost_argument(args[3]); - for (size_t i = 4; i <= 5; i++) verify_const_id_argument(args[i]); + // verify arg 0, 1, 2 + for (size_t i = 0; i < 3; i++) + if (verify_id_argument(args[i])) + return true; + // verify arg 3 + if (verify_cost_argument(args[3])) + return true; + // verify arg 4, 5 TODO: only once per agg + for (size_t i = 4; i < 6; i++) + if (verify_const_id_argument(args[i])) + return true; + // get data id = args[0]->val_int(); from_id = args[1]->val_int(); to_id = args[2]->val_int(); cost = args[3]->val_real(); + // TODO only once per agg m_begin_node = args[4]->val_int(); m_end_node = args[5]->val_int(); + + // catch any leftover type errors if (thd->is_error()) return true; - for (int i = 0; i < 4; i++) if (args[i]->null_value) return true; + for (int i = 0; i < 4; i++) + if (args[i]->null_value) + return true; - m_edge_map.insert(std::pair(from_id, new (thd->mem_root) Edge{id, from_id, to_id, cost})); + // store edge + Edge *edge = new (thd->mem_root, std::nothrow) Edge{ id, from_id, to_id, cost }; + if (edge == nullptr) return false; + m_edge_map.insert(std::pair(from_id, edge)); return false; } Item *Item_sum_shortest_dir_path::copy_or_same(THD *thd) { assert(!m_is_window_function); - Json_object *object = ::new (thd->mem_root, std::nothrow) Json_object; - if (object == nullptr) return nullptr; - // object not owned (deallocated) by wrapper - auto wrapper = make_unique_destroy_only(thd->mem_root, object, true); + auto wrapper = make_unique_destroy_only(thd->mem_root); if (wrapper == nullptr) return nullptr; return new (thd->mem_root) Item_sum_shortest_dir_path(thd, this, std::move(wrapper)); } @@ -164,9 +180,7 @@ bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *sele } inline bool Item_sum_shortest_dir_path::verify_const_id_argument(Item *item) { - if (!item->const_item() || - (!item->is_null() && - (item->result_type() != INT_RESULT))) { + if (!item->const_item() || item->is_null() || item->result_type() != INT_RESULT) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; } @@ -174,24 +188,24 @@ inline bool Item_sum_shortest_dir_path::verify_const_id_argument(Item *item) { } inline bool Item_sum_shortest_dir_path::verify_id_argument(Item *item) { - if (!item->is_null() && (item->result_type() != INT_RESULT)) { + if (item->is_null() || item->result_type() != INT_RESULT) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; - } + } return false; } inline bool Item_sum_shortest_dir_path::verify_cost_argument(Item *item) { - if (!item->is_null() && (item->result_type() != REAL_RESULT)) { + if (item->is_null() || item->result_type() != REAL_RESULT) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); return true; - } + } return false; } -inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(const int& i) { - return new (std::nothrow) Json_int(i); +inline Json_dom_ptr Item_sum_shortest_dir_path::jsonify_to_heap(const int& i) { + return Json_dom_ptr(new (std::nothrow) Json_int(i)); } -inline Json_dom *Item_sum_shortest_dir_path::jsonify_to_heap(const double& d) { - return new (std::nothrow) Json_double(d); +inline Json_dom_ptr Item_sum_shortest_dir_path::jsonify_to_heap(const double& d) { + return Json_dom_ptr(new (std::nothrow) Json_double(d)); } diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 894d46d73e6e..a09f85439f80 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -3,18 +3,21 @@ #include "sql/item_sum.h" #include "sql/Dijkstras_functor.h" +#include "sql/json_dom.h" class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; // accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) std::unordered_multimap m_edge_map; + // root json_dom in m_wrapper (not needed before val_str(), but allocated as a member for consistency) + Json_object m_json_obj; public: /** * @brief Construct a new Item_sum_shortest_dir_path object * * @param thd * @param item - * @param wrapper must contain Json_object + * @param wrapper */ Item_sum_shortest_dir_path(THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper); @@ -24,7 +27,7 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { * @param pos * @param args * @param w - * @param wrapper must contain Json_object + * @param wrapper */ Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, PT_window *w, unique_ptr_destroy_only wrapper); @@ -72,16 +75,16 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { * @brief allocates Json_int on heap with given value * * @param i value - * @return Json_dom* ptr to Json_int + * @return Json_dom_ptr to Json_int */ - inline Json_dom *jsonify_to_heap(const int& i); + inline Json_dom_ptr jsonify_to_heap(const int& i); /** * @brief allocates Json_double on heap with given value * * @param d value - * @return Json_dom* ptr to Json_double + * @return Json_dom_ptr to Json_double */ - inline Json_dom *jsonify_to_heap(const double& d); + inline Json_dom_ptr jsonify_to_heap(const double& d); }; #endif /* ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a01340ba466f..9a5bd71fd9a4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11107,10 +11107,7 @@ sum_expr: } | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' opt_windowing_clause { - Json_object *object = ::new (YYMEM_ROOT, std::nothrow) Json_object; - if (object == nullptr) YYABORT; - // object not owned (deallocated) by wrapper - auto wrapper = make_unique_destroy_only(YYMEM_ROOT, object, true); + auto wrapper = make_unique_destroy_only(YYMEM_ROOT); if (wrapper == nullptr) YYABORT; PT_item_list *args= NEW_PTN PT_item_list; From 97c6ff575654b1357fafaa9d4f2eb2973cf314ee Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 16:36:07 +0100 Subject: [PATCH 26/67] st_sdp replace raw with unique ptr --- sql/Item_sum_shortest_dir_path.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index e99923358691..963e1a92e889 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -77,7 +77,7 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { const THD *thd = base_query_block->parent_lex->thd; if (thd->is_error()) return error_str(); - Json_array *arr = new (std::nothrow) Json_array(); + Json_array_ptr arr(new (std::nothrow) Json_array()); Dijkstra dijkstra(&m_edge_map); double cost; // jsonifying path from dijkstra into arr @@ -89,12 +89,12 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || - arr->append_alias(Json_dom_ptr(std::move(json_edge)))) + arr->append_alias(std::move(json_edge))) return error_str(); } // inserting path and path cost into m_json_obj (which is wrapped into m_wrapper in clear()) if ( - m_json_obj.add_alias("path", arr) || + m_json_obj.add_alias("path", std::move(arr)) || m_json_obj.add_alias("cost", jsonify_to_heap(cost))) return error_str(); From be48fa256b8417e5c3c09fbdbc751da8c6ad06bc Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 20:00:06 +0100 Subject: [PATCH 27/67] st_sdp rm double error check --- sql/Dijkstras_functor.h | 2 +- sql/Item_sum_shortest_dir_path.cc | 6 ++---- sql/Item_sum_shortest_dir_path.h | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 4a96ded8d013..24d1cad5dbaf 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -75,7 +75,7 @@ class Dijkstra { private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order - * NB: will deref invalid ptr if path doesn't exist + * ! NB: will deref invalid ptr if path doesn't exist * @param from_point node id of path start * @param to_point node id of path end * @return std::vector path found in m_point_map.path diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 963e1a92e889..923e77dbcfea 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -73,9 +73,6 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { } String *Item_sum_shortest_dir_path::val_str(String *str) { assert(!m_is_window_function); - // could be redundant - const THD *thd = base_query_block->parent_lex->thd; - if (thd->is_error()) return error_str(); Json_array_ptr arr(new (std::nothrow) Json_array()); Dijkstra dijkstra(&m_edge_map); @@ -139,7 +136,8 @@ bool Item_sum_shortest_dir_path::add() { // verify arg 3 if (verify_cost_argument(args[3])) return true; - // verify arg 4, 5 TODO: only once per agg + // verify arg 4, 5 + // TODO only once per agg for (size_t i = 4; i < 6; i++) if (verify_const_id_argument(args[i])) return true; diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index a09f85439f80..fb779504b153 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -7,7 +7,7 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; - // accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) + // * accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) std::unordered_multimap m_edge_map; // root json_dom in m_wrapper (not needed before val_str(), but allocated as a member for consistency) Json_object m_json_obj; From da0b73e864a43aa09d9e324ced9ec26657133e1d Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 23 Mar 2022 21:07:59 +0100 Subject: [PATCH 28/67] st_sdp make json in val_json --- sql/Item_sum_shortest_dir_path.cc | 35 ++++++++++++++++++------------- sql/Item_sum_shortest_dir_path.h | 6 +++--- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 923e77dbcfea..bff83dc4859d 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -67,14 +67,10 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( : Item_sum_json(std::move(wrapper), pos, args, w) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { - assert(false); - - return Item_sum_json::val_json(wr); -} -String *Item_sum_shortest_dir_path::val_str(String *str) { assert(!m_is_window_function); Json_array_ptr arr(new (std::nothrow) Json_array()); + if (arr == nullptr) return true; Dijkstra dijkstra(&m_edge_map); double cost; // jsonifying path from dijkstra into arr @@ -87,16 +83,29 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || arr->append_alias(std::move(json_edge))) - return error_str(); + return true; } - // inserting path and path cost into m_json_obj (which is wrapped into m_wrapper in clear()) + // inserting path and cost into obj + Json_object_ptr obj = Json_object_ptr(new (std::nothrow) Json_object()); if ( - m_json_obj.add_alias("path", std::move(arr)) || - m_json_obj.add_alias("cost", jsonify_to_heap(cost))) - return error_str(); + obj == nullptr || + obj->add_alias("path", std::move(arr)) || + obj->add_alias("cost", jsonify_to_heap(cost))) + return true; + + *wr = Json_wrapper(std::move(obj)); + return false; +} +String *Item_sum_shortest_dir_path::val_str(String *str) { + assert(!m_is_window_function); + + Json_wrapper wr; + if (val_json(&wr)) + return error_str(); str->length(0); - if (m_wrapper->to_string(str, true, func_name())) return error_str(); + if (wr.to_string(str, true, func_name())) + return error_str(); if(aggr) aggr->endup(); @@ -106,11 +115,7 @@ String *Item_sum_shortest_dir_path::val_str(String *str) { void Item_sum_shortest_dir_path::clear() { null_value = true; - m_json_obj.clear(); m_edge_map.clear(); - - // insert m_json_obj into m_wrapper, but keep the ownership - *m_wrapper = Json_wrapper(&m_json_obj, true); } bool Item_sum_shortest_dir_path::add() { diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index fb779504b153..3d312a337562 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -9,8 +9,6 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; // * accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) std::unordered_multimap m_edge_map; - // root json_dom in m_wrapper (not needed before val_str(), but allocated as a member for consistency) - Json_object m_json_obj; public: /** * @brief Construct a new Item_sum_shortest_dir_path object @@ -26,8 +24,10 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { * * @param pos * @param args - * @param w + * @param w * @param wrapper + * ! wrapper not needed + * TODO inherit from Item_sum? */ Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, PT_window *w, unique_ptr_destroy_only wrapper); From 39e3b522dd3d7ee8776b39037c63f40b23608390 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 5 Apr 2022 15:15:08 +0200 Subject: [PATCH 29/67] Dijkstra clear heap before rerun --- sql/Dijkstras_functor.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 7201da55d979..973c9ce02495 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -2,6 +2,7 @@ std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { m_point_map.clear(); + point_heap.clear(); int point = start_point_id; // node id Point& node = m_point_map[point] = Point{ 0, m_heu(point), nullptr }; // A* From be2f656314919301033b69a83cc6f82777df3406 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 5 Apr 2022 15:23:33 +0200 Subject: [PATCH 30/67] unittest dijkstra --- unittest/gunit/CMakeLists.txt | 1 + unittest/gunit/dijkstra/CMakeLists.txt | 30 +++++++ unittest/gunit/dijkstra/dijkstra-t.cc | 103 +++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 unittest/gunit/dijkstra/CMakeLists.txt create mode 100644 unittest/gunit/dijkstra/dijkstra-t.cc diff --git a/unittest/gunit/CMakeLists.txt b/unittest/gunit/CMakeLists.txt index 4955e52cc997..dd475ecf24a7 100644 --- a/unittest/gunit/CMakeLists.txt +++ b/unittest/gunit/CMakeLists.txt @@ -401,6 +401,7 @@ MYSQL_ADD_EXECUTABLE(rpl_commit_order_queue-t rpl_commit_order_queue-t.cc ) ADD_SUBDIRECTORY(ddl_rewriter) +ADD_SUBDIRECTORY(dijkstra) ADD_SUBDIRECTORY(innodb) ADD_SUBDIRECTORY(keyring) ADD_SUBDIRECTORY(components/mysql_server) diff --git a/unittest/gunit/dijkstra/CMakeLists.txt b/unittest/gunit/dijkstra/CMakeLists.txt new file mode 100644 index 000000000000..fea9e958bb9f --- /dev/null +++ b/unittest/gunit/dijkstra/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2018, 2021, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an additional +# permission to link the program and your derivative works with the +# separately licensed software that they have included with MySQL. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +MYSQL_ADD_EXECUTABLE(dijkstra-t + dijkstra-t.cc + ${CMAKE_SOURCE_DIR}/sql/Dijkstras_functor.cc + ADD_TEST dijkstra-t +) +SET_TARGET_PROPERTIES(dijkstra-t PROPERTIES ENABLE_EXPORTS TRUE) +TARGET_LINK_LIBRARIES(dijkstra-t gunit_small) + diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc new file mode 100644 index 000000000000..5c3debefe527 --- /dev/null +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -0,0 +1,103 @@ +/* Copyright (c) 2009, 2021, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License, version 2.0, for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This is a simple example of how to use the google unit test framework. + + For an introduction to the constructs used below, see: + http://code.google.com/p/googletest/wiki/GoogleTestPrimer +*/ + +#include + +#include "unittest/gunit/gunit_test_main.h" + +#include "sql/Dijkstras_functor.h" + +namespace dijkstra_unittest { + +class DijkstraTest : public ::testing::Test { + protected: + DijkstraTest(){} + + private: + // Declares (but does not define) copy constructor and assignment operator. + GTEST_DISALLOW_COPY_AND_ASSIGN_(DijkstraTest); +}; + +// check that cost is total path cost +// ! floating point cmp +void check_cost(const std::vector& path , const double& cost){ + double expected_cost = 0; + for (const Edge* e : path) expected_cost += e->cost; + EXPECT_FLOAT_EQ(cost, expected_cost); +} + +// test dijkstra without heuristic +TEST_F(DijkstraTest, NullHeuristic) { + Edge edges[] = { + // Edge{ id, from, to, cost } + Edge{ 0, 0, 1, 5.0 }, + Edge{ 1, 0, 2, 12.0 }, + Edge{ 2, 1, 2, 5.0 }, + Edge{ 3, 1, 3, 15.0 }, + Edge{ 4, 2, 3, 5.0 }, + Edge{ 5, 0, 3, 20.0 }, + Edge{ 6, 3, 0, 1.0 }, + Edge{ 7, 3, 4, 3.0 }, + Edge{ 8, 0, 4, 17.0 } + }; + size_t n_edges = sizeof(edges) / sizeof(Edge); + + std::unordered_multimap edge_map; + for (size_t i = 0; i < n_edges; i++) { + Edge& e = edges[i]; + edge_map.insert(std::pair(e.from, &e)); + } + double cost; + Dijkstra dijkstra(&edge_map); + + // check 0 -> 3 + std::vector path = dijkstra(0, 3, cost); + std::vector expected_path = { &edges[0], &edges[2], &edges[4] }; + EXPECT_EQ(path, expected_path); + check_cost(path, cost); + + // check 0 -> 4 + path = dijkstra(0, 4, cost); + expected_path = { &edges[8] }; + EXPECT_EQ(path, expected_path); + check_cost(path, cost); + + // check 1 -> 0 + path = dijkstra(1, 0, cost); + expected_path = { &edges[2], &edges[4], &edges[6] }; + EXPECT_EQ(path, expected_path); + check_cost(path, cost); +} + +// test dijkstra with heuristic (A*) +TEST_F(DijkstraTest, EuclideanHeuristic) { + // TODO +} + +} // namespace dijkstra_unittest From 02c4fc940c465662eac33ce8ac920b11f7a28144 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 5 Apr 2022 22:57:04 +0200 Subject: [PATCH 31/67] st_sdp malloc_allocator --- sql/Dijkstras_functor.cc | 7 +++ sql/Dijkstras_functor.h | 23 +++++---- sql/Item_sum_shortest_dir_path.cc | 69 +++++++++++++++------------ sql/Item_sum_shortest_dir_path.h | 4 +- sql/psi_memory_key.cc | 3 ++ sql/psi_memory_key.h | 1 + unittest/gunit/dijkstra/dijkstra-t.cc | 3 +- 7 files changed, 68 insertions(+), 42 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 973c9ce02495..62c9f56c128c 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -1,5 +1,12 @@ #include "sql/Dijkstras_functor.h" +Dijkstra::Dijkstra(const malloc_unordered_multimap* edges, + const std::function& heu_func, + PSI_memory_key psi_key) + : m_edges(edges), m_heu(heu_func), + m_point_map(psi_key), heap_cmp{ &m_point_map }, + point_heap(Malloc_allocator(psi_key)) {} + std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { m_point_map.clear(); point_heap.clear(); diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 24d1cad5dbaf..b5a2df36cbcb 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -7,6 +7,8 @@ #include // std::push_heap & std::reverse #include // INFINITY +#include "include/map_helpers.h" + /** * @brief Edge data for Dijkstra functor * @@ -24,10 +26,11 @@ struct Edge { * */ class Dijkstra { - typedef std::unordered_multimap::const_iterator edge_iterator; + PSI_memory_key psi_key; + typedef malloc_unordered_multimap::const_iterator edge_iterator; // key = Edge.from - const std::unordered_multimap* m_edges; + const malloc_unordered_multimap* m_edges; const std::function m_heu; /** @@ -43,14 +46,14 @@ class Dijkstra { // linked list in Edge would speed up retrace(), but also mutate m_edges const Edge* path = nullptr; }; - std::unordered_map m_point_map; + malloc_unordered_map m_point_map; // comparator used for point_heap sorting to make min heap based on m_point_map.cost_heu struct greater_point_heuristic_comparator { - bool operator()(const int& a, const int& b) { return point_map.at(a).cost_heu > point_map.at(b).cost_heu; } - const std::unordered_map& point_map; - } heap_cmp{ m_point_map }; - std::deque point_heap; + bool operator()(const int& a, const int& b) { return point_map->at(a).cost_heu > point_map->at(b).cost_heu; } + const malloc_unordered_map* point_map = nullptr; + } heap_cmp; + std::deque> point_heap; public: /** @@ -59,9 +62,9 @@ class Dijkstra { * @param edges_lookup_from key must equal edge start node id (i.e. Edge.from) * @param heu_func A* heuristic. If not supplied normal dijkstra will be used */ - Dijkstra(const std::unordered_multimap* edges, - const std::function& heu_func = [](const int&) -> double { return 0.0; }) - : m_edges(edges), m_heu(heu_func) {} + Dijkstra(const malloc_unordered_multimap* edges, + const std::function& heu_func = [](const int&) -> double { return 0.0; }, + PSI_memory_key psi_key = PSI_NOT_INSTRUMENTED); /** * @brief runs A* to find shortest path through m_edges * diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index bff83dc4859d..2f95fe6134d9 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -59,42 +59,47 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper) - : Item_sum_json(std::move(wrapper), thd, item) {} + : Item_sum_json(std::move(wrapper), thd, item), m_edge_map(key_memory_Dijkstra) {} Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( const POS &pos, PT_item_list *args, PT_window *w, unique_ptr_destroy_only wrapper) - : Item_sum_json(std::move(wrapper), pos, args, w) {} + : Item_sum_json(std::move(wrapper), pos, args, w), m_edge_map(key_memory_Dijkstra) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { assert(!m_is_window_function); - - Json_array_ptr arr(new (std::nothrow) Json_array()); - if (arr == nullptr) return true; - Dijkstra dijkstra(&m_edge_map); - double cost; - // jsonifying path from dijkstra into arr - for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { - Json_object_ptr json_edge(new (std::nothrow) Json_object()); + try { + Json_array_ptr arr(new (std::nothrow) Json_array()); + if (arr == nullptr) + return error_json(); + Dijkstra dijkstra(&m_edge_map, [](const int&) -> double { return 0.0; }, key_memory_Dijkstra); + double cost; + // jsonifying path from dijkstra into arr + for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { + Json_object_ptr json_edge(new (std::nothrow) Json_object()); + if ( + json_edge == nullptr || + json_edge->add_alias("id", jsonify_to_heap(edge->id)) || + json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || + //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || + //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || + arr->append_alias(std::move(json_edge))) + return error_json(); + } + // inserting path and cost into obj + Json_object_ptr obj = Json_object_ptr(new (std::nothrow) Json_object()); if ( - json_edge == nullptr || - json_edge->add_alias("id", jsonify_to_heap(edge->id)) || - json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || - //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || - //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || - arr->append_alias(std::move(json_edge))) - return true; + obj == nullptr || + obj->add_alias("path", std::move(arr)) || + obj->add_alias("cost", jsonify_to_heap(cost))) + return error_json(); + + *wr = Json_wrapper(std::move(obj)); + return false; + } catch(...) { // expects to catch std::bad_alloc + handle_std_exception(func_name()); + return error_json(); } - // inserting path and cost into obj - Json_object_ptr obj = Json_object_ptr(new (std::nothrow) Json_object()); - if ( - obj == nullptr || - obj->add_alias("path", std::move(arr)) || - obj->add_alias("cost", jsonify_to_heap(cost))) - return true; - - *wr = Json_wrapper(std::move(obj)); - return false; } String *Item_sum_shortest_dir_path::val_str(String *str) { assert(!m_is_window_function); @@ -164,9 +169,13 @@ bool Item_sum_shortest_dir_path::add() { // store edge Edge *edge = new (thd->mem_root, std::nothrow) Edge{ id, from_id, to_id, cost }; - if (edge == nullptr) return false; - m_edge_map.insert(std::pair(from_id, edge)); - + if (edge == nullptr) return true; + try { + m_edge_map.insert(std::pair(from_id, edge)); + } catch (...) { // expects to catch std::bad_alloc + handle_std_exception(func_name()); + return true; + } return false; } diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 3d312a337562..e792360d7918 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -4,11 +4,13 @@ #include "sql/item_sum.h" #include "sql/Dijkstras_functor.h" #include "sql/json_dom.h" +#include "include/map_helpers.h" +#include "sql/psi_memory_key.h" class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; // * accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) - std::unordered_multimap m_edge_map; + malloc_unordered_multimap m_edge_map; public: /** * @brief Construct a new Item_sum_shortest_dir_path object diff --git a/sql/psi_memory_key.cc b/sql/psi_memory_key.cc index 844c3e10d50f..279bc5a055ea 100644 --- a/sql/psi_memory_key.cc +++ b/sql/psi_memory_key.cc @@ -39,6 +39,7 @@ PSI_memory_key key_memory_DD_default_values; PSI_memory_key key_memory_DD_import; PSI_memory_key key_memory_DD_objects; PSI_memory_key key_memory_DD_String_type; +PSI_memory_key key_memory_Dijkstra; PSI_memory_key key_memory_Event_queue_element_for_exec_names; PSI_memory_key key_memory_Event_scheduler_scheduler_param; PSI_memory_key key_memory_File_query_log_name; @@ -324,6 +325,8 @@ static PSI_memory_info all_server_memory[] = { "File name of slow log and general log."}, {&key_memory_DD_String_type, "dd::String_type", 0, 0, "Character strings used by data dictionary objects."}, + {&key_memory_Dijkstra, "shortest_dir_path::dijkstra", 0, 0, + "Memory used by st_shortest_dir_path"}, {&key_memory_ST_SCHEMA_TABLE, "ST_SCHEMA_TABLE", 0, 0, "Structure describing an information schema table implemented by a " "plugin."}, diff --git a/sql/psi_memory_key.h b/sql/psi_memory_key.h index 373c09f16468..30736a48820f 100644 --- a/sql/psi_memory_key.h +++ b/sql/psi_memory_key.h @@ -65,6 +65,7 @@ extern PSI_memory_key key_memory_DD_default_values; extern PSI_memory_key key_memory_DD_import; extern PSI_memory_key key_memory_DD_objects; extern PSI_memory_key key_memory_DD_String_type; +extern PSI_memory_key key_memory_Dijkstra; extern PSI_memory_key key_memory_Event_queue_element_for_exec_names; extern PSI_memory_key key_memory_Event_scheduler_scheduler_param; extern PSI_memory_key key_memory_File_query_log_name; diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index 5c3debefe527..7eff320db40c 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -32,6 +32,7 @@ #include "unittest/gunit/gunit_test_main.h" #include "sql/Dijkstras_functor.h" +#include "include/map_helpers.h" namespace dijkstra_unittest { @@ -68,7 +69,7 @@ TEST_F(DijkstraTest, NullHeuristic) { }; size_t n_edges = sizeof(edges) / sizeof(Edge); - std::unordered_multimap edge_map; + malloc_unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; edge_map.insert(std::pair(e.from, &e)); From 55f31d87e92e479f163ee80334ae8cdf01a2f83f Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 6 Apr 2022 12:51:27 +0200 Subject: [PATCH 32/67] dijkstra euclidean heuristic test --- unittest/gunit/dijkstra/dijkstra-t.cc | 81 +++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index 7eff320db40c..1fee36b8c02c 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -57,14 +57,14 @@ void check_cost(const std::vector& path , const double& cost){ TEST_F(DijkstraTest, NullHeuristic) { Edge edges[] = { // Edge{ id, from, to, cost } - Edge{ 0, 0, 1, 5.0 }, + Edge{ 0, 0, 1, 5.0 }, Edge{ 1, 0, 2, 12.0 }, - Edge{ 2, 1, 2, 5.0 }, + Edge{ 2, 1, 2, 5.0 }, Edge{ 3, 1, 3, 15.0 }, - Edge{ 4, 2, 3, 5.0 }, + Edge{ 4, 2, 3, 5.0 }, Edge{ 5, 0, 3, 20.0 }, - Edge{ 6, 3, 0, 1.0 }, - Edge{ 7, 3, 4, 3.0 }, + Edge{ 6, 3, 0, 1.0 }, + Edge{ 7, 3, 4, 3.0 }, Edge{ 8, 0, 4, 17.0 } }; size_t n_edges = sizeof(edges) / sizeof(Edge); @@ -98,7 +98,76 @@ TEST_F(DijkstraTest, NullHeuristic) { // test dijkstra with heuristic (A*) TEST_F(DijkstraTest, EuclideanHeuristic) { - // TODO + typedef std::pair Point; + Point points[]{ + { 0, 0 }, // A 0 + { 2, 1 }, // B 1 + { -1, -1 }, // C 2 + { 2, -2 }, // D 3 + { 1, 3 }, // E 4 + { 4, 3 }, // F 5 + { 4, 1 }, // G 6 + { 3, -1 }, // H 7 + { 6, 2 }, // I 8 + { -2, 1 }, // J 9 + { -3, -2 }, // K 10 + { -1, 3 } // L 11 + }; + Edge edges[] = { + // Edge{ id, from, to, cost } + Edge{ 0, 0, 2, 1.5 }, // A 0 -> C 2 + Edge{ 1, 0, 3, 2.9 }, // A 0 -> D 3 + Edge{ 2, 0, 5, 5.0 }, // A 0 -> F 5 + Edge{ 3, 3, 1, 3.0 }, // D 3 -> B 1 + Edge{ 4, 2, 11, 2.0 }, // C 2 -> L 11 + Edge{ 5, 2, 10, 2.4 }, // C 2 -> K 10 + Edge{ 6, 2, 9, 2.4 }, // C 2 -> J 9 + Edge{ 7, 9, 4, 3.65 }, // J 9 -> E 4 + Edge{ 8, 4, 5, 4.0 }, // E 4 -> F 5 + Edge{ 9, 5, 8, 2.4 }, // F 5 -> I 8 + Edge{ 10, 5, 6, 2.0 }, // F 5 -> G 6 + Edge{ 11, 0, 7, 3.2 }, // A 0 -> H 7 + Edge{ 12, 7, 6, 2.3 }, // H 7 -> G 6 + Edge{ 13, 8, 6, 2.3 }, // I 8 -> G 6 + Edge{ 14, 1, 6, 2.0 } // B 1 -> G 6 + }; + // A 0 -> H 7 -> G 6 : 5.5m (test 1) + // A 0 -> F 5 -> G 6 : 7.0m (test 2) + // A 0 -> D 3 -> B 1 -> G 6 : 7.9m (test 3) + size_t n_edges = sizeof(edges) / sizeof(Edge); + + malloc_unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); + for (size_t i = 0; i < n_edges; i++) { + Edge& e = edges[i]; + edge_map.insert(std::pair(e.from, &e)); + } + double cost; + int target_point = 6; // G + Dijkstra dijkstra(&edge_map, [&points, &target_point](const int& point) -> double { + return std::sqrt( + std::pow(points[point].first - points[target_point].first, 2) + + std::pow(points[point].second - points[target_point].second, 2) + ); + }); + // test 1 + std::vector path = dijkstra(0, target_point, cost); + std::vector expected_path = { &edges[11], &edges[12] }; + EXPECT_EQ(path, expected_path); + EXPECT_EQ(cost, 5.5); + + // test 2 + edges[12].cost = 999.9; // disables prev best path (fine since heu <= cost) + path = dijkstra(0, target_point, cost); + expected_path = { &edges[2], &edges[10] }; + EXPECT_EQ(path, expected_path); + EXPECT_EQ(cost, 7.0); + + // test 3 + edges[10].cost = 999.9; // disables prev best path + path = dijkstra(0, target_point, cost); + expected_path = { &edges[1], &edges[3], &edges[14] }; + EXPECT_EQ(path, expected_path); + EXPECT_EQ(cost, 7.9); } } // namespace dijkstra_unittest From 03b8687f8df8321c65ad536b84d7784b8fa69472 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 6 Apr 2022 14:25:12 +0200 Subject: [PATCH 33/67] st_sdp not a window func --- sql/Item_sum_shortest_dir_path.cc | 7 ++++--- sql/Item_sum_shortest_dir_path.h | 2 +- sql/sql_yacc.yy | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 2f95fe6134d9..19d5475ff0db 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -62,9 +62,9 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( : Item_sum_json(std::move(wrapper), thd, item), m_edge_map(key_memory_Dijkstra) {} Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( - const POS &pos, PT_item_list *args, PT_window *w, + const POS &pos, PT_item_list *args, unique_ptr_destroy_only wrapper) - : Item_sum_json(std::move(wrapper), pos, args, w), m_edge_map(key_memory_Dijkstra) {} + : Item_sum_json(std::move(wrapper), pos, args, nullptr), m_edge_map(key_memory_Dijkstra) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { assert(!m_is_window_function); @@ -169,7 +169,8 @@ bool Item_sum_shortest_dir_path::add() { // store edge Edge *edge = new (thd->mem_root, std::nothrow) Edge{ id, from_id, to_id, cost }; - if (edge == nullptr) return true; + if (edge == nullptr) + return true; try { m_edge_map.insert(std::pair(from_id, edge)); } catch (...) { // expects to catch std::bad_alloc diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index e792360d7918..8dda859159ec 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -31,7 +31,7 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { * ! wrapper not needed * TODO inherit from Item_sum? */ - Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, PT_window *w, + Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, unique_ptr_destroy_only wrapper); ~Item_sum_shortest_dir_path() override = default; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9a5bd71fd9a4..a136ea14b66f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11105,7 +11105,7 @@ sum_expr: $$ = NEW_PTN Item_sum_json_object( @$, $3, $5, $7, std::move(wrapper), std::move(object)); } - | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' opt_windowing_clause + | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' { auto wrapper = make_unique_destroy_only(YYMEM_ROOT); if (wrapper == nullptr) YYABORT; @@ -11114,7 +11114,7 @@ sum_expr: args->push_back($3);args->push_back($5);args->push_back($7);args->push_back($9); args->push_back($11);args->push_back($13); - $$ = NEW_PTN Item_sum_shortest_dir_path(@$, args, $15, std::move(wrapper)); + $$ = NEW_PTN Item_sum_shortest_dir_path(@$, args, std::move(wrapper)); } | ST_COLLECT_SYM '(' in_sum_expr ')' opt_windowing_clause { From e9d90d421daaabdb557e36588d00893b2435de5f Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 6 Apr 2022 20:11:29 +0200 Subject: [PATCH 34/67] dijkstra cancel callback --- sql/Dijkstras_functor.cc | 9 +++++++-- sql/Dijkstras_functor.h | 11 ++++++++--- sql/Item_sum_shortest_dir_path.cc | 27 +++++++++++++++++++++++---- sql/Item_sum_shortest_dir_path.h | 3 +++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 62c9f56c128c..c4f80ca48282 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -7,11 +7,14 @@ Dijkstra::Dijkstra(const malloc_unordered_multimap* edges, m_point_map(psi_key), heap_cmp{ &m_point_map }, point_heap(Malloc_allocator(psi_key)) {} -std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost) { +std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, const std::function& stop) { m_point_map.clear(); point_heap.clear(); int point = start_point_id; // node id - Point& node = m_point_map[point] = Point{ 0, m_heu(point), nullptr }; + Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; + // #iterations, for callback func stop(). + // incremented once for every node popped. + unsigned int iters = 0; // A* while (point != end_point_id) { const std::pair edge_range_it = m_edges->equal_range(point); @@ -37,6 +40,8 @@ std::vector Dijkstra::operator()(const int& start_point_id, const i point = point_heap.front(); point_heap.pop_front(); node = m_point_map[point]; + if (++iters % iters_per_callback == 0 && stop()) + return {}; } total_cost = node.cost; return retrace(point, start_point_id); diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index b5a2df36cbcb..d575330c5d98 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -26,9 +26,12 @@ struct Edge { * */ class Dijkstra { - PSI_memory_key psi_key; typedef malloc_unordered_multimap::const_iterator edge_iterator; + // #iterations/#nodes popped in Dijkstra::operator() before callback param stop is called + static constexpr unsigned int iters_per_callback = 255; + PSI_memory_key psi_key; + // key = Edge.from const malloc_unordered_multimap* m_edges; const std::function m_heu; @@ -71,10 +74,12 @@ class Dijkstra { * @param start_point_id node id of path start * @param end_point_id node if of path end * @param total_cost l-val-ref returns total cost of found path (if path exists) + * @param stop callback for exiting function. called every ... * @return std::vector vector of pointers, pointing to edges in - * m_edges, representing found path + * m_edges, representing found path, or empty vector if stoped by param stop or no path exists */ - std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost); + std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost, + const std::function& stop = []() -> bool { return false; }); private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 19d5475ff0db..8b4d9c286e61 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -7,6 +7,7 @@ #include #include #include // std::forward +#include #include "decimal.h" #include "my_alloc.h" @@ -59,23 +60,41 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper) - : Item_sum_json(std::move(wrapper), thd, item), m_edge_map(key_memory_Dijkstra) {} + : Item_sum_json(std::move(wrapper), thd, item), + m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra) {} Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( const POS &pos, PT_item_list *args, unique_ptr_destroy_only wrapper) - : Item_sum_json(std::move(wrapper), pos, args, nullptr), m_edge_map(key_memory_Dijkstra) {} + : Item_sum_json(std::move(wrapper), pos, args, nullptr), + m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { assert(!m_is_window_function); + + const THD *thd = base_query_block->parent_lex->thd; + static std::function stop_dijkstra = [&thd]() -> bool { + return thd->is_error() || thd->is_fatal_error() || thd->is_killed(); + }; + static std::function null_heuristic = [](const int&) -> double { + return 0.0; + }; + static std::function geom_heuristic = [](const int&) -> double { + return 0.0; // TODO implement + }; + std::function& heuristic = true ? null_heuristic : geom_heuristic; + try { Json_array_ptr arr(new (std::nothrow) Json_array()); if (arr == nullptr) return error_json(); - Dijkstra dijkstra(&m_edge_map, [](const int&) -> double { return 0.0; }, key_memory_Dijkstra); + Dijkstra dijkstra(&m_edge_map, heuristic, key_memory_Dijkstra); double cost; + std::vector path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); + if (stop_dijkstra()) + return error_json(); // jsonifying path from dijkstra into arr - for (const Edge* edge : dijkstra(m_begin_node, m_end_node, cost)) { + for (const Edge* edge : path) { Json_object_ptr json_edge(new (std::nothrow) Json_object()); if ( json_edge == nullptr || diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 8dda859159ec..3627ec8e62c4 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -11,6 +11,9 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; // * accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) malloc_unordered_multimap m_edge_map; + // * accumulated points from ::add. map key = node id + // TODO use + malloc_unordered_map m_point_map; public: /** * @brief Construct a new Item_sum_shortest_dir_path object From 592b2c37bf9e74710bcbfc380c0d767be6ce6a99 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 7 Apr 2022 13:22:32 +0200 Subject: [PATCH 35/67] Started adding error handling for no path --- mysql-test/suite/gis/r/st_shortest_dir_path.result | 1 - sql/Item_sum_shortest_dir_path.cc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index c36e64118bba..858a4257aa84 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -15,6 +15,5 @@ insert into paths values ; select st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) from paths group by type; st_shortest_dir_path(id, from_id, to_id, cost, 0, 2) -{"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} {"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} drop table paths; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index a76cb11cdf04..d0476b9183ce 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -7,7 +7,7 @@ #include #include #include // std::forward - +#include #include "decimal.h" #include "my_alloc.h" #include "my_base.h" From a2e3c64edc1a70788ddadad86b7939909eca196f Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 7 Apr 2022 14:51:06 +0200 Subject: [PATCH 36/67] Now throws error if no path is found --- mysql-test/suite/gis/r/st_shortest_dir_path.result | 6 +++--- mysql-test/suite/gis/t/st_shortest_dir_path.test | 2 +- share/messages_to_clients.txt | 3 +++ sql/Item_sum_shortest_dir_path.cc | 6 ++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 098198ffc4ef..b1869505c018 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -21,9 +21,9 @@ INSERT INTO paths VALUES (56, 2, 5, 12.0, "undefined"), (57, 4, 5, 10.0, "undefined") ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 6) FROM paths; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 6) -{"cost": 6.9083599623988e-310, "path": []} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) +{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}]} INSERT INTO paths VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 95f08fd6be71..bcde131aaa46 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -27,7 +27,7 @@ INSERT INTO paths VALUES (57, 4, 5, 10.0, "undefined") ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 6) FROM paths; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths; INSERT INTO paths VALUES (10, 0, 1, 2.0, "car"), diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index e8138571d325..55c5ed101186 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -9700,6 +9700,9 @@ ER_DEFINITION_CONTAINS_INVALID_STRING ER_CANT_EXECUTE_COMMAND_WITH_ASSIGNED_GTID_NEXT eng "Can't execute the given command when @@SESSION.GTID_NEXT == 'UUID:NUMBER'." +ER_NO_PATH_FOUND + eng "No path found on %s" + # # End of 8.0 error messages (server-to-client). diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 8b4d9c286e61..2946998c261c 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -91,6 +91,12 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { Dijkstra dijkstra(&m_edge_map, heuristic, key_memory_Dijkstra); double cost; std::vector path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); + + if (path.empty()) { + my_error(ER_NO_PATH_FOUND, MYF(0), func_name()); + return true; + } + if (stop_dijkstra()) return error_json(); // jsonifying path from dijkstra into arr From 75f9a709792c830c87f006e187897e3dbd48a4e7 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 7 Apr 2022 15:26:38 +0200 Subject: [PATCH 37/67] start and end nodes can no longer be the same --- share/messages_to_clients.txt | 2 ++ sql/Item_sum_shortest_dir_path.cc | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index 55c5ed101186..06fc06a5205d 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -9703,6 +9703,8 @@ ER_CANT_EXECUTE_COMMAND_WITH_ASSIGNED_GTID_NEXT ER_NO_PATH_FOUND eng "No path found on %s" +ER_START_AND_END_NODE_CONFLICT + eng "End node can't be equals to start node on %s" # # End of 8.0 error messages (server-to-client). diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 2946998c261c..a3971fa162ed 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -185,6 +185,12 @@ bool Item_sum_shortest_dir_path::add() { // TODO only once per agg m_begin_node = args[4]->val_int(); m_end_node = args[5]->val_int(); + + if (m_begin_node == m_end_node) + { + my_error(ER_START_AND_END_NODE_CONFLICT, MYF(0), func_name()); + return true; + } // catch any leftover type errors if (thd->is_error()) return true; From 518760f8fdf436f32872f44ee071aeb7538bd86e Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 7 Apr 2022 17:17:19 +0200 Subject: [PATCH 38/67] st_sdp geom input --- .../suite/gis/r/st_shortest_dir_path.result | 38 ++++++++++++++---- .../suite/gis/t/st_shortest_dir_path.test | 40 +++++++++++++++---- sql/Item_sum_shortest_dir_path.cc | 15 +++---- sql/sql_yacc.yy | 8 +++- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index b1869505c018..9d976f9dbe0c 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -1,7 +1,7 @@ # --------------------- # |[0] creating tables| # --------------------- -CREATE TABLE paths ( +CREATE TABLE edges ( id INT PRIMARY KEY, from_id INT, to_id INT, @@ -11,7 +11,7 @@ type ENUM("undefined", "car", "bike", "pedestrian") # -------------------------------------------- # |[1] testing Dijkstra i.e. without geometry| # -------------------------------------------- -INSERT INTO paths VALUES +INSERT INTO edges VALUES (50, 0, 1, 3.0, "undefined"), (51, 0, 2, 5.0, "undefined"), (52, 0, 3, 8.0, "undefined"), @@ -21,10 +21,10 @@ INSERT INTO paths VALUES (56, 2, 5, 12.0, "undefined"), (57, 4, 5, 10.0, "undefined") ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) {"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}]} -INSERT INTO paths VALUES +INSERT INTO edges VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), (12, 0, 2, 8.0, "car"), @@ -37,13 +37,35 @@ INSERT INTO paths VALUES (33, 1, 3, 40.0, "pedestrian"), (34, 3, 2, 20.0, "pedestrian") ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) FROM paths GROUP BY type; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) FROM edges GROUP BY type; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) {"cost": 5.0, "path": [{"id": 51, "cost": 5.0}]} {"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} {"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} {"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}]} -DROP TABLE paths; # ----------------------------------- # |[2] testing A* i.e. with geometry| # ----------------------------------- +DROP TABLE edges; +CREATE TABLE points ( +id INT PRIMARY KEY, +p POINT NOT NULL SRID 0 +); +INSERT INTO points VALUES +( 0, POINT( 0, 0 ) ), +( 1, POINT( 2, 1 ) ), +( 2, POINT( -1, -1 ) ), +( 3, POINT( 2, -2 ) ), +( 4, POINT( 1, 3 ) ), +( 5, POINT( 4, 3 ) ), +( 6, POINT( 4, 1 ) ), +( 7, POINT( 3, -1 ) ), +( 8, POINT( 6, 2 ) ), +( 9, POINT( -2, 1 ) ), +( 10, POINT( -3, -2 ) ), +( 11, POINT( -1, 3 ) ) +; +# ------------- +# |[3] cleanup| +# ------------- +DROP TABLE points; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index bcde131aaa46..244dde92b2a2 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -4,7 +4,7 @@ --echo # |[0] creating tables| --echo # --------------------- -CREATE TABLE paths ( +CREATE TABLE edges ( id INT PRIMARY KEY, from_id INT, to_id INT, @@ -16,7 +16,7 @@ CREATE TABLE paths ( --echo # |[1] testing Dijkstra i.e. without geometry| --echo # -------------------------------------------- -INSERT INTO paths VALUES +INSERT INTO edges VALUES (50, 0, 1, 3.0, "undefined"), (51, 0, 2, 5.0, "undefined"), (52, 0, 3, 8.0, "undefined"), @@ -27,9 +27,9 @@ INSERT INTO paths VALUES (57, 4, 5, 10.0, "undefined") ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 5) FROM paths; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) FROM edges; -INSERT INTO paths VALUES +INSERT INTO edges VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), (12, 0, 2, 8.0, "car"), @@ -45,10 +45,36 @@ INSERT INTO paths VALUES (34, 3, 2, 20.0, "pedestrian") ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0, 2) FROM paths GROUP BY type; - -DROP TABLE paths; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) FROM edges GROUP BY type; --echo # ----------------------------------- --echo # |[2] testing A* i.e. with geometry| --echo # ----------------------------------- + +DROP TABLE edges; + +CREATE TABLE points ( + id INT PRIMARY KEY, + p POINT NOT NULL SRID 0 +); + +INSERT INTO points VALUES +( 0, POINT( 0, 0 ) ), +( 1, POINT( 2, 1 ) ), +( 2, POINT( -1, -1 ) ), +( 3, POINT( 2, -2 ) ), +( 4, POINT( 1, 3 ) ), +( 5, POINT( 4, 3 ) ), +( 6, POINT( 4, 1 ) ), +( 7, POINT( 3, -1 ) ), +( 8, POINT( 6, 2 ) ), +( 9, POINT( -2, 1 ) ), +( 10, POINT( -3, -2 ) ), +( 11, POINT( -1, 3 ) ) +; + +--echo # ------------- +--echo # |[3] cleanup| +--echo # ------------- + +DROP TABLE points; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index a3971fa162ed..860f4faeec0c 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -150,7 +150,7 @@ void Item_sum_shortest_dir_path::clear() { bool Item_sum_shortest_dir_path::add() { assert(fixed); - assert(arg_count == 6); + assert(arg_count == 7); assert(!m_is_window_function); const THD *thd = base_query_block->parent_lex->thd; @@ -171,9 +171,10 @@ bool Item_sum_shortest_dir_path::add() { // verify arg 3 if (verify_cost_argument(args[3])) return true; - // verify arg 4, 5 - // TODO only once per agg - for (size_t i = 4; i < 6; i++) + // verify arg 4 + // TODO verify point + // verify arg 5, 6 + for (size_t i = 5; i < 7; i++) if (verify_const_id_argument(args[i])) return true; @@ -182,9 +183,9 @@ bool Item_sum_shortest_dir_path::add() { from_id = args[1]->val_int(); to_id = args[2]->val_int(); cost = args[3]->val_real(); - // TODO only once per agg - m_begin_node = args[4]->val_int(); - m_end_node = args[5]->val_int(); + // TODO get point + m_begin_node = args[5]->val_int(); + m_end_node = args[6]->val_int(); if (m_begin_node == m_end_node) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a136ea14b66f..aab307fabbf0 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11105,14 +11105,18 @@ sum_expr: $$ = NEW_PTN Item_sum_json_object( @$, $3, $5, $7, std::move(wrapper), std::move(object)); } - | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' + | ST_SHORTEST_DIR_PATH_SYM '(' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' in_sum_expr ',' expr ',' expr ')' { auto wrapper = make_unique_destroy_only(YYMEM_ROOT); if (wrapper == nullptr) YYABORT; PT_item_list *args= NEW_PTN PT_item_list; + // Edge {id, from_id, to_id, cost} args->push_back($3);args->push_back($5);args->push_back($7);args->push_back($9); - args->push_back($11);args->push_back($13); + // Point gis::point + args->push_back($11); + // Path start & Path end + args->push_back($13);args->push_back($15); $$ = NEW_PTN Item_sum_shortest_dir_path(@$, args, std::move(wrapper)); } From b63acc3fd6957317346ebcd0b0a59789bc54864e Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 7 Apr 2022 19:35:19 +0200 Subject: [PATCH 39/67] st_sdp geometry --- .../suite/gis/r/st_shortest_dir_path.result | 32 ++++++- .../suite/gis/t/st_shortest_dir_path.test | 32 ++++++- sql/Item_sum_shortest_dir_path.cc | 96 +++++++++++++++---- sql/Item_sum_shortest_dir_path.h | 16 +++- sql/item_sum.cc | 4 +- 5 files changed, 149 insertions(+), 31 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 9d976f9dbe0c..42a4745a1d92 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -6,7 +6,7 @@ id INT PRIMARY KEY, from_id INT, to_id INT, cost DOUBLE, -type ENUM("undefined", "car", "bike", "pedestrian") +type ENUM("undefined", "car", "bike", "pedestrian", "spatial") ); # -------------------------------------------- # |[1] testing Dijkstra i.e. without geometry| @@ -46,11 +46,13 @@ ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) # ----------------------------------- # |[2] testing A* i.e. with geometry| # ----------------------------------- -DROP TABLE edges; +DELETE FROM edges; CREATE TABLE points ( id INT PRIMARY KEY, -p POINT NOT NULL SRID 0 +point POINT NOT NULL SRID 0 ); +ALTER TABLE edges +ADD CONSTRAINT point_ref FOREIGN KEY (to_id) REFERENCES points(id); INSERT INTO points VALUES ( 0, POINT( 0, 0 ) ), ( 1, POINT( 2, 1 ) ), @@ -65,7 +67,31 @@ INSERT INTO points VALUES ( 10, POINT( -3, -2 ) ), ( 11, POINT( -1, 3 ) ) ; +INSERT INTO edges VALUES +( 0, 0, 2, 1.5 , "spatial" ), +( 1, 0, 3, 2.9 , "spatial" ), +( 2, 0, 5, 5.0 , "spatial" ), +( 3, 3, 1, 3.0 , "spatial" ), +( 4, 2, 11, 2.0 , "spatial" ), +( 5, 2, 10, 2.4 , "spatial" ), +( 6, 2, 9, 2.4 , "spatial" ), +( 7, 9, 4, 3.65, "spatial" ), +( 8, 4, 5, 4.0 , "spatial" ), +( 9, 5, 8, 2.4 , "spatial" ), +( 10, 5, 6, 2.0 , "spatial" ), +( 11, 0, 7, 3.2 , "spatial" ), +( 12, 7, 6, 2.3 , "spatial" ), +( 13, 8, 6, 2.3 , "spatial" ), +( 14, 1, 6, 2.0 , "spatial" ) +; +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +FROM edges +JOIN points ON points.id = edges.to_id +; +ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +{"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}]} # ------------- # |[3] cleanup| # ------------- +DROP TABLE edges; DROP TABLE points; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 244dde92b2a2..21cbf818b770 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -9,7 +9,7 @@ CREATE TABLE edges ( from_id INT, to_id INT, cost DOUBLE, - type ENUM("undefined", "car", "bike", "pedestrian") + type ENUM("undefined", "car", "bike", "pedestrian", "spatial") ); --echo # -------------------------------------------- @@ -51,12 +51,14 @@ SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) FROM edges GRO --echo # |[2] testing A* i.e. with geometry| --echo # ----------------------------------- -DROP TABLE edges; +DELETE FROM edges; CREATE TABLE points ( id INT PRIMARY KEY, - p POINT NOT NULL SRID 0 + point POINT NOT NULL SRID 0 ); +ALTER TABLE edges +ADD CONSTRAINT point_ref FOREIGN KEY (to_id) REFERENCES points(id); INSERT INTO points VALUES ( 0, POINT( 0, 0 ) ), @@ -73,8 +75,32 @@ INSERT INTO points VALUES ( 11, POINT( -1, 3 ) ) ; +INSERT INTO edges VALUES +( 0, 0, 2, 1.5 , "spatial" ), +( 1, 0, 3, 2.9 , "spatial" ), +( 2, 0, 5, 5.0 , "spatial" ), +( 3, 3, 1, 3.0 , "spatial" ), +( 4, 2, 11, 2.0 , "spatial" ), +( 5, 2, 10, 2.4 , "spatial" ), +( 6, 2, 9, 2.4 , "spatial" ), +( 7, 9, 4, 3.65, "spatial" ), +( 8, 4, 5, 4.0 , "spatial" ), +( 9, 5, 8, 2.4 , "spatial" ), +( 10, 5, 6, 2.0 , "spatial" ), +( 11, 0, 7, 3.2 , "spatial" ), +( 12, 7, 6, 2.3 , "spatial" ), +( 13, 8, 6, 2.3 , "spatial" ), +( 14, 1, 6, 2.0 , "spatial" ) +; + +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +FROM edges +JOIN points ON points.id = edges.to_id +; + --echo # ------------- --echo # |[3] cleanup| --echo # ------------- +DROP TABLE edges; DROP TABLE points; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 860f4faeec0c..e59a998639cb 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -58,6 +58,8 @@ #include "sql/uniques.h" // Unique #include "sql/window.h" +// PUBLIC: + Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper) : Item_sum_json(std::move(wrapper), thd, item), @@ -121,7 +123,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { *wr = Json_wrapper(std::move(obj)); return false; - } catch(...) { // expects to catch std::bad_alloc + } catch(...) { // handles std::bad_alloc handle_std_exception(func_name()); return error_json(); } @@ -148,21 +150,12 @@ void Item_sum_shortest_dir_path::clear() { m_edge_map.clear(); } -bool Item_sum_shortest_dir_path::add() { - assert(fixed); - assert(arg_count == 7); +bool Item_sum_shortest_dir_path::fix_fields(THD *thd, Item **pItem) { + assert(!fixed); assert(!m_is_window_function); - - const THD *thd = base_query_block->parent_lex->thd; - /* - Checking if an error happened inside one of the functions that have no - way of returning an error status. (reset_field(), update_field() or - clear()) - */ - if (thd->is_error()) return error_json(); - - int id, from_id, to_id; - double cost; + + if (Item_sum_json::fix_fields(thd, pItem)) + return true; // verify arg 0, 1, 2 for (size_t i = 0; i < 3; i++) @@ -178,14 +171,33 @@ bool Item_sum_shortest_dir_path::add() { if (verify_const_id_argument(args[i])) return true; + return false; +} + +bool Item_sum_shortest_dir_path::add() { + assert(arg_count == 7); + + THD *thd = base_query_block->parent_lex->thd; + if (thd->is_error()) + return error_json(); + + int id, from_id, to_id; + double cost; + // get data id = args[0]->val_int(); from_id = args[1]->val_int(); to_id = args[2]->val_int(); cost = args[3]->val_real(); - // TODO get point - m_begin_node = args[5]->val_int(); - m_end_node = args[6]->val_int(); + add_geom(args[4], to_id, thd); + if (m_edge_map.empty()){ + m_begin_node = args[5]->val_int(); + m_end_node = args[6]->val_int(); + } + else if (m_begin_node != args[5]->val_int() || m_end_node != args[6]->val_int()){ + // TODO my_error + return true; + } if (m_begin_node == m_end_node) { @@ -194,9 +206,10 @@ bool Item_sum_shortest_dir_path::add() { } // catch any leftover type errors + // TODO evaluate necessity if (thd->is_error()) return true; - for (int i = 0; i < 4; i++) - if (args[i]->null_value) + for (int i = 0; i < 7; i++) + if (i != 4 && args[i]->null_value) return true; // store edge @@ -205,7 +218,7 @@ bool Item_sum_shortest_dir_path::add() { return true; try { m_edge_map.insert(std::pair(from_id, edge)); - } catch (...) { // expects to catch std::bad_alloc + } catch (...) { // handles std::bad_alloc handle_std_exception(func_name()); return true; } @@ -224,6 +237,47 @@ bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *sele return Item_sum::check_wf_semantics1(thd, select, reqs); } +// PRIVATE: + +inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, THD *thd) { + GeometryExtractionResult geomRes = ExtractGeometry(arg, thd, func_name()); + + switch (geomRes.GetResultType()) { + case ResultType::Error: + return true; + case ResultType::NullValue: + if (!m_point_map.empty()){ + //TODO my_error(ER_ALL_OR_NONE_MUST_BE_NULL) + } + return false; + default: break; + } + + gis::srid_t srid = geomRes.GetSrid(); + + if (m_point_map.empty()) + m_srid = srid; + else if (m_srid != srid){ + my_error(ER_GIS_DIFFERENT_SRIDS_AGGREGATION, MYF(0), func_name(), m_srid, srid); + return true; + } + + std::unique_ptr geom = geomRes.GetValue(); + + if (geom.get()->type() != gis::Geometry_type::kPoint){ + // TODO make error my_error(ER_GIS_WRONG_GEOM_TYPE, MYF(0), func_name(), "Point"); + return true; + } + + try { + m_point_map.insert(std::pair(node_id, std::move(geom))); + } catch (...) { // handles std::bad_alloc + handle_std_exception(func_name()); + return true; + } + return false; +} + inline bool Item_sum_shortest_dir_path::verify_const_id_argument(Item *item) { if (!item->const_item() || item->is_null() || item->result_type() != INT_RESULT) { my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 3627ec8e62c4..b227f703e088 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -12,8 +12,9 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { // * accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) malloc_unordered_multimap m_edge_map; // * accumulated points from ::add. map key = node id - // TODO use - malloc_unordered_map m_point_map; + malloc_unordered_map> m_point_map; + // coord type of points in m_point_map + gis::srid_t m_srid; public: /** * @brief Construct a new Item_sum_shortest_dir_path object @@ -46,12 +47,23 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { String *val_str(String *str) override; void clear() override; + bool fix_fields(THD *thd, Item **pItem) override; bool add() override; Item *copy_or_same(THD *thd) override; bool check_wf_semantics1(THD *thd, Query_block *select, Window_evaluation_requirements *reqs) override; private: + /** + * @brief inserts geometry from arg into m_point_map + * + * @param arg with geom + * @param node_id id of node associated with arg + * @param thd + * @return true if invalid or not geom + * @return false if NULL or valid geom + */ + inline bool add_geom(Item *arg, const int& node_id, THD *thd); /** * @brief verifies that item is a valid const id * diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 9f7721840d2c..875bcc133c76 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -484,9 +484,9 @@ bool Item_sum::resolve_type(THD *thd) { const Sumfunctype t = sum_func(); - // None except these 4 types are allowed for geometry arguments. + // None except these 5 types are allowed for geometry arguments. if (!(t == COUNT_FUNC || t == COUNT_DISTINCT_FUNC || t == SUM_BIT_FUNC || - t == GEOMETRY_AGGREGATE_FUNC)) + t == GEOMETRY_AGGREGATE_FUNC || t == SHORTEST_DIR_PATH_FUNC)) return reject_geometry_args(arg_count, args, this); return false; } From 42d5b3a06480423f0cd965f3cc12fa91554c1598 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 7 Apr 2022 21:33:24 +0200 Subject: [PATCH 40/67] st_sdp heuristic lambda for dijkstra --- .../suite/gis/r/st_shortest_dir_path.result | 14 +++++++++++++ .../suite/gis/t/st_shortest_dir_path.test | 16 +++++++++++++++ sql/Item_sum_shortest_dir_path.cc | 20 ++++++++++++------- unittest/gunit/dijkstra/dijkstra-t.cc | 4 ++-- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 42a4745a1d92..86647c018039 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -90,6 +90,20 @@ JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) {"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}]} +UPDATE edges SET cost = 1000.0 where id = 12; +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +FROM edges +JOIN points ON points.id = edges.to_id +; +ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +{"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}]} +UPDATE edges SET cost = 1000.0 where id = 10; +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +FROM edges +JOIN points ON points.id = edges.to_id +; +ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +{"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}]} # ------------- # |[3] cleanup| # ------------- diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 21cbf818b770..e7ff2984a925 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -93,6 +93,22 @@ INSERT INTO edges VALUES ( 14, 1, 6, 2.0 , "spatial" ) ; +# Same test as in dijkstra-t.cc se dijkstra-t.cc for more details TODO try other bigger dataset + +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +FROM edges +JOIN points ON points.id = edges.to_id +; + +UPDATE edges SET cost = 1000.0 where id = 12; + +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +FROM edges +JOIN points ON points.id = edges.to_id +; + +UPDATE edges SET cost = 1000.0 where id = 10; + SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) FROM edges JOIN points ON points.id = edges.to_id diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index e59a998639cb..d86e76dd4f1d 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -57,6 +57,7 @@ #include "sql/temp_table_param.h" // Temp_table_param #include "sql/uniques.h" // Unique #include "sql/window.h" +#include "sql/gis/distance_functor.h" // PUBLIC: @@ -81,10 +82,14 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { static std::function null_heuristic = [](const int&) -> double { return 0.0; }; - static std::function geom_heuristic = [](const int&) -> double { - return 0.0; // TODO implement + // m_point_map.at() should always find something (enforced in ::add_geom()) + gis::Geometry* end_geom = m_point_map.empty() ? nullptr : &*m_point_map.at(m_end_node); + std::function geom_heuristic = [this, &end_geom](const int& node) -> double { + static gis::Distance dst(NAN, NAN); + std::unique_ptr& geom = this->m_point_map.at(node); + return dst(end_geom, &*geom); }; - std::function& heuristic = true ? null_heuristic : geom_heuristic; + std::function& heuristic = m_point_map.empty() ? null_heuristic : geom_heuristic; try { Json_array_ptr arr(new (std::nothrow) Json_array()); @@ -123,8 +128,9 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { *wr = Json_wrapper(std::move(obj)); return false; - } catch(...) { // handles std::bad_alloc + } catch(...) { // handles std::bad_alloc and gis_exceptions from gis::Distance handle_std_exception(func_name()); + handle_gis_exception(func_name()); return error_json(); } } @@ -148,6 +154,7 @@ void Item_sum_shortest_dir_path::clear() { null_value = true; m_edge_map.clear(); + m_point_map.clear(); } bool Item_sum_shortest_dir_path::fix_fields(THD *thd, Item **pItem) { @@ -164,8 +171,7 @@ bool Item_sum_shortest_dir_path::fix_fields(THD *thd, Item **pItem) { // verify arg 3 if (verify_cost_argument(args[3])) return true; - // verify arg 4 - // TODO verify point + // * skips arg 4 (geom) // verify arg 5, 6 for (size_t i = 5; i < 7; i++) if (verify_const_id_argument(args[i])) @@ -247,7 +253,7 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, return true; case ResultType::NullValue: if (!m_point_map.empty()){ - //TODO my_error(ER_ALL_OR_NONE_MUST_BE_NULL) + // TODO my_error(ER_ALL_OR_NONE_MUST_BE_NULL) } return false; default: break; diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index 1fee36b8c02c..d28af02c38cf 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -156,14 +156,14 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { EXPECT_EQ(cost, 5.5); // test 2 - edges[12].cost = 999.9; // disables prev best path (fine since heu <= cost) + edges[12].cost = INFINITY; // disables prev best path (fine since heu <= cost) path = dijkstra(0, target_point, cost); expected_path = { &edges[2], &edges[10] }; EXPECT_EQ(path, expected_path); EXPECT_EQ(cost, 7.0); // test 3 - edges[10].cost = 999.9; // disables prev best path + edges[10].cost = INFINITY; // disables prev best path path = dijkstra(0, target_point, cost); expected_path = { &edges[1], &edges[3], &edges[14] }; EXPECT_EQ(path, expected_path); From 7d69b7766ceb6ea241b8196eebacfaf99a9fb841 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Fri, 8 Apr 2022 12:47:08 +0200 Subject: [PATCH 41/67] added multiple myErrors --- share/messages_to_clients.txt | 9 +++++++++ sql/Item_sum_shortest_dir_path.cc | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index 06fc06a5205d..86f056ad034a 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -9706,6 +9706,15 @@ ER_NO_PATH_FOUND ER_START_AND_END_NODE_CONFLICT eng "End node can't be equals to start node on %s" +ER_START_AND_END_NODE_CONSTANT + eng "Start node and end node must be constant on %s" + +ER_ALL_OR_NONE_NULL + eng "All points or none must be null on %s" + +ER_GIS_WRONG_GEOM_TYPE + eng "Wrong geometry type on %s" + # # End of 8.0 error messages (server-to-client). # Do NOT add messages intended for the error log above! diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index d86e76dd4f1d..614c3086cdd4 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -201,7 +201,7 @@ bool Item_sum_shortest_dir_path::add() { m_end_node = args[6]->val_int(); } else if (m_begin_node != args[5]->val_int() || m_end_node != args[6]->val_int()){ - // TODO my_error + my_error(ER_START_AND_END_NODE_CONSTANT, MYF(0), func_name()); return true; } @@ -253,7 +253,8 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, return true; case ResultType::NullValue: if (!m_point_map.empty()){ - // TODO my_error(ER_ALL_OR_NONE_MUST_BE_NULL) + my_error(ER_ALL_OR_NONE_NULL, MYF(0), my_func()); + return true; } return false; default: break; @@ -271,8 +272,8 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, std::unique_ptr geom = geomRes.GetValue(); if (geom.get()->type() != gis::Geometry_type::kPoint){ - // TODO make error my_error(ER_GIS_WRONG_GEOM_TYPE, MYF(0), func_name(), "Point"); - return true; + my_error(ER_GIS_WRONG_GEOM_TYPE, MYF(0), func_name()); + return true; } try { From 660bdcf3ad9b2d21a41bab0286c8bf6dacee1cb4 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 8 Apr 2022 17:01:33 +0200 Subject: [PATCH 42/67] st_sdp reorder inefficient logic --- sql/Item_sum_shortest_dir_path.cc | 35 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index d86e76dd4f1d..79870f1d0fa5 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -76,28 +76,37 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { assert(!m_is_window_function); const THD *thd = base_query_block->parent_lex->thd; + static std::function stop_dijkstra = [&thd]() -> bool { return thd->is_error() || thd->is_fatal_error() || thd->is_killed(); }; static std::function null_heuristic = [](const int&) -> double { return 0.0; }; - // m_point_map.at() should always find something (enforced in ::add_geom()) - gis::Geometry* end_geom = m_point_map.empty() ? nullptr : &*m_point_map.at(m_end_node); - std::function geom_heuristic = [this, &end_geom](const int& node) -> double { - static gis::Distance dst(NAN, NAN); - std::unique_ptr& geom = this->m_point_map.at(node); - return dst(end_geom, &*geom); - }; - std::function& heuristic = m_point_map.empty() ? null_heuristic : geom_heuristic; + + gis::Geometry* end_geom = nullptr; + std::function& heuristic = null_heuristic; + + if (!m_point_map.empty()){ + // m_point_map.at(Edge.to_id) should always find something (enforced in ::add_geom()) + end_geom = &*m_point_map.at(m_end_node); + std::function spatial_heuristic = [this, &end_geom](const int& node) -> double { + static gis::Distance dst(NAN, NAN); + std::unique_ptr& geom = this->m_point_map.at(node); + return dst(end_geom, &*geom); + }; + heuristic = spatial_heuristic; + } try { Json_array_ptr arr(new (std::nothrow) Json_array()); if (arr == nullptr) return error_json(); + // TODO move try catch: try { Dijkstra dijkstra(&m_edge_map, heuristic, key_memory_Dijkstra); double cost; std::vector path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); + // TODO move try catch: } catch if (path.empty()) { my_error(ER_NO_PATH_FOUND, MYF(0), func_name()); @@ -199,17 +208,15 @@ bool Item_sum_shortest_dir_path::add() { if (m_edge_map.empty()){ m_begin_node = args[5]->val_int(); m_end_node = args[6]->val_int(); + if (m_begin_node == m_end_node) { + my_error(ER_START_AND_END_NODE_CONFLICT, MYF(0), func_name()); + return true; + } } else if (m_begin_node != args[5]->val_int() || m_end_node != args[6]->val_int()){ // TODO my_error return true; } - - if (m_begin_node == m_end_node) - { - my_error(ER_START_AND_END_NODE_CONFLICT, MYF(0), func_name()); - return true; - } // catch any leftover type errors // TODO evaluate necessity From 41c647e4dbdcf9dfdd064f777c842392dcb00977 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 8 Apr 2022 17:19:59 +0200 Subject: [PATCH 43/67] st_sdp typo --- sql/Item_sum_shortest_dir_path.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index b3f757632e80..d54b42d6c2a8 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -260,7 +260,7 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, return true; case ResultType::NullValue: if (!m_point_map.empty()){ - my_error(ER_ALL_OR_NONE_NULL, MYF(0), my_func()); + my_error(ER_ALL_OR_NONE_NULL, MYF(0), func_name()); return true; } return false; From 89566543d651d3e9282f24594d829ff653bcfd4a Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 8 Apr 2022 18:22:16 +0200 Subject: [PATCH 44/67] includes cleanup --- sql/Dijkstras_functor.h | 2 +- sql/Item_sum_shortest_dir_path.cc | 59 ------------------------------- sql/Item_sum_shortest_dir_path.h | 16 ++++++--- 3 files changed, 12 insertions(+), 65 deletions(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index d575330c5d98..ee9e55a7ecab 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -7,7 +7,7 @@ #include // std::push_heap & std::reverse #include // INFINITY -#include "include/map_helpers.h" +#include "include/map_helpers.h" // malloc_unordered_multimap /** * @brief Edge data for Dijkstra functor diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index d54b42d6c2a8..7977431aac6a 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -1,64 +1,5 @@ #include "sql/Item_sum_shortest_dir_path.h" -#include -#include -#include -#include -#include -#include -#include // std::forward -#include - -#include "decimal.h" -#include "my_alloc.h" -#include "my_base.h" -#include "my_byteorder.h" -#include "my_compare.h" -#include "my_dbug.h" -#include "my_double2ulonglong.h" -#include "my_sys.h" -#include "mysql_com.h" -#include "mysqld_error.h" -#include "sql/aggregate_check.h" // Distinct_check -#include "sql/create_field.h" -#include "sql/current_thd.h" // current_thd -#include "sql/dd/cache/dictionary_client.h" -#include "sql/derror.h" // ER_THD -#include "sql/field.h" -#include "sql/gis/gc_utils.h" -#include "sql/gis/geometries.h" -#include "sql/gis/geometry_extraction.h" -#include "sql/gis/relops.h" -#include "sql/handler.h" -#include "sql/item_cmpfunc.h" -#include "sql/item_func.h" -#include "sql/item_json_func.h" -#include "sql/item_subselect.h" -#include "sql/key_spec.h" -#include "sql/mysqld.h" -#include "sql/parse_tree_helpers.h" // PT_item_list -#include "sql/parse_tree_node_base.h" // Parse_context -#include "sql/parse_tree_nodes.h" // PT_order_list -#include "sql/parser_yystype.h" -#include "sql/sql_array.h" -#include "sql/sql_class.h" // THD -#include "sql/sql_const.h" -#include "sql/sql_error.h" -#include "sql/sql_exception_handler.h" // handle_std_exception -#include "sql/sql_executor.h" -#include "sql/sql_lex.h" -#include "sql/sql_list.h" -#include "sql/sql_resolver.h" // setup_order -#include "sql/sql_select.h" -#include "sql/sql_tmp_table.h" // create_tmp_table -#include "sql/srs_fetcher.h" // Srs_fetcher -#include "sql/system_variables.h" -#include "sql/table.h" -#include "sql/temp_table_param.h" // Temp_table_param -#include "sql/uniques.h" // Unique -#include "sql/window.h" -#include "sql/gis/distance_functor.h" - // PUBLIC: Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index b227f703e088..828407e6f086 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -1,11 +1,17 @@ #ifndef ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED #define ITEM_SUM_SHORTEST_DIR_PATH_INCLUDED -#include "sql/item_sum.h" -#include "sql/Dijkstras_functor.h" -#include "sql/json_dom.h" -#include "include/map_helpers.h" -#include "sql/psi_memory_key.h" +#include // std::function + +#include "sql/item_sum.h" // Item_sum_json +#include "sql/gis/geometries.h" // gis::Point +#include "sql/gis/geometry_extraction.h" // ExtractGeometry +#include "sql/sql_exception_handler.h" // handle_std_exception +#include "sql/gis/distance_functor.h" // Distance +#include "sql/Dijkstras_functor.h" // Dijkstra +#include "sql/json_dom.h" // Json_dom +#include "include/map_helpers.h" // Malloc_unordered_map +#include "sql/psi_memory_key.h" // PSI_memory_key class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; From d9f46e3525a47f7396ca45fa3716ee53b948716f Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 8 Apr 2022 18:31:29 +0200 Subject: [PATCH 45/67] cleanup --- sql/Item_sum_shortest_dir_path.cc | 74 +++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 7977431aac6a..cd75f3121393 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -39,50 +39,50 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { heuristic = spatial_heuristic; } + Json_array_ptr arr(new (std::nothrow) Json_array()); + if (arr == nullptr) + return error_json(); + + double cost; + std::vector path; try { - Json_array_ptr arr(new (std::nothrow) Json_array()); - if (arr == nullptr) - return error_json(); - // TODO move try catch: try { Dijkstra dijkstra(&m_edge_map, heuristic, key_memory_Dijkstra); - double cost; - std::vector path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); - // TODO move try catch: } catch - - if (path.empty()) { - my_error(ER_NO_PATH_FOUND, MYF(0), func_name()); - return true; - } - - if (stop_dijkstra()) - return error_json(); - // jsonifying path from dijkstra into arr - for (const Edge* edge : path) { - Json_object_ptr json_edge(new (std::nothrow) Json_object()); - if ( - json_edge == nullptr || - json_edge->add_alias("id", jsonify_to_heap(edge->id)) || - json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || - //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || - //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || - arr->append_alias(std::move(json_edge))) - return error_json(); - } - // inserting path and cost into obj - Json_object_ptr obj = Json_object_ptr(new (std::nothrow) Json_object()); - if ( - obj == nullptr || - obj->add_alias("path", std::move(arr)) || - obj->add_alias("cost", jsonify_to_heap(cost))) - return error_json(); - - *wr = Json_wrapper(std::move(obj)); - return false; + path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); } catch(...) { // handles std::bad_alloc and gis_exceptions from gis::Distance handle_std_exception(func_name()); handle_gis_exception(func_name()); return error_json(); } + + if (path.empty()) { + my_error(ER_NO_PATH_FOUND, MYF(0), func_name()); + return true; + } + + if (stop_dijkstra()) + return error_json(); + // jsonifying path from dijkstra into arr + for (const Edge* edge : path) { + Json_object_ptr json_edge(new (std::nothrow) Json_object()); + if ( + json_edge == nullptr || + json_edge->add_alias("id", jsonify_to_heap(edge->id)) || + json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || + //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || + //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || + arr->append_alias(std::move(json_edge))) + return error_json(); + } + // inserting path and cost into obj + Json_object_ptr obj = Json_object_ptr(new (std::nothrow) Json_object()); + if ( + obj == nullptr || + obj->add_alias("path", std::move(arr)) || + obj->add_alias("cost", jsonify_to_heap(cost))) + return error_json(); + + *wr = Json_wrapper(std::move(obj)); + return false; } String *Item_sum_shortest_dir_path::val_str(String *str) { assert(!m_is_window_function); From 1152df5ac9a5232d1430eaa09cf25893f9bf5a63 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 8 Apr 2022 19:22:50 +0200 Subject: [PATCH 46/67] st_sdp geom verification --- sql/Item_sum_shortest_dir_path.cc | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index cd75f3121393..e87a60b754de 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -200,12 +200,19 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, case ResultType::Error: return true; case ResultType::NullValue: + // null geom after non null geom if (!m_point_map.empty()){ my_error(ER_ALL_OR_NONE_NULL, MYF(0), func_name()); return true; } return false; - default: break; + default: + // non null geom after null geom + // * expects add_geom to be called before adding first edge to m_edge_map + if (m_point_map.empty() && !m_edge_map.empty()) { + my_error(ER_ALL_OR_NONE_NULL, MYF(0), func_name()); + return true; + } } gis::srid_t srid = geomRes.GetSrid(); @@ -221,7 +228,18 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, if (geom.get()->type() != gis::Geometry_type::kPoint){ my_error(ER_GIS_WRONG_GEOM_TYPE, MYF(0), func_name()); - return true; + return true; + } + + // redefinition of already defined geom + if (m_point_map.find(node_id) != m_point_map.end()){ + gis::Point* _p = down_cast(&*m_point_map.at(node_id)); + gis::Point* p = down_cast(&*geom); + static constexpr double tol = 0.001; + if (std::abs(_p->x() - p->x()) > tol || std::abs(_p->y() - p->y()) > tol) { + // TODO my_error(ERR_GEOMETRY_REDEFINED) + return true; + } } try { From 724e9acfdfe5da5f7e862ad7992d86c06b3c4fc3 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Sat, 9 Apr 2022 22:47:31 +0200 Subject: [PATCH 47/67] st_sdp error msg --- share/messages_to_clients.txt | 3 +++ sql/Item_sum_shortest_dir_path.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index 86f056ad034a..9aff691cd5ab 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -8604,6 +8604,9 @@ ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE 22S02 ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE 22S03 eng "A parameter of function %.192s contains a geometry with latitude %f, which is out of range. It must be within [%f, %f]." +ER_GEOMETRY_REDEFINED + eng "A parameter of function %.192s contains different geometries to define same node [id: %d]" + ER_FK_CANNOT_USE_VIRTUAL_COLUMN eng "Foreign key '%s' uses virtual column '%s' which is not supported." diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index e87a60b754de..3b46bcab5748 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -237,7 +237,7 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, gis::Point* p = down_cast(&*geom); static constexpr double tol = 0.001; if (std::abs(_p->x() - p->x()) > tol || std::abs(_p->y() - p->y()) > tol) { - // TODO my_error(ERR_GEOMETRY_REDEFINED) + my_error(ER_GEOMETRY_REDEFINED, MYF(0), func_name(), node_id); return true; } } From 5afd264b22ad2f86425252ac82d1d3c487bfcd3a Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 13 Apr 2022 13:37:50 +0200 Subject: [PATCH 48/67] st_sdp input validation --- .../suite/gis/r/st_shortest_dir_path.result | 74 ++++++++++++- .../suite/gis/t/st_shortest_dir_path.test | 104 +++++++++++++++++- share/messages_to_clients.txt | 15 ++- sql/Item_sum_shortest_dir_path.cc | 31 +++++- sql/Item_sum_shortest_dir_path.h | 2 + 5 files changed, 216 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 86647c018039..cd67f9d88a75 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -104,8 +104,78 @@ JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) {"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}]} +# ----------------------------- +# |[3] testing error detection| +# ----------------------------- +DELETE FROM edges; +ALTER TABLE edges DROP FOREIGN KEY point_ref; +DROP TABLE points; +ALTER TABLE edges ADD COLUMN to_point POINT SRID 0; +INSERT INTO edges VALUES +( 0, 0, 1, 1.1 , "spatial", POINT( 2, 1 ) ), +( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ), +( 2, 1, 2, 1.8 , "spatial", POINT( 0, 4 ) ) +; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: Parameters of function st_shortest_dir_path contains different geometries to define same node [node/point id = 2] +DELETE FROM edges WHERE id = 2; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 1, 0) FROM edges; +ERROR HY000: No path found on st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 0) FROM edges; +ERROR HY000: End node can't be equal to start node on st_shortest_dir_path +DELETE FROM edges WHERE id = 2; +INSERT INTO edges VALUES +( 2, 0, 2, 2.1 , "spatial", NULL ) +; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: All point geometries or none of the point geometries must be null on st_shortest_dir_path +DELETE FROM edges; +INSERT INTO edges VALUES +( 0, 0, 1, 1.1 , "spatial", NULL ), +( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ) +; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: All point geometries or none of the point geometries must be null on st_shortest_dir_path +DELETE FROM edges; +INSERT INTO edges VALUES +( 0, 0, 1, 1.1 , "spatial", POINT( 2, 1 ) ), +( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ), +( 2, 1, 0, 1.8 , "spatial", POINT( 0, 4 ) ) +; +SELECT ST_SHORTEST_DIR_PATH(0 , from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: Error on st_shortest_dir_path, edge id 0 used more than once. Ids must be unique +INSERT INTO edges VALUES +( 9, 9, 9, 9.0, "spatial", POINT(9,9) ); +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: Error on st_shortest_dir_path, from_node can't equal to_node (loop found in edge 9) +DELETE FROM edges WHERE id = 9; +INSERT INTO edges VALUES +( 9, 8, 9, 0.0, "spatial", POINT(9, 9)); +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: Error on st_shortest_dir_path, edge cost must be greater than or equal to 0, found 0.000000 on edge 9 +UPDATE edges SET cost = -1.0 WHERE id = 9; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +ERROR HY000: Error on st_shortest_dir_path, edge cost must be greater than or equal to 0, found -1.000000 on edge 9 +DELETE FROM edges WHERE id = 9; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0 , 0, 1) FROM edges; +ERROR 22023: Invalid GIS data provided to function st_shortest_dir_path. +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, id, 1) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, id) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(NULL, from_id, to_id, cost, to_point, 0, id) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, NULL , to_id, cost, to_point, 0, id) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, from_id, NULL , cost, to_point, 0, id) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, NULL , to_point, 0, id) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, NULL, id) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, NULL) FROM edges; +ERROR HY000: Incorrect arguments to st_shortest_dir_path # ------------- -# |[3] cleanup| +# |[4] cleanup| # ------------- DROP TABLE edges; -DROP TABLE points; diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index e7ff2984a925..edd8ba31eea9 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -114,9 +114,109 @@ FROM edges JOIN points ON points.id = edges.to_id ; +--echo # ----------------------------- +--echo # |[3] testing error detection| +--echo # ----------------------------- + +DELETE FROM edges; +ALTER TABLE edges DROP FOREIGN KEY point_ref; +DROP TABLE points; + +ALTER TABLE edges ADD COLUMN to_point POINT SRID 0; + +INSERT INTO edges VALUES +( 0, 0, 1, 1.1 , "spatial", POINT( 2, 1 ) ), +( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ), +( 2, 1, 2, 1.8 , "spatial", POINT( 0, 4 ) ) +; + +--error ER_GEOMETRY_REDEFINED +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +DELETE FROM edges WHERE id = 2; + +# no path +--error ER_NO_PATH_FOUND +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 1, 0) FROM edges; + +# start = end +--error ER_START_AND_END_NODE_CONFLICT +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 0) FROM edges; + +## inconsistent use of geometry +DELETE FROM edges WHERE id = 2; +INSERT INTO edges VALUES +( 2, 0, 2, 2.1 , "spatial", NULL ) +; +# geom to null +--error ER_ALL_OR_NONE_NULL +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +# null to geom +DELETE FROM edges; +INSERT INTO edges VALUES +( 0, 0, 1, 1.1 , "spatial", NULL ), +( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ) +; +--error ER_ALL_OR_NONE_NULL +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; + +DELETE FROM edges; +INSERT INTO edges VALUES +( 0, 0, 1, 1.1 , "spatial", POINT( 2, 1 ) ), +( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ), +( 2, 1, 0, 1.8 , "spatial", POINT( 0, 4 ) ) +; + +## bad input +# same id +--error ER_DUPLICATE_EDGE_ID +SELECT ST_SHORTEST_DIR_PATH(0 , from_id, to_id, cost, to_point, 0, 1) FROM edges; + +# edge loop +INSERT INTO edges VALUES +( 9, 9, 9, 9.0, "spatial", POINT(9,9) ); +--error ER_EDGE_LOOP +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +DELETE FROM edges WHERE id = 9; + +# cost <= 0 +INSERT INTO edges VALUES +( 9, 8, 9, 0.0, "spatial", POINT(9, 9)); +--error ER_NEGATIVE_OR_ZERO_EDGE_COST +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +UPDATE edges SET cost = -1.0 WHERE id = 9; +--error ER_NEGATIVE_OR_ZERO_EDGE_COST +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; +DELETE FROM edges WHERE id = 9; + +# bad geom +--error ER_GIS_INVALID_DATA +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, 0 , 0, 1) FROM edges; +# wrong geom +--error ER_GIS_WRONG_GEOM_TYPE +# SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, ???, 0, 1) FROM edges; + +# in_sum_expr instead of const expr +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, id, 1) FROM edges; +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, id) FROM edges; + +# NULL when not allowed +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(NULL, from_id, to_id, cost, to_point, 0, id) FROM edges; +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, NULL , to_id, cost, to_point, 0, id) FROM edges; +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, from_id, NULL , cost, to_point, 0, id) FROM edges; +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, NULL , to_point, 0, id) FROM edges; +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, NULL, id) FROM edges; +--error ER_WRONG_ARGUMENTS +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, NULL) FROM edges; + --echo # ------------- ---echo # |[3] cleanup| +--echo # |[4] cleanup| --echo # ------------- DROP TABLE edges; -DROP TABLE points; diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index 9aff691cd5ab..cd9144850a2a 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -8605,7 +8605,7 @@ ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE 22S03 eng "A parameter of function %.192s contains a geometry with latitude %f, which is out of range. It must be within [%f, %f]." ER_GEOMETRY_REDEFINED - eng "A parameter of function %.192s contains different geometries to define same node [id: %d]" + eng "Parameters of function %.192s contains different geometries to define same node [node/point id = %d]" ER_FK_CANNOT_USE_VIRTUAL_COLUMN eng "Foreign key '%s' uses virtual column '%s' which is not supported." @@ -9707,17 +9707,26 @@ ER_NO_PATH_FOUND eng "No path found on %s" ER_START_AND_END_NODE_CONFLICT - eng "End node can't be equals to start node on %s" + eng "End node can't be equal to start node on %s" ER_START_AND_END_NODE_CONSTANT eng "Start node and end node must be constant on %s" ER_ALL_OR_NONE_NULL - eng "All points or none must be null on %s" + eng "All point geometries or none of the point geometries must be null on %s" ER_GIS_WRONG_GEOM_TYPE eng "Wrong geometry type on %s" +ER_DUPLICATE_EDGE_ID + eng "Error on %s, edge id %d used more than once. Ids must be unique" + +ER_NEGATIVE_OR_ZERO_EDGE_COST + eng "Error on %s, edge cost must be greater than or equal to 0, found %lf on edge %d" + +ER_EDGE_LOOP + eng "Error on %s, from_node can't equal to_node (loop found in edge %d)" + # # End of 8.0 error messages (server-to-client). # Do NOT add messages intended for the error log above! diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 3b46bcab5748..e643ff463c2c 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -5,13 +5,15 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( THD *thd, Item_sum *item, unique_ptr_destroy_only wrapper) : Item_sum_json(std::move(wrapper), thd, item), - m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra) {} + m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra), + m_edge_ids(key_memory_Dijkstra) {} Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( const POS &pos, PT_item_list *args, unique_ptr_destroy_only wrapper) : Item_sum_json(std::move(wrapper), pos, args, nullptr), - m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra) {} + m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra), + m_edge_ids(key_memory_Dijkstra) {} bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { assert(!m_is_window_function); @@ -29,7 +31,12 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { std::function& heuristic = null_heuristic; if (!m_point_map.empty()){ - // m_point_map.at(Edge.to_id) should always find something (enforced in ::add_geom()) + // no path can exist if end_point geom doesn't exist in non empty node set + // begin_node geom is not needed + if (m_point_map.find(m_end_node) == m_point_map.end()) { + my_error(ER_NO_PATH_FOUND, MYF(0), func_name()); + return true; + } end_geom = &*m_point_map.at(m_end_node); std::function spatial_heuristic = [this, &end_geom](const int& node) -> double { static gis::Distance dst(NAN, NAN); @@ -105,6 +112,7 @@ void Item_sum_shortest_dir_path::clear() { m_edge_map.clear(); m_point_map.clear(); + m_edge_ids.clear(); } bool Item_sum_shortest_dir_path::fix_fields(THD *thd, Item **pItem) { @@ -146,6 +154,23 @@ bool Item_sum_shortest_dir_path::add() { to_id = args[2]->val_int(); cost = args[3]->val_real(); add_geom(args[4], to_id, thd); + // id error + if (m_edge_ids.find(id) != m_edge_ids.end()){ + my_error(ER_DUPLICATE_EDGE_ID, MYF(0), func_name(), id); + return true; + } + // cost error + if (cost <= 0){ + my_error(ER_NEGATIVE_OR_ZERO_EDGE_COST, MYF(0), func_name(), cost, id); + return true; + } + // from/to error + if (from_id == to_id){ + my_error(ER_EDGE_LOOP, MYF(0), func_name(), id); + return true; + } + m_edge_ids[id] = true; + // geom error if (m_edge_map.empty()){ m_begin_node = args[5]->val_int(); m_end_node = args[6]->val_int(); diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 828407e6f086..239e54e707d8 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -19,6 +19,8 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { malloc_unordered_multimap m_edge_map; // * accumulated points from ::add. map key = node id malloc_unordered_map> m_point_map; + // map of all used edge ids (used to check for id overlap) + malloc_unordered_map m_edge_ids; // coord type of points in m_point_map gis::srid_t m_srid; public: From 871042b45db7faffa26a46add8305069f7eae715 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 14 Apr 2022 22:54:43 +0200 Subject: [PATCH 49/67] Dijkstras_functor no longer dependent on MySQL --- sql/Dijkstras_functor.cc | 57 +------------ sql/Dijkstras_functor.h | 114 ++++++++++++++++++++++---- sql/Item_sum_shortest_dir_path.cc | 9 +- unittest/gunit/dijkstra/dijkstra-t.cc | 5 +- 4 files changed, 107 insertions(+), 78 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index c4f80ca48282..6b793f3d1be7 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -1,58 +1,3 @@ #include "sql/Dijkstras_functor.h" -Dijkstra::Dijkstra(const malloc_unordered_multimap* edges, - const std::function& heu_func, - PSI_memory_key psi_key) - : m_edges(edges), m_heu(heu_func), - m_point_map(psi_key), heap_cmp{ &m_point_map }, - point_heap(Malloc_allocator(psi_key)) {} - -std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, const std::function& stop) { - m_point_map.clear(); - point_heap.clear(); - int point = start_point_id; // node id - Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; - // #iterations, for callback func stop(). - // incremented once for every node popped. - unsigned int iters = 0; - // A* - while (point != end_point_id) { - const std::pair edge_range_it = m_edges->equal_range(point); - // checks all edges from point (i.e. current point) - for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { - const Edge *edge = edge_it->second; - // grabs existing node or creates new with cost of INFINITY inside map - Point& node_to = m_point_map[edge->to]; - - // ignore longer paths - double new_cost = node.cost + edge->cost; - if (new_cost >= node_to.cost) - continue; - - node_to.cost = new_cost; - node_to.cost_heu = new_cost + m_heu(edge->to); - node_to.path = edge; - - point_heap.push_back(edge->to); - std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); - } - if (point_heap.empty()) return {}; - point = point_heap.front(); - point_heap.pop_front(); - node = m_point_map[point]; - if (++iters % iters_per_callback == 0 && stop()) - return {}; - } - total_cost = node.cost; - return retrace(point, start_point_id); -} -std::vector Dijkstra::retrace(int from_point, const int& to_point) { - std::vector path; - while (from_point != to_point) { - const Edge* path_ = m_point_map[from_point].path; - path.push_back(path_); - from_point = path_->from; - } - std::reverse(path.begin(), path.end()); - return path; -} +// TODO move from .h diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index ee9e55a7ecab..298c69b83f26 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -7,8 +7,6 @@ #include // std::push_heap & std::reverse #include // INFINITY -#include "include/map_helpers.h" // malloc_unordered_multimap - /** * @brief Edge data for Dijkstra functor * @@ -21,19 +19,52 @@ struct Edge { double cost; }; +/** + * @brief allocator for external allocation e.g. by callback func. + * * doesn't deallocate unless default i.e. CallbackAllocator() + * + * @tparam T allocated type + */ +template +class CallbackAllocator { + public: + std::function m_callback; + typedef T value_type; + typedef size_t size_type; + template + CallbackAllocator(const CallbackAllocator& other) noexcept : + m_callback(other.m_callback) {} + /** + * @brief Construct a new Callback Allocator object + * + * @param callback must allocate n bytes + */ + CallbackAllocator(const std::function& callback = {}) : + m_callback(callback) {} + T* allocate(const size_t n) { + if (n == 0) + return nullptr; + if (m_callback) + return (T*) m_callback(n * sizeof(T)); + return (T*) malloc(n * sizeof(T)); + } + void deallocate(T* const ptr, const size_t) { + if (m_callback) + return; + free(ptr); + } +}; + /** * @brief functor for A* (finds shortest path) * */ +template class Dijkstra { - typedef malloc_unordered_multimap::const_iterator edge_iterator; - - // #iterations/#nodes popped in Dijkstra::operator() before callback param stop is called - static constexpr unsigned int iters_per_callback = 255; - PSI_memory_key psi_key; - + typedef std::unordered_multimap, std::equal_to, EdgeAllocator> EdgeMapType; + typedef typename EdgeMapType::const_iterator edge_iterator; // key = Edge.from - const malloc_unordered_multimap* m_edges; + const EdgeMapType* m_edges; const std::function m_heu; /** @@ -49,25 +80,28 @@ class Dijkstra { // linked list in Edge would speed up retrace(), but also mutate m_edges const Edge* path = nullptr; }; - malloc_unordered_map m_point_map; + std::unordered_map, std::equal_to, CallbackAllocator>> m_point_map; + std::deque> point_heap; // comparator used for point_heap sorting to make min heap based on m_point_map.cost_heu struct greater_point_heuristic_comparator { bool operator()(const int& a, const int& b) { return point_map->at(a).cost_heu > point_map->at(b).cost_heu; } - const malloc_unordered_map* point_map = nullptr; - } heap_cmp; - std::deque> point_heap; + const decltype(m_point_map)* point_map = nullptr; + } heap_cmp { &m_point_map }; public: /** * @brief Construct a new Dijkstra object * - * @param edges_lookup_from key must equal edge start node id (i.e. Edge.from) + * @param edges key must equal edge start node id (i.e. Edge.from) * @param heu_func A* heuristic. If not supplied normal dijkstra will be used + * @param allocate custom allocator e.g. for measuring memory usage */ - Dijkstra(const malloc_unordered_multimap* edges, + Dijkstra(const EdgeMapType* edges, const std::function& heu_func = [](const int&) -> double { return 0.0; }, - PSI_memory_key psi_key = PSI_NOT_INSTRUMENTED); + const std::function& allocate = {}) + : m_edges(edges), m_heu(heu_func), + m_point_map(CallbackAllocator>(allocate)), point_heap(CallbackAllocator(allocate)) {} /** * @brief runs A* to find shortest path through m_edges * @@ -79,7 +113,41 @@ class Dijkstra { * m_edges, representing found path, or empty vector if stoped by param stop or no path exists */ std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - const std::function& stop = []() -> bool { return false; }); + const std::function& stop = []() -> bool { return false; }){ + m_point_map.clear(); + point_heap.clear(); + int point = start_point_id; // node id + Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; + // A* + while (point != end_point_id) { + const std::pair edge_range_it = m_edges->equal_range(point); + // checks all edges from point (i.e. current point) + for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { + const Edge *edge = edge_it->second; + // grabs existing node or creates new with cost of INFINITY inside map + Point& node_to = m_point_map[edge->to]; + + // ignore longer paths + double new_cost = node.cost + edge->cost; + if (new_cost >= node_to.cost) + continue; + + node_to.cost = new_cost; + node_to.cost_heu = new_cost + m_heu(edge->to); + node_to.path = edge; + + point_heap.push_back(edge->to); + std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); + } + if (point_heap.empty() || stop()) + return {}; + point = point_heap.front(); + point_heap.pop_front(); + node = m_point_map[point]; + } + total_cost = node.cost; + return retrace(point, start_point_id); + } private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order @@ -88,7 +156,17 @@ class Dijkstra { * @param to_point node id of path end * @return std::vector path found in m_point_map.path */ - std::vector retrace(int from_point, const int& to_point); + std::vector retrace(int from_point, const int& to_point){ + std::vector path; + while (from_point != to_point) { + const Edge* path_ = m_point_map[from_point].path; + path.push_back(path_); + from_point = path_->from; + } + std::reverse(path.begin(), path.end()); + return path; + } + }; #endif /* DIJKSTRAS_FUNCTOR_INCLUDED */ diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index e643ff463c2c..812942b25e46 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -53,8 +53,15 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { double cost; std::vector path; try { - Dijkstra dijkstra(&m_edge_map, heuristic, key_memory_Dijkstra); + std::deque allocated_memory; + Dijkstra dijkstra(&m_edge_map, heuristic, [&allocated_memory](const size_t n) -> void* { + void* p = my_malloc(key_memory_Dijkstra, n, MYF(MY_WME | ME_FATALERROR)); + allocated_memory.push_front(p); + return p; + }); path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); + for (void* p : allocated_memory) + my_free(p); } catch(...) { // handles std::bad_alloc and gis_exceptions from gis::Distance handle_std_exception(func_name()); handle_gis_exception(func_name()); diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index d28af02c38cf..edc779db582a 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -32,7 +32,6 @@ #include "unittest/gunit/gunit_test_main.h" #include "sql/Dijkstras_functor.h" -#include "include/map_helpers.h" namespace dijkstra_unittest { @@ -69,7 +68,7 @@ TEST_F(DijkstraTest, NullHeuristic) { }; size_t n_edges = sizeof(edges) / sizeof(Edge); - malloc_unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); + std::unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; edge_map.insert(std::pair(e.from, &e)); @@ -136,7 +135,7 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { // A 0 -> D 3 -> B 1 -> G 6 : 7.9m (test 3) size_t n_edges = sizeof(edges) / sizeof(Edge); - malloc_unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); + std::unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; edge_map.insert(std::pair(e.from, &e)); From 46cbad3b5d4d3e47fd179c4f35ede08292603efb Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 15 Apr 2022 14:12:06 +0200 Subject: [PATCH 50/67] st_sdp & dijkstra cleanup --- sql/Dijkstras_functor.cc | 61 ++++++++++++- sql/Dijkstras_functor.h | 121 +++++++++----------------- sql/Item_sum_shortest_dir_path.cc | 51 +++++------ unittest/gunit/dijkstra/dijkstra-t.cc | 4 +- 4 files changed, 125 insertions(+), 112 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 6b793f3d1be7..7afec17c87bb 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -1,3 +1,62 @@ #include "sql/Dijkstras_functor.h" +#ifdef MYSQL_SERVER +#include "sql/malloc_allocator.h" +#endif -// TODO move from .h +// TODO move constructor from .h + +template +std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, + const std::function& stop){ + m_point_map.clear(); + point_heap.clear(); + int point = start_point_id; // node id + Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; + // A* + while (point != end_point_id) { + const std::pair edge_range_it = m_edges->equal_range(point); + // checks all edges from point (i.e. current point) + for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { + const Edge *edge = edge_it->second; + // grabs existing node or creates new with cost of INFINITY inside map + Point& node_to = m_point_map[edge->to]; + + // ignore longer paths + double new_cost = node.cost + edge->cost; + if (new_cost >= node_to.cost) + continue; + + node_to.cost = new_cost; + node_to.cost_heu = new_cost + m_heu(edge->to); + node_to.path = edge; + + point_heap.push_back(edge->to); + std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); + } + if (point_heap.empty() || stop()) + return {}; + point = point_heap.front(); + point_heap.pop_front(); + node = m_point_map[point]; + } + total_cost = node.cost; + return retrace(point, start_point_id); +} + +template +std::vector Dijkstra::retrace(int from_point, const int& to_point){ + std::vector path; + while (from_point != to_point) { + const Edge* path_ = m_point_map[from_point].path; + path.push_back(path_); + from_point = path_->from; + } + std::reverse(path.begin(), path.end()); + return path; +} + + +template class Dijkstra>>; +#ifdef MYSQL_SERVER +template class Dijkstra>>; +#endif diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 298c69b83f26..fd04d619308a 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -19,50 +19,51 @@ struct Edge { double cost; }; -/** - * @brief allocator for external allocation e.g. by callback func. - * * doesn't deallocate unless default i.e. CallbackAllocator() - * - * @tparam T allocated type - */ -template -class CallbackAllocator { - public: - std::function m_callback; - typedef T value_type; - typedef size_t size_type; - template - CallbackAllocator(const CallbackAllocator& other) noexcept : - m_callback(other.m_callback) {} - /** - * @brief Construct a new Callback Allocator object - * - * @param callback must allocate n bytes - */ - CallbackAllocator(const std::function& callback = {}) : - m_callback(callback) {} - T* allocate(const size_t n) { - if (n == 0) - return nullptr; - if (m_callback) - return (T*) m_callback(n * sizeof(T)); - return (T*) malloc(n * sizeof(T)); - } - void deallocate(T* const ptr, const size_t) { - if (m_callback) - return; - free(ptr); - } -}; - /** * @brief functor for A* (finds shortest path) * */ template class Dijkstra { + /** + * @brief allocator for external allocation e.g. by callback func. + * * doesn't deallocate unless default i.e. CallbackAllocator() + * + * @tparam T allocated type + */ + template + class CallbackAllocator { + public: + std::function m_callback; + typedef T value_type; + typedef size_t size_type; + template + CallbackAllocator(const CallbackAllocator& other) noexcept : + m_callback(other.m_callback) {} + /** + * @brief Construct a new Callback Allocator object + * + * @param callback must allocate n bytes + */ + CallbackAllocator(const std::function& callback = {}) : + m_callback(callback) {} + T* allocate(const size_t n) { + if (n == 0) + return nullptr; + if (m_callback) + return (T*) m_callback(n * sizeof(T)); + return (T*) malloc(n * sizeof(T)); + } + void deallocate(T* const ptr, const size_t) { + if (m_callback) + return; + free(ptr); + } + }; + typedef std::unordered_multimap, std::equal_to, EdgeAllocator> EdgeMapType; typedef typename EdgeMapType::const_iterator edge_iterator; + // key = Edge.from const EdgeMapType* m_edges; const std::function m_heu; @@ -96,6 +97,7 @@ class Dijkstra { * @param edges key must equal edge start node id (i.e. Edge.from) * @param heu_func A* heuristic. If not supplied normal dijkstra will be used * @param allocate custom allocator e.g. for measuring memory usage + * * memory will not be deallocated if custom allocate function is given */ Dijkstra(const EdgeMapType* edges, const std::function& heu_func = [](const int&) -> double { return 0.0; }, @@ -113,41 +115,7 @@ class Dijkstra { * m_edges, representing found path, or empty vector if stoped by param stop or no path exists */ std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - const std::function& stop = []() -> bool { return false; }){ - m_point_map.clear(); - point_heap.clear(); - int point = start_point_id; // node id - Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; - // A* - while (point != end_point_id) { - const std::pair edge_range_it = m_edges->equal_range(point); - // checks all edges from point (i.e. current point) - for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { - const Edge *edge = edge_it->second; - // grabs existing node or creates new with cost of INFINITY inside map - Point& node_to = m_point_map[edge->to]; - - // ignore longer paths - double new_cost = node.cost + edge->cost; - if (new_cost >= node_to.cost) - continue; - - node_to.cost = new_cost; - node_to.cost_heu = new_cost + m_heu(edge->to); - node_to.path = edge; - - point_heap.push_back(edge->to); - std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); - } - if (point_heap.empty() || stop()) - return {}; - point = point_heap.front(); - point_heap.pop_front(); - node = m_point_map[point]; - } - total_cost = node.cost; - return retrace(point, start_point_id); - } + const std::function& stop = []() -> bool { return false; }); private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order @@ -156,16 +124,7 @@ class Dijkstra { * @param to_point node id of path end * @return std::vector path found in m_point_map.path */ - std::vector retrace(int from_point, const int& to_point){ - std::vector path; - while (from_point != to_point) { - const Edge* path_ = m_point_map[from_point].path; - path.push_back(path_); - from_point = path_->from; - } - std::reverse(path.begin(), path.end()); - return path; - } + std::vector retrace(int from_point, const int& to_point); }; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 812942b25e46..5143777c1be3 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -26,8 +26,6 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { static std::function null_heuristic = [](const int&) -> double { return 0.0; }; - - gis::Geometry* end_geom = nullptr; std::function& heuristic = null_heuristic; if (!m_point_map.empty()){ @@ -37,11 +35,10 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { my_error(ER_NO_PATH_FOUND, MYF(0), func_name()); return true; } - end_geom = &*m_point_map.at(m_end_node); - std::function spatial_heuristic = [this, &end_geom](const int& node) -> double { + const gis::Geometry *end_geom = m_point_map.at(m_end_node).get(); + std::function spatial_heuristic = [this, end_geom](const int& node) -> double { static gis::Distance dst(NAN, NAN); - std::unique_ptr& geom = this->m_point_map.at(node); - return dst(end_geom, &*geom); + return dst(end_geom, this->m_point_map.at(node).get()); }; heuristic = spatial_heuristic; } @@ -177,7 +174,7 @@ bool Item_sum_shortest_dir_path::add() { return true; } m_edge_ids[id] = true; - // geom error + // begin/end error if (m_edge_map.empty()){ m_begin_node = args[5]->val_int(); m_end_node = args[6]->val_int(); @@ -211,11 +208,9 @@ bool Item_sum_shortest_dir_path::add() { return false; } -Item *Item_sum_shortest_dir_path::copy_or_same(THD *thd) { +Item *Item_sum_shortest_dir_path::copy_or_same(THD *) { assert(!m_is_window_function); - auto wrapper = make_unique_destroy_only(thd->mem_root); - if (wrapper == nullptr) return nullptr; - return new (thd->mem_root) Item_sum_shortest_dir_path(thd, this, std::move(wrapper)); + return this; } bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *select, @@ -258,15 +253,15 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, std::unique_ptr geom = geomRes.GetValue(); - if (geom.get()->type() != gis::Geometry_type::kPoint){ + if (geom->type() != gis::Geometry_type::kPoint){ my_error(ER_GIS_WRONG_GEOM_TYPE, MYF(0), func_name()); return true; } // redefinition of already defined geom if (m_point_map.find(node_id) != m_point_map.end()){ - gis::Point* _p = down_cast(&*m_point_map.at(node_id)); - gis::Point* p = down_cast(&*geom); + const gis::Point* _p = down_cast(m_point_map.at(node_id).get()); + const gis::Point* p = down_cast(geom.get()); static constexpr double tol = 0.001; if (std::abs(_p->x() - p->x()) > tol || std::abs(_p->y() - p->y()) > tol) { my_error(ER_GEOMETRY_REDEFINED, MYF(0), func_name(), node_id); @@ -284,27 +279,27 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, } inline bool Item_sum_shortest_dir_path::verify_const_id_argument(Item *item) { - if (!item->const_item() || item->is_null() || item->result_type() != INT_RESULT) { - my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); - return true; - } - return false; + if (!item->const_item() || item->is_null() || item->result_type() != INT_RESULT) { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return true; + } + return false; } inline bool Item_sum_shortest_dir_path::verify_id_argument(Item *item) { - if (item->is_null() || item->result_type() != INT_RESULT) { - my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); - return true; - } - return false; + if (item->is_null() || item->result_type() != INT_RESULT) { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return true; + } + return false; } inline bool Item_sum_shortest_dir_path::verify_cost_argument(Item *item) { if (item->is_null() || item->result_type() != REAL_RESULT) { - my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); - return true; - } - return false; + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return true; + } + return false; } inline Json_dom_ptr Item_sum_shortest_dir_path::jsonify_to_heap(const int& i) { diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index edc779db582a..85b18785c607 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -68,7 +68,7 @@ TEST_F(DijkstraTest, NullHeuristic) { }; size_t n_edges = sizeof(edges) / sizeof(Edge); - std::unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); + std::unordered_multimap edge_map; for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; edge_map.insert(std::pair(e.from, &e)); @@ -135,7 +135,7 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { // A 0 -> D 3 -> B 1 -> G 6 : 7.9m (test 3) size_t n_edges = sizeof(edges) / sizeof(Edge); - std::unordered_multimap edge_map(PSI_NOT_INSTRUMENTED); + std::unordered_multimap edge_map; for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; edge_map.insert(std::pair(e.from, &e)); From 9fc5de5acd5f845befeab5e8b545482f5f7e0d17 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 15 Apr 2022 14:30:58 +0200 Subject: [PATCH 51/67] dijkstra move constructor to .cc --- sql/Dijkstras_functor.cc | 7 ++++++- sql/Dijkstras_functor.h | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 7afec17c87bb..5269106f5995 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -3,7 +3,12 @@ #include "sql/malloc_allocator.h" #endif -// TODO move constructor from .h +template +Dijkstra::Dijkstra(const EdgeMapType* edges, + const std::function& heu_func, + const std::function& allocate) + : m_edges(edges), m_heu(heu_func), + m_point_map(CallbackAllocator>(allocate)), point_heap(CallbackAllocator(allocate)) {} template std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index fd04d619308a..480d15b1856d 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -101,9 +101,7 @@ class Dijkstra { */ Dijkstra(const EdgeMapType* edges, const std::function& heu_func = [](const int&) -> double { return 0.0; }, - const std::function& allocate = {}) - : m_edges(edges), m_heu(heu_func), - m_point_map(CallbackAllocator>(allocate)), point_heap(CallbackAllocator(allocate)) {} + const std::function& allocate = {}); /** * @brief runs A* to find shortest path through m_edges * From 86b343db69fdb6e92f36b4faa1ba17ebb2bb29a1 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 15 Apr 2022 14:35:30 +0200 Subject: [PATCH 52/67] typo --- sql/Dijkstras_functor.cc | 14 +++++++------- sql/Dijkstras_functor.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 5269106f5995..ba270c450c69 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -8,13 +8,13 @@ Dijkstra::Dijkstra(const EdgeMapType* edges, const std::function& heu_func, const std::function& allocate) : m_edges(edges), m_heu(heu_func), - m_point_map(CallbackAllocator>(allocate)), point_heap(CallbackAllocator(allocate)) {} + m_point_map(CallbackAllocator>(allocate)), m_point_heap(CallbackAllocator(allocate)) {} template std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, const std::function& stop){ m_point_map.clear(); - point_heap.clear(); + m_point_heap.clear(); int point = start_point_id; // node id Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; // A* @@ -35,13 +35,13 @@ std::vector Dijkstra::operator()(const int& start_po node_to.cost_heu = new_cost + m_heu(edge->to); node_to.path = edge; - point_heap.push_back(edge->to); - std::push_heap(point_heap.begin(), point_heap.end(), heap_cmp); + m_point_heap.push_back(edge->to); + std::push_heap(m_point_heap.begin(), m_point_heap.end(), heap_cmp); } - if (point_heap.empty() || stop()) + if (m_point_heap.empty() || stop()) return {}; - point = point_heap.front(); - point_heap.pop_front(); + point = m_point_heap.front(); + m_point_heap.pop_front(); node = m_point_map[point]; } total_cost = node.cost; diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 480d15b1856d..39f031aaa274 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -82,7 +82,7 @@ class Dijkstra { const Edge* path = nullptr; }; std::unordered_map, std::equal_to, CallbackAllocator>> m_point_map; - std::deque> point_heap; + std::deque> m_point_heap; // comparator used for point_heap sorting to make min heap based on m_point_map.cost_heu struct greater_point_heuristic_comparator { From 74de74ecd2a8dc4ad86ff6e5371ac1071d52b8e3 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 15 Apr 2022 14:42:35 +0200 Subject: [PATCH 53/67] st_sdp doc --- sql/Item_sum_shortest_dir_path.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 5143777c1be3..9f972a273376 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -50,6 +50,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { double cost; std::vector path; try { + // Dijkstra's externally allocated memory (my_malloc) std::deque allocated_memory; Dijkstra dijkstra(&m_edge_map, heuristic, [&allocated_memory](const size_t n) -> void* { void* p = my_malloc(key_memory_Dijkstra, n, MYF(MY_WME | ME_FATALERROR)); @@ -57,6 +58,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { return p; }); path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); + // deallocating Dijkstra's borrowed memory for (void* p : allocated_memory) my_free(p); } catch(...) { // handles std::bad_alloc and gis_exceptions from gis::Distance From 162e3bcfbc0d387e830ae2962c1720d8e4cd0046 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Sat, 16 Apr 2022 16:38:34 +0200 Subject: [PATCH 54/67] st_sdp show #visited points --- .../suite/gis/r/st_shortest_dir_path.result | 16 +++++++------- sql/Dijkstras_functor.cc | 6 ++++- sql/Dijkstras_functor.h | 4 +++- sql/Item_sum_shortest_dir_path.cc | 22 +++++++++---------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index cd67f9d88a75..87b4b31a6baa 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -23,7 +23,7 @@ INSERT INTO edges VALUES ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) FROM edges; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) -{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}]} +{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}], "visited_nodes": 5} INSERT INTO edges VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), @@ -39,10 +39,10 @@ INSERT INTO edges VALUES ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) FROM edges GROUP BY type; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) -{"cost": 5.0, "path": [{"id": 51, "cost": 5.0}]} -{"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}]} -{"cost": 40.0, "path": [{"id": 22, "cost": 40.0}]} -{"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}]} +{"cost": 5.0, "path": [{"id": 51, "cost": 5.0}], "visited_nodes": 4} +{"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}], "visited_nodes": 2} +{"cost": 40.0, "path": [{"id": 22, "cost": 40.0}], "visited_nodes": 2} +{"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}], "visited_nodes": 3} # ----------------------------------- # |[2] testing A* i.e. with geometry| # ----------------------------------- @@ -89,21 +89,21 @@ FROM edges JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) -{"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}]} +{"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}], "visited_nodes": 2} UPDATE edges SET cost = 1000.0 where id = 12; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) FROM edges JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) -{"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}]} +{"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}], "visited_nodes": 5} UPDATE edges SET cost = 1000.0 where id = 10; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) FROM edges JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) -{"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}]} +{"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}], "visited_nodes": 7} # ----------------------------- # |[3] testing error detection| # ----------------------------- diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index ba270c450c69..cd023ed48353 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -12,9 +12,10 @@ Dijkstra::Dijkstra(const EdgeMapType* edges, template std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - const std::function& stop){ + const std::function& stop, int *popped_points){ m_point_map.clear(); m_point_heap.clear(); + int popped_points_ = 0; int point = start_point_id; // node id Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; // A* @@ -43,7 +44,10 @@ std::vector Dijkstra::operator()(const int& start_po point = m_point_heap.front(); m_point_heap.pop_front(); node = m_point_map[point]; + popped_points_++; } + if (popped_points) + *popped_points = popped_points_; total_cost = node.cost; return retrace(point, start_point_id); } diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 39f031aaa274..6ae1ce3d552f 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -109,11 +109,13 @@ class Dijkstra { * @param end_point_id node if of path end * @param total_cost l-val-ref returns total cost of found path (if path exists) * @param stop callback for exiting function. called every ... + * @param popped_points ptr to return #popped/visited points/nodes (can be nullptr) * @return std::vector vector of pointers, pointing to edges in * m_edges, representing found path, or empty vector if stoped by param stop or no path exists */ std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - const std::function& stop = []() -> bool { return false; }); + const std::function& stop = []() -> bool { return false; }, + int *popped_points = nullptr); private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 9f972a273376..d7c99e0b94c5 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -23,11 +23,11 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { static std::function stop_dijkstra = [&thd]() -> bool { return thd->is_error() || thd->is_fatal_error() || thd->is_killed(); }; - static std::function null_heuristic = [](const int&) -> double { + + std::function heuristic = [](const int&) -> double { return 0.0; }; - std::function& heuristic = null_heuristic; - + if (!m_point_map.empty()){ // no path can exist if end_point geom doesn't exist in non empty node set // begin_node geom is not needed @@ -36,11 +36,10 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { return true; } const gis::Geometry *end_geom = m_point_map.at(m_end_node).get(); - std::function spatial_heuristic = [this, end_geom](const int& node) -> double { + heuristic = [this, end_geom](const int& node) -> double { static gis::Distance dst(NAN, NAN); return dst(end_geom, this->m_point_map.at(node).get()); }; - heuristic = spatial_heuristic; } Json_array_ptr arr(new (std::nothrow) Json_array()); @@ -48,6 +47,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { return error_json(); double cost; + int popped_points; std::vector path; try { // Dijkstra's externally allocated memory (my_malloc) @@ -57,7 +57,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { allocated_memory.push_front(p); return p; }); - path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra); + path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra, &popped_points); // deallocating Dijkstra's borrowed memory for (void* p : allocated_memory) my_free(p); @@ -91,7 +91,8 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { if ( obj == nullptr || obj->add_alias("path", std::move(arr)) || - obj->add_alias("cost", jsonify_to_heap(cost))) + obj->add_alias("cost", jsonify_to_heap(cost)) || + obj->add_alias("visited_nodes", jsonify_to_heap(popped_points))) return error_json(); *wr = Json_wrapper(std::move(obj)); @@ -198,10 +199,8 @@ bool Item_sum_shortest_dir_path::add() { return true; // store edge - Edge *edge = new (thd->mem_root, std::nothrow) Edge{ id, from_id, to_id, cost }; - if (edge == nullptr) - return true; try { + Edge *edge = new (thd->mem_root) Edge{ id, from_id, to_id, cost }; m_edge_map.insert(std::pair(from_id, edge)); } catch (...) { // handles std::bad_alloc handle_std_exception(func_name()); @@ -215,8 +214,7 @@ Item *Item_sum_shortest_dir_path::copy_or_same(THD *) { return this; } -bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *select, - Window_evaluation_requirements *reqs) { +bool Item_sum_shortest_dir_path::check_wf_semantics1(THD *thd, Query_block *select, Window_evaluation_requirements *reqs) { return Item_sum::check_wf_semantics1(thd, select, reqs); } From 1a513ade04673846cc75017f9315db4de1aa6176 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Sat, 16 Apr 2022 17:17:03 +0200 Subject: [PATCH 55/67] gunittest dijkstra fewer #popped_points with good heuristic --- sql/Dijkstras_functor.cc | 2 +- sql/Dijkstras_functor.h | 4 ++-- sql/Item_sum_shortest_dir_path.cc | 2 +- unittest/gunit/dijkstra/dijkstra-t.cc | 31 +++++++++++++++++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index cd023ed48353..7ec4573ba3b7 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -12,7 +12,7 @@ Dijkstra::Dijkstra(const EdgeMapType* edges, template std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - const std::function& stop, int *popped_points){ + int *popped_points, const std::function& stop){ m_point_map.clear(); m_point_heap.clear(); int popped_points_ = 0; diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 6ae1ce3d552f..1744059b5437 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -114,8 +114,8 @@ class Dijkstra { * m_edges, representing found path, or empty vector if stoped by param stop or no path exists */ std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - const std::function& stop = []() -> bool { return false; }, - int *popped_points = nullptr); + int *popped_points = nullptr, + const std::function& stop = []() -> bool { return false; }); private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index d7c99e0b94c5..08e4666d072f 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -57,7 +57,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { allocated_memory.push_front(p); return p; }); - path = dijkstra(m_begin_node, m_end_node, cost, stop_dijkstra, &popped_points); + path = dijkstra(m_begin_node, m_end_node, cost, &popped_points, stop_dijkstra); // deallocating Dijkstra's borrowed memory for (void* p : allocated_memory) my_free(p); diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index 85b18785c607..71876d8db497 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -142,31 +142,48 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { } double cost; int target_point = 6; // G - Dijkstra dijkstra(&edge_map, [&points, &target_point](const int& point) -> double { + int popped_points_null, popped_points_euclid; + Dijkstra null_dijkstra(&edge_map); + Dijkstra euclidean_dijkstra(&edge_map, [&points, &target_point](const int& point) -> double { return std::sqrt( std::pow(points[point].first - points[target_point].first, 2) + std::pow(points[point].second - points[target_point].second, 2) ); }); - // test 1 - std::vector path = dijkstra(0, target_point, cost); + // test 1 (euclid) + std::vector path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); std::vector expected_path = { &edges[11], &edges[12] }; EXPECT_EQ(path, expected_path); EXPECT_EQ(cost, 5.5); + // test 1 (null) + path = null_dijkstra(0, target_point, cost, &popped_points_null); + EXPECT_EQ(path, expected_path); + EXPECT_EQ(cost, 5.5); + EXPECT_TRUE(popped_points_euclid < popped_points_null); - // test 2 + // test 2 (euclid) edges[12].cost = INFINITY; // disables prev best path (fine since heu <= cost) - path = dijkstra(0, target_point, cost); + path = euclidean_dijkstra(0, target_point, cost); expected_path = { &edges[2], &edges[10] }; EXPECT_EQ(path, expected_path); EXPECT_EQ(cost, 7.0); + // test 2 (null) + path = null_dijkstra(0, target_point, cost, &popped_points_null); + EXPECT_EQ(path, expected_path); + EXPECT_EQ(cost, 7.0); + EXPECT_TRUE(popped_points_euclid < popped_points_null); - // test 3 + // test 3 (euclid) edges[10].cost = INFINITY; // disables prev best path - path = dijkstra(0, target_point, cost); + path = euclidean_dijkstra(0, target_point, cost); expected_path = { &edges[1], &edges[3], &edges[14] }; EXPECT_EQ(path, expected_path); EXPECT_EQ(cost, 7.9); + // test 2 (null) + path = null_dijkstra(0, target_point, cost, &popped_points_null); + EXPECT_EQ(path, expected_path); + EXPECT_EQ(cost, 7.9); + EXPECT_TRUE(popped_points_euclid < popped_points_null); } } // namespace dijkstra_unittest From ea8a4bf22be1af6ff82c7f568bc349e2d487b7f6 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Sat, 16 Apr 2022 17:31:42 +0200 Subject: [PATCH 56/67] indentation --- unittest/gunit/dijkstra/dijkstra-t.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index 71876d8db497..2df7d3e3c26f 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -70,8 +70,8 @@ TEST_F(DijkstraTest, NullHeuristic) { std::unordered_multimap edge_map; for (size_t i = 0; i < n_edges; i++) { - Edge& e = edges[i]; - edge_map.insert(std::pair(e.from, &e)); + Edge& e = edges[i]; + edge_map.insert(std::pair(e.from, &e)); } double cost; Dijkstra dijkstra(&edge_map); @@ -137,8 +137,8 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { std::unordered_multimap edge_map; for (size_t i = 0; i < n_edges; i++) { - Edge& e = edges[i]; - edge_map.insert(std::pair(e.from, &e)); + Edge& e = edges[i]; + edge_map.insert(std::pair(e.from, &e)); } double cost; int target_point = 6; // G From 8aed3c9b8f841a6afc7f6a75849a1850f0bdeb50 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Sat, 16 Apr 2022 17:59:19 +0200 Subject: [PATCH 57/67] w --- mysql-test/suite/gis/r/st_shortest_dir_path.result | 9 +++++++++ mysql-test/suite/gis/t/st_shortest_dir_path.test | 3 +++ unittest/gunit/dijkstra/dijkstra-t.cc | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 87b4b31a6baa..47f1f17b3946 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -90,6 +90,9 @@ JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) {"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}], "visited_nodes": 2} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) +{"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}], "visited_nodes": 9} UPDATE edges SET cost = 1000.0 where id = 12; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) FROM edges @@ -97,6 +100,9 @@ JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) {"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}], "visited_nodes": 5} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) +{"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}], "visited_nodes": 9} UPDATE edges SET cost = 1000.0 where id = 10; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) FROM edges @@ -104,6 +110,9 @@ JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) {"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}], "visited_nodes": 7} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) +{"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}], "visited_nodes": 11} # ----------------------------- # |[3] testing error detection| # ----------------------------- diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index edd8ba31eea9..bba186b002b2 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -99,6 +99,7 @@ SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, po FROM edges JOIN points ON points.id = edges.to_id ; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; UPDATE edges SET cost = 1000.0 where id = 12; @@ -106,6 +107,7 @@ SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, po FROM edges JOIN points ON points.id = edges.to_id ; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; UPDATE edges SET cost = 1000.0 where id = 10; @@ -113,6 +115,7 @@ SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, po FROM edges JOIN points ON points.id = edges.to_id ; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; --echo # ----------------------------- --echo # |[3] testing error detection| diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/dijkstra/dijkstra-t.cc index 2df7d3e3c26f..0a77fa9bd130 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/dijkstra/dijkstra-t.cc @@ -163,7 +163,7 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { // test 2 (euclid) edges[12].cost = INFINITY; // disables prev best path (fine since heu <= cost) - path = euclidean_dijkstra(0, target_point, cost); + path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); expected_path = { &edges[2], &edges[10] }; EXPECT_EQ(path, expected_path); EXPECT_EQ(cost, 7.0); @@ -175,7 +175,7 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { // test 3 (euclid) edges[10].cost = INFINITY; // disables prev best path - path = euclidean_dijkstra(0, target_point, cost); + path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); expected_path = { &edges[1], &edges[3], &edges[14] }; EXPECT_EQ(path, expected_path); EXPECT_EQ(cost, 7.9); From 98cd85069e4f59bd86dd06ee103e911b5a390220 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Sat, 16 Apr 2022 18:53:47 +0200 Subject: [PATCH 58/67] indentation --- sql/Dijkstras_functor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 7ec4573ba3b7..f28138cc9c98 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -30,7 +30,7 @@ std::vector Dijkstra::operator()(const int& start_po // ignore longer paths double new_cost = node.cost + edge->cost; if (new_cost >= node_to.cost) - continue; + continue; node_to.cost = new_cost; node_to.cost_heu = new_cost + m_heu(edge->to); From 3e2cdfb4433d4938b346f33c70b214414e6e61f1 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Mon, 25 Apr 2022 22:39:32 +0200 Subject: [PATCH 59/67] ER rename --- mysql-test/suite/gis/t/st_shortest_dir_path.test | 4 ++-- share/messages_to_clients.txt | 2 +- sql/Item_sum_shortest_dir_path.cc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index bba186b002b2..e9b492fe2fc4 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -151,7 +151,7 @@ INSERT INTO edges VALUES ( 2, 0, 2, 2.1 , "spatial", NULL ) ; # geom to null ---error ER_ALL_OR_NONE_NULL +--error ER_INCONSISTENT_GEOMETRY_NULLNESS SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; # null to geom DELETE FROM edges; @@ -159,7 +159,7 @@ INSERT INTO edges VALUES ( 0, 0, 1, 1.1 , "spatial", NULL ), ( 1, 0, 2, 2.1 , "spatial", POINT( 3, 2 ) ) ; ---error ER_ALL_OR_NONE_NULL +--error ER_INCONSISTENT_GEOMETRY_NULLNESS SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, to_point, 0, 1) FROM edges; DELETE FROM edges; diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index cd9144850a2a..eba0a2ed4508 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -9712,7 +9712,7 @@ ER_START_AND_END_NODE_CONFLICT ER_START_AND_END_NODE_CONSTANT eng "Start node and end node must be constant on %s" -ER_ALL_OR_NONE_NULL +ER_INCONSISTENT_GEOMETRY_NULLNESS eng "All point geometries or none of the point geometries must be null on %s" ER_GIS_WRONG_GEOM_TYPE diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 08e4666d072f..63dd8f39d184 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -229,7 +229,7 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, case ResultType::NullValue: // null geom after non null geom if (!m_point_map.empty()){ - my_error(ER_ALL_OR_NONE_NULL, MYF(0), func_name()); + my_error(ER_INCONSISTENT_GEOMETRY_NULLNESS, MYF(0), func_name()); return true; } return false; @@ -237,7 +237,7 @@ inline bool Item_sum_shortest_dir_path::add_geom(Item *arg, const int& node_id, // non null geom after null geom // * expects add_geom to be called before adding first edge to m_edge_map if (m_point_map.empty() && !m_edge_map.empty()) { - my_error(ER_ALL_OR_NONE_NULL, MYF(0), func_name()); + my_error(ER_INCONSISTENT_GEOMETRY_NULLNESS, MYF(0), func_name()); return true; } } From c111bf48a28868aea283b4d1c7d2a3116e453c00 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Tue, 26 Apr 2022 20:38:11 +0200 Subject: [PATCH 60/67] dijkstra unittest refactor --- unittest/gunit/CMakeLists.txt | 7 ++- unittest/gunit/dijkstra/CMakeLists.txt | 30 ----------- .../dijkstra-t.cc => gis_dijkstra-t.cc} | 50 ++++--------------- 3 files changed, 17 insertions(+), 70 deletions(-) delete mode 100644 unittest/gunit/dijkstra/CMakeLists.txt rename unittest/gunit/{dijkstra/dijkstra-t.cc => gis_dijkstra-t.cc} (74%) diff --git a/unittest/gunit/CMakeLists.txt b/unittest/gunit/CMakeLists.txt index dd475ecf24a7..e796add16386 100644 --- a/unittest/gunit/CMakeLists.txt +++ b/unittest/gunit/CMakeLists.txt @@ -400,8 +400,13 @@ MYSQL_ADD_EXECUTABLE(rpl_commit_order_queue-t rpl_commit_order_queue-t.cc LINK_LIBRARIES rpl_commit_order_queue_lib gunit_small sqlgunitlib ) +MYSQL_ADD_EXECUTABLE(gis_dijkstra-t gis_dijkstra-t.cc + ${CMAKE_SOURCE_DIR}/sql/Dijkstras_functor.cc + LINK_LIBRARIES gunit_small sqlgunitlib + ADD_TEST gis_dijkstra-t +) + ADD_SUBDIRECTORY(ddl_rewriter) -ADD_SUBDIRECTORY(dijkstra) ADD_SUBDIRECTORY(innodb) ADD_SUBDIRECTORY(keyring) ADD_SUBDIRECTORY(components/mysql_server) diff --git a/unittest/gunit/dijkstra/CMakeLists.txt b/unittest/gunit/dijkstra/CMakeLists.txt deleted file mode 100644 index fea9e958bb9f..000000000000 --- a/unittest/gunit/dijkstra/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License, version 2.0, -# as published by the Free Software Foundation. -# -# This program is also distributed with certain software (including -# but not limited to OpenSSL) that is licensed under separate terms, -# as designated in a particular file or component or in included license -# documentation. The authors of MySQL hereby grant you an additional -# permission to link the program and your derivative works with the -# separately licensed software that they have included with MySQL. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License, version 2.0, for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -MYSQL_ADD_EXECUTABLE(dijkstra-t - dijkstra-t.cc - ${CMAKE_SOURCE_DIR}/sql/Dijkstras_functor.cc - ADD_TEST dijkstra-t -) -SET_TARGET_PROPERTIES(dijkstra-t PROPERTIES ENABLE_EXPORTS TRUE) -TARGET_LINK_LIBRARIES(dijkstra-t gunit_small) - diff --git a/unittest/gunit/dijkstra/dijkstra-t.cc b/unittest/gunit/gis_dijkstra-t.cc similarity index 74% rename from unittest/gunit/dijkstra/dijkstra-t.cc rename to unittest/gunit/gis_dijkstra-t.cc index 0a77fa9bd130..a58e2aec571e 100644 --- a/unittest/gunit/dijkstra/dijkstra-t.cc +++ b/unittest/gunit/gis_dijkstra-t.cc @@ -1,32 +1,3 @@ -/* Copyright (c) 2009, 2021, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License, version 2.0, for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/* - This is a simple example of how to use the google unit test framework. - - For an introduction to the constructs used below, see: - http://code.google.com/p/googletest/wiki/GoogleTestPrimer -*/ - #include #include "unittest/gunit/gunit_test_main.h" @@ -48,8 +19,9 @@ class DijkstraTest : public ::testing::Test { // ! floating point cmp void check_cost(const std::vector& path , const double& cost){ double expected_cost = 0; - for (const Edge* e : path) expected_cost += e->cost; - EXPECT_FLOAT_EQ(cost, expected_cost); + for (const Edge* e : path) + expected_cost += e->cost; + EXPECT_DOUBLE_EQ(cost, expected_cost); } // test dijkstra without heuristic @@ -144,21 +116,21 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { int target_point = 6; // G int popped_points_null, popped_points_euclid; Dijkstra null_dijkstra(&edge_map); - Dijkstra euclidean_dijkstra(&edge_map, [&points, &target_point](const int& point) -> double { + Dijkstra euclidean_dijkstra{&edge_map, [&points, &target_point](const int& point) -> double { return std::sqrt( std::pow(points[point].first - points[target_point].first, 2) + std::pow(points[point].second - points[target_point].second, 2) ); - }); + }}; // test 1 (euclid) std::vector path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); std::vector expected_path = { &edges[11], &edges[12] }; EXPECT_EQ(path, expected_path); - EXPECT_EQ(cost, 5.5); + EXPECT_DOUBLE_EQ(cost, 5.5); // test 1 (null) path = null_dijkstra(0, target_point, cost, &popped_points_null); EXPECT_EQ(path, expected_path); - EXPECT_EQ(cost, 5.5); + EXPECT_DOUBLE_EQ(cost, 5.5); EXPECT_TRUE(popped_points_euclid < popped_points_null); // test 2 (euclid) @@ -166,11 +138,11 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); expected_path = { &edges[2], &edges[10] }; EXPECT_EQ(path, expected_path); - EXPECT_EQ(cost, 7.0); + EXPECT_DOUBLE_EQ(cost, 7.0); // test 2 (null) path = null_dijkstra(0, target_point, cost, &popped_points_null); EXPECT_EQ(path, expected_path); - EXPECT_EQ(cost, 7.0); + EXPECT_DOUBLE_EQ(cost, 7.0); EXPECT_TRUE(popped_points_euclid < popped_points_null); // test 3 (euclid) @@ -178,11 +150,11 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); expected_path = { &edges[1], &edges[3], &edges[14] }; EXPECT_EQ(path, expected_path); - EXPECT_EQ(cost, 7.9); + EXPECT_DOUBLE_EQ(cost, 7.9); // test 2 (null) path = null_dijkstra(0, target_point, cost, &popped_points_null); EXPECT_EQ(path, expected_path); - EXPECT_EQ(cost, 7.9); + EXPECT_DOUBLE_EQ(cost, 7.9); EXPECT_TRUE(popped_points_euclid < popped_points_null); } From 8f731e2de8141c11637dc1c8439f01871eaf7020 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 27 Apr 2022 18:58:20 +0200 Subject: [PATCH 61/67] Dijkstra fewer ptrs --- sql/Dijkstras_functor.cc | 30 ++++++++------- sql/Dijkstras_functor.h | 25 +++++++------ sql/Item_sum_shortest_dir_path.cc | 24 ++++++------ sql/Item_sum_shortest_dir_path.h | 2 +- unittest/gunit/gis_dijkstra-t.cc | 61 +++++++++++++++++++------------ 5 files changed, 81 insertions(+), 61 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index f28138cc9c98..32a0bf8a446d 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -11,8 +11,9 @@ Dijkstra::Dijkstra(const EdgeMapType* edges, m_point_map(CallbackAllocator>(allocate)), m_point_heap(CallbackAllocator(allocate)) {} template -std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - int *popped_points, const std::function& stop){ +std::vector Dijkstra::operator()(const int& start_point_id, const int& end_point_id, + double* total_cost, int *popped_points, + const std::function& stop) { m_point_map.clear(); m_point_heap.clear(); int popped_points_ = 0; @@ -23,20 +24,20 @@ std::vector Dijkstra::operator()(const int& start_po const std::pair edge_range_it = m_edges->equal_range(point); // checks all edges from point (i.e. current point) for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { - const Edge *edge = edge_it->second; + const Edge& edge = edge_it->second; // grabs existing node or creates new with cost of INFINITY inside map - Point& node_to = m_point_map[edge->to]; + Point& node_to = m_point_map[edge.to]; // ignore longer paths - double new_cost = node.cost + edge->cost; + double new_cost = node.cost + edge.cost; if (new_cost >= node_to.cost) continue; node_to.cost = new_cost; - node_to.cost_heu = new_cost + m_heu(edge->to); - node_to.path = edge; + node_to.cost_heu = new_cost + m_heu(edge.to); + node_to.path = &edge; - m_point_heap.push_back(edge->to); + m_point_heap.push_back(edge.to); std::push_heap(m_point_heap.begin(), m_point_heap.end(), heap_cmp); } if (m_point_heap.empty() || stop()) @@ -48,16 +49,17 @@ std::vector Dijkstra::operator()(const int& start_po } if (popped_points) *popped_points = popped_points_; - total_cost = node.cost; + if (total_cost) + *total_cost = node.cost; return retrace(point, start_point_id); } template -std::vector Dijkstra::retrace(int from_point, const int& to_point){ - std::vector path; +std::vector Dijkstra::retrace(int from_point, const int& to_point){ + std::vector path; while (from_point != to_point) { const Edge* path_ = m_point_map[from_point].path; - path.push_back(path_); + path.push_back(*path_); from_point = path_->from; } std::reverse(path.begin(), path.end()); @@ -65,7 +67,7 @@ std::vector Dijkstra::retrace(int from_point, const } -template class Dijkstra>>; +template class Dijkstra>>; #ifdef MYSQL_SERVER -template class Dijkstra>>; +template class Dijkstra>>; #endif diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 1744059b5437..2f0d752f5c8c 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -6,6 +6,7 @@ #include // std::function #include // std::push_heap & std::reverse #include // INFINITY +#include // memcmp /** * @brief Edge data for Dijkstra functor @@ -17,6 +18,10 @@ struct Edge { int from, to; // weight double cost; + // TODO mv to gis_dijkstra-t.cc + bool operator==(const Edge& other) const { + return !memcmp(this, &other, sizeof(Edge)); + } }; /** @@ -61,7 +66,7 @@ class Dijkstra { } }; - typedef std::unordered_multimap, std::equal_to, EdgeAllocator> EdgeMapType; + typedef std::unordered_multimap, std::equal_to, EdgeAllocator> EdgeMapType; typedef typename EdgeMapType::const_iterator edge_iterator; // key = Edge.from @@ -107,24 +112,22 @@ class Dijkstra { * * @param start_point_id node id of path start * @param end_point_id node if of path end - * @param total_cost l-val-ref returns total cost of found path (if path exists) - * @param stop callback for exiting function. called every ... + * @param total_cost ptr to return total cost of found path (if path exists) (can be nulltpr) * @param popped_points ptr to return #popped/visited points/nodes (can be nullptr) - * @return std::vector vector of pointers, pointing to edges in - * m_edges, representing found path, or empty vector if stoped by param stop or no path exists + * @param stop callback for exiting function. called every ... + * @return std::vector vector of edges in representing found path, or empty vector if stoped by param stop or no path exists */ - std::vector operator()(const int& start_point_id, const int& end_point_id, double& total_cost, - int *popped_points = nullptr, - const std::function& stop = []() -> bool { return false; }); + std::vector operator()(const int& start_point_id, const int& end_point_id, + double* total_cost = nullptr, int *popped_points = nullptr, + const std::function& stop = []() -> bool { return false; }); private: /** * @brief finds path by accumulating Point.path from m_point_map and reverting their order - * ! NB: will deref invalid ptr if path doesn't exist * @param from_point node id of path start * @param to_point node id of path end - * @return std::vector path found in m_point_map.path + * @return std::vector path found in m_point_map.path */ - std::vector retrace(int from_point, const int& to_point); + std::vector retrace(int from_point, const int& to_point); }; diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 63dd8f39d184..823b24696212 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -48,16 +48,18 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { double cost; int popped_points; - std::vector path; + std::vector path; try { // Dijkstra's externally allocated memory (my_malloc) std::deque allocated_memory; - Dijkstra dijkstra(&m_edge_map, heuristic, [&allocated_memory](const size_t n) -> void* { - void* p = my_malloc(key_memory_Dijkstra, n, MYF(MY_WME | ME_FATALERROR)); - allocated_memory.push_front(p); - return p; - }); - path = dijkstra(m_begin_node, m_end_node, cost, &popped_points, stop_dijkstra); + { + Dijkstra dijkstra(&m_edge_map, heuristic, [&allocated_memory](const size_t n) -> void* { + void* p = my_malloc(key_memory_Dijkstra, n, MYF(MY_WME | ME_FATALERROR)); + allocated_memory.push_front(p); + return p; + }); + path = dijkstra(m_begin_node, m_end_node, &cost, &popped_points, stop_dijkstra); + } // deallocating Dijkstra's borrowed memory for (void* p : allocated_memory) my_free(p); @@ -75,12 +77,12 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { if (stop_dijkstra()) return error_json(); // jsonifying path from dijkstra into arr - for (const Edge* edge : path) { + for (const Edge edge : path) { Json_object_ptr json_edge(new (std::nothrow) Json_object()); if ( json_edge == nullptr || - json_edge->add_alias("id", jsonify_to_heap(edge->id)) || - json_edge->add_alias("cost", jsonify_to_heap(edge->cost)) || + json_edge->add_alias("id", jsonify_to_heap(edge.id)) || + json_edge->add_alias("cost", jsonify_to_heap(edge.cost)) || //json_edge->add_alias("from", jsonify_to_heap(edge->from)) || //json_edge->add_alias("to", jsonify_to_heap(edge->to)) || arr->append_alias(std::move(json_edge))) @@ -200,7 +202,7 @@ bool Item_sum_shortest_dir_path::add() { // store edge try { - Edge *edge = new (thd->mem_root) Edge{ id, from_id, to_id, cost }; + Edge edge = Edge{ id, from_id, to_id, cost }; m_edge_map.insert(std::pair(from_id, edge)); } catch (...) { // handles std::bad_alloc handle_std_exception(func_name()); diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 239e54e707d8..c8fc65d7580a 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -16,7 +16,7 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { int m_begin_node, m_end_node; // * accumulated edges from ::add. map key = node id of edge origin (i.e. Edge.from) - malloc_unordered_multimap m_edge_map; + malloc_unordered_multimap m_edge_map; // * accumulated points from ::add. map key = node id malloc_unordered_map> m_point_map; // map of all used edge ids (used to check for id overlap) diff --git a/unittest/gunit/gis_dijkstra-t.cc b/unittest/gunit/gis_dijkstra-t.cc index a58e2aec571e..987ce4843eaf 100644 --- a/unittest/gunit/gis_dijkstra-t.cc +++ b/unittest/gunit/gis_dijkstra-t.cc @@ -17,13 +17,26 @@ class DijkstraTest : public ::testing::Test { // check that cost is total path cost // ! floating point cmp -void check_cost(const std::vector& path , const double& cost){ +void check_cost(const std::vector& path , const double& cost){ double expected_cost = 0; - for (const Edge* e : path) - expected_cost += e->cost; + for (const Edge& e : path) + expected_cost += e.cost; EXPECT_DOUBLE_EQ(cost, expected_cost); } +// disables edge in map by setting cost to INFINITY +void disable_edge(const std::unordered_multimap& map, Edge& edge){ + auto range = map.equal_range(edge.from); + for (auto it = range.first; it != range.second; it++) { + if (it->second == edge) { + // TODO remove const cast + const_cast(&it->second)->cost = INFINITY; + edge.cost = INFINITY; + return; + } + } +} + // test dijkstra without heuristic TEST_F(DijkstraTest, NullHeuristic) { Edge edges[] = { @@ -40,29 +53,29 @@ TEST_F(DijkstraTest, NullHeuristic) { }; size_t n_edges = sizeof(edges) / sizeof(Edge); - std::unordered_multimap edge_map; + std::unordered_multimap edge_map; for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; - edge_map.insert(std::pair(e.from, &e)); + edge_map.insert(std::pair(e.from, e)); } double cost; Dijkstra dijkstra(&edge_map); // check 0 -> 3 - std::vector path = dijkstra(0, 3, cost); - std::vector expected_path = { &edges[0], &edges[2], &edges[4] }; + std::vector path = dijkstra(0, 3, &cost); + std::vector expected_path = { edges[0], edges[2], edges[4] }; EXPECT_EQ(path, expected_path); check_cost(path, cost); // check 0 -> 4 - path = dijkstra(0, 4, cost); - expected_path = { &edges[8] }; + path = dijkstra(0, 4, &cost); + expected_path = { edges[8] }; EXPECT_EQ(path, expected_path); check_cost(path, cost); // check 1 -> 0 - path = dijkstra(1, 0, cost); - expected_path = { &edges[2], &edges[4], &edges[6] }; + path = dijkstra(1, 0, &cost); + expected_path = { edges[2], edges[4], edges[6] }; EXPECT_EQ(path, expected_path); check_cost(path, cost); } @@ -107,10 +120,10 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { // A 0 -> D 3 -> B 1 -> G 6 : 7.9m (test 3) size_t n_edges = sizeof(edges) / sizeof(Edge); - std::unordered_multimap edge_map; + std::unordered_multimap edge_map; for (size_t i = 0; i < n_edges; i++) { Edge& e = edges[i]; - edge_map.insert(std::pair(e.from, &e)); + edge_map.insert(std::pair(e.from, e)); } double cost; int target_point = 6; // G @@ -123,36 +136,36 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { ); }}; // test 1 (euclid) - std::vector path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); - std::vector expected_path = { &edges[11], &edges[12] }; + std::vector path = euclidean_dijkstra(0, target_point, &cost, &popped_points_euclid); + std::vector expected_path = { edges[11], edges[12] }; EXPECT_EQ(path, expected_path); EXPECT_DOUBLE_EQ(cost, 5.5); // test 1 (null) - path = null_dijkstra(0, target_point, cost, &popped_points_null); + path = null_dijkstra(0, target_point, &cost, &popped_points_null); EXPECT_EQ(path, expected_path); EXPECT_DOUBLE_EQ(cost, 5.5); EXPECT_TRUE(popped_points_euclid < popped_points_null); // test 2 (euclid) - edges[12].cost = INFINITY; // disables prev best path (fine since heu <= cost) - path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); - expected_path = { &edges[2], &edges[10] }; + disable_edge(edge_map, edges[12]); // disables prev best path (fine since heu <= cost) + path = euclidean_dijkstra(0, target_point, &cost, &popped_points_euclid); + expected_path = { edges[2], edges[10] }; EXPECT_EQ(path, expected_path); EXPECT_DOUBLE_EQ(cost, 7.0); // test 2 (null) - path = null_dijkstra(0, target_point, cost, &popped_points_null); + path = null_dijkstra(0, target_point, &cost, &popped_points_null); EXPECT_EQ(path, expected_path); EXPECT_DOUBLE_EQ(cost, 7.0); EXPECT_TRUE(popped_points_euclid < popped_points_null); // test 3 (euclid) - edges[10].cost = INFINITY; // disables prev best path - path = euclidean_dijkstra(0, target_point, cost, &popped_points_euclid); - expected_path = { &edges[1], &edges[3], &edges[14] }; + disable_edge(edge_map, edges[10]); // disables prev best path + path = euclidean_dijkstra(0, target_point, &cost, &popped_points_euclid); + expected_path = { edges[1], edges[3], edges[14] }; EXPECT_EQ(path, expected_path); EXPECT_DOUBLE_EQ(cost, 7.9); // test 2 (null) - path = null_dijkstra(0, target_point, cost, &popped_points_null); + path = null_dijkstra(0, target_point, &cost, &popped_points_null); EXPECT_EQ(path, expected_path); EXPECT_DOUBLE_EQ(cost, 7.9); EXPECT_TRUE(popped_points_euclid < popped_points_null); From 07e0659848c50a59e1f2ccff21f2a455ecdb67e5 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Wed, 27 Apr 2022 19:27:54 +0200 Subject: [PATCH 62/67] st_sdp rename param args --- sql/Item_sum_shortest_dir_path.cc | 4 ++-- sql/Item_sum_shortest_dir_path.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 823b24696212..304fd26888c2 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -9,9 +9,9 @@ Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( m_edge_ids(key_memory_Dijkstra) {} Item_sum_shortest_dir_path::Item_sum_shortest_dir_path( - const POS &pos, PT_item_list *args, + const POS &pos, PT_item_list *parent_args, unique_ptr_destroy_only wrapper) - : Item_sum_json(std::move(wrapper), pos, args, nullptr), + : Item_sum_json(std::move(wrapper), pos, parent_args, nullptr), m_edge_map(key_memory_Dijkstra), m_point_map(key_memory_Dijkstra), m_edge_ids(key_memory_Dijkstra) {} diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index c8fc65d7580a..4df242846e93 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -43,7 +43,7 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { * ! wrapper not needed * TODO inherit from Item_sum? */ - Item_sum_shortest_dir_path(const POS &pos, PT_item_list *args, + Item_sum_shortest_dir_path(const POS &pos, PT_item_list *parent_args, unique_ptr_destroy_only wrapper); ~Item_sum_shortest_dir_path() override = default; From 65478d55f1460ca132e84f1611ee1845d5b29bb2 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Fri, 29 Apr 2022 18:00:49 +0200 Subject: [PATCH 63/67] st_sdp override reset_field & update_field without assert --- sql/Item_sum_shortest_dir_path.cc | 29 +++++++++++++++++++++++++++++ sql/Item_sum_shortest_dir_path.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 304fd26888c2..4f7538fe61ac 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -147,6 +147,35 @@ bool Item_sum_shortest_dir_path::fix_fields(THD *thd, Item **pItem) { return false; } +void Item_sum_shortest_dir_path::reset_field() { + clear(); + add(); + /* + field_type is MYSQL_TYPE_JSON so Item::make_string_field will always + create a Field_json(in Item_sum::create_tmp_field). + The cast is need since Field does not expose store_json function. + */ + Field_json *json_result_field = down_cast(result_field); + // Store the container inside the field. + json_result_field->store_json(m_wrapper.get()); + json_result_field->set_notnull(); +} + +void Item_sum_shortest_dir_path::update_field() { + /* + field_type is MYSQL_TYPE_JSON so Item::make_string_field will always + create a Field_json(in Item_sum::create_tmp_field). + The cast is need since Field does not expose store_json function. + */ + Field_json *json_result_field = down_cast(result_field); + // Restore the container(m_wrapper) from the field + json_result_field->val_json(m_wrapper.get()); + add(); + // Store the container inside the field. + json_result_field->store_json(m_wrapper.get()); + json_result_field->set_notnull(); +} + bool Item_sum_shortest_dir_path::add() { assert(arg_count == 7); diff --git a/sql/Item_sum_shortest_dir_path.h b/sql/Item_sum_shortest_dir_path.h index 4df242846e93..89624354de7c 100644 --- a/sql/Item_sum_shortest_dir_path.h +++ b/sql/Item_sum_shortest_dir_path.h @@ -56,6 +56,9 @@ class Item_sum_shortest_dir_path final : public Item_sum_json { void clear() override; bool fix_fields(THD *thd, Item **pItem) override; + void reset_field() override; + void update_field() override; + bool add() override; Item *copy_or_same(THD *thd) override; bool check_wf_semantics1(THD *thd, Query_block *select, From a8224ea861a13acab4f79c9e75adbcd2f3bc07c6 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Thu, 5 May 2022 15:28:56 +0200 Subject: [PATCH 64/67] extended the mtr dataset --- .../suite/gis/r/st_shortest_dir_path.result | 171 ++++++++++++++---- .../suite/gis/t/st_shortest_dir_path.test | 137 +++++++++++--- 2 files changed, 248 insertions(+), 60 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index 47f1f17b3946..f083034eda45 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -19,11 +19,59 @@ INSERT INTO edges VALUES (54, 4, 3, 2.0, "undefined"), (55, 3, 5, 6.0, "undefined"), (56, 2, 5, 12.0, "undefined"), -(57, 4, 5, 10.0, "undefined") +(57, 4, 5, 10.0, "undefined"), +(58, 5, 7, 3.0, "undefined"), +(59, 6, 10, 17.0, "undefined"), +(60, 4, 8, 15.0, "undefined"), +(61, 2, 6, 16.0, "undefined"), +(62, 4, 7, 9.0, "undefined"), +(63, 5, 8, 13.0, "undefined"), +(64, 8, 9, 4.0, "undefined"), +(65, 8, 10, 12.0, "undefined"), +(66, 10, 12, 8.0, "undefined"), +(67, 9, 11, 5.0, "undefined"), +(68, 8, 11, 8.0, "undefined"), +(69, 4, 12, 25.0, "undefined"), +(70, 12, 13, 3.0, "undefined"), +(71, 12, 15, 9.0, "undefined"), +(72, 13, 17, 11.0, "undefined"), +(73, 14, 15, 2.0, "undefined"), +(74, 12, 14, 6.0, "undefined"), +(75, 15, 18, 10.0, "undefined"), +(76, 18, 20, 7.0, "undefined"), +(77, 16, 21, 13.0, "undefined"), +(78, 17, 19, 8.0, "undefined"), +(79, 15, 19, 14.0, "undefined"), +(80, 21, 23, 4.0, "undefined"), +(81, 21, 22, 3.0, "undefined"), +(82, 21, 25, 9.0, "undefined"), +(83, 23, 26, 8.0, "undefined"), +(84, 22, 24, 5.0, "undefined"), +(85, 20, 24, 10.0, "undefined"), +(86, 15, 16, 2.0, "undefined"), +(87, 24, 25, 3.0, "undefined") ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) FROM edges; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) -{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}], "visited_nodes": 5} +{"cost": 12.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 54, "cost": 2.0}, {"id": 55, "cost": 6.0}], "visited_nodes": 7} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 12) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 12) +{"cost": 25.0, "path": [{"id": 69, "cost": 25.0}], "visited_nodes": 4} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 3, 8) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 3, 8) +{"cost": 19.0, "path": [{"id": 55, "cost": 6.0}, {"id": 63, "cost": 13.0}], "visited_nodes": 3} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 10) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 10) +{"cost": 27.0, "path": [{"id": 60, "cost": 15.0}, {"id": 65, "cost": 12.0}], "visited_nodes": 8} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 22) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 22) +{"cost": 55.0, "path": [{"id": 50, "cost": 3.0}, {"id": 53, "cost": 1.0}, {"id": 69, "cost": 25.0}, {"id": 74, "cost": 6.0}, {"id": 73, "cost": 2.0}, {"id": 86, "cost": 2.0}, {"id": 77, "cost": 13.0}, {"id": 81, "cost": 3.0}], "visited_nodes": 27} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 26) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 26) +{"cost": 60.0, "path": [{"id": 69, "cost": 25.0}, {"id": 74, "cost": 6.0}, {"id": 73, "cost": 2.0}, {"id": 86, "cost": 2.0}, {"id": 77, "cost": 13.0}, {"id": 80, "cost": 4.0}, {"id": 83, "cost": 8.0}], "visited_nodes": 27} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 3, 19) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 3, 19) +{"cost": 61.0, "path": [{"id": 55, "cost": 6.0}, {"id": 63, "cost": 13.0}, {"id": 65, "cost": 12.0}, {"id": 66, "cost": 8.0}, {"id": 74, "cost": 6.0}, {"id": 73, "cost": 2.0}, {"id": 79, "cost": 14.0}], "visited_nodes": 16} INSERT INTO edges VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), @@ -39,7 +87,7 @@ INSERT INTO edges VALUES ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) FROM edges GROUP BY type; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 2) -{"cost": 5.0, "path": [{"id": 51, "cost": 5.0}], "visited_nodes": 4} +{"cost": 5.0, "path": [{"id": 51, "cost": 5.0}], "visited_nodes": 3} {"cost": 5.0, "path": [{"id": 10, "cost": 2.0}, {"id": 11, "cost": 3.0}], "visited_nodes": 2} {"cost": 40.0, "path": [{"id": 22, "cost": 40.0}], "visited_nodes": 2} {"cost": 140.0, "path": [{"id": 30, "cost": 80.0}, {"id": 33, "cost": 40.0}, {"id": 34, "cost": 20.0}], "visited_nodes": 3} @@ -65,54 +113,103 @@ INSERT INTO points VALUES ( 8, POINT( 6, 2 ) ), ( 9, POINT( -2, 1 ) ), ( 10, POINT( -3, -2 ) ), -( 11, POINT( -1, 3 ) ) +( 11, POINT( -1, 3 ) ), +( 12, POINT( -1, 5 ) ), +( 13, POINT( -2, 3 ) ), +( 14, POINT( 3, 3 ) ), +( 15, POINT( 2, 4 ) ), +( 16, POINT( 4, 6 ) ), +( 17, POINT( 4, 7 ) ), +( 18, POINT( 6, 3 ) ), +( 19, POINT( 5, 9 ) ), +( 20, POINT( 7, 10 ) ), +( 21, POINT( 9, 11 ) ), +( 22, POINT( 10, 4 ) ), +( 23, POINT( 13, 15 ) ), +( 24, POINT( 12, 14 ) ), +( 25, POINT( 14, 16 ) ), +( 26, POINT( 15, 19 ) ), +( 27, POINT( 17, 22 ) ), +( 28, POINT( 19, 24 ) ), +( 29, POINT( 21, 25 ) ), +( 30, POINT( 26, 30 ) ), +( 31, POINT( 29, 32 ) ), +( 32, POINT( 31, 36 ) ), +( 33, POINT( 35, 39 ) ), +( 34, POINT( 37, 42 ) ), +( 35, POINT( 38, 40 ) ), +( 36, POINT( 25, 32 ) ), +( 37, POINT( 40, 42 ) ) ; INSERT INTO edges VALUES -( 0, 0, 2, 1.5 , "spatial" ), -( 1, 0, 3, 2.9 , "spatial" ), -( 2, 0, 5, 5.0 , "spatial" ), -( 3, 3, 1, 3.0 , "spatial" ), -( 4, 2, 11, 2.0 , "spatial" ), -( 5, 2, 10, 2.4 , "spatial" ), -( 6, 2, 9, 2.4 , "spatial" ), -( 7, 9, 4, 3.65, "spatial" ), -( 8, 4, 5, 4.0 , "spatial" ), -( 9, 5, 8, 2.4 , "spatial" ), -( 10, 5, 6, 2.0 , "spatial" ), -( 11, 0, 7, 3.2 , "spatial" ), -( 12, 7, 6, 2.3 , "spatial" ), -( 13, 8, 6, 2.3 , "spatial" ), -( 14, 1, 6, 2.0 , "spatial" ) +(0, 0, 1, 3.0, "spatial"), +(1, 0, 2, 5.0, "spatial"), +(2, 0, 3, 8.0, "spatial"), +(3, 1, 4, 1.0, "spatial"), +(4, 4, 3, 2.0, "spatial"), +(5, 3, 5, 6.0, "spatial"), +(6, 2, 5, 12.0, "spatial"), +(7, 4, 5, 10.0, "spatial"), +(8, 5, 7, 3.0, "spatial"), +(9, 6, 10, 17.0, "spatial"), +(10, 4, 8, 15.0, "spatial"), +(11, 2, 6, 16.0, "spatial"), +(12, 4, 7, 9.0, "spatial"), +(13, 5, 8, 13.0, "spatial"), +(14, 8, 9, 4.0, "spatial"), +(15, 8, 10, 12.0, "spatial"), +(16, 10, 12, 8.0, "spatial"), +(17, 9, 11, 5.0, "spatial"), +(18, 8, 11, 8.0, "spatial"), +(19, 4, 12, 25.0, "spatial"), +(20, 12, 13, 3.0, "spatial"), +(21, 12, 15, 9.0, "spatial"), +(22, 13, 17, 11.0, "spatial"), +(23, 14, 15, 2.0, "spatial"), +(24, 12, 14, 6.0, "spatial"), +(25, 15, 18, 10.0, "spatial"), +(26, 18, 20, 7.0, "spatial"), +(27, 16, 21, 13.0, "spatial"), +(28, 17, 19, 8.0, "spatial"), +(29, 15, 19, 14.0, "spatial"), +(30, 21, 23, 4.0, "spatial"), +(31, 21, 22, 3.0, "spatial"), +(32, 21, 25, 9.0, "spatial"), +(33, 23, 26, 8.0, "spatial"), +(34, 22, 24, 5.0, "spatial"), +(35, 20, 24, 10.0, "spatial"), +(36, 15, 16, 2.0, "spatial"), +(37, 24, 25, 3.0, "spatial") ; -SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; -ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) -{"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}], "visited_nodes": 2} -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) -{"cost": 5.5, "path": [{"id": 11, "cost": 3.2}, {"id": 12, "cost": 2.3}], "visited_nodes": 9} +ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) +{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 25} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) +{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 30} UPDATE edges SET cost = 1000.0 where id = 12; -SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; -ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) -{"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}], "visited_nodes": 5} -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) -{"cost": 7.0, "path": [{"id": 2, "cost": 5.0}, {"id": 10, "cost": 2.0}], "visited_nodes": 9} +ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) +{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 27} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) +{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 31} UPDATE edges SET cost = 1000.0 where id = 10; -SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; -ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) -{"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}], "visited_nodes": 7} -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; -ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) -{"cost": 7.9, "path": [{"id": 1, "cost": 2.9}, {"id": 3, "cost": 3.0}, {"id": 14, "cost": 2.0}], "visited_nodes": 11} +ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) +{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 24} +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; +ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) +{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 32} # ----------------------------- # |[3] testing error detection| # ----------------------------- diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index e9b492fe2fc4..46640fa52f41 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -24,11 +24,53 @@ INSERT INTO edges VALUES (54, 4, 3, 2.0, "undefined"), (55, 3, 5, 6.0, "undefined"), (56, 2, 5, 12.0, "undefined"), -(57, 4, 5, 10.0, "undefined") +(57, 4, 5, 10.0, "undefined"), +(58, 5, 7, 3.0, "undefined"), +(59, 6, 10, 17.0, "undefined"), +(60, 4, 8, 15.0, "undefined"), +(61, 2, 6, 16.0, "undefined"), +(62, 4, 7, 9.0, "undefined"), +(63, 5, 8, 13.0, "undefined"), +(64, 8, 9, 4.0, "undefined"), +(65, 8, 10, 12.0, "undefined"), +(66, 10, 12, 8.0, "undefined"), +(67, 9, 11, 5.0, "undefined"), +(68, 8, 11, 8.0, "undefined"), +(69, 4, 12, 25.0, "undefined"), +(70, 12, 13, 3.0, "undefined"), +(71, 12, 15, 9.0, "undefined"), +(72, 13, 17, 11.0, "undefined"), +(73, 14, 15, 2.0, "undefined"), +(74, 12, 14, 6.0, "undefined"), +(75, 15, 18, 10.0, "undefined"), +(76, 18, 20, 7.0, "undefined"), +(77, 16, 21, 13.0, "undefined"), +(78, 17, 19, 8.0, "undefined"), +(79, 15, 19, 14.0, "undefined"), +(80, 21, 23, 4.0, "undefined"), +(81, 21, 22, 3.0, "undefined"), +(82, 21, 25, 9.0, "undefined"), +(83, 23, 26, 8.0, "undefined"), +(84, 22, 24, 5.0, "undefined"), +(85, 20, 24, 10.0, "undefined"), +(86, 15, 16, 2.0, "undefined"), +(87, 24, 25, 3.0, "undefined") ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 5) FROM edges; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 12) FROM edges; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 3, 8) FROM edges; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 10) FROM edges; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 22) FROM edges; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 4, 26) FROM edges; + +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 3, 19) FROM edges; + INSERT INTO edges VALUES (10, 0, 1, 2.0, "car"), (11, 1, 2, 3.0, "car"), @@ -72,50 +114,99 @@ INSERT INTO points VALUES ( 8, POINT( 6, 2 ) ), ( 9, POINT( -2, 1 ) ), ( 10, POINT( -3, -2 ) ), -( 11, POINT( -1, 3 ) ) +( 11, POINT( -1, 3 ) ), +( 12, POINT( -1, 5 ) ), +( 13, POINT( -2, 3 ) ), +( 14, POINT( 3, 3 ) ), +( 15, POINT( 2, 4 ) ), +( 16, POINT( 4, 6 ) ), +( 17, POINT( 4, 7 ) ), +( 18, POINT( 6, 3 ) ), +( 19, POINT( 5, 9 ) ), +( 20, POINT( 7, 10 ) ), +( 21, POINT( 9, 11 ) ), +( 22, POINT( 10, 4 ) ), +( 23, POINT( 13, 15 ) ), +( 24, POINT( 12, 14 ) ), +( 25, POINT( 14, 16 ) ), +( 26, POINT( 15, 19 ) ), +( 27, POINT( 17, 22 ) ), +( 28, POINT( 19, 24 ) ), +( 29, POINT( 21, 25 ) ), +( 30, POINT( 26, 30 ) ), +( 31, POINT( 29, 32 ) ), +( 32, POINT( 31, 36 ) ), +( 33, POINT( 35, 39 ) ), +( 34, POINT( 37, 42 ) ), +( 35, POINT( 38, 40 ) ), +( 36, POINT( 25, 32 ) ), +( 37, POINT( 40, 42 ) ) ; INSERT INTO edges VALUES -( 0, 0, 2, 1.5 , "spatial" ), -( 1, 0, 3, 2.9 , "spatial" ), -( 2, 0, 5, 5.0 , "spatial" ), -( 3, 3, 1, 3.0 , "spatial" ), -( 4, 2, 11, 2.0 , "spatial" ), -( 5, 2, 10, 2.4 , "spatial" ), -( 6, 2, 9, 2.4 , "spatial" ), -( 7, 9, 4, 3.65, "spatial" ), -( 8, 4, 5, 4.0 , "spatial" ), -( 9, 5, 8, 2.4 , "spatial" ), -( 10, 5, 6, 2.0 , "spatial" ), -( 11, 0, 7, 3.2 , "spatial" ), -( 12, 7, 6, 2.3 , "spatial" ), -( 13, 8, 6, 2.3 , "spatial" ), -( 14, 1, 6, 2.0 , "spatial" ) +(0, 0, 1, 3.0, "spatial"), +(1, 0, 2, 5.0, "spatial"), +(2, 0, 3, 8.0, "spatial"), +(3, 1, 4, 1.0, "spatial"), +(4, 4, 3, 2.0, "spatial"), +(5, 3, 5, 6.0, "spatial"), +(6, 2, 5, 12.0, "spatial"), +(7, 4, 5, 10.0, "spatial"), +(8, 5, 7, 3.0, "spatial"), +(9, 6, 10, 17.0, "spatial"), +(10, 4, 8, 15.0, "spatial"), +(11, 2, 6, 16.0, "spatial"), +(12, 4, 7, 9.0, "spatial"), +(13, 5, 8, 13.0, "spatial"), +(14, 8, 9, 4.0, "spatial"), +(15, 8, 10, 12.0, "spatial"), +(16, 10, 12, 8.0, "spatial"), +(17, 9, 11, 5.0, "spatial"), +(18, 8, 11, 8.0, "spatial"), +(19, 4, 12, 25.0, "spatial"), +(20, 12, 13, 3.0, "spatial"), +(21, 12, 15, 9.0, "spatial"), +(22, 13, 17, 11.0, "spatial"), +(23, 14, 15, 2.0, "spatial"), +(24, 12, 14, 6.0, "spatial"), +(25, 15, 18, 10.0, "spatial"), +(26, 18, 20, 7.0, "spatial"), +(27, 16, 21, 13.0, "spatial"), +(28, 17, 19, 8.0, "spatial"), +(29, 15, 19, 14.0, "spatial"), +(30, 21, 23, 4.0, "spatial"), +(31, 21, 22, 3.0, "spatial"), +(32, 21, 25, 9.0, "spatial"), +(33, 23, 26, 8.0, "spatial"), +(34, 22, 24, 5.0, "spatial"), +(35, 20, 24, 10.0, "spatial"), +(36, 15, 16, 2.0, "spatial"), +(37, 24, 25, 3.0, "spatial") ; # Same test as in dijkstra-t.cc se dijkstra-t.cc for more details TODO try other bigger dataset -SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; UPDATE edges SET cost = 1000.0 where id = 12; -SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; UPDATE edges SET cost = 1000.0 where id = 10; -SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 6) +SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; -SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 6) FROM edges; +SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; --echo # ----------------------------- --echo # |[3] testing error detection| From 08d8952cafc5a518485846571a173c56368c08c7 Mon Sep 17 00:00:00 2001 From: Vilhelm Seip Date: Mon, 9 May 2022 14:25:30 +0200 Subject: [PATCH 65/67] improved mtr tests --- mysql-test/suite/gis/r/st_shortest_dir_path.result | 12 ++++++------ mysql-test/suite/gis/t/st_shortest_dir_path.test | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/gis/r/st_shortest_dir_path.result b/mysql-test/suite/gis/r/st_shortest_dir_path.result index f083034eda45..a6873861d784 100644 --- a/mysql-test/suite/gis/r/st_shortest_dir_path.result +++ b/mysql-test/suite/gis/r/st_shortest_dir_path.result @@ -190,26 +190,26 @@ ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.po SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) {"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 30} -UPDATE edges SET cost = 1000.0 where id = 12; +UPDATE edges SET cost = 1000.0 where id = 3; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) -{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 27} +{"cost": 81.0, "path": [{"id": 1, "cost": 5.0}, {"id": 11, "cost": 16.0}, {"id": 9, "cost": 17.0}, {"id": 16, "cost": 8.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 23} SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) -{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 31} -UPDATE edges SET cost = 1000.0 where id = 10; +{"cost": 81.0, "path": [{"id": 1, "cost": 5.0}, {"id": 11, "cost": 16.0}, {"id": 9, "cost": 17.0}, {"id": 16, "cost": 8.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 31} +UPDATE edges SET cost = 1000.0 where id = 11; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges JOIN points ON points.id = edges.to_id ; ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) -{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 24} +{"cost": 82.0, "path": [{"id": 2, "cost": 8.0}, {"id": 5, "cost": 6.0}, {"id": 13, "cost": 13.0}, {"id": 15, "cost": 12.0}, {"id": 16, "cost": 8.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 25} SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) -{"cost": 64.0, "path": [{"id": 0, "cost": 3.0}, {"id": 3, "cost": 1.0}, {"id": 19, "cost": 25.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 32} +{"cost": 82.0, "path": [{"id": 2, "cost": 8.0}, {"id": 5, "cost": 6.0}, {"id": 13, "cost": 13.0}, {"id": 15, "cost": 12.0}, {"id": 16, "cost": 8.0}, {"id": 24, "cost": 6.0}, {"id": 23, "cost": 2.0}, {"id": 36, "cost": 2.0}, {"id": 27, "cost": 13.0}, {"id": 30, "cost": 4.0}, {"id": 33, "cost": 8.0}], "visited_nodes": 33} # ----------------------------- # |[3] testing error detection| # ----------------------------- diff --git a/mysql-test/suite/gis/t/st_shortest_dir_path.test b/mysql-test/suite/gis/t/st_shortest_dir_path.test index 46640fa52f41..c0a9d46d97a1 100644 --- a/mysql-test/suite/gis/t/st_shortest_dir_path.test +++ b/mysql-test/suite/gis/t/st_shortest_dir_path.test @@ -192,7 +192,7 @@ JOIN points ON points.id = edges.to_id ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; -UPDATE edges SET cost = 1000.0 where id = 12; +UPDATE edges SET cost = 1000.0 where id = 3; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges @@ -200,7 +200,7 @@ JOIN points ON points.id = edges.to_id ; SELECT ST_SHORTEST_DIR_PATH(id, from_id, to_id, cost, NULL, 0, 26) FROM edges; -UPDATE edges SET cost = 1000.0 where id = 10; +UPDATE edges SET cost = 1000.0 where id = 11; SELECT ST_SHORTEST_DIR_PATH(edges.id, edges.from_id, edges.to_id, edges.cost, points.point, 0, 26) FROM edges From 34e6b86cd5c9106465ee194391e60693b23ab3b3 Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 19 May 2022 15:03:57 +0200 Subject: [PATCH 66/67] st_sdp replace memcmp on padded type --- sql/Dijkstras_functor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 2f0d752f5c8c..27916add8828 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -20,7 +20,7 @@ struct Edge { double cost; // TODO mv to gis_dijkstra-t.cc bool operator==(const Edge& other) const { - return !memcmp(this, &other, sizeof(Edge)); + return id == other.id /* && from == other.from && to == other.to && cost == other.cost */; } }; From 29778acd7ae689b03614eb48e7c0b2a93a06b46c Mon Sep 17 00:00:00 2001 From: "Erik K. Haugen" Date: Thu, 19 May 2022 16:13:50 +0200 Subject: [PATCH 67/67] dijkstra.m_edges raw ptr replaced by ref --- sql/Dijkstras_functor.cc | 4 ++-- sql/Dijkstras_functor.h | 5 ++--- sql/Item_sum_shortest_dir_path.cc | 2 +- unittest/gunit/gis_dijkstra-t.cc | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sql/Dijkstras_functor.cc b/sql/Dijkstras_functor.cc index 32a0bf8a446d..a7c38d4107f2 100644 --- a/sql/Dijkstras_functor.cc +++ b/sql/Dijkstras_functor.cc @@ -4,7 +4,7 @@ #endif template -Dijkstra::Dijkstra(const EdgeMapType* edges, +Dijkstra::Dijkstra(const EdgeMapType& edges, const std::function& heu_func, const std::function& allocate) : m_edges(edges), m_heu(heu_func), @@ -21,7 +21,7 @@ std::vector Dijkstra::operator()(const int& start_point_id, Point& node = m_point_map[point] = Point{ 0, /*m_heu(point)*/ 0, nullptr }; // A* while (point != end_point_id) { - const std::pair edge_range_it = m_edges->equal_range(point); + const std::pair edge_range_it = m_edges.equal_range(point); // checks all edges from point (i.e. current point) for (edge_iterator edge_it = edge_range_it.first; edge_it != edge_range_it.second; edge_it++) { const Edge& edge = edge_it->second; diff --git a/sql/Dijkstras_functor.h b/sql/Dijkstras_functor.h index 27916add8828..ea1f6e9e1f2a 100644 --- a/sql/Dijkstras_functor.h +++ b/sql/Dijkstras_functor.h @@ -6,7 +6,6 @@ #include // std::function #include // std::push_heap & std::reverse #include // INFINITY -#include // memcmp /** * @brief Edge data for Dijkstra functor @@ -70,7 +69,7 @@ class Dijkstra { typedef typename EdgeMapType::const_iterator edge_iterator; // key = Edge.from - const EdgeMapType* m_edges; + const EdgeMapType& m_edges; const std::function m_heu; /** @@ -104,7 +103,7 @@ class Dijkstra { * @param allocate custom allocator e.g. for measuring memory usage * * memory will not be deallocated if custom allocate function is given */ - Dijkstra(const EdgeMapType* edges, + Dijkstra(const EdgeMapType& edges, const std::function& heu_func = [](const int&) -> double { return 0.0; }, const std::function& allocate = {}); /** diff --git a/sql/Item_sum_shortest_dir_path.cc b/sql/Item_sum_shortest_dir_path.cc index 4f7538fe61ac..40519964e748 100644 --- a/sql/Item_sum_shortest_dir_path.cc +++ b/sql/Item_sum_shortest_dir_path.cc @@ -53,7 +53,7 @@ bool Item_sum_shortest_dir_path::val_json(Json_wrapper *wr) { // Dijkstra's externally allocated memory (my_malloc) std::deque allocated_memory; { - Dijkstra dijkstra(&m_edge_map, heuristic, [&allocated_memory](const size_t n) -> void* { + Dijkstra dijkstra(m_edge_map, heuristic, [&allocated_memory](const size_t n) -> void* { void* p = my_malloc(key_memory_Dijkstra, n, MYF(MY_WME | ME_FATALERROR)); allocated_memory.push_front(p); return p; diff --git a/unittest/gunit/gis_dijkstra-t.cc b/unittest/gunit/gis_dijkstra-t.cc index 987ce4843eaf..ed52ac9a6de2 100644 --- a/unittest/gunit/gis_dijkstra-t.cc +++ b/unittest/gunit/gis_dijkstra-t.cc @@ -59,7 +59,7 @@ TEST_F(DijkstraTest, NullHeuristic) { edge_map.insert(std::pair(e.from, e)); } double cost; - Dijkstra dijkstra(&edge_map); + Dijkstra dijkstra(edge_map); // check 0 -> 3 std::vector path = dijkstra(0, 3, &cost); @@ -128,8 +128,8 @@ TEST_F(DijkstraTest, EuclideanHeuristic) { double cost; int target_point = 6; // G int popped_points_null, popped_points_euclid; - Dijkstra null_dijkstra(&edge_map); - Dijkstra euclidean_dijkstra{&edge_map, [&points, &target_point](const int& point) -> double { + Dijkstra null_dijkstra(edge_map); + Dijkstra euclidean_dijkstra{edge_map, [&points, &target_point](const int& point) -> double { return std::sqrt( std::pow(points[point].first - points[target_point].first, 2) + std::pow(points[point].second - points[target_point].second, 2)