Merge 10.3 into 10.4

This commit is contained in:
Marko Mäkelä 2019-05-22 08:42:31 +03:00
commit cf77951fb6
18 changed files with 299 additions and 73 deletions

View File

@ -3343,6 +3343,63 @@ C
B
DROP TABLE t1;
#
# MDEV-16214: Incorrect plan taken by the optimizer , uses INDEX instead of ref access with ORDER BY
#
create table t1(a int);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2(
id int primary key,
key1 int,key2 int,
col1 int,
key(key1), key(key2)
);
insert into t2
select
A.a + B.a*10 + C.a*100,
A.a + 10*B.a, A.a + 10*B.a,
123456
from t1 A, t1 B, t1 C;
# here type should show ref not index
explain select
(SELECT concat(id, '-', key1, '-', col1)
FROM t2
WHERE
t2.key1 = t1.a and t2.key1 IS NOT NULL
ORDER BY
t2.key2 ASC
LIMIT 1)
from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 10
2 DEPENDENT SUBQUERY t2 ref key1 key1 5 test.t1.a 10 Using index condition; Using where; Using filesort
select
(SELECT concat(id, '-', key1, '-', col1)
FROM t2
WHERE
t2.key1 = t1.a and t2.key1 IS NOT NULL
ORDER BY
t2.key2 ASC
LIMIT 1)
from t1;
(SELECT concat(id, '-', key1, '-', col1)
FROM t2
WHERE
t2.key1 = t1.a and t2.key1 IS NOT NULL
ORDER BY
t2.key2 ASC
LIMIT 1)
900-0-123456
901-1-123456
902-2-123456
903-3-123456
904-4-123456
905-5-123456
906-6-123456
907-7-123456
908-8-123456
909-9-123456
drop table t1,t2;
#
# MDEV-17761: Odd optimizer choice with ORDER BY LIMIT and condition selectivity
#
create table t1(a int);

View File

@ -2204,6 +2204,43 @@ ORDER BY id+1 DESC;
DROP TABLE t1;
--echo #
--echo # MDEV-16214: Incorrect plan taken by the optimizer , uses INDEX instead of ref access with ORDER BY
--echo #
create table t1(a int);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2(
id int primary key,
key1 int,key2 int,
col1 int,
key(key1), key(key2)
);
insert into t2
select
A.a + B.a*10 + C.a*100,
A.a + 10*B.a, A.a + 10*B.a,
123456
from t1 A, t1 B, t1 C;
let $query= select
(SELECT concat(id, '-', key1, '-', col1)
FROM t2
WHERE
t2.key1 = t1.a and t2.key1 IS NOT NULL
ORDER BY
t2.key2 ASC
LIMIT 1)
from t1;
--echo # here type should show ref not index
eval explain $query;
eval $query;
drop table t1,t2;
--echo #
--echo # MDEV-17761: Odd optimizer choice with ORDER BY LIMIT and condition selectivity
--echo #

View File

@ -520,5 +520,12 @@ row_end bigint as row end,
period for system_time (row_start, row_end)
) engine=myisam with system versioning;
ERROR HY000: `row_start` must be of type TIMESTAMP(6) for system-versioned table `t1`
create table t (
a int,
row_start datetime(6) generated always as row start,
row_end datetime(6) generated always as row end,
period for system_time(row_start, row_end)
) with system versioning;
ERROR HY000: `row_start` must be of type TIMESTAMP(6) for system-versioned table `t`
drop database test;
create database test;

View File

@ -530,6 +530,25 @@ set timestamp=1523466002.799571;
insert into t1 values (11),(12);
set timestamp=1523466004.169435;
delete from t1 where pk in (11, 12);
#
# MDEV-18136 Server crashes in Item_func_dyncol_create::prepare_arguments
#
create or replace table t1 (pk int) with system versioning
partition by system_time interval 7 second (
partition ver_p1 history,
partition ver_pn current);
alter table t1
partition by system_time interval column_get(column_create(7,7), 7 as int) second (
partition ver_p1 history,
partition ver_pn current);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`pk` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 7 SECOND
(PARTITION `ver_p1` HISTORY ENGINE = DEFAULT_ENGINE,
PARTITION `ver_pn` CURRENT ENGINE = DEFAULT_ENGINE)
# Test cleanup
drop database test;
create database test;

View File

@ -32,5 +32,19 @@ insert into t1 values (1,1);
create or replace table t2 (c int);
create or replace view v as select t1.* from t1 join t2;
replace into v (a, b) select a, b from t1;
drop table t1;
CREATE TABLE t1 (
pk INT AUTO_INCREMENT,
f INT,
row_start SYS_DATATYPE AS ROW START INVISIBLE,
row_end SYS_DATATYPE AS ROW END INVISIBLE,
PRIMARY KEY(pk),
UNIQUE(f),
PERIOD FOR SYSTEM_TIME(row_start, row_end)
) WITH SYSTEM VERSIONING;
INSERT INTO t1 () VALUES (),(),(),(),(),();
UPDATE IGNORE t1 SET f = 1;
REPLACE t1 SELECT * FROM t1;
DROP TABLE t1;
drop database test;
create database test;

View File

@ -387,5 +387,14 @@ create or replace table t1 (
period for system_time (row_start, row_end)
) engine=myisam with system versioning;
--error ER_VERS_FIELD_WRONG_TYPE
create table t (
a int,
row_start datetime(6) generated always as row start,
row_end datetime(6) generated always as row end,
period for system_time(row_start, row_end)
) with system versioning;
drop database test;
create database test;

View File

@ -466,6 +466,20 @@ insert into t1 values (11),(12);
set timestamp=1523466004.169435;
delete from t1 where pk in (11, 12);
--echo #
--echo # MDEV-18136 Server crashes in Item_func_dyncol_create::prepare_arguments
--echo #
create or replace table t1 (pk int) with system versioning
partition by system_time interval 7 second (
partition ver_p1 history,
partition ver_pn current);
alter table t1
partition by system_time interval column_get(column_create(7,7), 7 as int) second (
partition ver_p1 history,
partition ver_pn current);
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
--echo # Test cleanup
drop database test;
create database test;

View File

@ -35,6 +35,23 @@ insert into t1 values (1,1);
create or replace table t2 (c int);
create or replace view v as select t1.* from t1 join t2;
replace into v (a, b) select a, b from t1;
drop table t1;
--replace_result $sys_datatype_expl SYS_DATATYPE
eval CREATE TABLE t1 (
pk INT AUTO_INCREMENT,
f INT,
row_start $sys_datatype_expl AS ROW START INVISIBLE,
row_end $sys_datatype_expl AS ROW END INVISIBLE,
PRIMARY KEY(pk),
UNIQUE(f),
PERIOD FOR SYSTEM_TIME(row_start, row_end)
) WITH SYSTEM VERSIONING;
INSERT INTO t1 () VALUES (),(),(),(),(),();
UPDATE IGNORE t1 SET f = 1;
REPLACE t1 SELECT * FROM t1;
DROP TABLE t1;
drop database test;
create database test;

View File

@ -7650,8 +7650,7 @@ bool Vers_parse_info::check_conditions(const Lex_table_name &table_name,
static bool is_versioning_timestamp(const Create_field *f)
{
return (f->type_handler() == &type_handler_datetime2 ||
f->type_handler() == &type_handler_timestamp2) &&
return f->type_handler() == &type_handler_timestamp2 &&
f->length == MAX_DATETIME_FULL_WIDTH;
}

View File

@ -401,11 +401,20 @@ public:
DBUG_ASSERT(part_type == VERSIONING_PARTITION);
vers_info->interval.type= int_type;
vers_info->interval.start= start;
return get_interval_value(thd, item, int_type, &vers_info->interval.step) ||
if (item->fix_fields_if_needed_for_scalar(thd, &item))
return true;
bool error= get_interval_value(thd, item, int_type, &vers_info->interval.step) ||
vers_info->interval.step.neg || vers_info->interval.step.second_part ||
!(vers_info->interval.step.year || vers_info->interval.step.month ||
vers_info->interval.step.day || vers_info->interval.step.hour ||
vers_info->interval.step.minute || vers_info->interval.step.second);
if (error)
{
my_error(ER_PART_WRONG_VALUE, MYF(0),
thd->lex->create_last_non_select_table->table_name.str,
"INTERVAL");
}
return error;
}
bool vers_set_limit(ulonglong limit)
{

View File

@ -10569,31 +10569,35 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
j->ref.null_rejecting|= (key_part_map)1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
/*
Todo: we should remove this check for thd->lex->describe on the next
line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
on it. However, removing the check caused change in lots of query
plans! Does the optimizer depend on the contents of
table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
We don't want to compute heavy expressions in EXPLAIN, an example would
select * from t1 where t1.key=(select thats very heavy);
(select thats very heavy) => is a constant here
eg: (select avg(order_cost) from orders) => constant but expensive
*/
if (!keyuse->val->used_tables() && !thd->lex->describe)
{ // Compare against constant
store_key_item tmp(thd,
store_key_item tmp(thd,
keyinfo->key_part[i].field,
key_buff + maybe_null,
maybe_null ? key_buff : 0,
keyinfo->key_part[i].length,
keyuse->val,
FALSE);
if (unlikely(thd->is_fatal_error))
DBUG_RETURN(TRUE);
tmp.copy();
if (unlikely(thd->is_fatal_error))
DBUG_RETURN(TRUE);
tmp.copy();
j->ref.const_ref_part_map |= key_part_map(1) << i ;
}
else
*ref_key++= get_store_key(thd,
keyuse,join->const_table_map,
&keyinfo->key_part[i],
key_buff, maybe_null);
{
*ref_key++= get_store_key(thd,
keyuse,join->const_table_map,
&keyinfo->key_part[i],
key_buff, maybe_null);
if (!keyuse->val->used_tables())
j->ref.const_ref_part_map |= key_part_map(1) << i ;
}
/*
Remember if we are going to use REF_OR_NULL
But only if field _really_ can be null i.e. we force JT_REF
@ -25925,6 +25929,15 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
{
if (!(eta->ref_list.append_str(thd->mem_root, "const")))
return 1;
/*
create_ref_for_key() handles keypart=const equalities as follows:
- non-EXPLAIN execution will copy the "const" to lookup tuple
immediately and will not add an element to ref.key_copy
- EXPLAIN will put an element into ref.key_copy. Since we've
just printed "const" for it, we should skip it here
*/
if (thd->lex->describe)
key_ref++;
}
else
{
@ -27661,7 +27674,15 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
{
if (table->quick_keys.is_set(ref_key))
/*
If ref access uses keypart=const for all its key parts,
and quick select uses the same # of key parts, then they are equivalent.
Reuse #rows estimate from quick select as it is more precise.
*/
if (tab->ref.const_ref_part_map ==
make_prev_keypart_map(tab->ref.key_parts) &&
table->quick_keys.is_set(ref_key) &&
table->quick_key_parts[ref_key] == tab->ref.key_parts)
refkey_rows_estimate= table->quick_rows[ref_key];
else
{

View File

@ -5974,12 +5974,7 @@ opt_versioning_rotation:
{
partition_info *part_info= Lex->part_info;
if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4)))
{
my_error(ER_PART_WRONG_VALUE, MYF(0),
Lex->create_last_non_select_table->table_name.str,
"INTERVAL");
MYSQL_YYABORT;
}
}
| LIMIT ulonglong_num
{

View File

@ -5999,12 +5999,7 @@ opt_versioning_rotation:
{
partition_info *part_info= Lex->part_info;
if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4)))
{
my_error(ER_PART_WRONG_VALUE, MYF(0),
Lex->create_last_non_select_table->table_name.str,
"INTERVAL");
MYSQL_YYABORT;
}
}
| LIMIT ulonglong_num
{

View File

@ -2314,7 +2314,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
switch (handler->real_field_type())
{
case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_DATETIME2:
break;
case MYSQL_TYPE_LONGLONG:
if (vers_can_native)

View File

@ -1418,26 +1418,24 @@ row_insert_for_mysql(
row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec,
&blob_heap);
if (ins_mode != ROW_INS_NORMAL)
{
if (ins_mode != ROW_INS_NORMAL) {
ut_ad(table->vers_start != table->vers_end);
/* Return back modified fields into mysql_rec, so that
upper logic may benefit from it (f.ex. 'on duplicate key'). */
const mysql_row_templ_t* t = prebuilt->get_template_by_col(table->vers_end);
const mysql_row_templ_t* t
= prebuilt->get_template_by_col(table->vers_end);
ut_ad(t);
ut_ad(t->mysql_col_len == 8);
if (ins_mode == ROW_INS_HISTORICAL) {
set_tuple_col_8(node->row, table->vers_end, trx->id, node->vers_end_buf);
}
else /* ROW_INS_VERSIONED */ {
set_tuple_col_8(node->row, table->vers_end, TRX_ID_MAX, node->vers_end_buf);
int8store(&mysql_rec[t->mysql_col_offset], TRX_ID_MAX);
set_tuple_col_8(node->row, table->vers_end, trx->id,
node->vers_end_buf);
} else /* ROW_INS_VERSIONED */ {
set_tuple_col_8(node->row, table->vers_end, TRX_ID_MAX,
node->vers_end_buf);
t = prebuilt->get_template_by_col(table->vers_start);
ut_ad(t);
ut_ad(t->mysql_col_len == 8);
set_tuple_col_8(node->row, table->vers_start, trx->id, node->vers_start_buf);
int8store(&mysql_rec[t->mysql_col_offset], trx->id);
set_tuple_col_8(node->row, table->vers_start, trx->id,
node->vers_start_buf);
}
}

View File

@ -3476,9 +3476,11 @@ void upd_node_t::make_versioned_helper(const trx_t* trx, ulint idx)
dict_index_t* clust_index = dict_table_get_first_index(table);
/* row_create_update_node_for_mysql() pre-allocated this much */
ut_ad(update->n_fields < ulint(table->n_cols + table->n_v_cols));
update->n_fields++;
upd_field_t* ufield =
upd_get_nth_field(update, upd_get_n_fields(update) - 1);
upd_field_t* ufield = upd_get_nth_field(update, update->n_fields - 1);
const dict_col_t* col = dict_table_get_nth_col(table, idx);
upd_field_set_field_no(ufield, dict_col_get_clust_pos(col, clust_index),

View File

@ -806,12 +806,10 @@ void trx_rollback_recovered(bool all)
trx_t *trx= trx_list.back();
trx_list.pop_back();
#ifdef UNIV_DEBUG
ut_ad(trx);
trx_mutex_enter(trx);
ut_d(trx_mutex_enter(trx));
ut_ad(trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE));
trx_mutex_exit(trx);
#endif
ut_d(trx_mutex_exit(trx));
if (!srv_is_being_started && !srv_undo_sources && srv_fast_shutdown)
goto discard;

View File

@ -111,18 +111,9 @@ trx_rseg_clear_wsrep_checkpoint(
- TRX_RSEG_WSREP_XID_INFO, 0, mtr);
}
/** Update WSREP checkpoint XID in first rollback segment header
as part of wsrep_set_SE_checkpoint() when it is guaranteed that there
are no wsrep transactions committing.
If the UUID part of the WSREP XID does not match to the UUIDs of XIDs already
stored into rollback segments, the WSREP XID in all the remaining rollback
segments will be reset.
@param[in] xid WSREP XID */
void trx_rseg_update_wsrep_checkpoint(const XID* xid)
static void
trx_rseg_update_wsrep_checkpoint(const XID* xid, mtr_t* mtr)
{
mtr_t mtr;
mtr.start();
const byte* xid_uuid = wsrep_xid_uuid(xid);
/* We must make check against wsrep_uuid here, the
trx_rseg_update_wsrep_checkpoint() writes over wsrep_uuid with
@ -133,12 +124,12 @@ void trx_rseg_update_wsrep_checkpoint(const XID* xid)
const trx_rseg_t* rseg = trx_sys.rseg_array[0];
trx_rsegf_t* rseg_header = trx_rsegf_get(rseg->space, rseg->page_no,
&mtr);
mtr);
if (UNIV_UNLIKELY(mach_read_from_4(rseg_header + TRX_RSEG_FORMAT))) {
trx_rseg_format_upgrade(rseg_header, &mtr);
trx_rseg_format_upgrade(rseg_header, mtr);
}
trx_rseg_update_wsrep_checkpoint(rseg_header, xid, &mtr);
trx_rseg_update_wsrep_checkpoint(rseg_header, xid, mtr);
if (must_clear_rsegs) {
/* Because the UUID part of the WSREP XID differed
@ -150,13 +141,25 @@ void trx_rseg_update_wsrep_checkpoint(const XID* xid)
trx_sys.rseg_array[rseg_id]) {
trx_rseg_clear_wsrep_checkpoint(
trx_rsegf_get(rseg->space,
rseg->page_no,
&mtr),
&mtr);
rseg->page_no, mtr),
mtr);
}
}
}
}
/** Update WSREP checkpoint XID in first rollback segment header
as part of wsrep_set_SE_checkpoint() when it is guaranteed that there
are no wsrep transactions committing.
If the UUID part of the WSREP XID does not match to the UUIDs of XIDs already
stored into rollback segments, the WSREP XID in all the remaining rollback
segments will be reset.
@param[in] xid WSREP XID */
void trx_rseg_update_wsrep_checkpoint(const XID* xid)
{
mtr_t mtr;
mtr.start();
trx_rseg_update_wsrep_checkpoint(xid, &mtr);
mtr.commit();
}
@ -232,16 +235,6 @@ bool trx_rseg_read_wsrep_checkpoint(XID& xid)
rseg_id++, mtr.commit()) {
mtr.start();
const buf_block_t* sys = trx_sysf_get(&mtr, false);
if (rseg_id == 0) {
found = trx_rseg_init_wsrep_xid(sys->frame, xid);
ut_ad(!found || xid.formatID == 1);
if (found) {
max_xid_seqno = wsrep_xid_seqno(&xid);
memcpy(wsrep_uuid, wsrep_xid_uuid(&xid),
sizeof wsrep_uuid);
}
}
const uint32_t page_no = trx_sysf_rseg_get_page_no(
sys, rseg_id);
@ -566,6 +559,9 @@ trx_rseg_array_init()
trx_sys.recovered_binlog_offset = 0;
#ifdef WITH_WSREP
trx_sys.recovered_wsrep_xid.null();
XID wsrep_sys_xid;
wsrep_sys_xid.null();
bool wsrep_xid_in_rseg_found = false;
#endif
for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
@ -580,6 +576,9 @@ trx_rseg_array_init()
TRX_SYS + TRX_SYS_TRX_ID_STORE
+ sys->frame);
trx_rseg_init_binlog_info(sys->frame);
#ifdef WITH_WSREP
wsrep_sys_xid.set(&trx_sys.recovered_wsrep_xid);
#endif
}
const uint32_t page_no = trx_sysf_rseg_get_page_no(
@ -595,12 +594,49 @@ trx_rseg_array_init()
ut_ad(!trx_sys.rseg_array[rseg_id]);
trx_sys.rseg_array[rseg_id] = rseg;
trx_rseg_mem_restore(rseg, max_trx_id, &mtr);
#ifdef WITH_WSREP
if (!wsrep_sys_xid.is_null() &&
!wsrep_sys_xid.eq(&trx_sys.recovered_wsrep_xid)) {
wsrep_xid_in_rseg_found = true;
ut_ad(memcmp(wsrep_xid_uuid(&wsrep_sys_xid),
wsrep_xid_uuid(&trx_sys.recovered_wsrep_xid),
sizeof wsrep_uuid)
|| wsrep_xid_seqno(
&wsrep_sys_xid)
<= wsrep_xid_seqno(
&trx_sys.recovered_wsrep_xid));
}
#endif
}
}
mtr.commit();
}
#ifdef WITH_WSREP
if (!wsrep_sys_xid.is_null()) {
/* Upgrade from a version prior to 10.3.5,
where WSREP XID was stored in TRX_SYS page.
If no rollback segment has a WSREP XID set,
we must copy the XID found in TRX_SYS page
to rollback segments. */
mtr_t mtr;
mtr.start();
if (!wsrep_xid_in_rseg_found) {
trx_rseg_update_wsrep_checkpoint(&wsrep_sys_xid, &mtr);
}
/* Finally, clear WSREP XID in TRX_SYS page. */
const buf_block_t* sys = trx_sysf_get(&mtr);
mlog_write_ulint(TRX_SYS + TRX_SYS_WSREP_XID_INFO +
+ TRX_SYS_WSREP_XID_MAGIC_N_FLD + sys->frame,
0, MLOG_4BYTES, &mtr);
mtr.commit();
}
#endif
trx_sys.init_max_trx_id(max_trx_id + 1);
}