Commit a296c526 authored by Vladislav Vaintroub's avatar Vladislav Vaintroub Committed by Sergei Golubchik

MDEV-26713 UTF8 support on Windows, mysql_upgrade_service preparation

- Tolerate situation, when datadir for service seems invalid/non-existing
prior to upgrade. It could be that my.ini contains legacy ANSI characters
for the data directory. Those can't be read correctly by
mysql_upgrade_service, which uses a different ANSI codepage(UTF8)
.
- schedule upgrade_config_file at later stage, because once we
convert it to UTF-8 (followup patch), this will render config file uselss
with the older version of mariadbd.exe

- Refactor upgrade_conf_file.cc, prepare for UTF-8 conversion.
parent a4fc41b6
...@@ -374,13 +374,17 @@ static void change_service_config() ...@@ -374,13 +374,17 @@ static void change_service_config()
Write datadir to my.ini, after converting backslashes to Write datadir to my.ini, after converting backslashes to
unix style slashes. unix style slashes.
*/ */
if (service_properties.datadir[0])
{
strcpy_s(buf, MAX_PATH, service_properties.datadir); strcpy_s(buf, MAX_PATH, service_properties.datadir);
for(i= 0; buf[i]; i++) for (i= 0; buf[i]; i++)
{ {
if (buf[i] == '\\') if (buf[i] == '\\')
buf[i]= '/'; buf[i]= '/';
} }
WritePrivateProfileString("mysqld", "datadir",buf, service_properties.inifile); WritePrivateProfileString("mysqld", "datadir", buf,
service_properties.inifile);
}
/* /*
Remove basedir from defaults file, otherwise the service wont come up in Remove basedir from defaults file, otherwise the service wont come up in
...@@ -465,13 +469,8 @@ int main(int argc, char **argv) ...@@ -465,13 +469,8 @@ int main(int argc, char **argv)
} }
} }
old_mysqld_exe_exists = (GetFileAttributes(service_properties.mysqld_exe) != INVALID_FILE_ATTRIBUTES); old_mysqld_exe_exists= (GetFileAttributes(service_properties.mysqld_exe) !=
log("Phase %d/%d: Fixing server config file%s", ++phase, max_phases, my_ini_exists ? "" : "(skipped)"); INVALID_FILE_ATTRIBUTES);
snprintf(my_ini_bck, sizeof(my_ini_bck), "%s.BCK", service_properties.inifile);
CopyFile(service_properties.inifile, my_ini_bck, FALSE);
upgrade_config_file(service_properties.inifile);
bool do_start_stop_server = old_mysqld_exe_exists && initial_service_state != SERVICE_RUNNING; bool do_start_stop_server = old_mysqld_exe_exists && initial_service_state != SERVICE_RUNNING;
log("Phase %d/%d: Start and stop server in the old version, to avoid crash recovery %s", ++phase, max_phases, log("Phase %d/%d: Start and stop server in the old version, to avoid crash recovery %s", ++phase, max_phases,
...@@ -526,6 +525,14 @@ int main(int argc, char **argv) ...@@ -526,6 +525,14 @@ int main(int argc, char **argv)
start_duration_ms += 500; start_duration_ms += 500;
} }
} }
log("Phase %d/%d: Fixing server config file%s", ++phase, max_phases,
my_ini_exists ? "" : "(skipped)");
snprintf(my_ini_bck, sizeof(my_ini_bck), "%s.BCK",
service_properties.inifile);
CopyFile(service_properties.inifile, my_ini_bck, FALSE);
upgrade_config_file(service_properties.inifile);
/* /*
Start mysqld.exe as non-service skipping privileges (so we do not Start mysqld.exe as non-service skipping privileges (so we do not
care about the password). But disable networking and enable pipe care about the password). But disable networking and enable pipe
......
...@@ -158,51 +158,103 @@ static int cmp_strings(const void* a, const void *b) ...@@ -158,51 +158,103 @@ static int cmp_strings(const void* a, const void *b)
return strcmp((const char *)a, *(const char **)b); return strcmp((const char *)a, *(const char **)b);
} }
/**
Convert file from a previous version, by removing #define MY_INI_SECTION_SIZE 32 * 1024 + 3
*/
int upgrade_config_file(const char *myini_path)
int fix_section(const char *myini_path, const char *section_name,
bool is_server)
{ {
#define MY_INI_SECTION_SIZE 32*1024 +3 if (!is_server)
return 0;
static char section_data[MY_INI_SECTION_SIZE]; static char section_data[MY_INI_SECTION_SIZE];
for (const char *section_name : { "mysqld","server","mariadb" }) DWORD size= GetPrivateProfileSection(section_name, section_data,
{ MY_INI_SECTION_SIZE, myini_path);
DWORD size = GetPrivateProfileSection(section_name, section_data, MY_INI_SECTION_SIZE, myini_path);
if (size == MY_INI_SECTION_SIZE - 2) if (size == MY_INI_SECTION_SIZE - 2)
{ {
return -1; return -1;
} }
for (char *keyval = section_data; *keyval; keyval += strlen(keyval) + 1) for (char *keyval= section_data; *keyval; keyval += strlen(keyval)+1)
{ {
char varname[256]; char varname[256];
char *key_end = strchr(keyval, '='); char *value;
char *key_end= strchr(keyval, '=');
if (!key_end) if (!key_end)
key_end = keyval+ strlen(keyval); key_end= keyval + strlen(keyval);
if (key_end - keyval > sizeof(varname)) if (key_end - keyval > sizeof(varname))
continue; continue;
// copy and normalize (convert dash to underscore) to variable names
for (char *p = keyval, *q = varname;; p++,q++) value= key_end + 1;
// Check if variable should be removed from config.
// First, copy and normalize (convert dash to underscore) to variable
// names
for (char *p= keyval, *q= varname;; p++, q++)
{ {
if (p == key_end) if (p == key_end)
{ {
*q = 0; *q= 0;
break; break;
} }
*q = (*p == '-') ? '_' : *p; *q= (*p == '-') ? '_' : *p;
} }
const char *v = (const char *)bsearch(varname, removed_variables, sizeof(removed_variables) / sizeof(removed_variables[0]), const char *v= (const char *) bsearch(varname, removed_variables, sizeof(removed_variables) / sizeof(removed_variables[0]),
sizeof(char *), cmp_strings); sizeof(char *), cmp_strings);
if (v) if (v)
{ {
fprintf(stdout, "Removing variable '%s' from config file\n", varname); fprintf(stdout, "Removing variable '%s' from config file\n", varname);
// delete variable // delete variable
*key_end = 0; *key_end= 0;
WritePrivateProfileString(section_name, keyval, 0, myini_path); WritePrivateProfileString(section_name, keyval, 0, myini_path);
} }
} }
return 0;
}
static bool is_mariadb_section(const char *name, bool *is_server)
{
if (strncmp(name, "mysql", 5)
&& strncmp(name, "mariadb", 7)
&& strcmp(name, "client")
&& strcmp(name, "client-server")
&& strcmp(name, "server"))
{
return false;
}
for (const char *section_name : {"mysqld", "server", "mariadb"})
if (*is_server= !strcmp(section_name, name))
break;
return *is_server;
}
/**
Convert file from a previous version, by removing obsolete variables
Also, fix values to be UTF8, if MariaDB is running in utf8 mode
*/
int upgrade_config_file(const char *myini_path)
{
static char all_sections[MY_INI_SECTION_SIZE];
int sz= GetPrivateProfileSectionNamesA(all_sections, MY_INI_SECTION_SIZE,
myini_path);
if (!sz)
return 0;
if (sz > MY_INI_SECTION_SIZE - 2)
{
fprintf(stderr, "Too many sections in config file\n");
return -1;
}
for (char *section= all_sections; *section; section+= strlen(section) + 1)
{
bool is_server_section;
if (is_mariadb_section(section, &is_server_section))
fix_section(myini_path, section, is_server_section);
} }
return 0; return 0;
} }
...@@ -134,6 +134,20 @@ static void get_datadir_from_ini(const char *ini, char *service_name, char *data ...@@ -134,6 +134,20 @@ static void get_datadir_from_ini(const char *ini, char *service_name, char *data
} }
static int fix_and_check_datadir(mysqld_service_properties *props)
{
normalize_path(props->datadir, MAX_PATH);
/* Check if datadir really exists */
if (GetFileAttributes(props->datadir) != INVALID_FILE_ATTRIBUTES)
return 0;
/*
It is possible, that datadir contains some unconvertable character.
We just pretend not to know what's the data directory
*/
props->datadir[0]= 0;
return 0;
}
/* /*
Retrieve some properties from windows mysqld service binary path. Retrieve some properties from windows mysqld service binary path.
We're interested in ini file location and datadir, and also in version of We're interested in ini file location and datadir, and also in version of
...@@ -284,16 +298,9 @@ int get_mysql_service_properties(const wchar_t *bin_path, ...@@ -284,16 +298,9 @@ int get_mysql_service_properties(const wchar_t *bin_path,
} }
} }
if (props->datadir[0]) if (props->datadir[0] == 0 || fix_and_check_datadir(props))
{
normalize_path(props->datadir, MAX_PATH);
/* Check if datadir really exists */
if (GetFileAttributes(props->datadir) == INVALID_FILE_ATTRIBUTES)
goto end;
}
else
{ {
/* There is no datadir in ini file, bail out.*/ /* There is no datadir in ini file, or non-existing dir, bail out.*/
goto end; goto end;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment