Skip to content

Commit 9edb1a5

Browse files
committed
MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
Problem: Field_timestampf implementations differ in MySQL and MariaDB: - MariaDB sets the UNSIGNED_FLAG in Field::flags - MySQL does not The reference table structures (defined in table_stats_schema and index_stats_schema) expected the last_update column to have the DATA_UNSIGNED flag, because MariaDB's Field_timestampf has the UNSIGNED_FLAG. It worked fine on pure MariaDB installations. However, if a MariaDB server starts over a MySQL-5.7 data directory during a migration, the last_update column does not have DATA_UNSIGNED flag, because MySQL's Field_timestampf does not have the UNSIGNED_FLAG. This made InnoDB (after the migration from MySQL) complain into the server error log about the unexpected data type. The actual fix is done in storage/innobase/dict/dict0stats.cc: It removes DATA_UNSIGNED from the prtype_mask member of the reference columns, so now it does not require the underlying columns to have this flag. The rest of the fix is needed for MTR tests. The new data type plugin TYPE_MYSQL_TIMESTAMP implements a slightly modified version of Field_timestampf, which removes the unsigned flag, so it works like MySQL's Field_timestampf. The MTR test ALTERs the data type of the columns table_stats_schema.last_update and index_stats_schema.last_update from TIMESTAMP to TYPE_MYSQL_TIMESTAMP, then makes InnoDB verify the structure of the two statistics tables by creating and populating an InnoDB table t1. Without the fix made storage/innobase/dict/dict0stats.cc, MTR complains about unexpected warnings in the server error log: [ERROR] InnoDB: Column last_update in table mysql.innodb_table_stats is ... [ERROR] InnoDB: Column last_update in table mysql.innodb_index_stats is ... With the fix made storage/innobase/dict/dict0stats.cc these warnings go away.
1 parent 9b3084b commit 9edb1a5

File tree

11 files changed

+382
-5
lines changed

11 files changed

+382
-5
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2019, MariaDB corporation
2+
#
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation; version 2 of the License.
6+
#
7+
# This program is distributed in the hope that it will be useful,
8+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
# GNU General Public License for more details.
11+
#
12+
# You should have received a copy of the GNU General Public License
13+
# along with this program; if not, write to the Free Software
14+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
15+
16+
MYSQL_ADD_PLUGIN(type_mysql_timestamp plugin.cc RECOMPILE_FOR_EMBEDDED
17+
MODULE_ONLY COMPONENT Test)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--plugin-load-add=$TYPE_MYSQL_TIMESTAMP_SO
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package My::Suite::Type_test;
2+
3+
@ISA = qw(My::Suite);
4+
5+
return "No TYPE_TEST plugin" unless $ENV{TYPE_MYSQL_TIMESTAMP_SO};
6+
7+
sub is_default { 1 }
8+
9+
bless { };
10+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#
2+
# MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
3+
#
4+
SELECT
5+
PLUGIN_NAME,
6+
PLUGIN_VERSION,
7+
PLUGIN_STATUS,
8+
PLUGIN_TYPE,
9+
PLUGIN_AUTHOR,
10+
PLUGIN_DESCRIPTION,
11+
PLUGIN_LICENSE,
12+
PLUGIN_MATURITY,
13+
PLUGIN_AUTH_VERSION
14+
FROM INFORMATION_SCHEMA.PLUGINS
15+
WHERE PLUGIN_TYPE='DATA TYPE'
16+
AND PLUGIN_NAME LIKE 'type_mysql_timestamp';
17+
PLUGIN_NAME type_mysql_timestamp
18+
PLUGIN_VERSION 1.0
19+
PLUGIN_STATUS ACTIVE
20+
PLUGIN_TYPE DATA TYPE
21+
PLUGIN_AUTHOR MariaDB Corporation
22+
PLUGIN_DESCRIPTION Data type TYPE_MYSQL_TIMESTAMP
23+
PLUGIN_LICENSE GPL
24+
PLUGIN_MATURITY Experimental
25+
PLUGIN_AUTH_VERSION 1.0
26+
CREATE TABLE t1 (a TYPE_MYSQL_TIMESTAMP);
27+
SHOW CREATE TABLE t1;
28+
Table Create Table
29+
t1 CREATE TABLE `t1` (
30+
`a` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
31+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
32+
DROP TABLE t1;
33+
CREATE TABLE t1 (a TIMESTAMP);
34+
SHOW CREATE TABLE t1;
35+
Table Create Table
36+
t1 CREATE TABLE `t1` (
37+
`a` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
38+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
39+
ALTER TABLE t1 MODIFY a TYPE_MYSQL_TIMESTAMP;
40+
SHOW CREATE TABLE t1;
41+
Table Create Table
42+
t1 CREATE TABLE `t1` (
43+
`a` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
44+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
45+
DROP TABLE t1;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--source include/have_innodb.inc
2+
3+
--echo #
4+
--echo # MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
5+
--echo #
6+
7+
--vertical_results
8+
SELECT
9+
PLUGIN_NAME,
10+
PLUGIN_VERSION,
11+
PLUGIN_STATUS,
12+
PLUGIN_TYPE,
13+
PLUGIN_AUTHOR,
14+
PLUGIN_DESCRIPTION,
15+
PLUGIN_LICENSE,
16+
PLUGIN_MATURITY,
17+
PLUGIN_AUTH_VERSION
18+
FROM INFORMATION_SCHEMA.PLUGINS
19+
WHERE PLUGIN_TYPE='DATA TYPE'
20+
AND PLUGIN_NAME LIKE 'type_mysql_timestamp';
21+
--horizontal_results
22+
23+
CREATE TABLE t1 (a TYPE_MYSQL_TIMESTAMP);
24+
SHOW CREATE TABLE t1;
25+
DROP TABLE t1;
26+
27+
CREATE TABLE t1 (a TIMESTAMP);
28+
SHOW CREATE TABLE t1;
29+
ALTER TABLE t1 MODIFY a TYPE_MYSQL_TIMESTAMP;
30+
SHOW CREATE TABLE t1;
31+
DROP TABLE t1;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#
2+
# MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
3+
#
4+
SET @@global.innodb_stats_persistent=0;
5+
SHOW CREATE TABLE mysql.innodb_table_stats;
6+
Table Create Table
7+
innodb_table_stats CREATE TABLE `innodb_table_stats` (
8+
`database_name` varchar(64) NOT NULL,
9+
`table_name` varchar(199) NOT NULL,
10+
`last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
11+
`n_rows` bigint(20) unsigned NOT NULL,
12+
`clustered_index_size` bigint(20) unsigned NOT NULL,
13+
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
14+
PRIMARY KEY (`database_name`,`table_name`)
15+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
16+
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
17+
SHOW CREATE TABLE mysql.innodb_table_stats;
18+
Table Create Table
19+
innodb_table_stats CREATE TABLE `innodb_table_stats` (
20+
`database_name` varchar(64) NOT NULL,
21+
`table_name` varchar(199) NOT NULL,
22+
`last_update` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
23+
`n_rows` bigint(20) unsigned NOT NULL,
24+
`clustered_index_size` bigint(20) unsigned NOT NULL,
25+
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
26+
PRIMARY KEY (`database_name`,`table_name`)
27+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
28+
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
29+
SHOW CREATE TABLE mysql.innodb_index_stats;
30+
Table Create Table
31+
innodb_index_stats CREATE TABLE `innodb_index_stats` (
32+
`database_name` varchar(64) NOT NULL,
33+
`table_name` varchar(199) NOT NULL,
34+
`index_name` varchar(64) NOT NULL,
35+
`last_update` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
36+
`stat_name` varchar(64) NOT NULL,
37+
`stat_value` bigint(20) unsigned NOT NULL,
38+
`sample_size` bigint(20) unsigned DEFAULT NULL,
39+
`stat_description` varchar(1024) NOT NULL,
40+
PRIMARY KEY (`database_name`,`table_name`,`index_name`,`stat_name`)
41+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
42+
SET @@global.innodb_stats_persistent=1;
43+
CREATE TABLE t1 (a INT, KEY(a)) ENGINE=InnoDB;
44+
INSERT INTO t1 VALUES (10);
45+
DROP TABLE t1;
46+
SET @@global.innodb_stats_persistent=0;
47+
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP;
48+
SHOW CREATE TABLE mysql.innodb_table_stats;
49+
Table Create Table
50+
innodb_table_stats CREATE TABLE `innodb_table_stats` (
51+
`database_name` varchar(64) NOT NULL,
52+
`table_name` varchar(199) NOT NULL,
53+
`last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
54+
`n_rows` bigint(20) unsigned NOT NULL,
55+
`clustered_index_size` bigint(20) unsigned NOT NULL,
56+
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
57+
PRIMARY KEY (`database_name`,`table_name`)
58+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
59+
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP;
60+
SHOW CREATE TABLE mysql.innodb_index_stats;
61+
Table Create Table
62+
innodb_index_stats CREATE TABLE `innodb_index_stats` (
63+
`database_name` varchar(64) NOT NULL,
64+
`table_name` varchar(199) NOT NULL,
65+
`index_name` varchar(64) NOT NULL,
66+
`last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
67+
`stat_name` varchar(64) NOT NULL,
68+
`stat_value` bigint(20) unsigned NOT NULL,
69+
`sample_size` bigint(20) unsigned DEFAULT NULL,
70+
`stat_description` varchar(1024) NOT NULL,
71+
PRIMARY KEY (`database_name`,`table_name`,`index_name`,`stat_name`)
72+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
73+
SET @@global.innodb_stats_persistent=1;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--source include/have_innodb.inc
2+
3+
--echo #
4+
--echo # MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
5+
--echo #
6+
7+
SET @@global.innodb_stats_persistent=0;
8+
SHOW CREATE TABLE mysql.innodb_table_stats;
9+
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
10+
SHOW CREATE TABLE mysql.innodb_table_stats;
11+
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
12+
SHOW CREATE TABLE mysql.innodb_index_stats;
13+
SET @@global.innodb_stats_persistent=1;
14+
15+
CREATE TABLE t1 (a INT, KEY(a)) ENGINE=InnoDB;
16+
INSERT INTO t1 VALUES (10);
17+
DROP TABLE t1;
18+
19+
SET @@global.innodb_stats_persistent=0;
20+
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP;
21+
SHOW CREATE TABLE mysql.innodb_table_stats;
22+
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP;
23+
SHOW CREATE TABLE mysql.innodb_index_stats;
24+
SET @@global.innodb_stats_persistent=1;

plugin/type_mysql_timestamp/plugin.cc

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
Copyright (c) 2023, MariaDB Corporation
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; version 2 of the License.
7+
8+
This program is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
GNU General Public License for more details.
12+
13+
You should have received a copy of the GNU General Public License
14+
along with this program; if not, write to the Free Software
15+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16+
17+
#include <my_global.h>
18+
#include <sql_class.h>
19+
#include <mysql/plugin_data_type.h>
20+
#include "sql_type.h"
21+
22+
23+
class Type_collection_local: public Type_collection
24+
{
25+
protected:
26+
const Type_handler *aggregate_common(const Type_handler *h1,
27+
const Type_handler *h2) const
28+
{
29+
if (h1 == h2)
30+
return h1;
31+
return NULL;
32+
}
33+
public:
34+
const Type_handler *handler_by_name(const LEX_CSTRING &name) const override
35+
{
36+
return NULL;
37+
}
38+
39+
const Type_handler *aggregate_for_result(const Type_handler *h1,
40+
const Type_handler *h2)
41+
const override
42+
{
43+
return aggregate_common(h1, h2);
44+
}
45+
46+
const Type_handler *aggregate_for_comparison(const Type_handler *h1,
47+
const Type_handler *h2)
48+
const override
49+
{
50+
return aggregate_common(h1, h2);
51+
}
52+
53+
const Type_handler *aggregate_for_min_max(const Type_handler *h1,
54+
const Type_handler *h2)
55+
const override
56+
{
57+
return aggregate_common(h1, h2);
58+
}
59+
60+
const Type_handler *aggregate_for_num_op(const Type_handler *h1,
61+
const Type_handler *h2)
62+
const override
63+
{
64+
return aggregate_common(h1, h2);
65+
}
66+
};
67+
68+
69+
static Type_collection_local type_collection_local;
70+
71+
72+
/*
73+
A more MySQL compatible Field:
74+
it does not set the UNSIGNED_FLAG.
75+
This is how MySQL's Field_timestampf works.
76+
*/
77+
class Field_mysql_timestampf :public Field_timestampf
78+
{
79+
public:
80+
Field_mysql_timestampf(const LEX_CSTRING &name,
81+
const Record_addr &addr,
82+
enum utype unireg_check_arg,
83+
TABLE_SHARE *share, decimal_digits_t dec_arg)
84+
:Field_timestampf(addr.ptr(), addr.null_ptr(), addr.null_bit(),
85+
unireg_check_arg, &name, share, dec_arg)
86+
{
87+
flags&= ~UNSIGNED_FLAG; // MySQL compatibility
88+
}
89+
void sql_type(String &str) const override
90+
{
91+
sql_type_opt_dec_comment(str,
92+
Field_mysql_timestampf::type_handler()->name(),
93+
dec, type_version_mysql56());
94+
}
95+
const Type_handler *type_handler() const override;
96+
};
97+
98+
99+
class Type_handler_mysql_timestamp2: public Type_handler_timestamp2
100+
{
101+
public:
102+
const Type_collection *type_collection() const override
103+
{
104+
return &type_collection_local;
105+
}
106+
Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *root,
107+
const LEX_CSTRING *name,
108+
const Record_addr &rec, const Bit_addr &bit,
109+
const Column_definition_attributes *attr,
110+
uint32 flags) const override
111+
{
112+
return new (root)
113+
Field_mysql_timestampf(*name, rec, attr->unireg_check, share,
114+
attr->temporal_dec(MAX_DATETIME_WIDTH));
115+
}
116+
void Column_definition_implicit_upgrade(Column_definition *c) const override
117+
{
118+
/*
119+
Suppress the automatic upgrade depending on opt_mysql56_temporal_format,
120+
derived from Type_handler_timestamp_common.
121+
*/
122+
}
123+
};
124+
125+
126+
static Type_handler_mysql_timestamp2 type_handler_mysql_timestamp2;
127+
128+
129+
const Type_handler *Field_mysql_timestampf::type_handler() const
130+
{
131+
return &type_handler_mysql_timestamp2;
132+
}
133+
134+
135+
static struct st_mariadb_data_type plugin_descriptor_type_mysql_timestamp=
136+
{
137+
MariaDB_DATA_TYPE_INTERFACE_VERSION,
138+
&type_handler_mysql_timestamp2
139+
};
140+
141+
142+
143+
/*************************************************************************/
144+
145+
maria_declare_plugin(type_mysql_timestamp)
146+
{
147+
MariaDB_DATA_TYPE_PLUGIN, // the plugin type (see include/mysql/plugin.h)
148+
&plugin_descriptor_type_mysql_timestamp, // pointer to type-specific plugin descriptor
149+
"type_mysql_timestamp", // plugin name
150+
"MariaDB Corporation", // plugin author
151+
"Data type TYPE_MYSQL_TIMESTAMP", // the plugin description
152+
PLUGIN_LICENSE_GPL, // the plugin license (see include/mysql/plugin.h)
153+
0, // Pointer to plugin initialization function
154+
0, // Pointer to plugin deinitialization function
155+
0x0100, // Numeric version 0xAABB means AA.BB version
156+
NULL, // Status variables
157+
NULL, // System variables
158+
"1.0", // String version representation
159+
MariaDB_PLUGIN_MATURITY_EXPERIMENTAL // Maturity(see include/mysql/plugin.h)*/
160+
}
161+
maria_declare_plugin_end;

sql/field.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3336,7 +3336,7 @@ class Field_timestamp_hires :public Field_timestamp_with_dec {
33363336
/**
33373337
TIMESTAMP(0..6) - MySQL56 version
33383338
*/
3339-
class Field_timestampf final :public Field_timestamp_with_dec {
3339+
class Field_timestampf :public Field_timestamp_with_dec {
33403340
void store_TIMEVAL(const timeval &tv) override;
33413341
public:
33423342
Field_timestampf(uchar *ptr_arg,

sql/sql_type.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7593,8 +7593,9 @@ extern Named_type_handler<Type_handler_time> type_handler_time;
75937593
extern Named_type_handler<Type_handler_time2> type_handler_time2;
75947594
extern Named_type_handler<Type_handler_datetime> type_handler_datetime;
75957595
extern Named_type_handler<Type_handler_datetime2> type_handler_datetime2;
7596-
extern Named_type_handler<Type_handler_timestamp> type_handler_timestamp;
7597-
extern Named_type_handler<Type_handler_timestamp2> type_handler_timestamp2;
7596+
7597+
extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_timestamp> type_handler_timestamp;
7598+
extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_timestamp2> type_handler_timestamp2;
75987599

75997600
extern Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff;
76007601

0 commit comments

Comments
 (0)