Commit 81b2bbac authored by unknown's avatar unknown

Merge mysql.com:/home/my/mysql-5.0

into  mysql.com:/home/my/mysql-5.1


BitKeeper/etc/ignore:
  auto-union
include/my_global.h:
  Auto merged
mysql-test/mysql-test-run.pl:
  Auto merged
mysql-test/r/grant.result:
  Auto merged
sql/ha_federated.cc:
  Auto merged
sql/mysql_priv.h:
  Auto merged
sql/slave.cc:
  Auto merged
sql/sql_acl.cc:
  Auto merged
sql/sql_base.cc:
  Auto merged
sql/sql_class.cc:
  Auto merged
sql/examples/ha_tina.h:
  Auto merged
sql/sql_insert.cc:
  Auto merged
sql/sql_parse.cc:
  Auto merged
sql/sql_udf.cc:
  Auto merged
sql/tztime.cc:
  Auto merged
sql/unireg.h:
  Auto merged
sql/examples/ha_tina.cc:
  manual merge
sql/share/errmsg.txt:
  manual merge
parents fe245ed8 97bfd41f
......@@ -469,6 +469,7 @@ libmysqld/get_password.c
libmysqld/gstream.cc
libmysqld/ha_archive.cc
libmysqld/ha_berkeley.cc
libmysqld/ha_blackhole.cc
libmysqld/ha_example.cc
libmysqld/ha_federated.cc
libmysqld/ha_heap.cc
......
......@@ -662,11 +662,7 @@ typedef SOCKET_SIZE_TYPE size_socket;
#define FN_LEN 256 /* Max file name len */
#define FN_HEADLEN 253 /* Max length of filepart of file name */
#define FN_EXTLEN 20 /* Max length of extension (part of FN_LEN) */
#ifdef PATH_MAX
#define FN_REFLEN PATH_MAX/* Max length of full path-name */
#else
#define FN_REFLEN 512 /* Max length of full path-name */
#endif
#define FN_EXTCHAR '.'
#define FN_HOMELIB '~' /* ~/ is used as abbrev for home dir */
#define FN_CURLIB '.' /* ./ is used as abbrev for current dir */
......
......@@ -9,7 +9,7 @@ use strict;
sub mtr_full_hostname ();
sub mtr_short_hostname ();
sub mtr_init_args ($);
sub mtr_add_arg ($$);
sub mtr_add_arg ($$@);
sub mtr_path_exists(@);
sub mtr_script_exists(@);
sub mtr_exe_exists(@);
......@@ -51,7 +51,7 @@ sub mtr_init_args ($) {
$$args = []; # Empty list
}
sub mtr_add_arg ($$) {
sub mtr_add_arg ($$@) {
my $args= shift;
my $format= shift;
my @fargs = @_;
......
# -*- cperl -*-
# This is a library file used by the Perl version of mysql-test-run,
# and is part of the translation of the Bourne shell script with the
# same name.
use strict;
use File::Spec;
# These are not to be prefixed with "mtr_"
sub run_stress_test ();
##############################################################################
#
# Run tests in the stress mode
#
##############################################################################
sub run_stress_test ()
{
my $args;
my $stress_basedir;
my $stress_suitedir;
mtr_report("Starting stress testing\n");
if ( ! $::glob_use_embedded_server and ! $::opt_local_master )
{
$::master->[0]->{'pid'}= mysqld_start('master',0,[],[]);
if ( ! $::master->[0]->{'pid'} )
{
mtr_error("Can't start the mysqld server");
}
}
my $stress_basedir=File::Spec->catdir($::opt_vardir, "stress");
#Clean up stress dir
if ( -d $stress_basedir )
{
rmtree($stress_basedir);
}
mkpath($stress_basedir);
if ($::opt_stress_suite ne 'main' && $::opt_stress_suite ne 'default' )
{
$stress_suitedir=File::Spec->catdir($::glob_mysql_test_dir, "suite",
$::opt_stress_suite);
}
else
{
$stress_suitedir=$::glob_mysql_test_dir;
}
if ( -d $stress_suitedir )
{
#$stress_suite_t_dir=File::Spec->catdir($stress_suitedir, "t");
#$stress_suite_r_dir=File::Spec->catdir($stress_suitedir, "r");
#FIXME: check dirs above for existence to ensure that test suite
# contains tests and results dirs
}
else
{
mtr_error("Specified test suite $::opt_stress_suite doesn't exist");
}
if ( @::opt_cases )
{
$::opt_stress_test_file=File::Spec->catfile($stress_basedir, "stress_tests.txt");
open(STRESS_FILE, ">$::opt_stress_test_file");
print STRESS_FILE join("\n",@::opt_cases),"\n";
close(STRESS_FILE);
}
elsif ( $::opt_stress_test_file )
{
$::opt_stress_test_file=File::Spec->catfile($stress_suitedir,
$::opt_stress_test_file);
if ( ! -f $::opt_stress_test_file )
{
mtr_error("Specified file $::opt_stress_test_file with list of tests does not exist\n",
"Please ensure that file exists and has proper permissions");
}
}
else
{
$::opt_stress_test_file=File::Spec->catfile($stress_suitedir,
"stress_tests.txt");
if ( ! -f $::opt_stress_test_file )
{
mtr_error("Default file $::opt_stress_test_file with list of tests does not exist\n",
"Please use --stress-test-file option to specify custom one or you can\n",
"just specify name of test for testing as last argument in command line");
}
}
if ( $::opt_stress_init_file )
{
$::opt_stress_init_file=File::Spec->catfile($stress_suitedir,
$::opt_stress_init_file);
if ( ! -f $::opt_stress_init_file )
{
mtr_error("Specified file $::opt_stress_init_file with list of tests does not exist\n",
"Please ensure that file exists and has proper permissions");
}
}
else
{
$::opt_stress_init_file=File::Spec->catfile($stress_suitedir,
"stress_init.txt");
if ( ! -f $::opt_stress_init_file )
{
$::opt_stress_init_file='';
}
}
if ( $::opt_stress_mode ne 'random' && $::opt_stress_mode ne 'seq' )
{
mtr_error("You specified wrong mode $::opt_stress_mode for stress test\n",
"Correct values are 'random' or 'seq'");
}
mtr_init_args(\$args);
mtr_add_arg($args, "--server-socket=%s", $::master->[0]->{'path_mysock'});
mtr_add_arg($args, "--server-user=%s", $::opt_user);
mtr_add_arg($args, "--server-database=%s", "test");
mtr_add_arg($args, "--stress-suite-basedir=%s", $::glob_mysql_test_dir);
mtr_add_arg($args, "--suite=%s", $::opt_stress_suite);
mtr_add_arg($args, "--stress-tests-file=%s", $::opt_stress_test_file);
mtr_add_arg($args, "--stress-basedir=%s", $stress_basedir);
mtr_add_arg($args, "--server-logs-dir=%s", $stress_basedir);
mtr_add_arg($args, "--stress-mode=%s", $::opt_stress_mode);
mtr_add_arg($args, "--mysqltest=%s", $::exe_mysqltest);
mtr_add_arg($args, "--threads=%s", $::opt_stress_threads);
mtr_add_arg($args, "--verbose");
mtr_add_arg($args, "--cleanup");
mtr_add_arg($args, "--log-error-details");
mtr_add_arg($args, "--abort-on-error");
if ( $::opt_stress_init_file )
{
mtr_add_arg($args, "--stress-init-file=%", $::opt_stress_init_file);
}
if ( $::opt_stress_loop_count )
{
mtr_add_arg($args, "--loop-count=%s", $::opt_stress_loop_count);
}
if ( $::opt_stress_test_count )
{
mtr_add_arg($args, "--test-count=%s", $::opt_stress_test_count);
}
if ( $::opt_stress_test_duration )
{
mtr_add_arg($args, "--test-duration=%s", $::opt_stress_test_duration);
}
#Run stress test
mtr_run("$::glob_mysql_test_dir/mysql-stress-test.pl", $args, "", "", "", "");
if ( ! $::glob_use_embedded_server )
{
stop_masters();
}
}
1;
......@@ -96,6 +96,7 @@ require "lib/mtr_report.pl";
require "lib/mtr_diff.pl";
require "lib/mtr_match.pl";
require "lib/mtr_misc.pl";
require "lib/mtr_stress.pl";
$Devel::Trace::TRACE= 1;
......@@ -271,6 +272,16 @@ our $opt_valgrind_mysqltest;
our $opt_valgrind_all;
our $opt_valgrind_options;
our $opt_stress= "";
our $opt_stress_suite= "main";
our $opt_stress_mode= "random";
our $opt_stress_threads= 5;
our $opt_stress_test_count= 20;
our $opt_stress_loop_count= "";
our $opt_stress_test_duration= "";
our $opt_stress_init_file= "";
our $opt_stress_test_file= "";
our $opt_verbose;
our $opt_wait_for_master;
......@@ -391,6 +402,10 @@ sub main () {
{
run_benchmarks(shift); # Shift what? Extra arguments?!
}
elsif ( $opt_stress )
{
run_stress_test()
}
else
{
run_tests();
......@@ -547,6 +562,17 @@ sub command_line_setup () {
'valgrind-all:s' => \$opt_valgrind_all,
'valgrind-options=s' => \$opt_valgrind_options,
# Stress testing
'stress' => \$opt_stress,
'stress-suite=s' => \$opt_stress_suite,
'stress-threads=i' => \$opt_stress_threads,
'stress-test-file=s' => \$opt_stress_test_file,
'stress-init-file=s' => \$opt_stress_init_file,
'stress-mode=s' => \$opt_stress_mode,
'stress-loop-count=i' => \$opt_stress_loop_count,
'stress-test-count=i' => \$opt_stress_test_count,
'stress-test-duration=i' => \$opt_stress_test_duration,
# Misc
'big-test' => \$opt_big_test,
'debug' => \$opt_debug,
......
......@@ -4976,3 +4976,27 @@ c1
4
5
DROP TABLE bug14672;
create table t1 (a int) engine=csv;
insert t1 values (1);
delete from t1;
affected rows: 1
delete from t1;
affected rows: 0
insert t1 values (1),(2);
delete from t1;
affected rows: 2
insert t1 values (1),(2),(3);
flush tables;
delete from t1;
affected rows: 3
insert t1 values (1),(2),(3),(4);
flush tables;
select count(*) from t1;
count(*)
4
delete from t1;
affected rows: 4
insert t1 values (1),(2),(3),(4),(5);
truncate table t1;
affected rows: 0
drop table t1;
......@@ -40,14 +40,14 @@ CREATE TABLE federated.t1 (
)
ENGINE="FEDERATED" DEFAULT CHARSET=latin1
CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t3';
ERROR HY000: Can't create federated table. Foreign data src error : ': 1146 : Table 'federated.t3' doesn't exist'
ERROR HY000: Can't create federated table. Foreign data src error: error: 1146 'Table 'federated.t3' doesn't exist'
CREATE TABLE federated.t1 (
`id` int(20) NOT NULL,
`name` varchar(32) NOT NULL default ''
)
ENGINE="FEDERATED" DEFAULT CHARSET=latin1
CONNECTION='mysql://user:pass@127.0.0.1:SLAVE_PORT/federated/t1';
ERROR HY000: Unable to connect to foreign data source - database ' database federated username user hostname 127.0.0.1'!
ERROR HY000: Unable to connect to foreign data source: database: 'federated' username: 'user' hostname: '127.0.0.1'
DROP TABLE IF EXISTS federated.t1;
Warnings:
Note 1051 Unknown table 't1'
......
......@@ -238,7 +238,7 @@ Grants for drop_user@localhost
GRANT USAGE ON *.* TO 'drop_user'@'localhost'
drop user drop_user@localhost;
revoke all privileges, grant option from drop_user@localhost;
ERROR HY000: Can't revoke all privileges, grant for one or more of the requested users
ERROR HY000: Can't revoke all privileges for one or more of the requested users
grant select(a) on test.t1 to drop_user1@localhost;
grant select on test.t1 to drop_user2@localhost;
grant select on test.* to drop_user3@localhost;
......@@ -247,7 +247,7 @@ drop user drop_user1@localhost, drop_user2@localhost, drop_user3@localhost,
drop_user4@localhost;
revoke all privileges, grant option from drop_user1@localhost, drop_user2@localhost,
drop_user3@localhost, drop_user4@localhost;
ERROR HY000: Can't revoke all privileges, grant for one or more of the requested users
ERROR HY000: Can't revoke all privileges for one or more of the requested users
drop user drop_user1@localhost, drop_user2@localhost, drop_user3@localhost,
drop_user4@localhost;
ERROR HY000: Operation DROP USER failed for 'drop_user1'@'localhost','drop_user2'@'localhost','drop_user3'@'localhost','drop_user4'@'localhost'
......
......@@ -191,7 +191,7 @@ flush privileges;
show grants for 'mysqltest_1';
ERROR 42000: There is no such grant defined for user 'mysqltest_1' on host '%'
revoke all privileges, grant option from 'mysqltest_1';
ERROR HY000: Can't revoke all privileges, grant for one or more of the requested users
ERROR HY000: Can't revoke all privileges for one or more of the requested users
drop user 'mysqltest_1';
select host,db,user from mysql.db where user = 'mysqltest_1' order by host,db,user;
host db user
......
......@@ -1352,3 +1352,35 @@ SELECT * FROM bug14672;
DROP TABLE bug14672;
# End of 4.1 tests
#
# BUG#13406 - incorrect amount of "records deleted"
#
create table t1 (a int) engine=csv;
insert t1 values (1);
--enable_info
delete from t1; -- delete_row
delete from t1; -- delete_all_rows
--disable_info
insert t1 values (1),(2);
--enable_info
delete from t1; -- delete_all_rows
--disable_info
insert t1 values (1),(2),(3);
flush tables;
--enable_info
delete from t1; -- delete_row
--disable_info
insert t1 values (1),(2),(3),(4);
flush tables;
select count(*) from t1;
--enable_info
delete from t1; -- delete_all_rows
--disable_info
insert t1 values (1),(2),(3),(4),(5);
--enable_info
truncate table t1; -- truncate
--disable_info
drop table t1;
......@@ -298,7 +298,7 @@ ha_tina::ha_tina(TABLE *table_arg)
These are not probably completely right.
*/
current_position(0), next_position(0), chain_alloced(0),
chain_size(DEFAULT_CHAIN_LENGTH)
chain_size(DEFAULT_CHAIN_LENGTH), records_is_known(0)
{
/* Set our original buffers from pre-allocated memory */
buffer.set(byte_buffer, IO_SIZE, system_charset_info);
......@@ -534,6 +534,7 @@ int ha_tina::write_row(byte * buf)
*/
if (get_mmap(share, 0) > 0)
DBUG_RETURN(-1);
records++;
DBUG_RETURN(0);
}
......@@ -700,6 +701,7 @@ int ha_tina::rnd_init(bool scan)
current_position= next_position= 0;
records= 0;
records_is_known= 0;
chain_ptr= chain;
#ifdef HAVE_MADVISE
if (scan)
......@@ -781,7 +783,7 @@ void ha_tina::info(uint flag)
{
DBUG_ENTER("ha_tina::info");
/* This is a lie, but you don't want the optimizer to see zero or 1 */
if (records < 2)
if (!records_is_known && records < 2)
records= 2;
DBUG_VOID_RETURN;
}
......@@ -818,6 +820,8 @@ int ha_tina::rnd_end()
{
DBUG_ENTER("ha_tina::rnd_end");
records_is_known= 1;
/* First position will be truncate position, second will be increment */
if ((chain_ptr - chain) > 0)
{
......@@ -862,18 +866,24 @@ int ha_tina::rnd_end()
DBUG_RETURN(0);
}
/*
Truncate table and others of its ilk call this.
/*
DELETE without WHERE calls this
*/
int ha_tina::delete_all_rows()
{
DBUG_ENTER("ha_tina::delete_all_rows");
if (!records_is_known)
return (my_errno=HA_ERR_WRONG_COMMAND);
int rc= my_chsize(share->data_file, 0, 0, MYF(MY_WME));
if (get_mmap(share, 0) > 0)
DBUG_RETURN(-1);
records=0;
DBUG_RETURN(rc);
}
......
......@@ -53,6 +53,7 @@ class ha_tina: public handler
tina_set *chain_ptr;
byte chain_alloced;
uint32 chain_size;
bool records_is_known;
public:
ha_tina(TABLE *table_arg);
......
......@@ -488,8 +488,7 @@ int federated_db_end(ha_panic_function type)
table, and if so, does the foreign table exist.
*/
static int check_foreign_data_source(
FEDERATED_SHARE *share,
static int check_foreign_data_source(FEDERATED_SHARE *share,
bool table_create_flag)
{
char escaped_table_name[NAME_LEN*2];
......@@ -515,15 +514,17 @@ static int check_foreign_data_source(
share->port,
share->socket, 0))
{
/*
we want the correct error message, but it to return
ER_CANT_CREATE_FEDERATED_TABLE if called by ::create
*/
error_code= table_create_flag?
ER_CANT_CREATE_FEDERATED_TABLE : ER_CONNECT_TO_FOREIGN_DATA_SOURCE;
/*
we want the correct error message, but it to return
ER_CANT_CREATE_FEDERATED_TABLE if called by ::create
*/
error_code= (table_create_flag ?
ER_CANT_CREATE_FEDERATED_TABLE :
ER_CONNECT_TO_FOREIGN_DATA_SOURCE);
my_sprintf(error_buffer,
(error_buffer, " database %s username %s hostname %s",
(error_buffer,
"database: '%s' username: '%s' hostname: '%s'",
share->database, share->username, share->hostname));
my_error(ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), error_buffer);
......@@ -564,8 +565,8 @@ static int check_foreign_data_source(
{
error_code= table_create_flag ?
ER_CANT_CREATE_FEDERATED_TABLE : ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST;
my_sprintf(error_buffer, (error_buffer, ": %d : %s",
mysql_errno(mysql), mysql_error(mysql)));
my_sprintf(error_buffer, (error_buffer, "error: %d '%s'",
mysql_errno(mysql), mysql_error(mysql)));
my_error(error_code, MYF(0), error_buffer);
goto error;
......@@ -2032,7 +2033,7 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key,
}
if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length()))
{
my_sprintf(error_buffer, (error_buffer, ": %d : %s",
my_sprintf(error_buffer, (error_buffer, "error: %d '%s'",
mysql_errno(mysql), mysql_error(mysql)));
retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
goto error;
......
......@@ -101,6 +101,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define MAX_FIELDS_BEFORE_HASH 32
#define USER_VARS_HASH_SIZE 16
#define STACK_MIN_SIZE 8192 // Abort if less stack during eval.
#define STACK_MIN_SIZE_FOR_OPEN 1024*80
#define STACK_BUFF_ALLOC 256 // For stack overrun checks
#ifndef MYSQLD_NET_RETRY_COUNT
#define MYSQLD_NET_RETRY_COUNT 10 // Abort read after this many int.
......
This diff is collapsed.
......@@ -3070,6 +3070,7 @@ pthread_handler_t handle_slave_io(void *arg)
THD_CHECK_SENTRY(thd);
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd; // remember where our stack is
if (init_slave_thread(thd, SLAVE_THD_IO))
{
pthread_cond_broadcast(&mi->start_cond);
......@@ -3078,7 +3079,6 @@ pthread_handler_t handle_slave_io(void *arg)
goto err;
}
mi->io_thd = thd;
thd->thread_stack = (char*)&thd; // remember where our stack is
pthread_mutex_lock(&LOCK_thread_count);
threads.append(thd);
pthread_mutex_unlock(&LOCK_thread_count);
......
......@@ -157,6 +157,7 @@ my_bool acl_init(bool dont_read_acl_tables)
*/
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: inspected */
thd->thread_stack= (char*) &thd;
thd->store_globals();
/*
It is safe to call acl_reload() since acl_* arrays and hashes which
......@@ -3263,6 +3264,7 @@ my_bool grant_init()
if (!(thd= new THD))
DBUG_RETURN(1); /* purecov: deadcode */
thd->thread_stack= (char*) &thd;
thd->store_globals();
return_val= grant_reload(thd);
delete thd;
......
......@@ -1090,7 +1090,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*refresh=0;
/* an open table operation needs a lot of the stack space */
if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char *)&alias))
if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (char *)&alias))
return 0;
if (thd->killed)
......
......@@ -517,6 +517,12 @@ void THD::awake(THD::killed_state state_to_set)
bool THD::store_globals()
{
/*
Assert that thread_stack is initialized: it's necessary to be able
to track stack overrun.
*/
DBUG_ASSERT(this->thread_stack);
if (my_pthread_setspecific_ptr(THR_THD, this) ||
my_pthread_setspecific_ptr(THR_MALLOC, &mem_root))
return 1;
......
......@@ -1736,6 +1736,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
#endif
DBUG_ENTER("handle_delayed_insert");
thd->thread_stack= (char*) &thd;
if (init_thr_lock() || thd->store_globals())
{
thd->fatal_error();
......
......@@ -1091,6 +1091,7 @@ pthread_handler_t handle_one_connection(void *arg)
VOID(sigemptyset(&set)); // Get mask in use
VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
#endif
thd->thread_stack= (char*) &thd;
if (thd->store_globals())
{
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
......@@ -1104,7 +1105,6 @@ pthread_handler_t handle_one_connection(void *arg)
int error;
NET *net= &thd->net;
Security_context *sctx= thd->security_ctx;
thd->thread_stack= (char*) &thd;
net->no_send_error= 0;
if ((error=check_connection(thd)))
......@@ -1195,6 +1195,7 @@ pthread_handler_t handle_bootstrap(void *arg)
char *buff;
/* The following must be called before DBUG_ENTER */
thd->thread_stack= (char*) &thd;
if (my_thread_init() || thd->store_globals())
{
#ifndef EMBEDDED_LIBRARY
......@@ -5286,6 +5287,7 @@ bool check_stack_overrun(THD *thd, long margin,
char *buf __attribute__((unused)))
{
long stack_used;
DBUG_ASSERT(thd == current_thd);
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
(long) (thread_stack - margin))
{
......@@ -6735,7 +6737,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
allocate temporary THD for execution of acl_reload()/grant_reload().
*/
if (!thd && (thd= (tmp_thd= new THD)))
{
thd->thread_stack= (char*) &tmp_thd;
thd->store_globals();
}
if (thd)
{
(void)acl_reload(thd);
......
......@@ -133,6 +133,7 @@ void udf_init()
DBUG_VOID_RETURN;
}
initialized = 1;
new_thd->thread_stack= (char*) &new_thd;
new_thd->store_globals();
new_thd->db= my_strdup("mysql", MYF(0));
new_thd->db_length=5;
......
......@@ -1532,6 +1532,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
*/
if (!(thd= new THD))
DBUG_RETURN(1);
thd->thread_stack= (char*) &thd;
thd->store_globals();
/* Init all memory structures that require explicit destruction */
......
......@@ -46,8 +46,8 @@
#define ERRMAPP 1 /* Errormap f|r my_error */
#define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */
#define MAX_DBKEY_LENGTH (FN_LEN*2+1+1+4+4) /* extra 4+4 bytes for slave tmp
* tables */
/* extra 4+4 bytes for slave tmp tables */
#define MAX_DBKEY_LENGTH (NAME_LEN*2+1+1+4+4)
#define MAX_ALIAS_NAME 256
#define MAX_FIELD_NAME 34 /* Max colum name length +2 */
#define MAX_SYS_VAR_LENGTH 32
......
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