mirror of
https://github.com/MariaDB/server.git
synced 2025-09-29 12:29:12 +02:00

Introduces NO_INDEX_MERGE and INDEX_MERGE, which control whether or not index merge strategies are used during query optimization. Here is an example query from the tests: SET optimizer_switch='index_merge_intersection=off'; EXPLAIN SELECT /*+ INDEX_MERGE(t1 f4, f2) */ COUNT(*) FROM t1 WHERE f4 = 'h' AND f2 = 2; with the hint in place, the query plan will employ the index_merge intersect strategy (abbreviated EXPLAIN output): type Extra index_merge Using intersect(f2,f4); Using where; Using index The presence of the [NO_]INDEX_MERGE hint overrides the optimizer's choice of keys during the index merge optimization. As we see in the above example, keys f2 and f4 and given and the optimizer will consider only those keys for this query. When the hint is given without any particular keys, as in INDEX_MERGE(table), then all keys are considered. In this case, the cheapest index merge among the keys should be used. When NO_INDEX_MERGE(table) is given, then index merge is disabled for that table. When the hint is given with one or more keys, then only those keys are considered. In the case of NO_INDEX_MERGE, those keys are excluded. This can lead to no merged indexes at all, because there may not be sufficient row-ordered read columns available for consideration. The index merge strategies of intersection, union, and sort union cannot themselves be directly controlled via the hints. In combination with the optimizer switches for the same, the strategy may be indirectly controlled but this is not guaranteed. When the hint directs the optimizer such that insufficient ROR scans are available, thus leading to a situation where the INDEX_MERGE hint cannot be honored, the server will emit a warning to that effect. In the hints module (opt_hints*{cc,h}), this commit adds some index merge-specific functionality to make interpreting hint state at callsites in the optimizer cleaner and more intuitive. Particularly, we add a bit field to the table hints class which indicates the keys that are marked by an [NO_]INDEX_MERGE hint, if present. A new function, index_merge_hint (and associated new helper functions) relies on this field when interpreting index merge hint state for the optimizer. If there are no index merges available prior to attemping to find a suitable union/sort union, then the optimizer will not attempt it. This change results in optimizer trace output which does not include the 'analyzing_index_merge_union' block when there are no merges. Parts of this implementation based on MySQL commit ebcb981807e3d91a64782e89d48e1a25622eafea
299 lines
9.9 KiB
Text
299 lines
9.9 KiB
Text
set @innodb_stats_persistent_save= @@innodb_stats_persistent;
|
|
set @innodb_stats_persistent_sample_pages_save=
|
|
@@innodb_stats_persistent_sample_pages;
|
|
set global innodb_stats_persistent= 1;
|
|
set global innodb_stats_persistent_sample_pages=100;
|
|
create table t1
|
|
(
|
|
pk1 int not null,
|
|
pk2 int not null,
|
|
key1 int not null,
|
|
key2 int not null,
|
|
key (key1),
|
|
key (key2),
|
|
primary key (pk1, pk2)
|
|
)engine=Innodb;
|
|
analyze table t1;
|
|
Table Op Msg_type Msg_text
|
|
test.t1 analyze status Engine-independent statistics collected
|
|
test.t1 analyze status OK
|
|
set optimizer_trace="enabled=on";
|
|
set @tmp_index_merge_ror_cpk=@@optimizer_switch;
|
|
set optimizer_switch='extended_keys=off';
|
|
explain select * from t1 where pk1 != 0 and key1 = 1;
|
|
id select_type table type possible_keys key key_len ref rows Extra
|
|
1 SIMPLE t1 ref PRIMARY,key1 key1 4 const 1 Using index condition
|
|
select * from information_schema.OPTIMIZER_TRACE;
|
|
QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
|
|
explain select * from t1 where pk1 != 0 and key1 = 1 {
|
|
"steps": [
|
|
{
|
|
"join_preparation": {
|
|
"select_id": 1,
|
|
"steps": [
|
|
{
|
|
"expanded_query": "select t1.pk1 AS pk1,t1.pk2 AS pk2,t1.key1 AS key1,t1.key2 AS key2 from t1 where t1.pk1 <> 0 and t1.key1 = 1"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"join_optimization": {
|
|
"select_id": 1,
|
|
"steps": [
|
|
{
|
|
"condition_processing": {
|
|
"condition": "WHERE",
|
|
"original_condition": "t1.pk1 <> 0 and t1.key1 = 1",
|
|
"steps": [
|
|
{
|
|
"transformation": "equality_propagation",
|
|
"resulting_condition": "t1.pk1 <> 0 and multiple equal(1, t1.key1)"
|
|
},
|
|
{
|
|
"transformation": "constant_propagation",
|
|
"resulting_condition": "t1.pk1 <> 0 and multiple equal(1, t1.key1)"
|
|
},
|
|
{
|
|
"transformation": "trivial_condition_removal",
|
|
"resulting_condition": "t1.pk1 <> 0 and multiple equal(1, t1.key1)"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"ref_optimizer_key_uses": [
|
|
{
|
|
"table": "t1",
|
|
"index": "key1",
|
|
"field": "key1",
|
|
"equals": "1",
|
|
"null_rejecting": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"rows_estimation": [
|
|
{
|
|
"table": "t1",
|
|
"range_analysis": {
|
|
"table_scan": {
|
|
"rows": 1000,
|
|
"cost": 0.1764192
|
|
},
|
|
"potential_range_indexes": [
|
|
{
|
|
"index": "PRIMARY",
|
|
"usable": true,
|
|
"key_parts": ["pk1", "pk2"]
|
|
},
|
|
{
|
|
"index": "key1",
|
|
"usable": true,
|
|
"key_parts": ["key1"]
|
|
},
|
|
{
|
|
"index": "key2",
|
|
"usable": false,
|
|
"cause": "not applicable"
|
|
}
|
|
],
|
|
"setup_range_conditions": [],
|
|
"analyzing_range_alternatives": {
|
|
"range_scan_alternatives": [
|
|
{
|
|
"index": "PRIMARY",
|
|
"ranges": ["(pk1) < (0)", "(0) < (pk1)"],
|
|
"rowid_ordered": true,
|
|
"using_mrr": false,
|
|
"index_only": false,
|
|
"rows": 1000,
|
|
"cost": 0.19598856,
|
|
"chosen": false,
|
|
"cause": "cost"
|
|
},
|
|
{
|
|
"index": "key1",
|
|
"ranges": ["(1) <= (key1) <= (1)"],
|
|
"rowid_ordered": true,
|
|
"using_mrr": false,
|
|
"index_only": false,
|
|
"rows": 1,
|
|
"cost": 0.00424968,
|
|
"chosen": true
|
|
}
|
|
],
|
|
"analyzing_roworder_intersect": {
|
|
"intersecting_indexes": [
|
|
{
|
|
"index": "key1",
|
|
"index_scan_cost": 0.001661605,
|
|
"cumulated_index_scan_cost": 0.001661605,
|
|
"disk_sweep_cost": 0.00171364,
|
|
"cumulative_total_cost": 0.003375245,
|
|
"usable": true,
|
|
"matching_rows_now": 1,
|
|
"intersect_covering_with_this_index": false,
|
|
"chosen": true
|
|
}
|
|
],
|
|
"clustered_pk": {
|
|
"index_scan_cost": 0.000002653,
|
|
"cumulated_index_scan_cost": 0.001664258,
|
|
"disk_sweep_cost": 0.00171364,
|
|
"clustered_pk_added_to_intersect": false,
|
|
"cause": "cost"
|
|
},
|
|
"chosen": false,
|
|
"cause": "too few indexes to merge"
|
|
}
|
|
},
|
|
"group_index_range": {
|
|
"chosen": false,
|
|
"cause": "no group by or distinct"
|
|
},
|
|
"chosen_range_access_summary": {
|
|
"range_access_plan": {
|
|
"type": "range_scan",
|
|
"index": "key1",
|
|
"rows": 1,
|
|
"ranges": ["(1) <= (key1) <= (1)"]
|
|
},
|
|
"rows_for_plan": 1,
|
|
"cost_for_plan": 0.00424968,
|
|
"chosen": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"table": "t1",
|
|
"rowid_filters": [
|
|
{
|
|
"key": "key1",
|
|
"build_cost": 0.001763258,
|
|
"rows": 1
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"selectivity_for_indexes": [
|
|
{
|
|
"index_name": "key1",
|
|
"selectivity_from_index": 0.001
|
|
},
|
|
{
|
|
"index_name": "PRIMARY",
|
|
"selectivity_from_index": 1
|
|
}
|
|
],
|
|
"selectivity_for_columns": [],
|
|
"cond_selectivity": 0.001
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"table_dependencies": [
|
|
{
|
|
"table": "t1",
|
|
"row_may_be_null": false,
|
|
"map_bit": 0,
|
|
"depends_on_map_bits": []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"considered_execution_plans": [
|
|
{
|
|
"plan_prefix": "",
|
|
"get_costs_for_tables": [
|
|
{
|
|
"best_access_path": {
|
|
"table": "t1",
|
|
"plan_details": {
|
|
"record_count": 1
|
|
},
|
|
"considered_access_paths": [
|
|
{
|
|
"access_type": "ref",
|
|
"index": "key1",
|
|
"used_range_estimates": true,
|
|
"rows": 1,
|
|
"cost": 0.00345856,
|
|
"chosen": true
|
|
},
|
|
{
|
|
"type": "scan",
|
|
"chosen": false,
|
|
"cause": "cost"
|
|
}
|
|
],
|
|
"chosen_access_method": {
|
|
"type": "ref",
|
|
"index": "key1",
|
|
"rows_read": 1,
|
|
"rows_out": 1,
|
|
"cost": 0.00345856,
|
|
"uses_join_buffering": false
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"plan_prefix": "",
|
|
"table": "t1",
|
|
"rows_for_plan": 1,
|
|
"cost_for_plan": 0.00345856
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"best_join_order": ["t1"],
|
|
"rows": 1,
|
|
"cost": 0.00345856
|
|
},
|
|
{
|
|
"substitute_best_equal": {
|
|
"condition": "WHERE",
|
|
"resulting_condition": "t1.key1 = 1 and t1.pk1 <> 0"
|
|
}
|
|
},
|
|
{
|
|
"attaching_conditions_to_tables": {
|
|
"attached_conditions_computation": [],
|
|
"attached_conditions_summary": [
|
|
{
|
|
"table": "t1",
|
|
"attached_condition": "t1.pk1 <> 0"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"make_join_readinfo": [
|
|
{
|
|
"table": "t1",
|
|
"index_condition": "t1.pk1 <> 0"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
} 0 0
|
|
drop table t1;
|
|
set @@optimizer_switch= @tmp_index_merge_ror_cpk;
|
|
set global innodb_stats_persistent= @innodb_stats_persistent_save;
|
|
set global innodb_stats_persistent_sample_pages=
|
|
@innodb_stats_persistent_sample_pages_save;
|
|
#
|
|
# MDEV-18962: ASAN heap-buffer-overflow in Single_line_formatting_helper::on_add_str with optimizer trace
|
|
#
|
|
CREATE TABLE t1 (a date not null, b time, key(a), key(b)) ENGINE=InnoDB;
|
|
INSERT INTO t1 VALUES ('1991-09-09','00:00:00'),('2032-08-24','02:22:24');
|
|
SET SESSION optimizer_trace = 'enabled=on';
|
|
SELECT * FROM t1 WHERE b IS NULL AND a = '2000-01-01';
|
|
a b
|
|
DROP TABLE t1;
|
|
set optimizer_trace="enabled=off";
|