Skip to content

Commit e222e44

Browse files
committed
Merge branch 'preview-10.8-MDEV-26713-Windows-i18-support' into 10.8
2 parents d9f7a6b + 2e48fbe commit e222e44

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1107
-401
lines changed

client/mysql.cc

+156-33
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ extern "C" {
8888
#endif /* defined(HAVE_CURSES_H) && defined(HAVE_TERM_H) */
8989

9090
#undef bcmp // Fix problem with new readline
91-
#if defined(_WIN32)
92-
#include <conio.h>
93-
#else
91+
#if !defined(_WIN32)
9492
# ifdef __APPLE__
9593
# include <editline/readline.h>
9694
# else
@@ -104,6 +102,98 @@ extern "C" {
104102
#define USE_POPEN
105103
}
106104

105+
static CHARSET_INFO *charset_info= &my_charset_latin1;
106+
107+
#if defined(_WIN32)
108+
/*
109+
Set console mode for the whole duration of the client session.
110+
111+
We need for input
112+
- line input (i.e read lines from console)
113+
- echo typed characters
114+
- "cooked" mode, i.e we do not want to handle all keystrokes,
115+
like DEL etc ourselves, yet. We might want handle keystrokes
116+
in the future, to implement tab completion, and better
117+
(multiline) history.
118+
119+
Disable VT escapes for the output.We do not know what kind of escapes SELECT would return.
120+
*/
121+
struct Console_mode
122+
{
123+
HANDLE in= GetStdHandle(STD_INPUT_HANDLE);
124+
HANDLE out= GetStdHandle(STD_OUTPUT_HANDLE);
125+
DWORD mode_in=0;
126+
DWORD mode_out=0;
127+
128+
enum {STDIN_CHANGED = 1, STDOUT_CHANGED = 2};
129+
int changes=0;
130+
131+
Console_mode()
132+
{
133+
if (in && in != INVALID_HANDLE_VALUE && GetConsoleMode(in, &mode_in))
134+
{
135+
SetConsoleMode(in, ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT);
136+
changes |= STDIN_CHANGED;
137+
}
138+
139+
if (out && out != INVALID_HANDLE_VALUE && GetConsoleMode(out, &mode_out))
140+
{
141+
#ifdef ENABLE_VIRTUAL_TERMINAL_INPUT
142+
SetConsoleMode(out, mode_out & ~ENABLE_VIRTUAL_TERMINAL_INPUT);
143+
changes |= STDOUT_CHANGED;
144+
#endif
145+
}
146+
}
147+
148+
~Console_mode()
149+
{
150+
if (changes & STDIN_CHANGED)
151+
SetConsoleMode(in, mode_in);
152+
153+
if(changes & STDOUT_CHANGED)
154+
SetConsoleMode(out, mode_out);
155+
}
156+
};
157+
158+
static Console_mode my_conmode;
159+
160+
#define MAX_CGETS_LINE_LEN 65535
161+
/** Read line from console, chomp EOL*/
162+
static char *win_readline()
163+
{
164+
static wchar_t wstrbuf[MAX_CGETS_LINE_LEN];
165+
static char strbuf[MAX_CGETS_LINE_LEN * 4];
166+
167+
DWORD nchars= 0;
168+
uint len= 0;
169+
SetLastError(0);
170+
if (!ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), wstrbuf, MAX_CGETS_LINE_LEN-1,
171+
&nchars, NULL))
172+
goto err;
173+
if (nchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED)
174+
goto err;
175+
176+
for (;nchars > 0; nchars--)
177+
{
178+
if (wstrbuf[nchars - 1] != '\n' && wstrbuf[nchars - 1] != '\r')
179+
break;
180+
}
181+
182+
if (nchars > 0)
183+
{
184+
uint errors;
185+
len= my_convert(strbuf, sizeof(strbuf), charset_info,
186+
(const char *) wstrbuf, nchars * sizeof(wchar_t),
187+
&my_charset_utf16le_bin, &errors);
188+
}
189+
strbuf[len]= 0;
190+
return strbuf;
191+
err:
192+
return NULL;
193+
}
194+
#endif
195+
196+
107197
#ifdef HAVE_VIDATTR
108198
static int have_curses= 0;
109199
static void my_vidattr(chtype attrs)
@@ -208,7 +298,6 @@ unsigned short terminal_width= 80;
208298

209299
static uint opt_protocol=0;
210300
static const char *opt_protocol_type= "";
211-
static CHARSET_INFO *charset_info= &my_charset_latin1;
212301

213302
static uint protocol_to_force= MYSQL_PROTOCOL_DEFAULT;
214303

@@ -1353,6 +1442,46 @@ sig_handler mysql_end(int sig)
13531442
exit(status.exit_status);
13541443
}
13551444

1445+
#ifdef _WIN32
1446+
#define CNV_BUFSIZE 1024
1447+
1448+
/**
1449+
Convert user,database,and password to requested charset.
1450+
1451+
This is done in the single case when user connects with non-UTF8
1452+
default-character-set, on UTF8 capable Windows.
1453+
1454+
User, password, and database are UTF8 encoded, prior to the function,
1455+
this needs to be fixed, in case they contain non-ASCIIs.
1456+
1457+
Mostly a workaround, to allow existng users with non-ASCII password
1458+
to survive upgrade without losing connectivity.
1459+
*/
1460+
static void maybe_convert_charset(const char **user, const char **password,
1461+
const char **database, const char *csname)
1462+
{
1463+
if (GetACP() != CP_UTF8 || !strncmp(csname, "utf8", 4))
1464+
return;
1465+
static char bufs[3][CNV_BUFSIZE];
1466+
const char **from[]= {user, password, database};
1467+
CHARSET_INFO *cs= get_charset_by_csname(csname, MY_CS_PRIMARY,
1468+
MYF(MY_UTF8_IS_UTF8MB3 | MY_WME));
1469+
if (!cs)
1470+
return;
1471+
for (int i= 0; i < 3; i++)
1472+
{
1473+
const char *str= *from[i];
1474+
if (!str)
1475+
continue;
1476+
uint errors;
1477+
uint len= my_convert(bufs[i], CNV_BUFSIZE, cs, str, (uint32) strlen(str),
1478+
&my_charset_utf8mb4_bin, &errors);
1479+
bufs[i][len]= 0;
1480+
*from[i]= bufs[i];
1481+
}
1482+
}
1483+
#endif
1484+
13561485
/*
13571486
set connection-specific options and call mysql_real_connect
13581487
*/
@@ -1384,6 +1513,10 @@ static bool do_connect(MYSQL *mysql, const char *host, const char *user,
13841513
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
13851514
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
13861515
"program_name", "mysql");
1516+
#ifdef _WIN32
1517+
maybe_convert_charset(&user, &password, &database,default_charset);
1518+
#endif
1519+
13871520
return mysql_real_connect(mysql, host, user, password, database,
13881521
opt_mysql_port, opt_mysql_unix_port, flags);
13891522
}
@@ -2033,11 +2166,6 @@ static int get_options(int argc, char **argv)
20332166

20342167
static int read_and_execute(bool interactive)
20352168
{
2036-
#if defined(_WIN32)
2037-
String tmpbuf;
2038-
String buffer;
2039-
#endif
2040-
20412169
char *line= NULL;
20422170
char in_string=0;
20432171
ulong line_number=0;
@@ -2115,26 +2243,7 @@ static int read_and_execute(bool interactive)
21152243

21162244
#if defined(_WIN32)
21172245
tee_fputs(prompt, stdout);
2118-
if (!tmpbuf.is_alloced())
2119-
tmpbuf.alloc(65535);
2120-
tmpbuf.length(0);
2121-
buffer.length(0);
2122-
size_t clen;
2123-
do
2124-
{
2125-
line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen);
2126-
buffer.append(line, clen);
2127-
/*
2128-
if we got buffer fully filled than there is a chance that
2129-
something else is still in console input buffer
2130-
*/
2131-
} while (tmpbuf.alloced_length() <= clen);
2132-
/*
2133-
An empty line is returned from my_cgets when there's error reading :
2134-
Ctrl-c for example
2135-
*/
2136-
if (line)
2137-
line= buffer.c_ptr();
2246+
line= win_readline();
21382247
#else
21392248
if (opt_outfile)
21402249
fputs(prompt, OUTFILE);
@@ -2201,10 +2310,7 @@ static int read_and_execute(bool interactive)
22012310
}
22022311
}
22032312

2204-
#if defined(_WIN32)
2205-
buffer.free();
2206-
tmpbuf.free();
2207-
#else
2313+
#if !defined(_WIN32)
22082314
if (interactive)
22092315
/*
22102316
free the last entered line.
@@ -3242,6 +3348,21 @@ com_clear(String *buffer,char *line __attribute__((unused)))
32423348
return 0;
32433349
}
32443350

3351+
static void adjust_console_codepage(const char *name __attribute__((unused)))
3352+
{
3353+
#ifdef _WIN32
3354+
if (my_set_console_cp(name) < 0)
3355+
{
3356+
char buf[128];
3357+
snprintf(buf, sizeof(buf),
3358+
"WARNING: Could not determine Windows codepage for charset '%s',"
3359+
"continue using codepage %u", name, GetConsoleOutputCP());
3360+
put_info(buf, INFO_INFO);
3361+
}
3362+
#endif
3363+
}
3364+
3365+
32453366
/* ARGSUSED */
32463367
static int
32473368
com_charset(String *buffer __attribute__((unused)), char *line)
@@ -3263,6 +3384,7 @@ com_charset(String *buffer __attribute__((unused)), char *line)
32633384
mysql_set_character_set(&mysql, charset_info->cs_name.str);
32643385
default_charset= (char *)charset_info->cs_name.str;
32653386
put_info("Charset changed", INFO_INFO);
3387+
adjust_console_codepage(charset_info->cs_name.str);
32663388
}
32673389
else put_info("Charset is not found", INFO_INFO);
32683390
return 0;
@@ -4806,6 +4928,7 @@ sql_real_connect(char *host,char *database,char *user,char *password,
48064928
put_info(buff, INFO_ERROR);
48074929
return 1;
48084930
}
4931+
adjust_console_codepage(charset_info->cs_name.str);
48094932
connected=1;
48104933
#ifndef EMBEDDED_LIBRARY
48114934
mysql_options(&mysql, MYSQL_OPT_RECONNECT, &debug_info_flag);

client/mysqladmin.cc

+1
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ int main(int argc,char *argv[])
438438
mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
439439
if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
440440
default_charset= (char *)my_default_csname();
441+
my_set_console_cp(default_charset);
441442
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
442443
error_flags= (myf)(opt_nobeep ? 0 : ME_BELL);
443444

client/mysqlcheck.c

+1
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ static int get_options(int *argc, char ***argv)
503503
printf("Unsupported character set: %s\n", default_charset);
504504
DBUG_RETURN(1);
505505
}
506+
my_set_console_cp(default_charset);
506507
if (*argc > 0 && opt_alldbs)
507508
{
508509
printf("You should give only options, no arguments at all, with option\n");

client/mysqlimport.c

+1
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ static MYSQL *db_connect(char *host, char *database,
525525
mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
526526
if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
527527
default_charset= (char *)my_default_csname();
528+
my_set_console_cp(default_charset);
528529
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, my_default_csname());
529530
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
530531
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,

client/mysqlshow.c

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ int main(int argc, char **argv)
147147

148148
if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
149149
default_charset= (char *)my_default_csname();
150+
my_set_console_cp(default_charset);
150151
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
151152

152153
if (opt_plugin_dir && *opt_plugin_dir)

client/mysqltest.cc

+52-1
Original file line numberDiff line numberDiff line change
@@ -3258,6 +3258,47 @@ static int replace(DYNAMIC_STRING *ds_str,
32583258
return 0;
32593259
}
32603260

3261+
#ifdef _WIN32
3262+
/**
3263+
Check if background execution of command was requested.
3264+
Like in Unix shell, we assume background execution of the last
3265+
character in command is a ampersand (we do not tokenize though)
3266+
*/
3267+
static bool is_background_command(const DYNAMIC_STRING *ds)
3268+
{
3269+
for (size_t i= ds->length - 1; i > 1; i--)
3270+
{
3271+
char c= ds->str[i];
3272+
if (!isspace(c))
3273+
return (c == '&');
3274+
}
3275+
return false;
3276+
}
3277+
3278+
/**
3279+
Execute OS command in background. We assume that the last character
3280+
is ampersand, i.e is_background_command() returned
3281+
*/
3282+
#include <string>
3283+
static int execute_in_background(char *cmd)
3284+
{
3285+
STARTUPINFO s{};
3286+
PROCESS_INFORMATION pi{};
3287+
char *end= strrchr(cmd, '&');
3288+
DBUG_ASSERT(end);
3289+
*end =0;
3290+
std::string scmd("cmd /c ");
3291+
scmd.append(cmd);
3292+
BOOL ok=
3293+
CreateProcess(0, (char *)scmd.c_str(), 0, 0, 0, CREATE_NO_WINDOW, 0, 0, &s, &pi);
3294+
*end= '&';
3295+
if (!ok)
3296+
return (int) GetLastError();
3297+
CloseHandle(pi.hProcess);
3298+
CloseHandle(pi.hThread);
3299+
return 0;
3300+
}
3301+
#endif
32613302

32623303
/*
32633304
Execute given command.
@@ -3332,6 +3373,14 @@ void do_exec(struct st_command *command)
33323373
DBUG_PRINT("info", ("Executing '%s' as '%s'",
33333374
command->first_argument, ds_cmd.str));
33343375

3376+
#ifdef _WIN32
3377+
if (is_background_command(&ds_cmd))
3378+
{
3379+
error= execute_in_background(ds_cmd.str);
3380+
goto end;
3381+
}
3382+
#endif
3383+
33353384
if (!(res_file= my_popen(ds_cmd.str, "r")))
33363385
{
33373386
dynstr_free(&ds_cmd);
@@ -3358,7 +3407,9 @@ void do_exec(struct st_command *command)
33583407
dynstr_append_sorted(&ds_res, &ds_sorted, 0);
33593408
dynstr_free(&ds_sorted);
33603409
}
3361-
3410+
#ifdef _WIN32
3411+
end:
3412+
#endif
33623413
if (error)
33633414
{
33643415
uint status= WEXITSTATUS(error);

cmake/os/Windows.cmake

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ ENDIF()
6060

6161
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
6262
ADD_DEFINITIONS(-D_WIN32_WINNT=0x0A00)
63-
# We do not want the windows.h macros min/max
64-
ADD_DEFINITIONS(-DNOMINMAX)
63+
# We do not want the windows.h , or winsvc.h macros min/max
64+
ADD_DEFINITIONS(-DNOMINMAX -DNOSERVICE)
6565
# Speed up build process excluding unused header files
6666
ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
6767

cmake/win_compatibility.manifest

+5
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@
1919

2020
</application>
2121
</compatibility>
22+
<application>
23+
<windowsSettings>
24+
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
25+
</windowsSettings>
26+
</application>
2227
</asmv1:assembly>

0 commit comments

Comments
 (0)