Commit 7d6d4c83 authored by unknown's avatar unknown

Fixed problems found by valgrind

Fixed problems in test suite where some test failed
Fixed access to not initialized memory in federated
Fixed access to not initialized memory when using BIT fields in internal temporary tables


BitKeeper/etc/ignore:
  added libmysqld/sql_cursor.h
mysql-test/r/information_schema.result:
  Change view names to 'v#' to not cause massive conflict with other tests if test dies in the middlecd
mysql-test/r/information_schema_inno.result:
  Remove used tables at start
mysql-test/r/multi_statement.result:
  Remove used tables at start
mysql-test/r/temp_table.result:
  Change view names to 'v#' to not cause massive conflict with other tests if test dies in the middle
mysql-test/r/type_bit.result:
  More tests
mysql-test/t/information_schema.test:
  Change view names to 'v#' to not cause massive conflict with other tests if test dies in the middle
mysql-test/t/information_schema_inno.test:
  Remove used tables at start
mysql-test/t/multi_statement.test:
  Remove used tables at start
mysql-test/t/temp_table.test:
  Change view names to 'v#' to not cause massive conflict with other tests if test dies in the middle
mysql-test/t/type_bit.test:
  More tests
mysql-test/valgrind.supp:
  Removed some valgrind warnings that isn't errors
sql/ha_federated.cc:
  Fixed errors discovered by valgrind:
  - Socket was not initialized
  - share->scheme was deleted while there was still pointer into it from the hash
sql/item_func.h:
  Remove access to table object from cleanup() as the table object may have been dropped earlier (In case of temporary tables or of close_thread_tables() is run before cleanup())
  This fixed a bug with access to already freed memory
sql/sql_base.cc:
  Reset variables used by fulltext
sql/sql_select.cc:
  Fixed various problems with bit fields when used in internal temporary tables.
  Calculate space needed for bit fields correctly (previously we allocated more space than needed for rows in heap/myisam tables)
parent 813fc410
...@@ -1125,3 +1125,5 @@ vio/test-ssl ...@@ -1125,3 +1125,5 @@ vio/test-ssl
vio/test-sslclient vio/test-sslclient
vio/test-sslserver vio/test-sslserver
vio/viotest-ssl vio/viotest-ssl
libmysqld/sql_cursor.cc
libmysqld/sql_cursor.h
DROP TABLE IF EXISTS t0,t1,t2,t3,t5; DROP TABLE IF EXISTS t0,t1,t2,t3,t4,t5;
DROP VIEW IF EXISTS v1;
show variables where variable_name like "skip_show_database"; show variables where variable_name like "skip_show_database";
Variable_name Value Variable_name Value
skip_show_database OFF skip_show_database OFF
...@@ -638,8 +639,8 @@ use test; ...@@ -638,8 +639,8 @@ use test;
create function sub1(i int) returns int create function sub1(i int) returns int
return i+1; return i+1;
create table t1(f1 int); create table t1(f1 int);
create view t2 (c) as select f1 from t1; create view v2 (c) as select f1 from t1;
create view t3 (c) as select sub1(1); create view v3 (c) as select sub1(1);
create table t4(f1 int, KEY f1_key (f1)); create table t4(f1 int, KEY f1_key (f1));
drop table t1; drop table t1;
drop function sub1; drop function sub1;
...@@ -647,29 +648,29 @@ select table_name from information_schema.views ...@@ -647,29 +648,29 @@ select table_name from information_schema.views
where table_schema='test'; where table_schema='test';
table_name table_name
Warnings: Warnings:
Warning 1356 View 'test.t2' references invalid table(s) or column(s) or function(s) Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.t3' references invalid table(s) or column(s) or function(s) Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
select table_name from information_schema.views select table_name from information_schema.views
where table_schema='test'; where table_schema='test';
table_name table_name
Warnings: Warnings:
Warning 1356 View 'test.t2' references invalid table(s) or column(s) or function(s) Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.t3' references invalid table(s) or column(s) or function(s) Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
select column_name from information_schema.columns select column_name from information_schema.columns
where table_schema='test'; where table_schema='test';
column_name column_name
f1 f1
Warnings: Warnings:
Warning 1356 View 'test.t2' references invalid table(s) or column(s) or function(s) Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.t3' references invalid table(s) or column(s) or function(s) Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
select index_name from information_schema.statistics where table_schema='test'; select index_name from information_schema.statistics where table_schema='test';
index_name index_name
f1_key f1_key
select constraint_name from information_schema.table_constraints select constraint_name from information_schema.table_constraints
where table_schema='test'; where table_schema='test';
constraint_name constraint_name
drop view t2; drop view v2;
drop view t3; drop view v3;
drop table t4; drop table t4;
select * from information_schema.table_names; select * from information_schema.table_names;
ERROR 42S02: Unknown table 'table_names' in information_schema ERROR 42S02: Unknown table 'table_names' in information_schema
......
DROP TABLE IF EXISTS t1,t2; DROP TABLE IF EXISTS t1,t2,t3;
CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB; CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id, id), CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id, id),
FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE, FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE,
......
DROP TABLE IF EXISTS t1;
select 1; select 1;
1 1
1 1
......
drop table if exists t1,t2; drop table if exists t1,t2;
drop view if exists v1;
CREATE TABLE t1 (c int not null, d char (10) not null); CREATE TABLE t1 (c int not null, d char (10) not null);
insert into t1 values(1,""),(2,"a"),(3,"b"); insert into t1 values(1,""),(2,"a"),(3,"b");
CREATE TEMPORARY TABLE t1 (a int not null, b char (10) not null); CREATE TEMPORARY TABLE t1 (a int not null, b char (10) not null);
...@@ -99,32 +100,32 @@ Variable_name Value ...@@ -99,32 +100,32 @@ Variable_name Value
Created_tmp_disk_tables 0 Created_tmp_disk_tables 0
Created_tmp_tables 2 Created_tmp_tables 2
drop table t1; drop table t1;
create temporary table t1 as select 'This is temp. table' A; create temporary table v1 as select 'This is temp. table' A;
create view t1 as select 'This is view' A; create view v1 as select 'This is view' A;
select * from t1; select * from v1;
A A
This is temp. table This is temp. table
show create table t1; show create table v1;
Table Create Table Table Create Table
t1 CREATE TEMPORARY TABLE `t1` ( v1 CREATE TEMPORARY TABLE `v1` (
`A` varchar(19) NOT NULL default '' `A` varchar(19) NOT NULL default ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
show create view t1; show create view v1;
View Create View View Create View
t1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `t1` AS select _latin1'This is view' AS `A` v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select _latin1'This is view' AS `A`
drop view t1; drop view v1;
select * from t1; select * from v1;
A A
This is temp. table This is temp. table
create view t1 as select 'This is view again' A; create view v1 as select 'This is view again' A;
select * from t1; select * from v1;
A A
This is temp. table This is temp. table
drop table t1; drop table v1;
select * from t1; select * from v1;
A A
This is view again This is view again
drop view t1; drop view v1;
create table t1 (a int, b int, index(a), index(b)); create table t1 (a int, b int, index(a), index(b));
create table t2 (c int auto_increment, d varchar(255), primary key (c)); create table t2 (c int auto_increment, d varchar(255), primary key (c));
insert into t1 values (3,1),(3,2); insert into t1 values (3,1),(3,2);
......
...@@ -553,3 +553,13 @@ sum(a1) b1+0 b2+0 ...@@ -553,3 +553,13 @@ sum(a1) b1+0 b2+0
2 0 0 2 0 0
4 2 2 4 2 2
8 1 1 8 1 1
select 1 from t1 join t2 on b1 = b2 group by b1 order by 1;
1
1
1
1
select b1+0,sum(b1), sum(b2) from t1 join t2 on b1 = b2 group by b1 order by 1;
b1+0 sum(b1) sum(b2)
0 0 0
1 4 4
2 2 2
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
# show databases # show databases
--disable_warnings --disable_warnings
DROP TABLE IF EXISTS t0,t1,t2,t3,t5; DROP TABLE IF EXISTS t0,t1,t2,t3,t4,t5;
DROP VIEW IF EXISTS v1;
--enable_warnings --enable_warnings
...@@ -364,8 +365,8 @@ use test; ...@@ -364,8 +365,8 @@ use test;
create function sub1(i int) returns int create function sub1(i int) returns int
return i+1; return i+1;
create table t1(f1 int); create table t1(f1 int);
create view t2 (c) as select f1 from t1; create view v2 (c) as select f1 from t1;
create view t3 (c) as select sub1(1); create view v3 (c) as select sub1(1);
create table t4(f1 int, KEY f1_key (f1)); create table t4(f1 int, KEY f1_key (f1));
drop table t1; drop table t1;
drop function sub1; drop function sub1;
...@@ -378,8 +379,8 @@ where table_schema='test'; ...@@ -378,8 +379,8 @@ where table_schema='test';
select index_name from information_schema.statistics where table_schema='test'; select index_name from information_schema.statistics where table_schema='test';
select constraint_name from information_schema.table_constraints select constraint_name from information_schema.table_constraints
where table_schema='test'; where table_schema='test';
drop view t2; drop view v2;
drop view t3; drop view v3;
drop table t4; drop table t4;
# #
......
-- source include/have_innodb.inc -- source include/have_innodb.inc
--disable_warnings --disable_warnings
DROP TABLE IF EXISTS t1,t2; DROP TABLE IF EXISTS t1,t2,t3;
--enable_warnings --enable_warnings
# #
......
# PS doesn't support multi-statements # PS doesn't support multi-statements
--disable_ps_protocol --disable_ps_protocol
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
select 1; select 1;
delimiter ||||; delimiter ||||;
select 2; select 2;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
--disable_warnings --disable_warnings
drop table if exists t1,t2; drop table if exists t1,t2;
drop view if exists v1;
--enable_warnings --enable_warnings
CREATE TABLE t1 (c int not null, d char (10) not null); CREATE TABLE t1 (c int not null, d char (10) not null);
...@@ -91,18 +92,18 @@ show status like "created_tmp%tables"; ...@@ -91,18 +92,18 @@ show status like "created_tmp%tables";
drop table t1; drop table t1;
# Fix for BUG#8921: Check that temporary table is ingored by view commands. # Fix for BUG#8921: Check that temporary table is ingored by view commands.
create temporary table t1 as select 'This is temp. table' A; create temporary table v1 as select 'This is temp. table' A;
create view t1 as select 'This is view' A; create view v1 as select 'This is view' A;
select * from t1; select * from v1;
show create table t1; show create table v1;
show create view t1; show create view v1;
drop view t1; drop view v1;
select * from t1; select * from v1;
create view t1 as select 'This is view again' A; create view v1 as select 'This is view again' A;
select * from t1; select * from v1;
drop table t1; drop table v1;
select * from t1; select * from v1;
drop view t1; drop view v1;
# Bug #8497: tmpdir with extra slashes would cause failures # Bug #8497: tmpdir with extra slashes would cause failures
# #
......
...@@ -224,3 +224,5 @@ select a1, a2, b1+0, b2+0 from t1 join t2 on a1 = a2; ...@@ -224,3 +224,5 @@ select a1, a2, b1+0, b2+0 from t1 join t2 on a1 = a2;
select a1, a2, b1+0, b2+0 from t1 join t2 on a1 = a2 order by a1; select a1, a2, b1+0, b2+0 from t1 join t2 on a1 = a2 order by a1;
select a1, a2, b1+0, b2+0 from t1 join t2 on b1 = b2; select a1, a2, b1+0, b2+0 from t1 join t2 on b1 = b2;
select sum(a1), b1+0, b2+0 from t1 join t2 on b1 = b2 group by b1 order by 1; select sum(a1), b1+0, b2+0 from t1 join t2 on b1 = b2 group by b1 order by 1;
select 1 from t1 join t2 on b1 = b2 group by b1 order by 1;
select b1+0,sum(b1), sum(b2) from t1 join t2 on b1 = b2 group by b1 order by 1;
...@@ -51,6 +51,16 @@ ...@@ -51,6 +51,16 @@
fun:pthread_create fun:pthread_create
} }
{
pthread pthread_key_create
Memcheck:Leak
fun:malloc
fun:*
fun:*
fun:pthread_key_create
fun:my_thread_global_init
}
{ {
pthread strstr uninit pthread strstr uninit
Memcheck:Cond Memcheck:Cond
...@@ -127,8 +137,18 @@ ...@@ -127,8 +137,18 @@
{ {
libz deflate libz deflate
Memcheck:Cond Memcheck:Cond
obj:/usr/lib/libz.so.* obj:*/libz.so.*
obj:/usr/lib/libz.so.* obj:*/libz.so.*
fun:deflate fun:deflate
fun:compress2 fun:compress2
} }
{
libz deflate2
Memcheck:Cond
obj:*/libz.so.*
obj:*/libz.so.*
fun:deflate
obj:*/libz.so.*
fun:gzflush
}
...@@ -588,12 +588,14 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, ...@@ -588,12 +588,14 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table,
DBUG_ENTER("ha_federated::parse_url"); DBUG_ENTER("ha_federated::parse_url");
share->port= 0; share->port= 0;
share->socket= 0;
DBUG_PRINT("info", ("Length %d \n", table->s->connect_string.length)); DBUG_PRINT("info", ("Length %d \n", table->s->connect_string.length));
DBUG_PRINT("info", ("String %.*s \n", table->s->connect_string.length, DBUG_PRINT("info", ("String %.*s \n", table->s->connect_string.length,
table->s->connect_string.str)); table->s->connect_string.str));
share->scheme= my_strdup_with_length( share->scheme= my_strdup_with_length((const byte*)table->s->
(const byte*)table->s->connect_string.str, connect_string.str,
table->s->connect_string.length+1, MYF(0)); table->s->connect_string.length,
MYF(0));
// Add a null for later termination of table name // Add a null for later termination of table name
share->scheme[table->s->connect_string.length]= 0; share->scheme[table->s->connect_string.length]= 0;
...@@ -1375,13 +1377,9 @@ static int free_share(FEDERATED_SHARE *share) ...@@ -1375,13 +1377,9 @@ static int free_share(FEDERATED_SHARE *share)
if (!--share->use_count) if (!--share->use_count)
{ {
if (share->scheme)
{
my_free((gptr) share->scheme, MYF(0));
share->scheme= 0;
}
hash_delete(&federated_open_tables, (byte*) share); hash_delete(&federated_open_tables, (byte*) share);
my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR0));
share->scheme= 0;
thr_lock_delete(&share->lock); thr_lock_delete(&share->lock);
VOID(pthread_mutex_destroy(&share->mutex)); VOID(pthread_mutex_destroy(&share->mutex));
my_free((gptr) share, MYF(0)); my_free((gptr) share, MYF(0));
......
...@@ -1286,9 +1286,6 @@ public: ...@@ -1286,9 +1286,6 @@ public:
{ {
ft_handler->please->close_search(ft_handler); ft_handler->please->close_search(ft_handler);
ft_handler=0; ft_handler=0;
if (join_key)
table->file->ft_handler=0;
table->fulltext_searched=0;
} }
concat= 0; concat= 0;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
......
...@@ -1327,6 +1327,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -1327,6 +1327,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->keys_in_use_for_query= table->s->keys_in_use; table->keys_in_use_for_query= table->s->keys_in_use;
table->insert_values= 0; table->insert_values= 0;
table->used_keys= table->s->keys_for_keyread; table->used_keys= table->s->keys_for_keyread;
table->fulltext_searched= 0;
table->file->ft_handler= 0;
if (table->timestamp_field) if (table->timestamp_field)
table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
......
...@@ -7817,18 +7817,24 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, ...@@ -7817,18 +7817,24 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
if (field->maybe_null && !field->field->maybe_null()) if (field->maybe_null && !field->field->maybe_null())
{ {
result= create_tmp_field_from_item(thd, item, table, NULL, result= create_tmp_field_from_item(thd, item, table, NULL,
modify_item, convert_blob_length); modify_item, convert_blob_length);
*from_field= field->field; *from_field= field->field;
if (result && modify_item) if (result && modify_item)
((Item_field*)item)->result_field= result; field->result_field= result;
} }
else if (table_cant_handle_bit_fields && field->field->type() == FIELD_TYPE_BIT) else if (table_cant_handle_bit_fields && field->field->type() ==
FIELD_TYPE_BIT)
{
*from_field= field->field;
result= create_tmp_field_from_item(thd, item, table, copy_func, result= create_tmp_field_from_item(thd, item, table, copy_func,
modify_item, convert_blob_length); modify_item, convert_blob_length);
if (result && modify_item)
field->result_field= result;
}
else else
result= create_tmp_field_from_field(thd, (*from_field= field->field), result= create_tmp_field_from_field(thd, (*from_field= field->field),
item->name, table, item->name, table,
modify_item ? (Item_field*) item : modify_item ? field :
NULL, NULL,
convert_blob_length); convert_blob_length);
if (orig_type == Item::REF_ITEM && orig_modify) if (orig_type == Item::REF_ITEM && orig_modify)
...@@ -8071,7 +8077,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -8071,7 +8077,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
Field *new_field= Field *new_field=
create_tmp_field(thd, table, arg, arg->type(), &copy_func, create_tmp_field(thd, table, arg, arg->type(), &copy_func,
tmp_from_field, group != 0,not_all_columns, tmp_from_field, group != 0,not_all_columns,
group || distinct, distinct,
param->convert_blob_length); param->convert_blob_length);
if (!new_field) if (!new_field)
goto err; // Should be OOM goto err; // Should be OOM
...@@ -8082,6 +8088,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -8082,6 +8088,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
*blob_field++= (uint) (reg_field - table->field); *blob_field++= (uint) (reg_field - table->field);
blob_count++; blob_count++;
} }
if (new_field->type() == FIELD_TYPE_BIT)
total_uneven_bit_length+= new_field->field_length & 7;
new_field->field_index= (uint) (reg_field - table->field); new_field->field_index= (uint) (reg_field - table->field);
*(reg_field++)= new_field; *(reg_field++)= new_field;
if (new_field->real_type() == MYSQL_TYPE_STRING || if (new_field->real_type() == MYSQL_TYPE_STRING ||
...@@ -8117,12 +8125,16 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -8117,12 +8125,16 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
write rows to the temporary table. write rows to the temporary table.
We here distinguish between UNION and multi-table-updates by the fact We here distinguish between UNION and multi-table-updates by the fact
that in the later case group is set to the row pointer. that in the later case group is set to the row pointer.
The test for item->marker == 4 is ensure we don't create a group-by
key over a bit field as heap tables can't handle that.
*/ */
Field *new_field= (param->schema_table) ? Field *new_field= (param->schema_table) ?
create_tmp_field_for_schema(thd, item, table) : create_tmp_field_for_schema(thd, item, table) :
create_tmp_field(thd, table, item, type, &copy_func, create_tmp_field(thd, table, item, type, &copy_func,
tmp_from_field, group != 0, tmp_from_field, group != 0,
not_all_columns || group != 0, 0, not_all_columns || group != 0,
item->marker == 4,
param->convert_blob_length); param->convert_blob_length);
if (!new_field) if (!new_field)
...@@ -8154,7 +8166,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -8154,7 +8166,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
*(reg_field++) =new_field; *(reg_field++) =new_field;
} }
if (!--hidden_field_count) if (!--hidden_field_count)
{
/*
This was the last hidden field; Remember how many hidden fields could
have null
*/
hidden_null_count=null_count; hidden_null_count=null_count;
null_count= 0;
}
} }
DBUG_ASSERT(field_count >= (uint) (reg_field - table->field)); DBUG_ASSERT(field_count >= (uint) (reg_field - table->field));
field_count= (uint) (reg_field - table->field); field_count= (uint) (reg_field - table->field);
...@@ -8189,8 +8208,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -8189,8 +8208,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
null_count++; null_count++;
} }
hidden_null_pack_length=(hidden_null_count+7)/8; hidden_null_pack_length=(hidden_null_count+7)/8;
null_pack_length= hidden_null_count + null_pack_length= (hidden_null_pack_length +
(null_count + total_uneven_bit_length + 7) / 8; (null_count + total_uneven_bit_length + 7) / 8);
reclength+=null_pack_length; reclength+=null_pack_length;
if (!reclength) if (!reclength)
reclength=1; // Dummy select reclength=1; // Dummy select
...@@ -12161,6 +12180,11 @@ calc_group_buffer(JOIN *join,ORDER *group) ...@@ -12161,6 +12180,11 @@ calc_group_buffer(JOIN *join,ORDER *group)
key_length+=MAX_BLOB_WIDTH; // Can't be used as a key key_length+=MAX_BLOB_WIDTH; // Can't be used as a key
else if (field->type() == MYSQL_TYPE_VARCHAR) else if (field->type() == MYSQL_TYPE_VARCHAR)
key_length+= field->field_length + HA_KEY_BLOB_LENGTH; key_length+= field->field_length + HA_KEY_BLOB_LENGTH;
else if (field->type() == FIELD_TYPE_BIT)
{
/* Bit is usually stored as a longlong key for group fields */
key_length+= 8; // Big enough
}
else else
key_length+= field->pack_length(); key_length+= field->pack_length();
} }
......
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