mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-40553 Print GIS ranges in optimizer trace and context
Ranges built over GIS (geometry) columns could not be printed in the optimizer trace or the recorded optimizer context: Field_geom printed every key value as the placeholder "unprintable_geometry_value", regardless of whether the index stored the column's raw value (or a prefix of it) or, for a SPATIAL index, its MBR (Minimum Bounding Rectangle). Field::print_key_part_value() now takes an image_type argument (see Field::image_type()) that says which of the two the key holds. For a SPATIAL index (image_type itMBR), Field_geom::print_key_part_value() decodes the four doubles the key stores and prints them as a WKT POLYGON. For every other index (image_type itRAW), the key holds the value or a prefix of it, and it now prints in binary form, the same way Field_blob does, instead of the placeholder. print_mbr_range_operator() prints the spatial relation a GEOM range carries (MBRWITHIN, MBRCONTAINS, MBRINTERSECTS, MBRDISJOINT, MBREQUALS), inverted where needed so the indexed column reads on the left. print_range() and print_key_value() thread the new image_type argument through to Field_geom::print_key_part_value().
This commit is contained in:
committed by
Sergei Petrunia
parent
67a5f3034f
commit
ef384ed95d
@@ -26,11 +26,13 @@ select *from json_table(
|
||||
--echo # === Range accesses
|
||||
set @list_ranges=
|
||||
(select JSON_DETAILED(JSON_EXTRACT(@opt_context, '$**.multi_range_read_info_const_calls')));
|
||||
# num_rows is bigint unsigned because MBRDISJOINT records HA_POS_ERROR
|
||||
# (18446744073709551615) there.
|
||||
select *from json_table(
|
||||
@list_ranges,
|
||||
'$[*][*]' columns(index_name text path '$.index_name',
|
||||
ranges json path '$.ranges',
|
||||
num_rows int path '$.num_rows',
|
||||
num_rows bigint unsigned path '$.num_rows',
|
||||
max_index_blocks int path '$.max_index_blocks',
|
||||
max_row_blocks int path '$.max_row_blocks'
|
||||
)) as jt;
|
||||
|
||||
121
mysql-test/include/opt_gis_range_print.inc
Normal file
121
mysql-test/include/opt_gis_range_print.inc
Normal file
@@ -0,0 +1,121 @@
|
||||
#
|
||||
# SUMMARY
|
||||
# MDEV-40553: printing of GIS ranges, shared between opt_trace.test
|
||||
# (dumps range_scan_alternatives from the optimizer trace) and
|
||||
# opt_context_store_stats.test (dumps the recorded optimizer context).
|
||||
# Both files run the exact same queries over the exact same fixture;
|
||||
# only the way each range is dumped differs, see
|
||||
# include/opt_gis_range_print_dump.inc.
|
||||
#
|
||||
# PARAMETERS
|
||||
# $opt_gis_range_print_use_trace See include/opt_gis_range_print_dump.inc
|
||||
#
|
||||
# EXAMPLE
|
||||
# opt_trace.test, opt_context_store_stats.test
|
||||
#
|
||||
create table t1 (
|
||||
id int,
|
||||
g1 geometry not null,
|
||||
g2 geometry,
|
||||
key k_prefix (g1(5)),
|
||||
key k_full (g1(200)),
|
||||
key k_null (g2(200))
|
||||
) engine=myisam;
|
||||
|
||||
insert into t1 select seq, point(seq, seq), point(seq, seq) from seq_1_to_5;
|
||||
insert into t1 values (6, point(6,6), NULL);
|
||||
analyze table t1;
|
||||
|
||||
--echo # The k_full prefix is long enough to hold every value this test
|
||||
--echo # inserts or compares against (points and short linestrings),
|
||||
--echo # so the index holds the whole value here. It is printed as a binary
|
||||
--echo # string, like any other BLOB prefix.
|
||||
explain
|
||||
select id from t1 force index (k_full) where g1=point(2,2);
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # multiple ranges
|
||||
explain
|
||||
select id from t1 force index (k_full)
|
||||
where g1=point(3,3) or g1=linestring(point(1,1), point(2,2));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # NULL values and multiple ranges
|
||||
explain
|
||||
select id from t1 force index (k_null)
|
||||
where g2=point(3,3) or g2 is null or g2=linestring(point(1,1), point(2,2));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # The index holds only a prefix of the value.
|
||||
--echo # It is printed as a binary string, like any other BLOB prefix.
|
||||
explain
|
||||
select id from t1 force index (k_prefix) where g1=point(2,2);
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # A SPATIAL index stores the MBR of the value. It is printed as the
|
||||
--echo # WKT of the MBR together with the spatial relation the range uses.
|
||||
--echo #
|
||||
|
||||
create table t2 (
|
||||
id int,
|
||||
g geometry not null,
|
||||
spatial key (g)
|
||||
) engine=myisam;
|
||||
|
||||
insert into t2 select seq, point(seq, seq) from seq_1_to_20;
|
||||
insert into t2 values
|
||||
(21, geomfromtext('LINESTRING(0 0, 5 8)')),
|
||||
(22, geomfromtext('POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))'));
|
||||
analyze table t2;
|
||||
|
||||
--echo # mbrcontains(value, column)
|
||||
explain
|
||||
select id from t2 where mbrcontains(geomfromtext('POLYGON((0 0,0 3,3 3,3 0,0 0))'), g);
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # mbrwithin(column, value)
|
||||
explain
|
||||
select id from t2 where mbrwithin(g, geomfromtext('LINESTRING(1 1, 9 12)'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # The other spatial relations
|
||||
explain
|
||||
select id from t2 where mbrcontains(g, geomfromtext('POINT(2 2)'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
explain
|
||||
select id from t2 where mbrequals(g, geomfromtext('POINT(7 7)'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
explain
|
||||
select id from t2 where mbrintersects(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # touches()/crosses()/overlaps() (and their MBRxxx aliases, where one
|
||||
--echo # exists) all use the mbrintersects relation
|
||||
explain
|
||||
select id from t2 where mbrtouches(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
explain
|
||||
select id from t2 where crosses(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
explain
|
||||
select id from t2 where mbroverlaps(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
--echo # MBRDISJOINT: the handler's records_in_range() cannot estimate this
|
||||
--echo # relation and returns HA_POS_ERROR, so the range optimizer discards
|
||||
--echo # this index as a candidate before it ever traces its ranges -- the
|
||||
--echo # trace shows the index with no further detail. The range text is
|
||||
--echo # still produced (and recorded) when the optimizer separately asks
|
||||
--echo # the handler for a cost estimate, see multi_range_read_info_const().
|
||||
explain
|
||||
select id from t2 where mbrdisjoint(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
--source include/opt_gis_range_print_dump.inc
|
||||
|
||||
drop table t2;
|
||||
22
mysql-test/include/opt_gis_range_print_dump.inc
Normal file
22
mysql-test/include/opt_gis_range_print_dump.inc
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# SUMMARY
|
||||
# Print the range(s) built for the query just run, either from the
|
||||
# optimizer trace or from the recorded optimizer context, so that the
|
||||
# two range-printing paths can share the same probe queries.
|
||||
#
|
||||
# PARAMETERS
|
||||
# $opt_gis_range_print_use_trace If set, dump range_scan_alternatives
|
||||
# from the optimizer trace. Otherwise,
|
||||
# dump the recorded optimizer context.
|
||||
#
|
||||
# EXAMPLE
|
||||
# opt_trace.test, opt_context_store_stats.test
|
||||
#
|
||||
if ($opt_gis_range_print_use_trace)
|
||||
{
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
}
|
||||
if (!$opt_gis_range_print_use_trace)
|
||||
{
|
||||
--source include/opt_context_list_tables_and_ranges.inc
|
||||
}
|
||||
@@ -705,4 +705,97 @@ id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range a_idx a_idx 13 NULL 4 Using where; Using index
|
||||
set optimizer_replay_context='';
|
||||
drop table t1;
|
||||
#
|
||||
# MDEV-40553: unprintable gis ranges in trace and context
|
||||
#
|
||||
CREATE TABLE t1 (
|
||||
id INT,
|
||||
g GEOMETRY NOT NULL,
|
||||
KEY k_prefix (g(6))
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 SELECT seq, POINT(seq, seq) FROM seq_1_to_5;
|
||||
INSERT INTO t1 VALUES (6, LINESTRING(POINT(1,1), POINT(2,2)));
|
||||
analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
db1.t1 analyze status Engine-independent statistics collected
|
||||
db1.t1 analyze status OK
|
||||
set optimizer_record_context=1;
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_prefix k_prefix 8 NULL 6 Using where
|
||||
select context into dumpfile "../../tmp/dump1.sql"
|
||||
from information_schema.optimizer_context;
|
||||
set optimizer_record_context=0;
|
||||
drop table t1;
|
||||
set optimizer_replay_context='opt_context';
|
||||
# The ranges must match the recorded ones, so no warning is produced
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_prefix k_prefix 8 NULL 6 Using where
|
||||
set optimizer_replay_context='';
|
||||
drop table t1;
|
||||
# The same, replayed from the context read straight out of
|
||||
# INFORMATION_SCHEMA.OPTIMIZER_CONTEXT rather than from the script.
|
||||
# The literal in the script must read back as the JSON itself, so
|
||||
# both ways of getting at it produce the same ranges.
|
||||
CREATE TABLE t1 (
|
||||
id INT,
|
||||
g GEOMETRY NOT NULL,
|
||||
KEY k_prefix (g(6))
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 SELECT seq, POINT(seq, seq) FROM seq_1_to_5;
|
||||
INSERT INTO t1 VALUES (6, LINESTRING(POINT(1,1), POINT(2,2)));
|
||||
analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
db1.t1 analyze status Engine-independent statistics collected
|
||||
db1.t1 analyze status OK
|
||||
set optimizer_record_context=1;
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_prefix k_prefix 8 NULL 6 Using where
|
||||
SET @opt_context=(value from information_schema.optimizer_context)
|
||||
set optimizer_record_context=0;
|
||||
set optimizer_replay_context='opt_context';
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_prefix k_prefix 8 NULL 6 Using where
|
||||
set optimizer_replay_context='';
|
||||
drop table t1;
|
||||
#
|
||||
# The same, but recording and replaying a range over a SPATIAL index,
|
||||
# whose key stores an MBR (image_type itMBR) rather than the column
|
||||
# value or its prefix.
|
||||
#
|
||||
CREATE TABLE t1 (
|
||||
id INT,
|
||||
g GEOMETRY NOT NULL,
|
||||
SPATIAL KEY g_idx (g)
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 SELECT seq, POINT(seq, seq) FROM seq_1_to_20;
|
||||
analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
db1.t1 analyze status Engine-independent statistics collected
|
||||
db1.t1 analyze status OK
|
||||
set optimizer_record_context=1;
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(g_idx)
|
||||
WHERE MBRWITHIN(g, GEOMFROMTEXT('POLYGON((0 0,0 5,5 5,5 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range g_idx g_idx 34 NULL 5 Using where
|
||||
select context into dumpfile "../../tmp/dump1.sql"
|
||||
from information_schema.optimizer_context;
|
||||
set optimizer_record_context=0;
|
||||
drop table t1;
|
||||
set optimizer_replay_context='opt_context';
|
||||
# The ranges must match the recorded ones, so no warning is produced
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(g_idx)
|
||||
WHERE MBRWITHIN(g, GEOMFROMTEXT('POLYGON((0 0,0 5,5 5,5 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range g_idx g_idx 34 NULL 5 Using where
|
||||
set optimizer_replay_context='';
|
||||
drop table t1;
|
||||
# End of 13.2 tests
|
||||
drop database db1;
|
||||
|
||||
@@ -511,4 +511,102 @@ set optimizer_replay_context='opt_context';
|
||||
explain select * from t1 where a=0x2700 or a=0x2701 or a=0x2702 or a=0x2703;
|
||||
set optimizer_replay_context='';
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40553: unprintable gis ranges in trace and context
|
||||
--echo #
|
||||
CREATE TABLE t1 (
|
||||
id INT,
|
||||
g GEOMETRY NOT NULL,
|
||||
KEY k_prefix (g(6))
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 SELECT seq, POINT(seq, seq) FROM seq_1_to_5;
|
||||
INSERT INTO t1 VALUES (6, LINESTRING(POINT(1,1), POINT(2,2)));
|
||||
analyze table t1;
|
||||
|
||||
set optimizer_record_context=1;
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
select context into dumpfile "../../tmp/dump1.sql"
|
||||
from information_schema.optimizer_context;
|
||||
set optimizer_record_context=0;
|
||||
drop table t1;
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--source "$MYSQLTEST_VARDIR/tmp/dump1.sql"
|
||||
--enable_query_log
|
||||
--enable_result_log
|
||||
|
||||
set optimizer_replay_context='opt_context';
|
||||
|
||||
--echo # The ranges must match the recorded ones, so no warning is produced
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
|
||||
set optimizer_replay_context='';
|
||||
--remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql"
|
||||
drop table t1;
|
||||
|
||||
--echo # The same, replayed from the context read straight out of
|
||||
--echo # INFORMATION_SCHEMA.OPTIMIZER_CONTEXT rather than from the script.
|
||||
--echo # The literal in the script must read back as the JSON itself, so
|
||||
--echo # both ways of getting at it produce the same ranges.
|
||||
CREATE TABLE t1 (
|
||||
id INT,
|
||||
g GEOMETRY NOT NULL,
|
||||
KEY k_prefix (g(6))
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 SELECT seq, POINT(seq, seq) FROM seq_1_to_5;
|
||||
INSERT INTO t1 VALUES (6, LINESTRING(POINT(1,1), POINT(2,2)));
|
||||
analyze table t1;
|
||||
|
||||
set optimizer_record_context=1;
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
--source include/opt_context_save_in_var.inc
|
||||
set optimizer_record_context=0;
|
||||
set optimizer_replay_context='opt_context';
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(k_prefix)
|
||||
WHERE g < POINT(1,1) OR g > LINESTRING(POINT(9,9), POINT(8,8));
|
||||
set optimizer_replay_context='';
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # The same, but recording and replaying a range over a SPATIAL index,
|
||||
--echo # whose key stores an MBR (image_type itMBR) rather than the column
|
||||
--echo # value or its prefix.
|
||||
--echo #
|
||||
CREATE TABLE t1 (
|
||||
id INT,
|
||||
g GEOMETRY NOT NULL,
|
||||
SPATIAL KEY g_idx (g)
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 SELECT seq, POINT(seq, seq) FROM seq_1_to_20;
|
||||
analyze table t1;
|
||||
|
||||
set optimizer_record_context=1;
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(g_idx)
|
||||
WHERE MBRWITHIN(g, GEOMFROMTEXT('POLYGON((0 0,0 5,5 5,5 0,0 0))'));
|
||||
select context into dumpfile "../../tmp/dump1.sql"
|
||||
from information_schema.optimizer_context;
|
||||
set optimizer_record_context=0;
|
||||
drop table t1;
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--source "$MYSQLTEST_VARDIR/tmp/dump1.sql"
|
||||
--enable_query_log
|
||||
--enable_result_log
|
||||
|
||||
set optimizer_replay_context='opt_context';
|
||||
|
||||
--echo # The ranges must match the recorded ones, so no warning is produced
|
||||
EXPLAIN SELECT id FROM t1 FORCE INDEX(g_idx)
|
||||
WHERE MBRWITHIN(g, GEOMFROMTEXT('POLYGON((0 0,0 5,5 5,5 0,0 0))'));
|
||||
|
||||
set optimizer_replay_context='';
|
||||
--remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql"
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 13.2 tests
|
||||
|
||||
drop database db1;
|
||||
|
||||
@@ -647,3 +647,235 @@ a ["(10) <= (a) <= (10)"] 49 1 11
|
||||
# == End of optimizer context
|
||||
drop table t1;
|
||||
# End of 13.1 tests
|
||||
#
|
||||
# MDEV-40553: unprintable gis ranges in trace and context
|
||||
#
|
||||
create table t1 (
|
||||
id int,
|
||||
g1 geometry not null,
|
||||
g2 geometry,
|
||||
key k_prefix (g1(5)),
|
||||
key k_full (g1(200)),
|
||||
key k_null (g2(200))
|
||||
) engine=myisam;
|
||||
insert into t1 select seq, point(seq, seq), point(seq, seq) from seq_1_to_5;
|
||||
insert into t1 values (6, point(6,6), NULL);
|
||||
analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status OK
|
||||
# The k_full prefix is long enough to hold every value this test
|
||||
# inserts or compares against (points and short linestrings),
|
||||
# so the index holds the whole value here. It is printed as a binary
|
||||
# string, like any other BLOB prefix.
|
||||
explain
|
||||
select id from t1 force index (k_full) where g1=point(2,2);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ref k_full k_full 202 const 1 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t1 6 NULL NULL
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
k_full ["(\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@) <= (g1) <= (\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@)"] 1 1 1
|
||||
# == End of optimizer context
|
||||
# multiple ranges
|
||||
explain
|
||||
select id from t1 force index (k_full)
|
||||
where g1=point(3,3) or g1=linestring(point(1,1), point(2,2));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_full k_full 202 NULL 2 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t1 6 NULL NULL
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
k_full [
|
||||
"(\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@) <= (g1) <= (\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@)",
|
||||
"(\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x02\\\\x00\\\\x00\\\\x00\\\\x02\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@) <= (g1) <= (\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x02\\\\x00\\\\x00\\\\x00\\\\x02\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@)"
|
||||
] 2 2 1
|
||||
# == End of optimizer context
|
||||
# NULL values and multiple ranges
|
||||
explain
|
||||
select id from t1 force index (k_null)
|
||||
where g2=point(3,3) or g2 is null or g2=linestring(point(1,1), point(2,2));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_null k_null 203 NULL 3 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t1 6 NULL NULL
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
k_null [
|
||||
"(NULL) <= (g2) <= (NULL)",
|
||||
"(\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@) <= (g2) <= (\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08@)",
|
||||
"(\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x02\\\\x00\\\\x00\\\\x00\\\\x02\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@) <= (g2) <= (\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x02\\\\x00\\\\x00\\\\x00\\\\x02\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xF0?\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00@)"
|
||||
] 3 3 1
|
||||
# == End of optimizer context
|
||||
# The index holds only a prefix of the value.
|
||||
# It is printed as a binary string, like any other BLOB prefix.
|
||||
explain
|
||||
select id from t1 force index (k_prefix) where g1=point(2,2);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ref k_prefix k_prefix 7 const 6 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t1 6 NULL NULL
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
k_prefix ["(\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01) <= (g1) <= (\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01)"] 6 1 1
|
||||
# == End of optimizer context
|
||||
drop table t1;
|
||||
#
|
||||
# A SPATIAL index stores the MBR of the value. It is printed as the
|
||||
# WKT of the MBR together with the spatial relation the range uses.
|
||||
#
|
||||
create table t2 (
|
||||
id int,
|
||||
g geometry not null,
|
||||
spatial key (g)
|
||||
) engine=myisam;
|
||||
insert into t2 select seq, point(seq, seq) from seq_1_to_20;
|
||||
insert into t2 values
|
||||
(21, geomfromtext('LINESTRING(0 0, 5 8)')),
|
||||
(22, geomfromtext('POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))'));
|
||||
analyze table t2;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t2 analyze status OK
|
||||
# mbrcontains(value, column)
|
||||
explain
|
||||
select id from t2 where mbrcontains(geomfromtext('POLYGON((0 0,0 3,3 3,3 0,0 0))'), g);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRWITHIN (POLYGON((0 0,3 0,3 3,0 3,0 0)))"] 4 1 1
|
||||
# == End of optimizer context
|
||||
# mbrwithin(column, value)
|
||||
explain
|
||||
select id from t2 where mbrwithin(g, geomfromtext('LINESTRING(1 1, 9 12)'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 9 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRWITHIN (POLYGON((1 1,9 1,9 12,1 12,1 1)))"] 9 1 1
|
||||
# == End of optimizer context
|
||||
# The other spatial relations
|
||||
explain
|
||||
select id from t2 where mbrcontains(g, geomfromtext('POINT(2 2)'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 3 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRCONTAINS (POLYGON((2 2,2 2,2 2,2 2,2 2)))"] 3 1 1
|
||||
# == End of optimizer context
|
||||
explain
|
||||
select id from t2 where mbrequals(g, geomfromtext('POINT(7 7)'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 1 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBREQUALS (POLYGON((7 7,7 7,7 7,7 7,7 7)))"] 1 1 1
|
||||
# == End of optimizer context
|
||||
explain
|
||||
select id from t2 where mbrintersects(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"] 4 1 1
|
||||
# == End of optimizer context
|
||||
# touches()/crosses()/overlaps() (and their MBRxxx aliases, where one
|
||||
# exists) all use the mbrintersects relation
|
||||
explain
|
||||
select id from t2 where mbrtouches(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"] 4 1 1
|
||||
# == End of optimizer context
|
||||
explain
|
||||
select id from t2 where crosses(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"] 4 1 1
|
||||
# == End of optimizer context
|
||||
explain
|
||||
select id from t2 where mbroverlaps(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"] 4 1 1
|
||||
# == End of optimizer context
|
||||
# MBRDISJOINT: the handler's records_in_range() cannot estimate this
|
||||
# relation and returns HA_POS_ERROR, so the range optimizer discards
|
||||
# this index as a candidate before it ever traces its ranges -- the
|
||||
# trace shows the index with no further detail. The range text is
|
||||
# still produced (and recorded) when the optimizer separately asks
|
||||
# the handler for a cost estimate, see multi_range_read_info_const().
|
||||
explain
|
||||
select id from t2 where mbrdisjoint(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 ALL g NULL NULL NULL 22 Using where
|
||||
# == Optimizer Context
|
||||
# === Tables
|
||||
# Tables in the context
|
||||
table_name file_stat_records index_name rec_per_key
|
||||
test.t2 22 g [22]
|
||||
# === Range accesses
|
||||
index_name ranges num_rows max_index_blocks max_row_blocks
|
||||
g ["(g) MBRDISJOINT (POLYGON((0 0,2 0,2 2,0 2,0 0)))"] 18446744073709551615 0 0
|
||||
# == End of optimizer context
|
||||
drop table t2;
|
||||
# End of 13.2 tests
|
||||
|
||||
@@ -471,3 +471,10 @@ select * from t1 partition (p1) where a=10;
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 13.1 tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40553: unprintable gis ranges in trace and context
|
||||
--echo #
|
||||
--source include/opt_gis_range_print.inc
|
||||
|
||||
--echo # End of 13.2 tests
|
||||
|
||||
@@ -13904,6 +13904,337 @@ select json_valid(@trace);
|
||||
json_valid(@trace)
|
||||
1
|
||||
drop table t1;
|
||||
# End of 13.1 tests
|
||||
#
|
||||
# MDEV-40553: unprintable gis ranges in trace and context
|
||||
#
|
||||
create table t1 (
|
||||
id int,
|
||||
g1 geometry not null,
|
||||
g2 geometry,
|
||||
key k_prefix (g1(5)),
|
||||
key k_full (g1(200)),
|
||||
key k_null (g2(200))
|
||||
) engine=myisam;
|
||||
insert into t1 select seq, point(seq, seq), point(seq, seq) from seq_1_to_5;
|
||||
insert into t1 values (6, point(6,6), NULL);
|
||||
analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
# The k_full prefix is long enough to hold every value this test
|
||||
# inserts or compares against (points and short linestrings),
|
||||
# so the index holds the whole value here. It is printed as a binary
|
||||
# string, like any other BLOB prefix.
|
||||
explain
|
||||
select id from t1 force index (k_full) where g1=point(2,2);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ref k_full k_full 202 const 1 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "k_full",
|
||||
"ranges":
|
||||
["(\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00@) <= (g1) <= (\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00@)"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 1,
|
||||
"cost": 0.002574553,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# multiple ranges
|
||||
explain
|
||||
select id from t1 force index (k_full)
|
||||
where g1=point(3,3) or g1=linestring(point(1,1), point(2,2));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_full k_full 202 NULL 2 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "k_full",
|
||||
"ranges":
|
||||
[
|
||||
"(\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x08@) <= (g1) <= (\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x08@)",
|
||||
"(\\x00\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00@) <= (g1) <= (\\x00\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00@)"
|
||||
],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 2,
|
||||
"cost": 0.004394164,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# NULL values and multiple ranges
|
||||
explain
|
||||
select id from t1 force index (k_null)
|
||||
where g2=point(3,3) or g2 is null or g2=linestring(point(1,1), point(2,2));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range k_null k_null 203 NULL 3 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "k_null",
|
||||
"ranges":
|
||||
[
|
||||
"(NULL) <= (g2) <= (NULL)",
|
||||
"(\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x08@) <= (g2) <= (\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x08@)",
|
||||
"(\\x00\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00@) <= (g2) <= (\\x00\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00@)"
|
||||
],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 3,
|
||||
"cost": 0.006213775,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# The index holds only a prefix of the value.
|
||||
# It is printed as a binary string, like any other BLOB prefix.
|
||||
explain
|
||||
select id from t1 force index (k_prefix) where g1=point(2,2);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ref k_prefix k_prefix 7 const 6 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "k_prefix",
|
||||
"ranges":
|
||||
["(\\x00\\x00\\x00\\x00\\x01) <= (g1) <= (\\x00\\x00\\x00\\x00\\x01)"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 6,
|
||||
"cost": 0.008743898,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
drop table t1;
|
||||
#
|
||||
# A SPATIAL index stores the MBR of the value. It is printed as the
|
||||
# WKT of the MBR together with the spatial relation the range uses.
|
||||
#
|
||||
create table t2 (
|
||||
id int,
|
||||
g geometry not null,
|
||||
spatial key (g)
|
||||
) engine=myisam;
|
||||
insert into t2 select seq, point(seq, seq) from seq_1_to_20;
|
||||
insert into t2 values
|
||||
(21, geomfromtext('LINESTRING(0 0, 5 8)')),
|
||||
(22, geomfromtext('POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))'));
|
||||
analyze table t2;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t2 analyze status Engine-independent statistics collected
|
||||
test.t2 analyze status OK
|
||||
# mbrcontains(value, column)
|
||||
explain
|
||||
select id from t2 where mbrcontains(geomfromtext('POLYGON((0 0,0 3,3 3,3 0,0 0))'), g);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRWITHIN (POLYGON((0 0,3 0,3 3,0 3,0 0)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 4,
|
||||
"cost": 0.00627616,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# mbrwithin(column, value)
|
||||
explain
|
||||
select id from t2 where mbrwithin(g, geomfromtext('LINESTRING(1 1, 9 12)'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 9 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRWITHIN (POLYGON((1 1,9 1,9 12,1 12,1 1)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 9,
|
||||
"cost": 0.012445505,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# The other spatial relations
|
||||
explain
|
||||
select id from t2 where mbrcontains(g, geomfromtext('POINT(2 2)'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 3 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRCONTAINS (POLYGON((2 2,2 2,2 2,2 2,2 2)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 3,
|
||||
"cost": 0.005042291,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
explain
|
||||
select id from t2 where mbrequals(g, geomfromtext('POINT(7 7)'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 1 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBREQUALS (POLYGON((7 7,7 7,7 7,7 7,7 7)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 1,
|
||||
"cost": 0.002574553,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
explain
|
||||
select id from t2 where mbrintersects(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 4,
|
||||
"cost": 0.00627616,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# touches()/crosses()/overlaps() (and their MBRxxx aliases, where one
|
||||
# exists) all use the mbrintersects relation
|
||||
explain
|
||||
select id from t2 where mbrtouches(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 4,
|
||||
"cost": 0.00627616,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
explain
|
||||
select id from t2 where crosses(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 4,
|
||||
"cost": 0.00627616,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
explain
|
||||
select id from t2 where mbroverlaps(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 range g g 34 NULL 4 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g",
|
||||
"ranges":
|
||||
["(g) MBRINTERSECTS (POLYGON((0 0,2 0,2 2,0 2,0 0)))"],
|
||||
"rowid_ordered": false,
|
||||
"using_mrr": false,
|
||||
"index_only": false,
|
||||
"rows": 4,
|
||||
"cost": 0.00627616,
|
||||
"chosen": true
|
||||
}
|
||||
]
|
||||
]
|
||||
# MBRDISJOINT: the handler's records_in_range() cannot estimate this
|
||||
# relation and returns HA_POS_ERROR, so the range optimizer discards
|
||||
# this index as a candidate before it ever traces its ranges -- the
|
||||
# trace shows the index with no further detail. The range text is
|
||||
# still produced (and recorded) when the optimizer separately asks
|
||||
# the handler for a cost estimate, see multi_range_read_info_const().
|
||||
explain
|
||||
select id from t2 where mbrdisjoint(g, geomfromtext('POLYGON((0 0,0 2,2 2,2 0,0 0))'));
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 ALL g NULL NULL NULL 22 Using where
|
||||
select json_detailed(json_extract(trace, '$**.range_scan_alternatives')) as rsa from information_schema.optimizer_trace;
|
||||
rsa
|
||||
[
|
||||
[
|
||||
{
|
||||
"index": "g"
|
||||
}
|
||||
]
|
||||
]
|
||||
drop table t2;
|
||||
# End of 13.2 tests
|
||||
set @@optimizer_switch= @save_optimizer_switch;
|
||||
set @@use_stat_tables= @save_use_stat_tables;
|
||||
set @@histogram_size= @save_histogram_size;
|
||||
|
||||
@@ -1407,6 +1407,17 @@ select json_valid(@trace);
|
||||
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 13.1 tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40553: unprintable gis ranges in trace and context
|
||||
--echo #
|
||||
--let $opt_gis_range_print_use_trace= 1
|
||||
--source include/opt_gis_range_print.inc
|
||||
--let $opt_gis_range_print_use_trace=
|
||||
|
||||
--echo # End of 13.2 tests
|
||||
|
||||
set @@optimizer_switch= @save_optimizer_switch;
|
||||
set @@use_stat_tables= @save_use_stat_tables;
|
||||
set @@histogram_size= @save_histogram_size;
|
||||
|
||||
@@ -11895,11 +11895,16 @@ void Field_blob::print_key_value(String *out, uint32 length)
|
||||
key value of the key
|
||||
length Length of field in bytes,
|
||||
excluding NULL flag and length bytes
|
||||
image_type Form in which the value is stored in the key,
|
||||
see Field::image_type(). Ignored by this base
|
||||
implementation; overridden by Field_geom to
|
||||
interpret itMBR key images.
|
||||
*/
|
||||
|
||||
|
||||
void
|
||||
Field::print_key_part_value(String *out, const uchar* key, uint32 length)
|
||||
Field::print_key_part_value(String *out, const uchar* key, uint32 length,
|
||||
imagetype image_type)
|
||||
{
|
||||
StringBuffer<128> tmp(system_charset_info);
|
||||
uint null_byte= 0;
|
||||
|
||||
14
sql/field.h
14
sql/field.h
@@ -860,6 +860,11 @@ public:
|
||||
TMYSQL_COMPRESSED= 24, // Compatibility with TMySQL
|
||||
};
|
||||
enum imagetype { itRAW, itMBR};
|
||||
/*
|
||||
Form in which an index stores its column values: itMBR for a SPATIAL
|
||||
(RTREE) index, which stores the column's MBR instead of its value;
|
||||
itRAW for every other index algorithm.
|
||||
*/
|
||||
static enum imagetype image_type(enum ha_key_alg alg)
|
||||
{ return alg == HA_KEY_ALG_RTREE ? itMBR : itRAW; }
|
||||
|
||||
@@ -1772,7 +1777,14 @@ public:
|
||||
bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
|
||||
int cuted_increment, ulong current_row=0) const;
|
||||
virtual void print_key_value(String *out, uint32 length);
|
||||
void print_key_part_value(String *out, const uchar *key, uint32 length);
|
||||
/*
|
||||
Field_geom overrides this rather than print_key_value(), because its
|
||||
itMBR key image is four raw doubles, not the field's normal on-disk
|
||||
representation, so it cannot be loaded via set_key_image() and handed
|
||||
to print_key_value() the way every other Field subclass does.
|
||||
*/
|
||||
virtual void print_key_part_value(String *out, const uchar *key, uint32 length,
|
||||
imagetype image_type);
|
||||
void print_key_value_binary(String *out, const uchar* key, uint32 length);
|
||||
void raise_note_cannot_use_key_part(THD *thd, uint keynr, uint part,
|
||||
const LEX_CSTRING &op,
|
||||
|
||||
@@ -1122,6 +1122,26 @@ const uchar *Optimizer_context_recorder::get_tbl_ctx_key(const void *entry_,
|
||||
return reinterpret_cast<const uchar *>(entry->name);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief
|
||||
Print the min/max keys of a records_in_range() call the way they are
|
||||
recorded in / replayed from the optimizer context.
|
||||
*/
|
||||
static void print_records_in_range_keys(const TABLE *tbl,
|
||||
const KEY_PART_INFO *key_part,
|
||||
uint keynr, const key_range *min_range,
|
||||
const key_range *max_range,
|
||||
String *min_key, String *max_key)
|
||||
{
|
||||
DBUG_ASSERT(min_range && max_range);
|
||||
Field::imagetype image_type=
|
||||
Field::image_type(tbl->key_info[keynr].algorithm);
|
||||
print_key_value(min_key, key_part, min_range->key, min_range->length,
|
||||
image_type);
|
||||
print_key_value(max_key, key_part, max_range->key, max_range->length,
|
||||
image_type);
|
||||
}
|
||||
|
||||
void Optimizer_context_recorder::record_records_in_range(
|
||||
const TABLE *tbl, const KEY_PART_INFO *key_part,
|
||||
uint keynr, const key_range *min_range, const key_range *max_range,
|
||||
@@ -1136,8 +1156,8 @@ void Optimizer_context_recorder::record_records_in_range(
|
||||
rec_in_range_ctx->keynr= keynr;
|
||||
String min_key;
|
||||
String max_key;
|
||||
print_key_value(&min_key, key_part, min_range->key, min_range->length);
|
||||
print_key_value(&max_key, key_part, max_range->key, max_range->length);
|
||||
print_records_in_range_keys(tbl, key_part, keynr, min_range, max_range,
|
||||
&min_key, &max_key);
|
||||
|
||||
if (!(rec_in_range_ctx->min_key= strdup_root(mem_root, &min_key)))
|
||||
return; // OOM
|
||||
@@ -1754,6 +1774,11 @@ bool Optimizer_context_replay::infuse_multi_range_read_info_const(
|
||||
const char *idx_name= keyinfo->name.str;
|
||||
const KEY_PART_INFO *key_part= keyinfo->key_part;
|
||||
uint n_key_parts= table->actual_n_key_parts(keyinfo);
|
||||
/*
|
||||
SEL_ARG_RANGE_SEQ (whose key_parts already holds this value) is only
|
||||
forward-declared here, so recompute it instead of reaching into *seq.
|
||||
*/
|
||||
Field::imagetype image_type= Field::image_type(keyinfo->algorithm);
|
||||
KEY_MULTI_RANGE multi_range;
|
||||
range_seq_t seq_it;
|
||||
List<Multi_range_read_const_call_record> mrr_const_calls;
|
||||
@@ -1766,7 +1791,7 @@ bool Optimizer_context_replay::infuse_multi_range_read_info_const(
|
||||
while (!seq_if->next(seq_it, &multi_range))
|
||||
{
|
||||
StringBuffer<128> range_info(system_charset_info);
|
||||
print_range(&range_info, key_part, &multi_range, n_key_parts);
|
||||
print_range(&range_info, key_part, &multi_range, n_key_parts, image_type);
|
||||
char *r1= range_info.c_ptr_safe();
|
||||
text_ranges.push_back(strdup_root(thd->mem_root, &range_info));
|
||||
act_ranges.append(r1, strlen(r1));
|
||||
@@ -1971,8 +1996,8 @@ bool Optimizer_context_replay::infuse_records_in_range(
|
||||
String min_key;
|
||||
String max_key;
|
||||
String tbl_name;
|
||||
print_key_value(&min_key, key_part, min_range->key, min_range->length);
|
||||
print_key_value(&max_key, key_part, max_range->key, max_range->length);
|
||||
print_records_in_range_keys(tbl, key_part, keynr, min_range, max_range,
|
||||
&min_key, &max_key);
|
||||
append_base_table_name(tbl, &tbl_name);
|
||||
|
||||
if (table_context_for_replay *tbl_ctx=
|
||||
|
||||
113
sql/opt_range.cc
113
sql/opt_range.cc
@@ -12391,7 +12391,7 @@ public:
|
||||
range_info.length(0);
|
||||
if (seq_if.next(seq_it, &range))
|
||||
return true; // No more ranges
|
||||
print_range(&range_info, cur_key_part, &range, n_key_parts);
|
||||
print_range(&range_info, cur_key_part, &range, n_key_parts, image_type);
|
||||
return false;
|
||||
}
|
||||
const String& get_interval_str() override { return range_info; }
|
||||
@@ -12403,6 +12403,7 @@ public:
|
||||
n_key_parts= param->table->actual_n_key_parts(keyinfo);
|
||||
seq.keyno= idx;
|
||||
seq.key_parts= param->key[idx];
|
||||
image_type= seq.key_parts->image_type;
|
||||
seq.real_keyno= param->real_keynr[idx];
|
||||
seq.param= param;
|
||||
seq.start= keypart;
|
||||
@@ -12421,6 +12422,8 @@ private:
|
||||
StringBuffer<128> range_info;
|
||||
uint n_key_parts;
|
||||
const KEY_PART_INFO *cur_key_part;
|
||||
/* Form in which this index stores the column values, see print_range() */
|
||||
Field::imagetype image_type;
|
||||
};
|
||||
|
||||
|
||||
@@ -17686,34 +17689,104 @@ static void print_max_range_operator(String *out, const ha_rkey_function flag)
|
||||
out->append(STRING_WITH_LEN(" ? "));
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Print the spatial relation used by a range over a SPATIAL index
|
||||
|
||||
@detail
|
||||
The relation is printed the way it reads with the indexed column on the
|
||||
left side, that is, the range
|
||||
(g) MBRWITHIN (POLYGON(...))
|
||||
reads rows whose MBR is within the MBR of the printed value.
|
||||
Note that touches(), crosses() and overlaps() (and their MBRxxx
|
||||
aliases, where one exists, e.g. MBRTOUCHES, MBROVERLAPS -- there is
|
||||
no MBRCROSSES) all use the same HA_READ_MBR_INTERSECT relation, so
|
||||
they are all printed as MBRINTERSECTS.
|
||||
*/
|
||||
|
||||
static void print_mbr_range_operator(String *out, const ha_rkey_function flag)
|
||||
{
|
||||
switch (flag) {
|
||||
case HA_READ_MBR_CONTAIN:
|
||||
out->append(STRING_WITH_LEN(" MBRWITHIN "));
|
||||
break;
|
||||
case HA_READ_MBR_WITHIN:
|
||||
out->append(STRING_WITH_LEN(" MBRCONTAINS "));
|
||||
break;
|
||||
case HA_READ_MBR_INTERSECT:
|
||||
out->append(STRING_WITH_LEN(" MBRINTERSECTS "));
|
||||
break;
|
||||
case HA_READ_MBR_DISJOINT:
|
||||
out->append(STRING_WITH_LEN(" MBRDISJOINT "));
|
||||
break;
|
||||
case HA_READ_MBR_EQUAL:
|
||||
out->append(STRING_WITH_LEN(" MBREQUALS "));
|
||||
break;
|
||||
default:
|
||||
/* The five values above are the only ones a GEOM range can carry. */
|
||||
DBUG_ASSERT(0);
|
||||
out->append(STRING_WITH_LEN(" ? "));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Print one range of an index
|
||||
|
||||
@param[out] out String the range is appended to
|
||||
@param[in] key_part Index components description
|
||||
@param[in] range The range to print
|
||||
@param[in] n_key_parts Number of keyparts in the index
|
||||
@param[in] image_type Form in which the index stores the column values,
|
||||
see Field::image_type()
|
||||
|
||||
@detail
|
||||
image_type is a property of the index, not of the range, so it is passed
|
||||
in rather than derived from range->range_flag. Today a GEOM_FLAG range
|
||||
and an itMBR keypart always coincide: Item_bool_func::get_mm_leaf() and
|
||||
get_mm_leaf_for_LIKE() build no range for a non-itRAW keypart, and
|
||||
Item_func_spatial_rel::get_mm_leaf() builds none for a non-itMBR one.
|
||||
Passing image_type covers the direction the GEOM_FLAG assert below does
|
||||
not: a non-GEOM range over an itMBR keypart.
|
||||
*/
|
||||
|
||||
void print_range(String *out, const KEY_PART_INFO *key_part,
|
||||
KEY_MULTI_RANGE *range, uint n_key_parts)
|
||||
KEY_MULTI_RANGE *range, uint n_key_parts,
|
||||
Field::imagetype image_type)
|
||||
{
|
||||
Check_level_instant_set check_field(current_thd, CHECK_FIELD_IGNORE);
|
||||
uint flag= range->range_flag;
|
||||
String key_name;
|
||||
key_name.set_charset(system_charset_info);
|
||||
key_part_map keypart_map= range->start_key.keypart_map |
|
||||
range->end_key.keypart_map;
|
||||
|
||||
/* GEOM ranges are only produced for SPATIAL indexes */
|
||||
DBUG_ASSERT(!(flag & GEOM_FLAG) || image_type == Field::itMBR);
|
||||
|
||||
if (flag & GEOM_FLAG)
|
||||
{
|
||||
/*
|
||||
The flags of GEOM ranges do not work the same way as for other
|
||||
range types, so printing "col < some_geom" doesn't make sense.
|
||||
Just print the column name, not operator.
|
||||
The flags of GEOM ranges do not work the same way as for other range
|
||||
types: there is no min/max bound, the range is the set of rows whose
|
||||
MBR has the given spatial relation with the MBR of the value.
|
||||
Print it as "col mbr_relation value".
|
||||
|
||||
Only start_key is filled in for a GEOM range, see
|
||||
sel_arg_range_seq_next(), so end_key must not be looked at here.
|
||||
*/
|
||||
print_keyparts_name(out, key_part, n_key_parts, keypart_map);
|
||||
out->append(STRING_WITH_LEN(" "));
|
||||
print_keyparts_name(out, key_part, n_key_parts,
|
||||
range->start_key.keypart_map);
|
||||
print_mbr_range_operator(out, range->start_key.flag);
|
||||
print_key_value(out, key_part, range->start_key.key,
|
||||
range->start_key.length);
|
||||
range->start_key.length, image_type);
|
||||
return;
|
||||
}
|
||||
|
||||
key_part_map keypart_map= range->start_key.keypart_map |
|
||||
range->end_key.keypart_map;
|
||||
|
||||
if (range->start_key.length)
|
||||
{
|
||||
print_key_value(out, key_part, range->start_key.key,
|
||||
range->start_key.length);
|
||||
range->start_key.length, image_type);
|
||||
print_min_range_operator(out, range->start_key.flag);
|
||||
}
|
||||
|
||||
@@ -17722,8 +17795,8 @@ void print_range(String *out, const KEY_PART_INFO *key_part,
|
||||
if (range->end_key.length)
|
||||
{
|
||||
print_max_range_operator(out, range->end_key.flag);
|
||||
print_key_value(out, key_part, range->end_key.key,
|
||||
range->end_key.length);
|
||||
print_key_value(out, key_part, range->end_key.key, range->end_key.length,
|
||||
image_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17747,7 +17820,9 @@ void print_range_for_non_indexed_field(String *out, Field *field,
|
||||
|
||||
if (range->start_key.length)
|
||||
{
|
||||
field->print_key_part_value(out, range->start_key.key, field->key_length());
|
||||
/* There is no index here, so the value is in its raw form */
|
||||
field->print_key_part_value(out, range->start_key.key, field->key_length(),
|
||||
Field::itRAW);
|
||||
print_min_range_operator(out, range->start_key.flag);
|
||||
}
|
||||
|
||||
@@ -17756,7 +17831,8 @@ void print_range_for_non_indexed_field(String *out, Field *field,
|
||||
if (range->end_key.length)
|
||||
{
|
||||
print_max_range_operator(out, range->end_key.flag);
|
||||
field->print_key_part_value(out, range->end_key.key, field->key_length());
|
||||
field->print_key_part_value(out, range->end_key.key, field->key_length(),
|
||||
Field::itRAW);
|
||||
}
|
||||
dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets);
|
||||
}
|
||||
@@ -17794,10 +17870,13 @@ static void trace_ranges(Json_writer_array *range_trace, PARAM *param,
|
||||
@param[in] key_part Index components description
|
||||
@param[in] key Key tuple
|
||||
@param[in] used_length length of the key tuple
|
||||
@param[in] image_type Form in which the key stores the column values,
|
||||
see Field::image_type()
|
||||
*/
|
||||
|
||||
void print_key_value(String *out, const KEY_PART_INFO *key_part,
|
||||
const uchar *key, uint used_length)
|
||||
const uchar *key, uint used_length,
|
||||
Field::imagetype image_type)
|
||||
{
|
||||
out->append(STRING_WITH_LEN("("));
|
||||
Field *field= key_part->field;
|
||||
@@ -17813,7 +17892,7 @@ void print_key_value(String *out, const KEY_PART_INFO *key_part,
|
||||
field= key_part->field;
|
||||
store_length= key_part->store_length;
|
||||
|
||||
field->print_key_part_value(out, key, key_part->length);
|
||||
field->print_key_part_value(out, key, key_part->length, image_type);
|
||||
|
||||
if (key + store_length < key_end)
|
||||
out->append(STRING_WITH_LEN(","));
|
||||
|
||||
@@ -2054,10 +2054,12 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond);
|
||||
bool eq_ranges_exceeds_limit(RANGE_SEQ_IF *seq, void *seq_init_param,
|
||||
uint limit);
|
||||
void print_range(String *out, const KEY_PART_INFO *key_part,
|
||||
KEY_MULTI_RANGE *range, uint n_key_parts);
|
||||
KEY_MULTI_RANGE *range, uint n_key_parts,
|
||||
Field::imagetype image_type);
|
||||
|
||||
void print_key_value(String *out, const KEY_PART_INFO *key_part,
|
||||
const uchar *key, uint used_length);
|
||||
const uchar *key, uint used_length,
|
||||
Field::imagetype image_type);
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond);
|
||||
|
||||
@@ -1001,6 +1001,96 @@ uint Field_geom::get_key_image(uchar *buff,uint length, const uchar *ptr_arg,
|
||||
return Field_blob::get_key_image_itRAW(ptr_arg, buff, length);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief
|
||||
Print the value of a key part over a GEOMETRY column.
|
||||
|
||||
@detail
|
||||
SPATIAL indexes do not store the column value. They store its MBR
|
||||
(Minimum Bounding Rectangle) instead, as four doubles:
|
||||
xmin, xmax, ymin, ymax
|
||||
Print the MBR as a WKT POLYGON, with the same vertex order
|
||||
ST_Envelope() uses (see Geometry::envelope()). That does not hold for
|
||||
an unparsable geometry (or a value shorter than SRID_SIZE): its key
|
||||
image is all-zero (see Geometry::get_key_image_itMBR()), which this
|
||||
function prints as the same POLYGON a real MBR at the origin would
|
||||
produce. An empty GEOMETRYCOLLECTION has a successful but default MBR
|
||||
(DBL_MAX/-DBL_MAX bounds, see MBR::MBR()), which prints as a huge
|
||||
inverted POLYGON. ST_Envelope() is unambiguous for the empty case, as
|
||||
it returns an empty GEOMETRYCOLLECTION for a geometry with no MBR.
|
||||
For the other index types the key holds the value (or its prefix),
|
||||
which is printed by Field::print_key_part_value().
|
||||
*/
|
||||
|
||||
void Field_geom::print_key_part_value(String *out, const uchar *key,
|
||||
uint32 length, imagetype image_type)
|
||||
{
|
||||
if (image_type != itMBR)
|
||||
{
|
||||
Field::print_key_part_value(out, key, length, image_type);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
The key part is expected to hold a full MBR (four doubles). A shorter
|
||||
key part should not happen for a SPATIAL index, whose key_part->length
|
||||
is always SIZEOF_STORED_DOUBLE * 4. The assert documents that; the
|
||||
fallback is kept because this runs while producing optimizer trace and
|
||||
context output, which must not read past the key (or crash) even if
|
||||
an invariant is broken elsewhere.
|
||||
*/
|
||||
DBUG_ASSERT(length >= SIZEOF_STORED_DOUBLE * 4);
|
||||
if (length < SIZEOF_STORED_DOUBLE * 4)
|
||||
{
|
||||
out->append(STRING_WITH_LEN("<invalid MBR key length>"));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
SPATIAL keys do not support NULL: the column must be NOT NULL
|
||||
(ER_INDEX_CANNOT_HAVE_NULL, raised in mysql_prepare_create_table()),
|
||||
and Item_func_spatial_rel::get_mm_leaf() builds no range otherwise.
|
||||
*/
|
||||
DBUG_ASSERT(!real_maybe_null() || !*key);
|
||||
if (real_maybe_null())
|
||||
{
|
||||
// Be graceful about a NULL key here, as this is only used for printing.
|
||||
if (*key)
|
||||
{
|
||||
out->append(NULL_clex_str);
|
||||
return;
|
||||
}
|
||||
key++; // Skip the null byte
|
||||
}
|
||||
|
||||
double xmin, xmax, ymin, ymax;
|
||||
float8get(xmin, key);
|
||||
float8get(xmax, key + SIZEOF_STORED_DOUBLE);
|
||||
float8get(ymin, key + SIZEOF_STORED_DOUBLE * 2);
|
||||
float8get(ymax, key + SIZEOF_STORED_DOUBLE * 3);
|
||||
|
||||
const double points[5][2]= {{xmin, ymin}, {xmax, ymin}, {xmax, ymax},
|
||||
{xmin, ymax}, {xmin, ymin}};
|
||||
/*
|
||||
2 * array_elements(points) doubles, plus "POLYGON((", "))", the commas and
|
||||
spaces between them (32 bytes is ample slack for those separators).
|
||||
*/
|
||||
if (out->reserve(MY_GCVT_MAX_FIELD_WIDTH * array_elements(points) * 2 + 32))
|
||||
return; // Out of memory, the allocator has already raised the error
|
||||
|
||||
out->qs_append(STRING_WITH_LEN("POLYGON(("));
|
||||
for (uint i= 0; i < array_elements(points); i++)
|
||||
{
|
||||
if (i)
|
||||
out->qs_append(',');
|
||||
out->qs_append(points[i][0]);
|
||||
out->qs_append(' ');
|
||||
out->qs_append(points[i][1]);
|
||||
}
|
||||
out->qs_append(STRING_WITH_LEN("))"));
|
||||
}
|
||||
|
||||
|
||||
Binlog_type_info Field_geom::binlog_type_info() const
|
||||
{
|
||||
DBUG_ASSERT(Field_geom::type() == binlog_type());
|
||||
|
||||
@@ -479,10 +479,8 @@ public:
|
||||
bool load_data_set_null(THD *thd) override;
|
||||
bool load_data_set_no_data(THD *thd, bool fixed_format) override;
|
||||
|
||||
void print_key_value(String *out, uint32 length) override
|
||||
{
|
||||
out->append(STRING_WITH_LEN("unprintable_geometry_value"));
|
||||
}
|
||||
void print_key_part_value(String *out, const uchar *key, uint32 length,
|
||||
imagetype image_type) override;
|
||||
Binlog_type_info binlog_type_info() const override;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user