Skip to content

Commit 37e00a1

Browse files
committedFeb 14, 2024
WL#16039 Get version and LTS information from server installation and data directory
Add information regarding long term support and server version: * Add an entry to INFO_SRC stating whether the build is an innovation release or not. This makes it possible to determine the maturity for a given installation. * Add a text file "mysql_upgrade_history" to be stored in the data directory. The file will be created during initialize, or if it does not exist already. Add an entry to the file for each upgrade, even if it is just a restart with a new update release. Change-Id: I79dcb741123a61d0bfdbe74d01e06c687d19a03a
1 parent f9e50ed commit 37e00a1

18 files changed

+277
-16
lines changed
 

‎MYSQL_VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ MYSQL_VERSION_MAJOR=8
22
MYSQL_VERSION_MINOR=4
33
MYSQL_VERSION_PATCH=0
44
MYSQL_VERSION_EXTRA=
5-
MYSQL_VERSION_STABILITY="LTS"
5+
MYSQL_VERSION_MATURITY="LTS"

‎cmake/info_macros.cmake.in

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ SET(MAJOR_VERSION "@MAJOR_VERSION@")
3535
SET(MINOR_VERSION "@MINOR_VERSION@")
3636
SET(PATCH_VERSION "@PATCH_VERSION@")
3737
SET(EXTRA_VERSION "@EXTRA_VERSION@")
38+
SET(MYSQL_VERSION_MATURITY @MYSQL_VERSION_MATURITY@)
3839
SET(CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@")
3940
SET(CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@")
4041
SET(CMAKE_GENERATOR "@CMAKE_GENERATOR@")
@@ -108,6 +109,9 @@ MACRO(CREATE_INFO_SRC target_dir)
108109
IF(DEFINED EXTRA_VERSION)
109110
FILE(APPEND ${INFO_SRC} "${EXTRA_VERSION}")
110111
ENDIF()
112+
IF(DEFINED MYSQL_VERSION_MATURITY)
113+
FILE(APPEND ${INFO_SRC} "\nMySQL release maturity ${MYSQL_VERSION_MATURITY}")
114+
ENDIF()
111115
FILE(APPEND ${INFO_SRC} "\n")
112116

113117
ELSEIF(EXISTS ${INFO_SRC})

‎cmake/mysql_version.cmake

+7-7
Original file line numberDiff line numberDiff line change
@@ -63,27 +63,27 @@ MACRO(GET_MYSQL_VERSION)
6363
MESSAGE(FATAL_ERROR "MYSQL_VERSION file cannot be parsed.")
6464
ENDIF()
6565

66-
MYSQL_GET_CONFIG_VALUE("MYSQL_VERSION_STABILITY" MYSQL_VERSION_STABILITY)
66+
MYSQL_GET_CONFIG_VALUE("MYSQL_VERSION_MATURITY" MYSQL_VERSION_MATURITY)
6767

68-
IF(NOT DEFINED MYSQL_VERSION_STABILITY)
68+
IF(NOT DEFINED MYSQL_VERSION_MATURITY)
6969
MESSAGE(FATAL_ERROR "MYSQL_VERSION file cannot be parsed, missing version attributes.")
7070
ENDIF()
7171

72-
IF(NOT MYSQL_VERSION_STABILITY STREQUAL "\"LTS\"" AND
73-
NOT MYSQL_VERSION_STABILITY STREQUAL "\"INNOVATION\"")
74-
MESSAGE(FATAL_ERROR "MYSQL_VERSION_STABILITY can be set to INNOVATION or LTS.")
72+
IF(NOT MYSQL_VERSION_MATURITY STREQUAL "\"LTS\"" AND
73+
NOT MYSQL_VERSION_MATURITY STREQUAL "\"INNOVATION\"")
74+
MESSAGE(FATAL_ERROR "MYSQL_VERSION_MATURITY can be set to INNOVATION or LTS.")
7575
ENDIF()
7676

7777
# Versions like 8.0.x, 8.4.x, and x.7.y (x > 8) should be LTS
7878
IF ((MAJOR_VERSION EQUAL "8" AND MINOR_VERSION EQUAL "0" AND PATCH_VERSION GREATER "34") OR
7979
(MAJOR_VERSION EQUAL "8" AND MINOR_VERSION EQUAL "4") OR
8080
(MAJOR_VERSION GREATER "8" AND MINOR_VERSION EQUAL "7"))
81-
IF (NOT MYSQL_VERSION_STABILITY STREQUAL "\"LTS\"")
81+
IF (NOT MYSQL_VERSION_MATURITY STREQUAL "\"LTS\"")
8282
MESSAGE(FATAL_ERROR "Version ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION} should "
8383
"be an LTS release.")
8484
ENDIF()
8585
ELSE()
86-
IF (NOT MYSQL_VERSION_STABILITY STREQUAL "\"INNOVATION\"")
86+
IF (NOT MYSQL_VERSION_MATURITY STREQUAL "\"INNOVATION\"")
8787
MESSAGE(FATAL_ERROR "Version ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION} should "
8888
"be an innovation release.")
8989
ENDIF()

‎include/mysql_version.h.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#define MYSQL_BASE_VERSION "mysqld-@MYSQL_BASE_VERSION@"
1414
#define MYSQL_SERVER_SUFFIX_DEF "@MYSQL_SERVER_SUFFIX@"
1515
#define MYSQL_VERSION_ID @MYSQL_VERSION_ID@
16-
#define MYSQL_VERSION_STABILITY @MYSQL_VERSION_STABILITY@
16+
#define MYSQL_VERSION_MATURITY @MYSQL_VERSION_MATURITY@
1717
#define MYSQL_PORT @MYSQL_TCP_PORT@
1818
#define MYSQL_ADMIN_PORT @MYSQL_ADMIN_TCP_PORT@
1919
#define MYSQL_PORT_DEFAULT @MYSQL_TCP_PORT_DEFAULT@

‎mysql-test/include/mysql_upgrade_cleanup.inc

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
let $MYSQLD_DATADIR= `select @@datadir`;
44

5+
# The server should have created a mysql_upgrade_history file,
6+
# so the following command will do a clean-up.
7+
--remove_file $MYSQLD_DATADIR/mysql_upgrade_history
8+
59
#
610
# See mysql_upgrade_preparation.inc
711
#
@@ -24,4 +28,3 @@ let $MYSQLD_DATADIR= `select @@datadir`;
2428

2529
--enable_result_log
2630
--enable_query_log
27-

‎mysql-test/r/initialize.result

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
# Try --initialize
66
#
77
# Run the server with --initialize
8+
# Make sure the mysql_upgrade_history file is created with "initialize" tag
9+
Pattern "initialize" found
10+
# Remove the upgrade history file
811
extract the root password
912
password found
1013
# Restart the server against DDIR
@@ -14,6 +17,9 @@ SELECT 1;
1417
ERROR HY000: You must reset your password using ALTER USER statement before executing this statement.
1518
# reset the password
1619
SET PASSWORD='';
20+
# Make sure the mysql_upgrade_history file is recreated without "initialize"
21+
include/assert_grep.inc [Assert that the mysql_upgrade_history file does not contain the initialize tag after restart]
22+
# Copy upgrade history file
1723
# Check the count of columns in mysql
1824
# check the user account
1925
SELECT user, host, plugin, LENGTH(authentication_string)
@@ -46,6 +52,8 @@ SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA = 'sys';
4652
COUNT(*)
4753
2
4854
CREATE DATABASE test;
55+
# Restart the server against DDIR again
56+
# Make sure the upgrade history file has not changed since the version is the same
4957
# shut server down
5058
# Server is down
5159
# close the test connection
@@ -57,7 +65,11 @@ CREATE DATABASE test;
5765
#
5866
# create bootstrap file
5967
# Run the server with --initialize-insecure --init-file
68+
# Make sure the mysql_upgrade_history file is created
69+
# Create history file with fake verson and make copy
70+
# This will make sure the file is changed upon restart
6071
# Restart the server against DDIR
72+
# Make sure the upgrade history file has changed
6173
# connect as root
6274
# must pass: no password expiration
6375
SELECT 1;

‎mysql-test/r/mysql_upgrade.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# restart:--upgrade=FORCE --sql-mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,ANSI_QUOTES --log-error=MYSQLD_LOG
22
# There should be no errors
33
Pattern "\[ERROR\]" not found
4+
# mysql_upgrade_history file should be created successfully.
45
# Restart server with defaults
56
# restart:
67
#

‎mysql-test/t/initialize.test

+42
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ let MYSQLD_LOG=$MYSQL_TMP_DIR/server.log;
77
let extra_args=--no-defaults --innodb_dedicated_server=OFF --console --loose-skip-auto_generate_certs --loose-skip-sha256_password_auto_generate_rsa_keys --skip-ssl --basedir=$BASEDIR --lc-messages-dir=$MYSQL_SHAREDIR;
88
let BOOTSTRAP_SQL=$MYSQL_TMP_DIR/tiny_bootstrap.sql;
99
let PASSWD_FILE=$MYSQL_TMP_DIR/password_file.txt;
10+
let UPGRADE_HISTORY_FILE=$DDIR/mysql_upgrade_history;
1011

1112
--echo # Save the count of columns in mysql
1213
--let $mysql_cnt=`SELECT COUNT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='mysql'`
@@ -24,6 +25,15 @@ let PASSWD_FILE=$MYSQL_TMP_DIR/password_file.txt;
2425
--echo # Run the server with --initialize
2526
--exec $MYSQLD $extra_args --initialize --datadir=$DDIR --log-error-verbosity=1 > $MYSQLD_LOG 2>&1
2627

28+
--echo # Make sure the mysql_upgrade_history file is created with "initialize" tag
29+
--file_exists $UPGRADE_HISTORY_FILE
30+
--let SEARCH_FILE= $UPGRADE_HISTORY_FILE
31+
--let SEARCH_PATTERN= initialize
32+
--source include/search_pattern.inc
33+
34+
--echo # Remove the upgrade history file
35+
--remove_file $UPGRADE_HISTORY_FILE
36+
2737
--echo extract the root password
2838
--perl
2939
use strict;
@@ -65,6 +75,17 @@ SELECT 1;
6575
--echo # reset the password
6676
SET PASSWORD='';
6777

78+
--echo # Make sure the mysql_upgrade_history file is recreated without "initialize"
79+
--file_exists $DDIR/mysql_upgrade_history
80+
--let $assert_text= Assert that the mysql_upgrade_history file does not contain the initialize tag after restart
81+
--let $assert_file= $UPGRADE_HISTORY_FILE
82+
--let $assert_select= 'initialize'
83+
--let $assert_count= 0
84+
--source include/assert_grep.inc
85+
86+
--echo # Copy upgrade history file
87+
--copy_file $UPGRADE_HISTORY_FILE $DDIR/old_upgrade_history_file
88+
6889
--echo # Check the count of columns in mysql
6990
--let $cnt=`SELECT COUNT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='mysql';`
7091
if ($cnt != $mysql_cnt)
@@ -94,6 +115,13 @@ SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA = 'sys';
94115

95116
CREATE DATABASE test;
96117

118+
--echo # Restart the server against DDIR again
119+
--exec echo "restart:--datadir=$DDIR " > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
120+
--source include/wait_until_connected_again.inc
121+
122+
--echo # Make sure the upgrade history file has not changed since the version is the same
123+
--diff_files $UPGRADE_HISTORY_FILE $DDIR/old_upgrade_history_file
124+
97125
--echo # shut server down
98126
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
99127
--shutdown_server
@@ -129,11 +157,25 @@ EOF
129157
--echo # Run the server with --initialize-insecure --init-file
130158
--exec $MYSQLD $extra_args --initialize-insecure --datadir=$DDIR --init-file=$BOOTSTRAP_SQL > $MYSQLD_LOG 2>&1
131159

160+
--echo # Make sure the mysql_upgrade_history file is created
161+
--file_exists $DDIR/mysql_upgrade_history
162+
163+
--echo # Create history file with fake verson and make copy
164+
--echo # This will make sure the file is changed upon restart
165+
--remove_file $UPGRADE_HISTORY_FILE
166+
--write_file $UPGRADE_HISTORY_FILE
167+
{"file_format":"1","upgrade_history":[{"date":"2024-01-08 13:27:26","version":"fake_version","stability":"LTS","initialize":true}]}
168+
EOF
169+
--copy_file $UPGRADE_HISTORY_FILE $DDIR/fake_upgrade_history_file
132170

133171
--echo # Restart the server against DDIR
134172
--exec echo "restart:--datadir=$DDIR " > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
135173
--source include/wait_until_connected_again.inc
136174

175+
--echo # Make sure the upgrade history file has changed
176+
--error 2
177+
--diff_files $UPGRADE_HISTORY_FILE $DDIR/fake_upgrade_history_file
178+
137179
--echo # connect as root
138180
connect(root_con,localhost,root,,mysql);
139181

‎mysql-test/t/mysql_80_inplace_upgrade.test

+8
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ if ($lctn == 2)
106106
--let SEARCH_PATTERN= \[ERROR\]
107107
--source include/search_pattern.inc
108108

109+
# It should have created a file in the MySQL Servers datadir
110+
--let $MYSQLD_DATADIR= `select @@datadir`
111+
--file_exists $MYSQLD_DATADIR/mysql_upgrade_history
112+
109113
--echo ###########################################################################
110114
--echo # Test upgrade of help tables
111115
--echo ###########################################################################
@@ -160,6 +164,10 @@ SELECT COUNT(*) != 0 FROM mysql.help_topic;
160164
--let SEARCH_PATTERN= \[ERROR\]
161165
--source include/search_pattern.inc
162166

167+
# It should have created a file in the MySQL Servers datadir
168+
--let $MYSQLD_DATADIR= `select @@datadir`
169+
--file_exists $MYSQLD_DATADIR/mysql_upgrade_history
170+
163171
--echo # Stop DB server
164172
--let $shutdown_server_timeout = 300
165173
--source include/shutdown_mysqld.inc

‎mysql-test/t/mysql_inplace_upgrade.test

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ DROP DATABASE db1;
3636
DROP DATABASE db2;
3737

3838
--let $restart_parameters = restart:
39+
--remove_file $MYSQLD_DATADIR/mysql_upgrade_history
3940

4041
--echo ###########################################################################
4142
--echo # Stop the default mtr server

‎mysql-test/t/mysql_upgrade.test

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
--let SEARCH_PATTERN= \[ERROR\]
2323
--source include/search_pattern.inc
2424

25+
--echo # mysql_upgrade_history file should be created successfully.
26+
--file_exists $MYSQLD_DATADIR/mysql_upgrade_history
27+
2528
--remove_file $MYSQLD_LOG
2629

2730
--echo # Restart server with defaults

‎packaging/deb-in/extra/mysql-helpers

+5
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ verify_ready() {
112112
chown --reference="${MYSQLDATA}" "${MYSQLDATA}/mysql_upgrade_info"
113113
fi
114114

115+
# Ensures that the mysql_upgrade_history file is owned by the database
116+
if [ -O "${MYSQLDATA}/mysql_upgrade_history" ]; then
117+
chown --reference="${MYSQLDATA}" "${MYSQLDATA}/mysql_upgrade_history"
118+
fi
119+
115120
if [ "${MYSQLFILES}" != "NULL" -a ! -d "${MYSQLFILES}" -a ! -L "${MYSQLFILES}" ]; then
116121
if [ "$(dirname ${MYSQLFILES})" = "/var/lib" ]; then
117122
install -d -m0770 -omysql -gmysql "${MYSQLFILES}"

‎scripts/systemd/mysqld_pre_systemd.in

+13
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,22 @@ install_validate_password_sql_file () {
4646
echo $initfile
4747
}
4848

49+
fix_mysql_upgrade_history () {
50+
datadir=$(get_option mysqld datadir "/var/lib/mysql${instance:+-$instance}" $instance)
51+
if [ -d "$datadir" ] && [ -O "$datadir/mysql_upgrade_history" ]; then
52+
chown --reference="$datadir" "$datadir/mysql_upgrade_history"
53+
if [ -x /usr/bin/chcon ]; then
54+
/usr/bin/chcon --reference="$datadir" "$datadir/mysql_upgrade_history" > /dev/null 2>&1
55+
fi
56+
fi
57+
}
58+
4959
install_db () {
5060
# Note: something different than datadir=/var/lib/mysql requires SELinux policy changes (in enforcing mode)
5161

62+
# mysql_upgrade_history file should be owned by mysql user since MySQL 8.4 (new file in 8.4)
63+
fix_mysql_upgrade_history
64+
5265
# No automatic init wanted
5366
[ -e /etc/sysconfig/mysql ] && . /etc/sysconfig/mysql
5467
[ -n "$NO_INIT" ] && exit 0

‎share/messages_to_error_log.txt

+3
Original file line numberDiff line numberDiff line change
@@ -12683,6 +12683,9 @@ ER_IB_LONG_ROLLBACK_FULL
1268312683
ER_IB_LONG_ROLLBACK
1268412684
eng "Still rolling back transaction %llu; %llu undo records rolled back out of %llu total (%lu%% complete)."
1268512685

12686+
ER_INVALID_FILE_FORMAT
12687+
eng "Invalid format of file '%s'."
12688+
1268612689
# DO NOT add server-to-client messages here;
1268712690
# they go in messages_to_clients.txt
1268812691
# in the same directory as this file.

‎sql/dd/impl/bootstrap/bootstrapper.cc

+8-5
Original file line numberDiff line numberDiff line change
@@ -1181,12 +1181,12 @@ bool initialize_dd_properties(THD *thd) {
11811181
Get information from DD properties. Do this after 8.2.0 / 8.0.35.
11821182
Older versions do not have the required information available.
11831183
*/
1184-
String_type mysql_version_stability{"INNOVATION"};
1184+
String_type mysql_version_maturity{"INNOVATION"};
11851185
uint server_downgrade_threshold = 0;
11861186
uint server_upgrade_threshold = 0;
11871187
/* purecov: begin inspected */
11881188
if (actual_server_version >= 80035 && actual_server_version != 80100) {
1189-
(void)getprop(thd, "MYSQL_VERSION_STABILITY", &mysql_version_stability,
1189+
(void)getprop(thd, "MYSQL_VERSION_STABILITY", &mysql_version_maturity,
11901190
false, WARNING_LEVEL);
11911191
(void)getprop(thd, "SERVER_DOWNGRADE_THRESHOLD",
11921192
&server_downgrade_threshold, false, WARNING_LEVEL);
@@ -1199,7 +1199,7 @@ bool initialize_dd_properties(THD *thd) {
11991199
if (MYSQL_VERSION_ID > actual_server_version) {
12001200
// This is an upgrade attempt.
12011201
if ((MYSQL_VERSION_ID / 10000) != (actual_server_version / 10000) &&
1202-
(mysql_version_stability != "LTS" ||
1202+
(mysql_version_maturity != "LTS" ||
12031203
actual_server_version / 100 == 800 ||
12041204
MYSQL_VERSION_ID / 10000 != actual_server_version / 10000 + 1)) {
12051205
LogErr(ERROR_LEVEL, ER_INVALID_SERVER_UPGRADE_NOT_LTS,
@@ -1862,7 +1862,7 @@ bool update_versions(THD *thd) {
18621862
dd::DD_VERSION_MINOR_DOWNGRADE_THRESHOLD) ||
18631863
setprop(thd, "SDI_VERSION", dd::SDI_VERSION) ||
18641864
setprop(thd, "LCTN", lower_case_table_names) ||
1865-
setprop(thd, "MYSQL_VERSION_STABILITY", MYSQL_VERSION_STABILITY) ||
1865+
setprop(thd, "MYSQL_VERSION_STABILITY", MYSQL_VERSION_MATURITY) ||
18661866
setprop(thd, "SERVER_DOWNGRADE_THRESHOLD",
18671867
SERVER_DOWNGRADE_THRESHOLD) ||
18681868
setprop(thd, "SERVER_UPGRADE_THRESHOLD", SERVER_UPGRADE_THRESHOLD) ||
@@ -1899,7 +1899,7 @@ bool update_versions(THD *thd) {
18991899
setprop(thd, "MYSQLD_VERSION_HI", MYSQL_VERSION_ID)) ||
19001900
(mysqld_version != MYSQL_VERSION_ID &&
19011901
(setprop(thd, "MYSQLD_VERSION", MYSQL_VERSION_ID) ||
1902-
setprop(thd, "MYSQL_VERSION_STABILITY", MYSQL_VERSION_STABILITY) ||
1902+
setprop(thd, "MYSQL_VERSION_STABILITY", MYSQL_VERSION_MATURITY) ||
19031903
setprop(thd, "SERVER_DOWNGRADE_THRESHOLD",
19041904
SERVER_DOWNGRADE_THRESHOLD) ||
19051905
setprop(thd, "SERVER_UPGRADE_THRESHOLD", SERVER_UPGRADE_THRESHOLD))))
@@ -1953,6 +1953,9 @@ bool update_versions(THD *thd) {
19531953
}
19541954
}
19551955

1956+
/* Keep a record of upgrades, including update releases. */
1957+
upgrade::update_upgrade_history_file(opt_initialize);
1958+
19561959
#ifndef NDEBUG
19571960
/*
19581961
Debug code to make sure that after updating version numbers, regardless

‎sql/dd/impl/tables/dd_properties.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ DD_properties::DD_properties() : m_properties() {
9595
upgrade.
9696
MYSQLD_VERSION_UPGRADED The server version of the last
9797
completed successful upgrade.
98-
MYSQL_VERSION_STABILITY Stability type of the GA release of the
98+
MYSQL_VERSION_STABILITY Maturity (should have had a different
99+
key) of the GA release of the
99100
server version MYSQLD_VERSION. Should be
100101
"LTS" or "INNOVATION". In the source code,
101102
we take a specific action if the value is

‎sql/dd/impl/upgrade/server.cc

+148
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
#include <fcntl.h>
2828
#include <stdint.h>
2929
#include <sys/types.h>
30+
#include <chrono>
31+
#include <iomanip>
32+
#include <sstream>
33+
#include <string>
3034

3135
#include "sql/dd/upgrade/server.h"
3236
#ifdef HAVE_UNISTD_H
@@ -35,9 +39,14 @@
3539
#include <vector>
3640

3741
#include "my_dbug.h"
42+
#include "my_rapidjson_size_t.h"
3843
#include "mysql/components/services/log_builtins.h"
44+
#include "mysql/psi/mysql_file.h"
3945
#include "mysql/strings/m_ctype.h"
4046
#include "nulls.h"
47+
#include "rapidjson/document.h"
48+
#include "rapidjson/prettywriter.h"
49+
#include "rapidjson/stringbuffer.h"
4150
#include "scripts/mysql_fix_privilege_tables_sql.h"
4251
#include "scripts/sql_commands_system_tables_data_fix.h"
4352
#include "scripts/sql_firewall_sp_firewall_group_delist.h"
@@ -811,8 +820,145 @@ static bool check_views(THD *thd, std::unique_ptr<Schema> &schema,
811820
return thd->dd_client()->foreach<dd::View>(view_key.get(), process_view);
812821
}
813822

823+
/* Make sure the old unsupported "mysql_upgrade_info" file is removed. */
824+
static void remove_legacy_upgrade_info_file() {
825+
char upgrade_file[FN_REFLEN] = {0};
826+
fn_format(upgrade_file, "mysql_upgrade_info", mysql_real_data_home_ptr, "",
827+
MYF(0));
828+
if (!my_access(upgrade_file, F_OK))
829+
std::ignore = mysql_file_delete(key_file_misc, upgrade_file, MYF(0));
830+
}
814831
} // namespace
815832

833+
/*
834+
Maintain a file named "mysql_upgrade_history" in the data directory.
835+
836+
The file will contain one entry for each upgrade. The format is structured
837+
text on JSON format.
838+
839+
Errors will be written as warnings to the error log; if we e.g. fail to
840+
open the upgrade history file, we will not abort the server since this file
841+
is not considered a critical feature of the server.
842+
843+
@param initialize If this is the initialization of the data directory.
844+
*/
845+
void update_upgrade_history_file(bool initialize) {
846+
/* Name of the "mysql_upgrade_history" file. */
847+
char upgrade_file[FN_REFLEN] = {0};
848+
fn_format(upgrade_file, "mysql_upgrade_history", mysql_real_data_home_ptr, "",
849+
MYF(0));
850+
851+
/* JSON keys. */
852+
constexpr char k_file_format[] = "file_format";
853+
constexpr char k_upgrade_history[] = "upgrade_history";
854+
constexpr char k_date[] = "date";
855+
constexpr char k_version[] = "version";
856+
constexpr char k_maturity[] = "maturity";
857+
constexpr char k_initialize[] = "initialize";
858+
constexpr char v_file_format[] = "1";
859+
860+
/* If > max entries, we keep the first and the (max - 1) last ones. */
861+
constexpr int MAX_HISTORY_SIZE = 1000;
862+
static_assert(MAX_HISTORY_SIZE >= 2,
863+
"The upgrade history should contain at least the first "
864+
"and last entry.");
865+
using namespace rapidjson;
866+
Document doc;
867+
868+
/* Open file if it exists, auto close on return. */
869+
auto deleter = [&](FILE *ptr) {
870+
if (ptr != nullptr) my_fclose(ptr, MYF(0));
871+
};
872+
std::unique_ptr<FILE, decltype(deleter)> fp(nullptr, deleter);
873+
874+
MY_STAT sa;
875+
char errbuf[MYSYS_STRERROR_SIZE];
876+
bool file_exists = (my_stat(upgrade_file, &sa, MYF(0)) != nullptr);
877+
bool append = file_exists; // Append to current doc if possible.
878+
879+
/* If the file exists, read the doc and see if it is valid. */
880+
if (file_exists) {
881+
fp.reset(my_fopen(upgrade_file, O_RDONLY, MYF(0)));
882+
if (fp == nullptr) {
883+
LogErr(WARNING_LEVEL, ER_SERVER_CANT_OPEN_FILE, upgrade_file, my_errno(),
884+
my_strerror(errbuf, sizeof(errbuf), my_errno()));
885+
return;
886+
}
887+
888+
/* Read contents into buffer. */
889+
char buff[512] = {0};
890+
std::string parsed_value;
891+
do {
892+
parsed_value.append(buff);
893+
buff[0] = '\0';
894+
} while (fgets(buff, sizeof(buff) - 1, fp.get()));
895+
896+
/* Parse JSON, check expected format. */
897+
ParseResult ok = doc.Parse(parsed_value.c_str());
898+
if (!(ok && doc.IsObject() && doc[k_file_format].IsString() &&
899+
doc[k_upgrade_history].IsArray())) {
900+
LogErr(WARNING_LEVEL, ER_INVALID_FILE_FORMAT, upgrade_file);
901+
append = false; // Cannot append, must overwrite with an empty doc.
902+
}
903+
}
904+
905+
/* If the file existed with valid contents, append, otherwise, overwrite. */
906+
if (append) {
907+
/* If current version is same as last entry, return. */
908+
Value &hist = doc[k_upgrade_history].GetArray();
909+
int count = hist.Size();
910+
if (count > 0 &&
911+
!strcmp(hist[count - 1][k_version].GetString(), server_version))
912+
return;
913+
914+
/* If the doc contains too many entries, remove from the second and on. */
915+
int remove_count = (count - MAX_HISTORY_SIZE) + 1;
916+
if (remove_count > 0) {
917+
hist.Erase(hist.Begin() + 1, hist.Begin() + remove_count + 1);
918+
}
919+
} else {
920+
/* Otherwise, if no file existed, initialize an empty JSON document. */
921+
doc.SetObject();
922+
doc.AddMember(k_file_format, v_file_format, doc.GetAllocator());
923+
Value history(kArrayType);
924+
doc.AddMember(k_upgrade_history, history, doc.GetAllocator());
925+
}
926+
927+
/* Append timestamp, MYSQL_SERVER_VERSION and LTS info to version array. */
928+
std::stringstream str;
929+
std::time_t sec = my_micro_time() / 1000000;
930+
str << std::put_time(std::gmtime(&sec), "%F %T");
931+
Value date(str.str().c_str(), str.str().size(), doc.GetAllocator());
932+
Value version(server_version, strlen(server_version), doc.GetAllocator());
933+
Value maturity(MYSQL_VERSION_MATURITY);
934+
935+
Value new_version;
936+
new_version.SetObject();
937+
new_version.AddMember(k_date, date, doc.GetAllocator());
938+
new_version.AddMember(k_version, version, doc.GetAllocator());
939+
new_version.AddMember(k_maturity, maturity, doc.GetAllocator());
940+
if (initialize) {
941+
Value init(true);
942+
new_version.AddMember(k_initialize, init, doc.GetAllocator());
943+
}
944+
doc[k_upgrade_history].GetArray().PushBack(new_version, doc.GetAllocator());
945+
946+
/* Reopen the file, which is auto closed on function return. */
947+
fp.reset(my_fopen(upgrade_file, O_CREAT | O_TRUNC | O_WRONLY, MYF(0)));
948+
if (fp == nullptr) {
949+
LogErr(WARNING_LEVEL, ER_SERVER_CANT_OPEN_FILE, upgrade_file, my_errno(),
950+
my_strerror(errbuf, sizeof(errbuf), my_errno()));
951+
return;
952+
}
953+
954+
/* Write JSON document to a buffer and further to file with a newline. */
955+
rapidjson::StringBuffer buffer;
956+
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
957+
doc.Accept(writer);
958+
fputs(buffer.GetString(), fp.get());
959+
fputs("\n", fp.get());
960+
}
961+
816962
/*
817963
This function runs checks on the database before running the upgrade to make
818964
sure that the database is ready to be upgraded to a newer version. New checks
@@ -1040,6 +1186,8 @@ bool upgrade_system_schemas(THD *thd) {
10401186
dd::tables::DD_properties::instance().set(
10411187
thd, "MYSQLD_VERSION_UPGRADED", MYSQL_VERSION_ID);
10421188
}
1189+
1190+
remove_legacy_upgrade_info_file();
10431191
bootstrap_error_handler.set_log_error(true);
10441192

10451193
if (dd::bootstrap::DD_bootstrap_ctx::instance().is_server_patch_downgrade()) {

‎sql/dd/impl/upgrade/server.h

+14
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,20 @@ class Routine_event_context_guard {
149149
~Routine_event_context_guard();
150150
};
151151

152+
/**
153+
Maintain a file named "mysql_upgrade_history" in the data directory.
154+
155+
The file will contain one entry for each upgrade. The format is structured
156+
text on JSON format.
157+
158+
Errors will be written as warnings to the error log; if we e.g. fail to
159+
open the upgrade history file, we will not abort the server since this file
160+
is not considered a critical feature of the server.
161+
162+
@param initialize If this is the initialization of the data directory.
163+
*/
164+
void update_upgrade_history_file(bool initialize);
165+
152166
/**
153167
Performs validation on server metadata.
154168

0 commit comments

Comments
 (0)
Please sign in to comment.