Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
88a1c9c9
Commit
88a1c9c9
authored
May 29, 2009
by
Tatiana A. Nurnberg
Browse files
Options
Browse Files
Download
Plain Diff
auto-merge
parents
2ba60c19
ca7a17dc
Changes
35
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
524 additions
and
207 deletions
+524
-207
CMakeLists.txt
CMakeLists.txt
+4
-0
client/mysql.cc
client/mysql.cc
+2
-2
client/mysql_upgrade.c
client/mysql_upgrade.c
+30
-4
include/myisam.h
include/myisam.h
+2
-1
mysql-test/include/mtr_check.sql
mysql-test/include/mtr_check.sql
+10
-0
mysql-test/lib/My/CoreDump.pm
mysql-test/lib/My/CoreDump.pm
+34
-4
mysql-test/lib/My/File/Path.pm
mysql-test/lib/My/File/Path.pm
+3
-0
mysql-test/lib/My/SafeProcess.pm
mysql-test/lib/My/SafeProcess.pm
+30
-0
mysql-test/lib/My/SafeProcess/Base.pm
mysql-test/lib/My/SafeProcess/Base.pm
+9
-0
mysql-test/lib/My/SafeProcess/safe_process_win.cc
mysql-test/lib/My/SafeProcess/safe_process_win.cc
+29
-7
mysql-test/lib/mtr_cases.pm
mysql-test/lib/mtr_cases.pm
+14
-3
mysql-test/lib/mtr_process.pl
mysql-test/lib/mtr_process.pl
+37
-1
mysql-test/lib/mtr_report.pm
mysql-test/lib/mtr_report.pm
+22
-2
mysql-test/lib/mtr_unique.pm
mysql-test/lib/mtr_unique.pm
+57
-125
mysql-test/mysql-test-run.pl
mysql-test/mysql-test-run.pl
+139
-39
mysql-test/r/init_file.result
mysql-test/r/init_file.result
+3
-0
mysql-test/r/mysql.result
mysql-test/r/mysql.result
+1
-0
mysql-test/r/sp-error.result
mysql-test/r/sp-error.result
+10
-0
mysql-test/suite/funcs_1/datadict/is_routines.inc
mysql-test/suite/funcs_1/datadict/is_routines.inc
+3
-2
mysql-test/suite/funcs_1/r/is_routines.result
mysql-test/suite/funcs_1/r/is_routines.result
+3
-2
mysql-test/t/init_file.test
mysql-test/t/init_file.test
+7
-2
mysql-test/t/mysql.test
mysql-test/t/mysql.test
+8
-0
mysql-test/t/sp-error.test
mysql-test/t/sp-error.test
+13
-0
scripts/mysqld_safe.sh
scripts/mysqld_safe.sh
+1
-1
sql/item_func.cc
sql/item_func.cc
+9
-1
sql/share/errmsg.txt
sql/share/errmsg.txt
+3
-0
sql/sql_derived.cc
sql/sql_derived.cc
+1
-0
sql/sql_lex.cc
sql/sql_lex.cc
+10
-0
sql/sql_lex.h
sql/sql_lex.h
+2
-0
sql/table.cc
sql/table.cc
+2
-1
storage/ibmdb2i/db2i_conversion.cc
storage/ibmdb2i/db2i_conversion.cc
+2
-2
storage/ibmdb2i/db2i_misc.h
storage/ibmdb2i/db2i_misc.h
+16
-0
storage/myisam/ha_myisam.cc
storage/myisam/ha_myisam.cc
+1
-1
storage/myisam/myisamchk.c
storage/myisam/myisamchk.c
+5
-5
storage/myisammrg/ha_myisammrg.cc
storage/myisammrg/ha_myisammrg.cc
+2
-2
No files found.
CMakeLists.txt
View file @
88a1c9c9
...
...
@@ -47,6 +47,10 @@ SET(WITH_MYISAMMRG_STORAGE_ENGINE TRUE)
ADD_DEFINITIONS
(
-DWITH_MYISAMMRG_STORAGE_ENGINE
)
SET
(
mysql_plugin_defs
"
${
mysql_plugin_defs
}
,builtin_myisammrg_plugin"
)
IF
(
WITH_COMMUNITY_FEATURES
)
ADD_DEFINITIONS
(
-DENABLED_PROFILING -DCOMMUNITY_SERVER
)
ENDIF
(
WITH_COMMUNITY_FEATURES
)
IF
(
WITH_ARCHIVE_STORAGE_ENGINE
)
ADD_DEFINITIONS
(
-DWITH_ARCHIVE_STORAGE_ENGINE
)
SET
(
mysql_plugin_defs
"
${
mysql_plugin_defs
}
,builtin_archive_plugin"
)
...
...
client/mysql.cc
View file @
88a1c9c9
...
...
@@ -1979,7 +1979,7 @@ static COMMANDS *find_command(char *name,char cmd_char)
*/
if
(
strstr
(
name
,
"
\\
g"
)
||
(
strstr
(
name
,
delimiter
)
&&
!
(
strlen
(
name
)
>=
9
&&
!
my_strnncoll
(
charset_info
,
!
my_strnncoll
(
&
my_charset_latin1
,
(
uchar
*
)
name
,
9
,
(
const
uchar
*
)
"delimiter"
,
9
))))
...
...
@@ -2000,7 +2000,7 @@ static COMMANDS *find_command(char *name,char cmd_char)
{
if
(
commands
[
i
].
func
&&
((
name
&&
!
my_strnncoll
(
charset_info
,(
uchar
*
)
name
,
len
,
!
my_strnncoll
(
&
my_charset_latin1
,
(
uchar
*
)
name
,
len
,
(
uchar
*
)
commands
[
i
].
name
,
len
)
&&
!
commands
[
i
].
name
[
len
]
&&
(
!
end
||
(
end
&&
commands
[
i
].
takes_params
)))
||
...
...
client/mysql_upgrade.c
View file @
88a1c9c9
...
...
@@ -39,6 +39,7 @@ static uint my_end_arg= 0;
static
char
*
opt_user
=
(
char
*
)
"root"
;
static
DYNAMIC_STRING
ds_args
;
static
DYNAMIC_STRING
conn_args
;
static
char
*
opt_password
=
0
;
static
my_bool
tty_password
=
0
;
...
...
@@ -135,6 +136,7 @@ static void free_used_memory(void)
free_defaults
(
defaults_argv
);
dynstr_free
(
&
ds_args
);
dynstr_free
(
&
conn_args
);
}
...
...
@@ -204,7 +206,7 @@ static void add_one_option(DYNAMIC_STRING* ds,
}
}
dynstr_append_os_quoted
(
ds
,
"--"
,
opt
->
name
,
eq
,
arg
,
NullS
);
dynstr_append
(
&
ds_arg
s
,
" "
);
dynstr_append
(
d
s
,
" "
);
}
...
...
@@ -256,6 +258,15 @@ get_one_option(int optid, const struct my_option *opt,
case
'f'
:
/* --force */
add_option
=
FALSE
;
break
;
case
'h'
:
/* --host */
case
'W'
:
/* --pipe */
case
'P'
:
/* --port */
case
'S'
:
/* --socket */
case
OPT_MYSQL_PROTOCOL
:
/* --protocol */
case
OPT_SHARED_MEMORY_BASE_NAME
:
/* --shared-memory-base-name */
add_one_option
(
&
conn_args
,
opt
,
argument
);
break
;
}
if
(
add_option
)
...
...
@@ -603,6 +614,20 @@ static void create_mysql_upgrade_info_file(void)
}
/*
Print connection-related arguments.
*/
static
void
print_conn_args
(
const
char
*
tool_name
)
{
if
(
conn_args
.
str
[
0
])
verbose
(
"Running '%s' with connection arguments: %s"
,
tool_name
,
conn_args
.
str
);
else
verbose
(
"Running '%s with default connection arguments"
,
tool_name
);
}
/*
Check and upgrade(if neccessary) all tables
in the server using "mysqlcheck --check-upgrade .."
...
...
@@ -610,7 +635,7 @@ static void create_mysql_upgrade_info_file(void)
static
int
run_mysqlcheck_upgrade
(
void
)
{
verbose
(
"Running 'mysqlcheck'...
"
);
print_conn_args
(
"mysqlcheck
"
);
return
run_tool
(
mysqlcheck_path
,
NULL
,
/* Send output from mysqlcheck directly to screen */
"--no-defaults"
,
...
...
@@ -624,7 +649,7 @@ static int run_mysqlcheck_upgrade(void)
static
int
run_mysqlcheck_fixnames
(
void
)
{
verbose
(
"Running 'mysqlcheck'...
"
);
print_conn_args
(
"mysqlcheck
"
);
return
run_tool
(
mysqlcheck_path
,
NULL
,
/* Send output from mysqlcheck directly to screen */
"--no-defaults"
,
...
...
@@ -753,7 +778,8 @@ int main(int argc, char **argv)
strncpy
(
self_name
,
argv
[
0
],
FN_REFLEN
);
}
if
(
init_dynamic_string
(
&
ds_args
,
""
,
512
,
256
))
if
(
init_dynamic_string
(
&
ds_args
,
""
,
512
,
256
)
||
init_dynamic_string
(
&
conn_args
,
""
,
512
,
256
))
die
(
"Out of memory"
);
load_defaults
(
"my"
,
load_default_groups
,
&
argc
,
&
argv
);
...
...
include/myisam.h
View file @
88a1c9c9
...
...
@@ -404,7 +404,8 @@ typedef struct st_mi_check_param
my_off_t
keydata
,
totaldata
,
key_blocks
,
start_check_pos
;
ha_rows
total_records
,
total_deleted
;
ha_checksum
record_checksum
,
glob_crc
;
ulong
use_buffers
,
read_buffer_length
,
write_buffer_length
,
ulonglong
use_buffers
;
ulong
read_buffer_length
,
write_buffer_length
,
sort_buffer_length
,
sort_key_blocks
;
uint
out_flag
,
warning_printed
,
error_printed
,
verbose
;
uint
opt_sort_key
,
total_files
,
max_level
;
...
...
mysql-test/include/mtr_check.sql
View file @
88a1c9c9
...
...
@@ -57,3 +57,13 @@ BEGIN
mysql
.
user
;
END
||
--
-- Procedure used by test case used to force all
-- servers to restart after testcase and thus skipping
-- check test case after test
--
CREATE
DEFINER
=
root
@
localhost
PROCEDURE
force_restart
()
BEGIN
SELECT
1
INTO
OUTFILE
'force_restart'
;
END
||
mysql-test/lib/My/CoreDump.pm
View file @
88a1c9c9
...
...
@@ -22,6 +22,33 @@ use My::Platform;
use
File::
Temp
qw/ tempfile tempdir /
;
my
$hint_mysqld
;
# Last resort guess for executable path
# If path in core file is 79 chars we assume it's been truncated
# Looks like we can still find the full path using 'strings'
# If that doesn't work, use the hint (mysqld path) as last resort.
sub
_verify_binpath
{
my
(
$binary
,
$core_name
)
=
@_
;
my
$binpath
;
if
(
length
$binary
!=
79
)
{
$binpath
=
$binary
;
print
"
Core generated by '
$binpath
'
\n
";
}
else
{
# Last occurrence of path ending in /mysql*, cut from first /
if
(`
strings '
$core_name
' | grep "/mysql[^/. ]*
\
$" | tail -1
`
=~
/(\/.*)/
)
{
$binpath
=
$1
;
print
"
Guessing that core was generated by '
$binpath
'
\n
";
}
else
{
return
unless
$hint_mysqld
;
$binpath
=
$hint_mysqld
;
print
"
Wild guess that core was generated by '
$binpath
'
\n
";
}
}
return
$binpath
;
}
sub
_gdb
{
my
(
$core_name
)
=
@_
;
...
...
@@ -33,7 +60,8 @@ sub _gdb {
`
gdb -c '
$core_name
' --batch 2>&1
`
=~
/Core was generated by `([^\s\'\`]+)/
;
my
$binary
=
$1
or
return
;
print
"
Core generated by '
$binary
'
\n
";
$binary
=
_verify_binpath
(
$binary
,
$core_name
)
or
return
;
# Create tempfile containing gdb commands
my
(
$tmp
,
$tmp_name
)
=
tempfile
();
...
...
@@ -73,7 +101,8 @@ sub _dbx {
`
echo | dbx - '
$core_name
' 2>&1
`
=~
/Corefile specified executable: "([^"]+)"/
;
my
$binary
=
$1
or
return
;
print
"
Core generated by '
$binary
'
\n
";
$binary
=
_verify_binpath
(
$binary
,
$core_name
)
or
return
;
# Find all threads
my
@thr_ids
=
`
echo threads | dbx '
$binary
' '
$core_name
' 2>&1
`
=~
/t@\d+/g
;
...
...
@@ -203,7 +232,7 @@ sub _cdb {
my $cdb_cmd = "!sym prompts off; !analyze -v; .ecxr; !for_each_frame dv /t;!uniqstack -p;q";
my $cdb_output=
`cdb -
z $core_name -i "$image_path" -y "$symbol_path" -t 0 -lines -c "$cdb_cmd"
2>&1`;
`cdb -
c "$cdb_cmd" -z $core_name -i "$image_path" -y "$symbol_path" -t 0 -lines
2>&1`;
return if $? >> 8;
return unless $cdb_output;
...
...
@@ -225,7 +254,8 @@ EOF
sub show {
my ($class, $core_name)= @_;
my ($class, $core_name, $exe_mysqld)= @_;
$hint_mysqld= $exe_mysqld;
# On Windows, rely on cdb to be there...
if (IS_WINDOWS)
...
...
mysql-test/lib/My/File/Path.pm
View file @
88a1c9c9
...
...
@@ -164,6 +164,9 @@ sub copytree {
copytree
("
$from_dir
/
$_
",
"
$to_dir
/
$_
");
next
;
}
# Only copy plain files
next
unless
-
f
"
$from_dir
/
$_
";
copy
("
$from_dir
/
$_
",
"
$to_dir
/
$_
");
}
closedir
(
DIR
);
...
...
mysql-test/lib/My/SafeProcess.pm
View file @
88a1c9c9
...
...
@@ -536,7 +536,37 @@ sub wait_any {
return
$proc
;
}
#
# Wait for all processes to exit
#
sub
wait_all
{
while
(
keys
%
running
)
{
wait_any
();
}
}
#
# Check if any process has exited, but don't wait.
#
# Returns a reference to the SafeProcess that
# exited or undefined
#
sub
check_any
{
for
my
$proc
(
values
%
running
){
if
(
$proc
->
is_child
(
$$
)
)
{
if
(
not
$proc
->
wait_one
(
0
))
{
_verbose
("
Found exited
$proc
");
return
$proc
;
}
}
}
return
undef
;
}
# Overload string operator
# and fallback to default functions if no
# overloaded function is found
...
...
mysql-test/lib/My/SafeProcess/Base.pm
View file @
88a1c9c9
...
...
@@ -83,6 +83,13 @@ sub exit_status {
};
}
# threads.pm may not exist everywhere, so use only on Windows.
use
if
$^O
eq
"
MSWin32
",
"
threads
";
use
if
$^O
eq
"
MSWin32
",
"
threads::shared
";
my
$win32_spawn_lock
:
shared
;
#
# Create a new process
...
...
@@ -104,6 +111,8 @@ sub create_process {
if
(
$^O
eq
"
MSWin32
"){
lock
(
$win32_spawn_lock
);
#printf STDERR "stdin %d, stdout %d, stderr %d\n",
# fileno STDIN, fileno STDOUT, fileno STDERR;
...
...
mysql-test/lib/My/SafeProcess/safe_process_win.cc
View file @
88a1c9c9
...
...
@@ -259,22 +259,37 @@ int main(int argc, const char** argv )
the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag, making sure it will be
terminated when the last handle to it is closed(which is owned by
this process).
If breakaway from job fails on some reason, fallback is to create a
new process group. Process groups also allow to kill process and its
descedants, subject to some restrictions (processes have to run within
the same console,and must not ignore CTRL_BREAK)
*/
if
(
CreateProcess
(
NULL
,
(
LPSTR
)
child_args
,
DWORD
create_flags
[]
=
{
CREATE_BREAKAWAY_FROM_JOB
,
CREATE_NEW_PROCESS_GROUP
,
0
};
BOOL
process_created
=
FALSE
;
BOOL
jobobject_assigned
=
FALSE
;
for
(
int
i
=
0
;
i
<
sizeof
(
create_flags
)
/
sizeof
(
create_flags
[
0
]);
i
++
)
{
process_created
=
CreateProcess
(
NULL
,
(
LPSTR
)
child_args
,
NULL
,
NULL
,
TRUE
,
/* inherit handles */
CREATE_SUSPENDED
|
CREATE_BREAKAWAY_FROM_JOB
,
CREATE_SUSPENDED
|
create_flags
[
i
]
,
NULL
,
NULL
,
&
si
,
&
process_info
)
==
0
)
die
(
"CreateProcess failed"
);
&
process_info
);
if
(
process_created
)
{
jobobject_assigned
=
AssignProcessToJobObject
(
job_handle
,
process_info
.
hProcess
);
break
;
}
}
if
(
AssignProcessToJobObject
(
job_handle
,
process_info
.
hProcess
)
==
0
)
if
(
!
process_created
)
{
TerminateProcess
(
process_info
.
hProcess
,
200
);
die
(
"AssignProcessToJobObject failed"
);
die
(
"CreateProcess failed"
);
}
ResumeThread
(
process_info
.
hThread
);
CloseHandle
(
process_info
.
hThread
);
...
...
@@ -312,6 +327,13 @@ int main(int argc, const char** argv )
message
(
"TerminateJobObject failed"
);
CloseHandle
(
job_handle
);
message
(
"Job terminated and closed"
);
if
(
!
jobobject_assigned
)
{
GenerateConsoleCtrlEvent
(
CTRL_BREAK_EVENT
,
process_info
.
dwProcessId
);
TerminateProcess
(
process_info
.
hProcess
,
202
);
}
if
(
wait_res
!=
WAIT_OBJECT_0
+
CHILD
)
{
/* The child has not yet returned, wait for it */
...
...
mysql-test/lib/mtr_cases.pm
View file @
88a1c9c9
...
...
@@ -33,7 +33,7 @@ our $print_testcases;
our
$skip_rpl
;
our
$do_test
;
our
$skip_test
;
our
$
opt_skip_combination
;
our
$
skip_combinations
;
our
$binlog_format
;
our
$enable_disabled
;
our
$default_storage_engine
;
...
...
@@ -119,11 +119,22 @@ sub collect_test_cases ($$) {
if
(
$test
->
{
name
}
=~
/.*\.$tname/
)
{
$found
=
1
;
last
;
}
}
if
(
not
$found
)
{
mtr_error
("
Could not find '
$tname
' in '
$suites
' suite(s)
");
mtr_error
("
Could not find '
$tname
' in '
$suites
' suite(s)
")
unless
$sname
;
# If suite was part of name, find it there
my
(
$this_case
)
=
collect_one_suite
(
$sname
,
[
$tname
]);
if
(
$this_case
)
{
push
(
@$cases
,
$this_case
);
}
else
{
mtr_error
("
Could not find '
$tname
' in '
$sname
' suite
");
}
}
}
}
...
...
@@ -375,7 +386,7 @@ sub collect_one_suite($)
# Read combinations for this suite and build testcases x combinations
# if any combinations exists
# ----------------------------------------------------------------------
if
(
!
$
opt_skip_combination
)
if
(
!
$
skip_combinations
)
{
my
@combinations
;
my
$combination_file
=
"
$suitedir
/combinations
";
...
...
mysql-test/lib/mtr_process.pl
View file @
88a1c9c9
...
...
@@ -21,7 +21,25 @@
use
strict
;
use
Socket
;
use
Errno
;
use
My::
Platform
;
use
if
IS_WINDOWS
,
"
Net::Ping
";
# Ancient perl might not have port_number method for Net::Ping.
# Check it and use fallback to connect() if it is not present.
BEGIN
{
my
$use_netping
=
0
;
if
(
IS_WINDOWS
)
{
my
$ping
=
Net::
Ping
->
new
();
if
(
$ping
->
can
("
port_number
"))
{
$use_netping
=
1
;
}
}
eval
'
sub USE_NETPING { $use_netping }
';
}
sub
sleep_until_file_created
($$$);
sub
mtr_ping_port
($);
...
...
@@ -30,6 +48,24 @@ sub mtr_ping_port ($) {
mtr_verbose
("
mtr_ping_port:
$port
");
if
(
IS_WINDOWS
&&
USE_NETPING
)
{
# Under Windows, connect to a port that is not open is slow
# It takes ~1sec. Net::Ping with small timeout is much faster.
my
$ping
=
Net::
Ping
->
new
();
$ping
->
port_number
(
$port
);
if
(
$ping
->
ping
("
localhost
",
0.1
))
{
mtr_verbose
("
USED
");
return
1
;
}
else
{
mtr_verbose
("
FREE
");
return
0
;
}
}
my
$remote
=
"
localhost
";
my
$iaddr
=
inet_aton
(
$remote
);
if
(
!
$iaddr
)
...
...
mysql-test/lib/mtr_report.pm
View file @
88a1c9c9
...
...
@@ -30,6 +30,8 @@ our @EXPORT= qw(report_option mtr_print_line mtr_print_thick_line
mtr_report_test)
;
use
mtr_match
;
use
My::
Platform
;
use
POSIX
qw[ _exit ]
;
require
"
mtr_io.pl
";
my
$tot_real_time
=
0
;
...
...
@@ -257,6 +259,17 @@ sub mtr_report_stats ($) {
$tot_restarts
++
;
}
# Add counts for repeated runs, if any.
# Note that the last run has already been counted above.
my
$num_repeat
=
$tinfo
->
{'
repeat
'}
-
1
;
if
(
$num_repeat
>
0
)
{
$tot_tests
+=
$num_repeat
;
my
$rep_failed
=
$tinfo
->
{'
rep_failures
'}
||
0
;
$tot_failed
+=
$rep_failed
;
$tot_passed
+=
$num_repeat
-
$rep_failed
;
}
# Look for warnings produced by mysqltest
my
$base_file
=
mtr_match_extension
(
$tinfo
->
{'
result_file
'},
"
result
");
# Trim extension
...
...
@@ -336,7 +349,7 @@ sub mtr_report_stats ($) {
foreach
my
$tinfo
(
@$tests
)
{
my
$tname
=
$tinfo
->
{'
name
'};
if
(
$tinfo
->
{
failures
}
and
!
$seen
{
$tname
})
if
(
(
$tinfo
->
{
failures
}
||
$tinfo
->
{
rep_failures
})
and
!
$seen
{
$tname
})
{
print
"
$tname
";
$seen
{
$tname
}
=
1
;
...
...
@@ -459,7 +472,14 @@ sub mtr_warning (@) {
sub
mtr_error
(@)
{
print
STDERR
_name
(),
_timestamp
(),
"
mysql-test-run: *** ERROR:
",
join
("
",
@_
),
"
\n
";
exit
(
1
);
if
(
IS_WINDOWS
)
{
POSIX::
_exit
(
1
);
}
else
{
exit
(
1
);
}
}
...
...
mysql-test/lib/mtr_unique.pm
View file @
88a1c9c9
...
...
@@ -28,32 +28,36 @@ sub msg {
# print "### unique($$) - ", join(" ", @_), "\n";
}
my
$
file
;
my
$
dir
;
if
(
!
IS_WINDOWS
)
{
$
file
=
"
/tmp/mysql-test-port
s
";
$
dir
=
"
/tmp/mysql-unique-id
s
";
}
else
{
$file
=
$ENV
{'
TEMP
'}
.
"
/mysql-test-ports
";
# Try to use machine-wide directory location for unique IDs,
# $ALLUSERSPROFILE . IF it is not available, fallback to $TEMP
# which is typically a per-user temporary directory
if
(
exists
$ENV
{'
ALLUSERSPROFILE
'}
&&
-
w
$ENV
{'
ALLUSERSPROFILE
'})
{
$dir
=
$ENV
{'
ALLUSERSPROFILE
'}
.
"
/mysql-unique-ids
";
}
else
{
$dir
=
$ENV
{'
TEMP
'}
.
"
/mysql-unique-ids
";
}
}
my
%
mtr_unique_ids
;
my
$mtr_unique_fh
=
undef
;
END
{
my
$allocated_id
=
$mtr_unique_ids
{
$$
};
if
(
defined
$allocated_id
)
{
mtr_release_unique_id
(
$allocated_id
);
}
delete
$mtr_unique_ids
{
$$
};
END
{
mtr_release_unique_id
();
}
#
# Get a unique, numerical ID, given a file name (where all
# requested IDs are stored), a minimum and a maximum value.
# Get a unique, numerical ID in a specified range.
#
# If no unique ID within the specified parameters can be
# obtained, return undef.
...
...
@@ -61,135 +65,63 @@ END {
sub
mtr_get_unique_id
($$)
{
my
(
$min
,
$max
)
=
@_
;;
msg
("
get, '
$file
',
$min
-
$max
");
die
"
Can only get one unique id per process!
"
if
$mtr_unique_ids
{
$$
};
msg
("
get
$min
-
$max
, $$
");
my
$ret
=
undef
;
my
$changed
=
0
;
if
(
eval
("
readlink '
$file
'
")
||
eval
("
readlink '
$file
.sem'
"))
{
die
'
lock file is a symbolic link
';
}
die
"
Can only get one unique id per process!
"
if
defined
$mtr_unique_fh
;
chmod
0777
,
"
$file
.sem
";
open
SEM
,
"
>
",
"
$file
.sem
"
or
die
"
can't write to
$file
.sem
";
flock
SEM
,
LOCK_EX
or
die
"
can't lock
$file
.sem
";
if
(
!
-
e
$file
)
{
open
FILE
,
"
>
",
$file
or
die
"
can't create
$file
";
close
FILE
;
}
msg
("
HAVE THE LOCK
");
# Make sure our ID directory exists
if
(
!
-
d
$dir
)
{
# If there is a file with the reserved
# directory name, just delete the file.
if
(
-
e
$dir
)
{
unlink
(
$dir
);
}
if
(
eval
("
readlink '
$file
'
")
||
eval
("
readlink '
$file
.sem'
"))
{
die
'
lock file is a symbolic link
';
}
mkdir
$dir
;
chmod
0777
,
$dir
;
chmod
0777
,
$file
;
open
FILE
,
"
+<
",
$file
or
die
"
can't open
$file
";
#select undef,undef,undef,0.2;
seek
FILE
,
0
,
0
;
my
%
taken
=
();
while
(
<
FILE
>
)
{
chomp
;
my
(
$id
,
$pid
)
=
split
/ /
;
$taken
{
$id
}
=
$pid
;
msg
("
taken:
$id
,
$pid
");
# Check if process with given pid is alive
if
(
!
process_alive
(
$pid
))
{
print
"
Removing slot
$id
used by missing process
$pid
\n
";
msg
("
Removing slot
$id
used by missing process
$pid
");
delete
$taken
{
$id
};
$changed
++
;
if
(
!
-
d
$dir
)
{
die
"
can't make directory
$dir
";
}
}
for
(
my
$i
=
$min
;
$i
<=
$max
;
++
$i
)
{
if
(
!
exists
$taken
{
$i
})
{
$ret
=
$i
;
$taken
{
$i
}
=
$$
;
$changed
++
;
# Remember the id this process got
$mtr_unique_ids
{
$$
}
=
$i
;
msg
("
got
$i
");
last
;
my
$fh
;
for
(
my
$id
=
$min
;
$id
<=
$max
;
$id
++
)
{
open
(
$fh
,
"
>
$dir
/
$id
");
chmod
0666
,
"
$dir
/
$id
";
# Try to lock the file exclusively. If lock succeeds, we're done.
if
(
flock
(
$fh
,
LOCK_EX
|
LOCK_NB
))
{
# Store file handle - we would need it to release the ID (==unlock the file)
$mtr_unique_fh
=
$fh
;
return
$id
;
}
}
if
(
$changed
)
{
seek
FILE
,
0
,
0
;
truncate
FILE
,
0
or
die
"
can't truncate
$file
";
for
my
$k
(
keys
%
taken
)
{
print
FILE
$k
.
'
'
.
$taken
{
$k
}
.
"
\n
";
else
{
close
$fh
;
}
}
close
FILE
;
msg
("
RELEASING THE LOCK
");
flock
SEM
,
LOCK_UN
or
warn
"
can't unlock
$file
.sem
";
close
SEM
;
return
$ret
;
return
undef
;
}
#
# Release a unique ID.
#
sub
mtr_release_unique_id
($)
{
my
(
$myid
)
=
@_
;
msg
("
release,
$myid
");
if
(
eval
("
readlink '
$file
'
")
||
eval
("
readlink '
$file
.sem'
"))
{
die
'
lock file is a symbolic link
';
}
open
SEM
,
"
>
",
"
$file
.sem
"
or
die
"
can't write to
$file
.sem
";
flock
SEM
,
LOCK_EX
or
die
"
can't lock
$file
.sem
";
msg
("
HAVE THE LOCK
");
if
(
eval
("
readlink '
$file
'
")
||
eval
("
readlink '
$file
.sem'
"))
{
die
'
lock file is a symbolic link
';
}
if
(
!
-
e
$file
)
{
open
FILE
,
"
>
",
$file
or
die
"
can't create
$file
";
close
FILE
;
}
open
FILE
,
"
+<
",
$file
or
die
"
can't open
$file
";
#select undef,undef,undef,0.2;
seek
FILE
,
0
,
0
;
my
%
taken
=
();
while
(
<
FILE
>
)
{
chomp
;
my
(
$id
,
$pid
)
=
split
/ /
;
msg
("
taken,
$id
$pid
");
$taken
{
$id
}
=
$pid
;
}
if
(
$taken
{
$myid
}
!=
$$
)
sub
mtr_release_unique_id
()
{
msg
("
release $$
");
if
(
defined
$mtr_unique_fh
)
{
msg
("
The unique id for this process does not match pid
");
close
$mtr_unique_fh
;
$mtr_unique_fh
=
undef
;
}
msg
("
removing
$myid
");
delete
$taken
{
$myid
};
seek
FILE
,
0
,
0
;
truncate
FILE
,
0
or
die
"
can't truncate
$file
";
for
my
$k
(
keys
%
taken
)
{
print
FILE
$k
.
'
'
.
$taken
{
$k
}
.
"
\n
";
}
close
FILE
;
msg
("
RELEASE THE LOCK
");
flock
SEM
,
LOCK_UN
or
warn
"
can't unlock
$file
.sem
";
close
SEM
;
delete
$mtr_unique_ids
{
$$
};
}
...
...
mysql-test/mysql-test-run.pl
View file @
88a1c9c9
This diff is collapsed.
Click to expand it.
mysql-test/r/init_file.result
View file @
88a1c9c9
...
...
@@ -4,6 +4,7 @@ SELECT * INTO @Y FROM init_file.startup limit 1,1;
SELECT YEAR(@X)-YEAR(@Y);
YEAR(@X)-YEAR(@Y)
0
DROP DATABASE init_file;
ok
end of 4.1 tests
select * from t1;
...
...
@@ -19,3 +20,5 @@ y
3
11
13
drop table t1, t2;
call mtr.force_restart();
mysql-test/r/mysql.result
View file @
88a1c9c9
...
...
@@ -198,6 +198,7 @@ COUNT (*)
1
COUNT (*)
1
ERROR 2005 (HY000) at line 1: Unknown MySQL server host 'invalid_hostname' (errno)
End of 5.0 tests
WARNING: --server-arg option not supported in this configuration.
Warning (Code 1286): Unknown table engine 'nonexistent'
...
...
mysql-test/r/sp-error.result
View file @
88a1c9c9
...
...
@@ -1660,3 +1660,13 @@ declare continue handler for sqlstate '00000' set @x=0;
end$$
ERROR 42000: Bad SQLSTATE: '00000'
LOAD DATA INFILE '../../tmp/proc.txt' INTO TABLE mysql.proc;
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (1,1), (2,2);
SELECT MAX (a) FROM t1 WHERE b = 999999;
ERROR 42000: FUNCTION test.MAX does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual
SELECT AVG (a) FROM t1 WHERE b = 999999;
AVG (a)
NULL
SELECT non_existent (a) FROM t1 WHERE b = 999999;
ERROR 42000: FUNCTION test.non_existent does not exist
DROP TABLE t1;
mysql-test/suite/funcs_1/datadict/is_routines.inc
View file @
88a1c9c9
...
...
@@ -96,10 +96,11 @@ CREATE FUNCTION function_for_routines() RETURNS INT RETURN 0;
SELECT
specific_name
,
routine_catalog
,
routine_schema
,
routine_name
,
routine_type
,
routine_body
,
external_name
,
external_language
,
parameter_style
,
sql_path
FROM
information_schema
.
routines
WHERE
routine_catalog
IS
NOT
NULL
OR
external_name
IS
NOT
NULL
WHERE
routine_schema
=
'test'
AND
(
routine_catalog
IS
NOT
NULL
OR
external_name
IS
NOT
NULL
OR
external_language
IS
NOT
NULL
OR
sql_path
IS
NOT
NULL
OR
routine_body
<>
'SQL'
OR
parameter_style
<>
'SQL'
OR
specific_name
<>
routine_name
;
OR
specific_name
<>
routine_name
)
;
DROP
PROCEDURE
sp_for_routines
;
DROP
FUNCTION
function_for_routines
;
...
...
mysql-test/suite/funcs_1/r/is_routines.result
View file @
88a1c9c9
...
...
@@ -111,10 +111,11 @@ CREATE FUNCTION function_for_routines() RETURNS INT RETURN 0;
SELECT specific_name,routine_catalog,routine_schema,routine_name,routine_type,
routine_body,external_name,external_language,parameter_style,sql_path
FROM information_schema.routines
WHERE routine_catalog IS NOT NULL OR external_name IS NOT NULL
WHERE routine_schema = 'test' AND
(routine_catalog IS NOT NULL OR external_name IS NOT NULL
OR external_language IS NOT NULL OR sql_path IS NOT NULL
OR routine_body <> 'SQL' OR parameter_style <> 'SQL'
OR specific_name <> routine_name;
OR specific_name <> routine_name
)
;
specific_name routine_catalog routine_schema routine_name routine_type routine_body external_name external_language parameter_style sql_path
DROP PROCEDURE sp_for_routines;
DROP FUNCTION function_for_routines;
...
...
mysql-test/t/init_file.test
View file @
88a1c9c9
...
...
@@ -14,7 +14,7 @@ SELECT * INTO @X FROM init_file.startup limit 0,1;
SELECT
*
INTO
@
Y
FROM
init_file
.
startup
limit
1
,
1
;
SELECT
YEAR
(
@
X
)
-
YEAR
(
@
Y
);
# Enable this DROP DATABASE only after resolving bug #42507
#
DROP DATABASE init_file;
DROP
DATABASE
init_file
;
--
echo
ok
--
echo
end
of
4.1
tests
...
...
@@ -28,4 +28,9 @@ select * from t1;
# 30, 3, 11, 13
select
*
from
t2
;
# Enable this DROP TABLE only after resolving bug #42507
#drop table t1, t2;
drop
table
t1
,
t2
;
# MTR will restart server anyway, but by forcing it we avoid being warned
# about the apparent side effect
call
mtr
.
force_restart
();
mysql-test/t/mysql.test
View file @
88a1c9c9
...
...
@@ -349,6 +349,14 @@ remove_file $MYSQLTEST_VARDIR/tmp/bug31060.sql;
--
exec
$MYSQL
--
ignore
-
spaces
-
e
"SELECT COUNT (*)"
--
exec
$MYSQL
-
b
-
i
-
e
"SELECT COUNT (*)"
#
# Bug#37268 'binary' character set makes CLI-internal commands case sensitive
#
--
replace_regex
/
\
([
0
-
9
]
*
\
)
/
(
errno
)
/
--
error
1
--
exec
$MYSQL
--
default
-
character
-
set
=
binary
test
-
e
"CONNECT test invalid_hostname"
2
>&
1
--
exec
$MYSQL
--
default
-
character
-
set
=
binary
test
-
e
"DELIMITER //"
2
>&
1
--
echo
End
of
5.0
tests
#
...
...
mysql-test/t/sp-error.test
View file @
88a1c9c9
...
...
@@ -2435,3 +2435,16 @@ delimiter ;$$
#
LOAD
DATA
INFILE
'../../tmp/proc.txt'
INTO
TABLE
mysql
.
proc
;
remove_file
$MYSQLTEST_VARDIR
/
tmp
/
proc
.
txt
;
#
# Bug #38159: Function parsing problem generates misleading error message
#
CREATE
TABLE
t1
(
a
INT
,
b
INT
);
INSERT
INTO
t1
VALUES
(
1
,
1
),
(
2
,
2
);
--
error
ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT
MAX
(
a
)
FROM
t1
WHERE
b
=
999999
;
SELECT
AVG
(
a
)
FROM
t1
WHERE
b
=
999999
;
--
error
ER_SP_DOES_NOT_EXIST
SELECT
non_existent
(
a
)
FROM
t1
WHERE
b
=
999999
;
DROP
TABLE
t1
;
scripts/mysqld_safe.sh
View file @
88a1c9c9
...
...
@@ -67,7 +67,7 @@ my_which ()
ret
=
0
for
file
do
for
dir
in
"
$PATH
"
for
dir
in
$PATH
do
if
[
-f
"
$dir
/
$file
"
]
then
...
...
sql/item_func.cc
View file @
88a1c9c9
...
...
@@ -5802,6 +5802,14 @@ Item_func_sp::func_name() const
}
int
my_missing_function_error
(
const
LEX_STRING
&
token
,
const
char
*
func_name
)
{
if
(
token
.
length
&&
is_lex_native_function
(
&
token
))
return
my_error
(
ER_FUNC_INEXISTENT_NAME_COLLISION
,
MYF
(
0
),
func_name
);
else
return
my_error
(
ER_SP_DOES_NOT_EXIST
,
MYF
(
0
),
"FUNCTION"
,
func_name
);
}
/**
@brief Initialize the result field by creating a temporary dummy table
...
...
@@ -5834,7 +5842,7 @@ Item_func_sp::init_result_field(THD *thd)
if
(
!
(
m_sp
=
sp_find_routine
(
thd
,
TYPE_ENUM_FUNCTION
,
m_name
,
&
thd
->
sp_func_cache
,
TRUE
)))
{
my_
error
(
ER_SP_DOES_NOT_EXIST
,
MYF
(
0
),
"FUNCTION"
,
m_name
->
m_qname
.
str
);
my_
missing_function_error
(
m_name
->
m_name
,
m_name
->
m_qname
.
str
);
context
->
process_error
(
thd
);
DBUG_RETURN
(
TRUE
);
}
...
...
sql/share/errmsg.txt
View file @
88a1c9c9
...
...
@@ -6177,3 +6177,6 @@ ER_TOO_LONG_TABLE_COMMENT
ER_TOO_LONG_FIELD_COMMENT
eng "Comment for field '%-.64s' is too long (max = %lu)"
por "Comentrio para o campo '%-.64s' longo demais (max = %lu)"
ER_FUNC_INEXISTENT_NAME_COLLISION 42000
eng "FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual"
sql/sql_derived.cc
View file @
88a1c9c9
...
...
@@ -179,6 +179,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
if
(
thd
->
is_error
()
&&
(
thd
->
main_da
.
sql_errno
()
==
ER_BAD_FIELD_ERROR
||
thd
->
main_da
.
sql_errno
()
==
ER_FUNC_INEXISTENT_NAME_COLLISION
||
thd
->
main_da
.
sql_errno
()
==
ER_SP_DOES_NOT_EXIST
))
{
thd
->
clear_error
();
...
...
sql/sql_lex.cc
View file @
88a1c9c9
...
...
@@ -433,6 +433,16 @@ bool is_keyword(const char *name, uint len)
return
get_hash_symbol
(
name
,
len
,
0
)
!=
0
;
}
/**
Check if name is a sql function
@param name checked name
@return is this a native function or not
@retval 0 name is a function
@retval 1 name isn't a function
*/
bool
is_lex_native_function
(
const
LEX_STRING
*
name
)
{
DBUG_ASSERT
(
name
!=
NULL
);
...
...
sql/sql_lex.h
View file @
88a1c9c9
...
...
@@ -1976,4 +1976,6 @@ extern bool is_lex_native_function(const LEX_STRING *name);
@} (End of group Semantic_Analysis)
*/
int
my_missing_function_error
(
const
LEX_STRING
&
token
,
const
char
*
name
);
#endif
/* MYSQL_SERVER */
sql/table.cc
View file @
88a1c9c9
...
...
@@ -779,7 +779,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
strpos
=
disk_buff
+
6
;
if
(
!
(
rec_per_key
=
(
ulong
*
)
alloc_root
(
&
share
->
mem_root
,
sizeof
(
ulong
*
)
*
key_parts
)))
sizeof
(
ulong
)
*
key_parts
)))
goto
err
;
for
(
i
=
0
;
i
<
keys
;
i
++
,
keyinfo
++
)
...
...
@@ -3341,6 +3341,7 @@ void TABLE_LIST::hide_view_error(THD *thd)
if
(
thd
->
main_da
.
sql_errno
()
==
ER_BAD_FIELD_ERROR
||
thd
->
main_da
.
sql_errno
()
==
ER_SP_DOES_NOT_EXIST
||
thd
->
main_da
.
sql_errno
()
==
ER_FUNC_INEXISTENT_NAME_COLLISION
||
thd
->
main_da
.
sql_errno
()
==
ER_PROCACCESS_DENIED_ERROR
||
thd
->
main_da
.
sql_errno
()
==
ER_COLUMNACCESS_DENIED_ERROR
||
thd
->
main_da
.
sql_errno
()
==
ER_TABLEACCESS_DENIED_ERROR
||
...
...
storage/ibmdb2i/db2i_conversion.cc
View file @
88a1c9c9
...
...
@@ -1085,7 +1085,7 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
if
(
bytesToStore
)
memcpy
(
db2Buf
,
dataToStore
,
bytesToStore
);
if
(
bytesToPad
)
wmemset
((
wchar_t
*
)
(
db2Buf
+
bytesToStore
),
0x0020
,
bytesToPad
/
2
);
memset16
(
(
db2Buf
+
bytesToStore
),
0x0020
,
bytesToPad
/
2
);
}
else
{
...
...
@@ -1108,7 +1108,7 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
bytesToStore
=
db2BytesToStore
;
}
if
(
db2BytesToStore
<
maxDb2BytesToStore
)
// If need to pad
wmemset
((
wchar_t
*
)
(
db2Buf
+
db2BytesToStore
),
0x0020
,
(
maxDb2BytesToStore
-
db2BytesToStore
)
/
2
);
memset16
(
(
db2Buf
+
db2BytesToStore
),
0x0020
,
(
maxDb2BytesToStore
-
db2BytesToStore
)
/
2
);
}
if
(
db2FieldType
==
QMY_VARGRAPHIC
)
...
...
storage/ibmdb2i/db2i_misc.h
View file @
88a1c9c9
...
...
@@ -109,5 +109,21 @@ bool isOrdinaryIdentifier(const char* s)
}
return
true
;
}
/**
Fill memory with a 16-bit word.
@param p Pointer to space to fill.
@param v Value to fill
@param l Length of space (in 16-bit words)
*/
void
memset16
(
void
*
p
,
uint16
v
,
size_t
l
)
{
uint16
*
p2
=
(
uint16
*
)
p
;
while
(
l
--
)
{
*
(
p2
++
)
=
v
;
}
}
#endif
storage/myisam/ha_myisam.cc
View file @
88a1c9c9
...
...
@@ -1807,7 +1807,7 @@ int ha_myisam::info(uint flag)
if
(
share
->
key_parts
)
memcpy
((
char
*
)
table
->
key_info
[
0
].
rec_per_key
,
(
char
*
)
misam_info
.
rec_per_key
,
sizeof
(
table
->
key_info
[
0
].
rec_per_key
[
0
])
*
share
->
key_parts
);
sizeof
(
table
->
key_info
[
0
].
rec_per_key
[
0
])
*
share
->
key_parts
);
if
(
share
->
tmp_table
==
NO_TMP_TABLE
)
pthread_mutex_unlock
(
&
share
->
mutex
);
...
...
storage/myisam/myisamchk.c
View file @
88a1c9c9
...
...
@@ -287,8 +287,8 @@ static struct my_option my_long_options[] =
0
,
0
,
0
,
GET_NO_ARG
,
NO_ARG
,
0
,
0
,
0
,
0
,
0
,
0
},
{
"key_buffer_size"
,
OPT_KEY_BUFFER_SIZE
,
""
,
(
uchar
**
)
&
check_param
.
use_buffers
,
(
uchar
**
)
&
check_param
.
use_buffers
,
0
,
GET_UL
ONG
,
REQUIRED_ARG
,
(
long
)
USE_BUFFER_INIT
,
(
long
)
MALLOC_OVERHEAD
,
(
long
)
~
0L
,
(
long
)
MALLOC_OVERHEAD
,
(
long
)
IO_SIZE
,
0
},
GET_UL
L
,
REQUIRED_ARG
,
USE_BUFFER_INIT
,
MALLOC_OVERHEAD
,
SIZE_T_MAX
,
MALLOC_OVERHEAD
,
IO_SIZE
,
0
},
{
"key_cache_block_size"
,
OPT_KEY_CACHE_BLOCK_SIZE
,
""
,
(
uchar
**
)
&
opt_key_cache_block_size
,
(
uchar
**
)
&
opt_key_cache_block_size
,
0
,
...
...
@@ -1102,7 +1102,7 @@ static int myisamchk(MI_CHECK *param, char * filename)
{
if
(
param
->
testflag
&
(
T_EXTEND
|
T_MEDIUM
))
VOID
(
init_key_cache
(
dflt_key_cache
,
opt_key_cache_block_size
,
param
->
use_buffers
,
0
,
0
));
(
size_t
)
param
->
use_buffers
,
0
,
0
));
VOID
(
init_io_cache
(
&
param
->
read_cache
,
datafile
,
(
uint
)
param
->
read_buffer_length
,
READ_CACHE
,
...
...
@@ -1525,8 +1525,8 @@ static int mi_sort_records(MI_CHECK *param,
if
(
share
->
state
.
key_root
[
sort_key
]
==
HA_OFFSET_ERROR
)
DBUG_RETURN
(
0
);
/* Nothing to do */
init_key_cache
(
dflt_key_cache
,
opt_key_cache_block_size
,
param
->
use_buffers
,
0
,
0
);
init_key_cache
(
dflt_key_cache
,
opt_key_cache_block_size
,
(
size_t
)
param
->
use_buffers
,
0
,
0
);
if
(
init_io_cache
(
&
info
->
rec_cache
,
-
1
,(
uint
)
param
->
write_buffer_length
,
WRITE_CACHE
,
share
->
pack
.
header_length
,
1
,
MYF
(
MY_WME
|
MY_WAIT_IF_FULL
)))
...
...
storage/myisammrg/ha_myisammrg.cc
View file @
88a1c9c9
...
...
@@ -925,11 +925,11 @@ int ha_myisammrg::info(uint flag)
with such a number, it'll be an error later anyway.
*/
bzero
((
char
*
)
table
->
key_info
[
0
].
rec_per_key
,
sizeof
(
table
->
key_info
[
0
].
rec_per_key
)
*
table
->
s
->
key_parts
);
sizeof
(
table
->
key_info
[
0
].
rec_per_key
[
0
]
)
*
table
->
s
->
key_parts
);
#endif
memcpy
((
char
*
)
table
->
key_info
[
0
].
rec_per_key
,
(
char
*
)
mrg_info
.
rec_per_key
,
sizeof
(
table
->
key_info
[
0
].
rec_per_key
)
*
sizeof
(
table
->
key_info
[
0
].
rec_per_key
[
0
]
)
*
min
(
file
->
keys
,
table
->
s
->
key_parts
));
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment