summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2020-07-21 16:36:47 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2020-07-21 16:36:47 +0300
commitb1538f4d60bf30de00417a7c3f948d2de654fcb3 (patch)
tree9690f28c9dbdcb5554c9762c81153f37571aa697
parentfc48c8ff4c6cb9aa7a0fd27e724e6e3acd555b0e (diff)
parentb75563cdfd161e373480949ffa498bd4a8087b12 (diff)
downloadmariadb-git-b1538f4d60bf30de00417a7c3f948d2de654fcb3.tar.gz
Merge 10.3 into 10.4
-rw-r--r--mysql-test/suite/sql_sequence/kill.result12
-rw-r--r--mysql-test/suite/sql_sequence/kill.test20
-rw-r--r--mysql-test/suite/versioning/r/update.result31
-rw-r--r--mysql-test/suite/versioning/t/update.test41
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_sequence.cc4
-rw-r--r--sql/table.cc25
-rw-r--r--storage/innobase/handler/ha_innodb.cc67
-rw-r--r--storage/innobase/include/row0ins.h3
-rw-r--r--storage/innobase/include/row0upd.h45
-rw-r--r--storage/innobase/row/row0mysql.cc58
-rw-r--r--storage/innobase/row/row0upd.cc69
-rw-r--r--storage/innobase/trx/trx0trx.cc9
13 files changed, 295 insertions, 93 deletions
diff --git a/mysql-test/suite/sql_sequence/kill.result b/mysql-test/suite/sql_sequence/kill.result
new file mode 100644
index 00000000000..6d966254de3
--- /dev/null
+++ b/mysql-test/suite/sql_sequence/kill.result
@@ -0,0 +1,12 @@
+#
+# MDEV-16929 Assertion ... in close_thread_tables upon killing connection
+# running SHOW on sequence
+#
+CREATE SEQUENCE s ENGINE=InnoDB;
+RENAME TABLE s TO s1;
+connect con1,localhost,root,,test;
+SHOW CREATE SEQUENCE s1;
+connection default;
+KILL thread_id;
+connection default;
+drop sequence s1;
diff --git a/mysql-test/suite/sql_sequence/kill.test b/mysql-test/suite/sql_sequence/kill.test
new file mode 100644
index 00000000000..9caebc57f12
--- /dev/null
+++ b/mysql-test/suite/sql_sequence/kill.test
@@ -0,0 +1,20 @@
+--source include/have_innodb.inc
+
+--echo #
+--echo # MDEV-16929 Assertion ... in close_thread_tables upon killing connection
+--echo # running SHOW on sequence
+--echo #
+
+CREATE SEQUENCE s ENGINE=InnoDB;
+RENAME TABLE s TO s1;
+--connect (con1,localhost,root,,test)
+--let $conid= `SELECT CONNECTION_ID()`
+--send
+ SHOW CREATE SEQUENCE s1;
+--connection default
+--replace_result $conid thread_id
+--eval KILL $conid
+
+# Cleanup
+--connection default
+drop sequence s1;
diff --git a/mysql-test/suite/versioning/r/update.result b/mysql-test/suite/versioning/r/update.result
index f7901d11d2a..cd26c341113 100644
--- a/mysql-test/suite/versioning/r/update.result
+++ b/mysql-test/suite/versioning/r/update.result
@@ -319,3 +319,34 @@ create or replace table t1 (f point, key(f)) with system versioning engine=myisa
update t1 set f = null where f = 'foo';
ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field
drop table t1;
+#
+# MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table
+#
+create or replace table t1 (
+a int,
+b int,
+row_start bigint(20) unsigned generated always as row start,
+row_end bigint(20) unsigned generated always as row end,
+unique key (b,row_end),
+key (row_start),
+period for system_time (row_start,row_end)
+) engine=innodb with system versioning;
+insert into t1 (a, b) values (1, 2);
+replace into t1 (a, b) values (3, 2);
+replace into t1 (a, b) values (4, 2);
+drop table t1;
+#
+# MDEV-20661 Virtual fields are not recalculated on system fields value assignment
+#
+create table t1 (
+a int,
+row_start SYS_DATATYPE as row start invisible,
+row_end SYS_DATATYPE as row end invisible,
+period for system_time (row_start, row_end),
+v1 bigint unsigned as (a ^ row_start) unique,
+v2 bigint unsigned as (a ^ row_end) unique
+) engine=innodb with system versioning;
+insert into t1 (a) values (1), (2);
+update ignore t1 set a= 3;
+delete history from t1;
+drop table t1;
diff --git a/mysql-test/suite/versioning/t/update.test b/mysql-test/suite/versioning/t/update.test
index 5b0a9eb5c42..06f81ea9064 100644
--- a/mysql-test/suite/versioning/t/update.test
+++ b/mysql-test/suite/versioning/t/update.test
@@ -245,4 +245,45 @@ update t1 set f = null where f = 'foo';
# cleanup
drop table t1;
+--echo #
+--echo # MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table
+--echo #
+create or replace table t1 (
+ a int,
+ b int,
+ row_start bigint(20) unsigned generated always as row start,
+ row_end bigint(20) unsigned generated always as row end,
+ unique key (b,row_end),
+ key (row_start),
+ period for system_time (row_start,row_end)
+) engine=innodb with system versioning;
+
+insert into t1 (a, b) values (1, 2);
+replace into t1 (a, b) values (3, 2);
+replace into t1 (a, b) values (4, 2);
+
+# cleanup
+drop table t1;
+
+--echo #
+--echo # MDEV-20661 Virtual fields are not recalculated on system fields value assignment
+--echo #
+
+replace_result $sys_datatype_expl SYS_DATATYPE;
+eval create table t1 (
+ a int,
+ row_start $sys_datatype_expl as row start invisible,
+ row_end $sys_datatype_expl as row end invisible,
+ period for system_time (row_start, row_end),
+ v1 bigint unsigned as (a ^ row_start) unique,
+ v2 bigint unsigned as (a ^ row_end) unique
+) engine=innodb with system versioning;
+
+insert into t1 (a) values (1), (2);
+update ignore t1 set a= 3;
+delete history from t1;
+
+# cleanup
+drop table t1;
+
source suite/versioning/common_finish.inc;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 807dd94c6f0..8d0e08d8c8c 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1682,6 +1682,10 @@ int vers_insert_history_row(TABLE *table)
if (row_start->cmp(row_start->ptr, row_end->ptr) >= 0)
return 0;
+ if (table->vfield &&
+ table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_READ))
+ return HA_ERR_GENERIC;
+
return table->file->ha_write_row(table->record[0]);
}
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
index 91959f85e64..a2bcfd5a4ff 100644
--- a/sql/sql_sequence.cc
+++ b/sql/sql_sequence.cc
@@ -485,6 +485,10 @@ int SEQUENCE::read_initial_values(TABLE *table)
if (mdl_lock_used)
thd->mdl_context.release_lock(mdl_request.ticket);
write_unlock(table);
+
+ if (!has_active_transaction && !thd->transaction.stmt.is_empty() &&
+ !thd->in_sub_stmt)
+ trans_commit_stmt(thd);
DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT);
}
DBUG_ASSERT(table->reginfo.lock_type == TL_READ);
diff --git a/sql/table.cc b/sql/table.cc
index 138f39e294d..c8594d25851 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -8546,29 +8546,24 @@ void TABLE::vers_update_fields()
bitmap_set_bit(write_set, vers_start_field()->field_index);
bitmap_set_bit(write_set, vers_end_field()->field_index);
- if (versioned(VERS_TIMESTAMP))
+ if (!vers_write)
{
- if (!vers_write)
- {
- file->column_bitmaps_signal();
- return;
- }
- if (vers_start_field()->store_timestamp(in_use->query_start(),
- in_use->query_start_sec_part()))
- DBUG_ASSERT(0);
+ file->column_bitmaps_signal();
+ return;
}
- else
+
+ if (versioned(VERS_TIMESTAMP) &&
+ vers_start_field()->store_timestamp(in_use->query_start(),
+ in_use->query_start_sec_part()))
{
- if (!vers_write)
- {
- file->column_bitmaps_signal();
- return;
- }
+ DBUG_ASSERT(0);
}
vers_end_field()->set_max();
bitmap_set_bit(read_set, vers_end_field()->field_index);
file->column_bitmaps_signal();
+ if (vfield)
+ update_virtual_fields(file, VCOL_UPDATE_FOR_READ);
}
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 1b3237459ba..aaadcb94946 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -21707,3 +21707,70 @@ ib_push_frm_error(
break;
}
}
+
+/** Writes 8 bytes to nth tuple field
+@param[in] tuple where to write
+@param[in] nth index in tuple
+@param[in] data what to write
+@param[in] buf field data buffer */
+static void set_tuple_col_8(dtuple_t *tuple, int col, uint64_t data, byte *buf)
+{
+ dfield_t *dfield= dtuple_get_nth_field(tuple, col);
+ ut_ad(dfield->type.len == 8);
+ if (dfield->len == UNIV_SQL_NULL)
+ {
+ dfield_set_data(dfield, buf, 8);
+ }
+ ut_ad(dfield->len == dfield->type.len && dfield->data);
+ mach_write_to_8(dfield->data, data);
+}
+
+void ins_node_t::vers_update_end(row_prebuilt_t *prebuilt, bool history_row)
+{
+ ut_ad(prebuilt->ins_node == this);
+ trx_t *trx= prebuilt->trx;
+#ifndef DBUG_OFF
+ ut_ad(table->vers_start != 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);
+#endif
+
+ if (history_row)
+ {
+ set_tuple_col_8(row, table->vers_end, trx->id, vers_end_buf);
+ }
+ else /* ROW_INS_VERSIONED */
+ {
+ set_tuple_col_8(row, table->vers_end, TRX_ID_MAX, vers_end_buf);
+#ifndef DBUG_OFF
+ t= prebuilt->get_template_by_col(table->vers_start);
+ ut_ad(t);
+ ut_ad(t->mysql_col_len == 8);
+#endif
+ set_tuple_col_8(row, table->vers_start, trx->id, vers_start_buf);
+ }
+ dict_index_t *clust_index= dict_table_get_first_index(table);
+ THD *thd= trx->mysql_thd;
+ TABLE *mysql_table= prebuilt->m_mysql_table;
+ mem_heap_t *local_heap= NULL;
+ for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
+ {
+
+ const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
+ for (ulint i= 0; i < unsigned(v_col->num_base); i++)
+ {
+ dict_col_t *base_col= v_col->base_col[i];
+ if (base_col->ind == table->vers_end)
+ {
+ innobase_get_computed_value(row, v_col, clust_index, &local_heap,
+ table->heap, NULL, thd, mysql_table,
+ mysql_table->record[0], NULL, NULL, NULL);
+ }
+ }
+ }
+ if (local_heap)
+ {
+ mem_heap_free(local_heap);
+ }
+}
diff --git a/storage/innobase/include/row0ins.h b/storage/innobase/include/row0ins.h
index 95f4388902d..34427dc6dc7 100644
--- a/storage/innobase/include/row0ins.h
+++ b/storage/innobase/include/row0ins.h
@@ -163,6 +163,8 @@ row_ins_step(
#define INS_NODE_INSERT_ENTRIES 3 /* index entries should be built and
inserted */
+struct row_prebuilt_t;
+
/** Insert node structure */
struct ins_node_t
{
@@ -203,6 +205,7 @@ struct ins_node_t
entry_list and sys fields are stored here;
if this is NULL, entry list should be created
and buffers for sys fields in row allocated */
+ void vers_update_end(row_prebuilt_t *prebuilt, bool history_row);
};
/** Create an insert object.
diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h
index 677af76c561..c5a0a3bf4f9 100644
--- a/storage/innobase/include/row0upd.h
+++ b/storage/innobase/include/row0upd.h
@@ -429,7 +429,32 @@ struct upd_t{
fields[n_fields++] = field;
}
- /** Determine if the given field_no is modified.
+ void remove_element(ulint i)
+ {
+ ut_ad(n_fields > 0);
+ ut_ad(i < n_fields);
+ while (i < n_fields - 1)
+ {
+ fields[i]= fields[i + 1];
+ i++;
+ }
+ n_fields--;
+ }
+
+ bool remove(const ulint field_no)
+ {
+ for (ulint i= 0; i < n_fields; ++i)
+ {
+ if (field_no == fields[i].field_no)
+ {
+ remove_element(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Determine if the given field_no is modified.
@return true if modified, false otherwise. */
bool is_modified(const ulint field_no) const
{
@@ -569,25 +594,25 @@ private:
make_versioned_delete().
@param[in] trx transaction
@param[in] vers_sys_idx table->row_start or table->row_end */
- void make_versioned_helper(const trx_t* trx, ulint idx);
+ void vers_update_fields(const trx_t *trx, ulint idx);
public:
/** Also set row_start = CURRENT_TIMESTAMP/trx->id
@param[in] trx transaction */
- void make_versioned_update(const trx_t* trx)
- {
- make_versioned_helper(trx, table->vers_start);
- }
+ void vers_make_update(const trx_t *trx)
+ {
+ vers_update_fields(trx, table->vers_start);
+ }
/** Only set row_end = CURRENT_TIMESTAMP/trx->id.
Do not touch other fields at all.
@param[in] trx transaction */
- void make_versioned_delete(const trx_t* trx)
- {
+ void vers_make_delete(const trx_t *trx)
+ {
update->n_fields = 0;
is_delete = VERSIONED_DELETE;
- make_versioned_helper(trx, table->vers_end);
- }
+ vers_update_fields(trx, table->vers_end);
+ }
};
#define UPD_NODE_MAGIC_N 1579975
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index a8b83ad65b0..e80e7889d19 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -1332,23 +1332,6 @@ row_mysql_get_table_status(
return(err);
}
-/** Writes 8 bytes to nth tuple field
-@param[in] tuple where to write
-@param[in] nth index in tuple
-@param[in] data what to write
-@param[in] buf field data buffer */
-static
-void
-set_tuple_col_8(dtuple_t* tuple, int col, uint64_t data, byte* buf) {
- dfield_t* dfield = dtuple_get_nth_field(tuple, col);
- ut_ad(dfield->type.len == 8);
- if (dfield->len == UNIV_SQL_NULL) {
- dfield_set_data(dfield, buf, 8);
- }
- ut_ad(dfield->len == dfield->type.len && dfield->data);
- mach_write_to_8(dfield->data, data);
-}
-
/** Does an insert for MySQL.
@param[in] mysql_rec row in the MySQL format
@param[in,out] prebuilt prebuilt struct in MySQL handle
@@ -1416,29 +1399,8 @@ row_insert_for_mysql(
&blob_heap);
if (ins_mode != ROW_INS_NORMAL) {
-#ifndef DBUG_OFF
- ut_ad(table->vers_start != 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);
-#endif
-
- 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);
-#ifndef DBUG_OFF
- t = prebuilt->get_template_by_col(table->vers_start);
- ut_ad(t);
- ut_ad(t->mysql_col_len == 8);
-#endif
- set_tuple_col_8(node->row, table->vers_start, trx->id,
- node->vers_start_buf);
- }
- }
+ node->vers_update_end(prebuilt, ins_mode == ROW_INS_HISTORICAL);
+ }
savept = trx_savept_take(trx);
@@ -1872,10 +1834,10 @@ row_update_for_mysql(row_prebuilt_t* prebuilt)
if (prebuilt->versioned_write) {
if (node->is_delete == VERSIONED_DELETE) {
- node->make_versioned_delete(trx);
- } else if (node->update->affects_versioned()) {
- node->make_versioned_update(trx);
- }
+ node->vers_make_delete(trx);
+ } else if (node->update->affects_versioned()) {
+ node->vers_make_update(trx);
+ }
}
for (;;) {
@@ -2240,14 +2202,14 @@ row_update_cascade_for_mysql(
if (table->versioned()) {
if (node->is_delete == PLAIN_DELETE) {
- node->make_versioned_delete(trx);
- } else if (node->update->affects_versioned()) {
+ node->vers_make_delete(trx);
+ } else if (node->update->affects_versioned()) {
dberr_t err = row_update_vers_insert(thr, node);
if (err != DB_SUCCESS) {
return err;
}
- node->make_versioned_update(trx);
- }
+ node->vers_make_update(trx);
+ }
}
for (;;) {
diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc
index 0116bfb1c6f..1f30a428d74 100644
--- a/storage/innobase/row/row0upd.cc
+++ b/storage/innobase/row/row0upd.cc
@@ -3477,32 +3477,65 @@ Supposed to be called only by make_versioned_update() and
make_versioned_delete().
@param[in] trx transaction
@param[in] vers_sys_idx table->row_start or table->row_end */
-void upd_node_t::make_versioned_helper(const trx_t* trx, ulint idx)
+void upd_node_t::vers_update_fields(const trx_t *trx, ulint idx)
{
ut_ad(in_mysql_interface); // otherwise needs to recalculate
// node->cmpl_info
ut_ad(idx == table->vers_start || idx == table->vers_end);
dict_index_t* clust_index = dict_table_get_first_index(table);
-
- /* row_create_update_node_for_mysql() pre-allocated this much.
+ const dict_col_t *col= dict_table_get_nth_col(table, idx);
+ ulint field_no= dict_col_get_clust_pos(col, clust_index);
+ upd_field_t *ufield;
+
+ for (ulint i= 0; i < update->n_fields; ++i)
+ {
+ if (update->fields[i].field_no == field_no)
+ {
+ ufield= &update->fields[i];
+ goto skip_append;
+ }
+ }
+
+ /* row_create_update_node_for_mysql() pre-allocated this much.
At least one PK column always remains unchanged. */
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, 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),
- clust_index);
-
- char* where = reinterpret_cast<char*>(update->vers_sys_value);
- if (col->vers_native()) {
- mach_write_to_8(where, trx->id);
- } else {
- thd_get_query_start_data(trx->mysql_thd, where);
- }
-
- dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len);
+ ufield= upd_get_nth_field(update, update->n_fields - 1);
+ upd_field_set_field_no(ufield, field_no, clust_index);
+
+skip_append:
+ char *where= reinterpret_cast<char *>(update->vers_sys_value);
+ if (col->vers_native())
+ {
+ mach_write_to_8(where, trx->id);
+ }
+ else
+ {
+ thd_get_query_start_data(trx->mysql_thd, where);
+ }
+
+ dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len);
+
+ for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
+ {
+
+ const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
+ if (!v_col->m_col.ord_part)
+ continue;
+ for (ulint i= 0; i < unsigned(v_col->num_base); i++)
+ {
+ dict_col_t *base_col= v_col->base_col[i];
+ if (base_col->ind == col->ind)
+ {
+ /* Virtual column depends on system field value
+ which we updated above. Remove it from update
+ vector, so it is recalculated in
+ row_upd_store_v_row() (see !update branch). */
+ update->remove(v_col->v_pos);
+ break;
+ }
+ }
+ }
}
-
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index c93ec9c1112..7da5b2bc738 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1288,7 +1288,8 @@ trx_update_mod_tables_timestamp(
dict_table_t* table = it->first;
table->update_time = now;
#ifdef UNIV_DEBUG
- if (preserve_tables || table->get_ref_count()) {
+ if (preserve_tables || table->get_ref_count()
+ || UT_LIST_GET_LEN(table->locks)) {
/* do not evict when committing DDL operations
or if some other transaction is holding the
table handle */
@@ -1297,7 +1298,11 @@ trx_update_mod_tables_timestamp(
/* recheck while holding the mutex that blocks
table->acquire() */
mutex_enter(&dict_sys.mutex);
- if (!table->get_ref_count()) {
+ mutex_enter(&lock_sys.mutex);
+ const bool do_evict = !table->get_ref_count()
+ && !UT_LIST_GET_LEN(table->locks);
+ mutex_exit(&lock_sys.mutex);
+ if (do_evict) {
dict_sys.remove(table, true);
}
mutex_exit(&dict_sys.mutex);