Commit 8f3d884d authored by Christopher Powers's avatar Christopher Powers

Bug#35333, "If Federated table can't connect to remote host, can't retrieve

metadata"

Improved error handling such that queries against Information_Schema.Tables won't
fail if a federated table can't make a remote connection.

mysql-test/r/lock_multi.result:
  Updated with warnings that were previously masked.
mysql-test/r/mdl_sync.result:
  Updated with warnings that were previously masked.
mysql-test/r/merge.result:
  Updated with warnings that were previously masked.
mysql-test/r/show_check.result:
  Updated with warnings that were previously masked.
mysql-test/r/view.result:
  Updated with warnings that were previously masked.
mysql-test/suite/federated/federated_bug_35333.result:
  New test results for bug#35333
mysql-test/suite/federated/federated_bug_35333.test:
  New test or bug#35333
sql/sql_show.cc:
  If get_schema_tables_record() encounters an error, push a warning,
  set the TABLE COMMENT column with the error text, and clear the
  error so that the operation can continue.
parent 7009fd0e
...@@ -430,6 +430,8 @@ SELECT table_name, table_comment FROM information_schema.tables ...@@ -430,6 +430,8 @@ SELECT table_name, table_comment FROM information_schema.tables
WHERE table_schema= 'test' AND table_name= 't1'; WHERE table_schema= 'test' AND table_name= 't1';
table_name table_comment table_name table_comment
t1 Lock wait timeout exceeded; try restarting transaction t1 Lock wait timeout exceeded; try restarting transaction
Warnings:
Warning 1205 Lock wait timeout exceeded; try restarting transaction
# Connection default # Connection default
UNLOCK TABLES; UNLOCK TABLES;
# Connection con3 # Connection con3
......
...@@ -2322,6 +2322,8 @@ select table_name, table_type, auto_increment, table_comment ...@@ -2322,6 +2322,8 @@ select table_name, table_type, auto_increment, table_comment
from information_schema.tables where table_schema='test' and table_name='t2'; from information_schema.tables where table_schema='test' and table_name='t2';
table_name table_type auto_increment table_comment table_name table_type auto_increment table_comment
t2 BASE TABLE NULL Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement t2 BASE TABLE NULL Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement
Warnings:
Warning 1684 Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement
# Switching to connection 'default'. # Switching to connection 'default'.
unlock tables; unlock tables;
# Switching to connection 'con46044'. # Switching to connection 'con46044'.
......
...@@ -2084,6 +2084,8 @@ SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE ...@@ -2084,6 +2084,8 @@ SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE
TABLE_SCHEMA = 'test' and TABLE_NAME='tm1'; TABLE_SCHEMA = 'test' and TABLE_NAME='tm1';
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT
def test tm1 BASE TABLE NULL NULL NULL # # # # # # # # # # NULL # # Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist def test tm1 BASE TABLE NULL NULL NULL # # # # # # # # # # NULL # # Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
Warnings:
Warning 1168 Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
DROP TABLE tm1; DROP TABLE tm1;
CREATE TABLE t1(C1 INT, C2 INT, KEY C1(C1), KEY C2(C2)) ENGINE=MYISAM; CREATE TABLE t1(C1 INT, C2 INT, KEY C1(C1), KEY C2(C2)) ENGINE=MYISAM;
CREATE TABLE t2(C1 INT, C2 INT, KEY C1(C1), KEY C2(C2)) ENGINE=MYISAM; CREATE TABLE t2(C1 INT, C2 INT, KEY C1(C1), KEY C2(C2)) ENGINE=MYISAM;
......
...@@ -663,6 +663,8 @@ flush tables; ...@@ -663,6 +663,8 @@ flush tables;
SHOW TABLE STATUS like 't1'; SHOW TABLE STATUS like 't1';
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
t1 NULL NULL NULL NULL # # # # NULL NULL NULL NULL NULL NULL NULL NULL Incorrect information in file: './test/t1.frm' t1 NULL NULL NULL NULL # # # # NULL NULL NULL NULL NULL NULL NULL NULL Incorrect information in file: './test/t1.frm'
Warnings:
Warning 1033 Incorrect information in file: './test/t1.frm'
show create table t1; show create table t1;
ERROR HY000: Incorrect information in file: './test/t1.frm' ERROR HY000: Incorrect information in file: './test/t1.frm'
drop table if exists t1; drop table if exists t1;
......
...@@ -840,6 +840,8 @@ show table status; ...@@ -840,6 +840,8 @@ show table status;
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
t1 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL t1 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL
v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
Warnings:
Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop view v1; drop view v1;
drop table t1; drop table t1;
create view v1 as select 99999999999999999999999999999999999999999999999999999 as col1; create view v1 as select 99999999999999999999999999999999999999999999999999999 as col1;
......
#
# Bug 35333 "If a Federated table can't connect to the remote hose, can't retrieve metadata"
#
# Queries such as SHOW TABLE STATUS and SELECT * FROM INFORMATION_SCHEMA.TABLES fail
# when encountering a federated table that cannot connect to its remote table.
#
# The fix is to store the error text in the TABLE COMMENTS column of I_S.TABLES, clear
# the remote connection error and push a warning instead. This allows the SELECT operation
# to complete while still indicating a problem. This fix applies to any non-fatal system
# error that occurs during a query against I_S.TABLES.de
CREATE DATABASE federated;
CREATE DATABASE federated;
CREATE DATABASE IF NOT EXISTS realdb;
DROP TABLE IF EXISTS realdb.t0;
DROP TABLE IF EXISTS federated.t0;
#
# Create the base table to be referenced
#
CREATE TABLE realdb.t0 (a text, b text) ENGINE=MYISAM;
#
# Create a federated table with a bogus port number
#
CREATE TABLE federated.t0 (a text, b text) ENGINE=FEDERATED
CONNECTION='mysql://root@127.0.0.1:63333/realdb/t0';
#
# Trigger a federated system error during a INFORMATION_SCHEMA.TABLES query
#
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'realdb' or TABLE_SCHEMA = 'federated';
TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE ROW_FORMAT TABLE_ROWS DATA_LENGTH TABLE_COMMENT
federated t0 BASE TABLE FEDERATED NULL 0 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno)
realdb t0 BASE TABLE MyISAM Dynamic 0 0
Warnings:
Warning 1429 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno)
SHOW WARNINGS;
Level Code Message
Warning 1429 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno)
#
# Create a MyISAM table then corrupt the file
#
USE realdb;
CREATE TABLE t1 (c1 int) ENGINE=MYISAM;
#
# Corrupt the MyISAM table by deleting the base file
#
#
# Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query
#
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE ROW_FORMAT TABLE_ROWS DATA_LENGTH TABLE_COMMENT
realdb t1 BASE TABLE NULL NULL NULL NULL Can't find file: 't1' (errno: 2)
Warnings:
Warning 1017 Can't find file: 't1' (errno: 2)
SHOW WARNINGS;
Level Code Message
Warning 1017 Can't find file: 't1' (errno: 2)
#
# Cleanup
#
DROP TABLE IF EXISTS realdb.t0;
DROP TABLE IF EXISTS federated.t0;
DROP DATABASE realdb;
DROP TABLE IF EXISTS federated.t1;
DROP DATABASE federated;
DROP TABLE IF EXISTS federated.t1;
DROP DATABASE federated;
--echo #
--echo # Bug 35333 "If a Federated table can't connect to the remote hose, can't retrieve metadata"
--echo #
--echo # Queries such as SHOW TABLE STATUS and SELECT * FROM INFORMATION_SCHEMA.TABLES fail
--echo # when encountering a federated table that cannot connect to its remote table.
--echo #
--echo # The fix is to store the error text in the TABLE COMMENTS column of I_S.TABLES, clear
--echo # the remote connection error and push a warning instead. This allows the SELECT operation
--echo # to complete while still indicating a problem. This fix applies to any non-fatal system
--echo # error that occurs during a query against I_S.TABLES.de
--source federated.inc
--disable_warnings
CREATE DATABASE IF NOT EXISTS realdb;
# Federated database exists
DROP TABLE IF EXISTS realdb.t0;
DROP TABLE IF EXISTS federated.t0;
--enable_warnings
--echo #
--echo # Create the base table to be referenced
--echo #
CREATE TABLE realdb.t0 (a text, b text) ENGINE=MYISAM;
--echo #
--echo # Create a federated table with a bogus port number
--echo #
CREATE TABLE federated.t0 (a text, b text) ENGINE=FEDERATED
CONNECTION='mysql://root@127.0.0.1:63333/realdb/t0';
#--warning ER_CONNECT_TO_FOREIGN_DATA_SOURCE
--echo #
--echo # Trigger a federated system error during a INFORMATION_SCHEMA.TABLES query
--echo #
# Remove O/S-specific socket error
--replace_regex /\(.*\)/(socket errno)/
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'realdb' or TABLE_SCHEMA = 'federated';
# Remove O/S-specific socket error
--replace_regex /\(.*\)/(socket errno)/
SHOW WARNINGS;
--echo #
--echo # Create a MyISAM table then corrupt the file
--echo #
USE realdb;
CREATE TABLE t1 (c1 int) ENGINE=MYISAM;
--echo #
--echo # Corrupt the MyISAM table by deleting the base file
--echo #
let $MYSQLD_DATADIR= `SELECT @@datadir`;
--remove_file $MYSQLD_DATADIR/realdb/t1.MYD
--remove_file $MYSQLD_DATADIR/realdb/t1.MYI
--echo #
--echo # Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query
--echo #
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
SHOW WARNINGS;
--echo #
--echo # Cleanup
--echo #
--disable_warnings
DROP TABLE IF EXISTS realdb.t0;
DROP TABLE IF EXISTS federated.t0;
DROP DATABASE realdb;
--enable_warnings
--source federated_cleanup.inc
...@@ -3781,6 +3781,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3781,6 +3781,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
{ {
const char *tmp_buff; const char *tmp_buff;
MYSQL_TIME time; MYSQL_TIME time;
int info_error= 0;
CHARSET_INFO *cs= system_charset_info; CHARSET_INFO *cs= system_charset_info;
DBUG_ENTER("get_schema_tables_record"); DBUG_ENTER("get_schema_tables_record");
...@@ -3788,22 +3789,21 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3788,22 +3789,21 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[1]->store(db_name->str, db_name->length, cs); table->field[1]->store(db_name->str, db_name->length, cs);
table->field[2]->store(table_name->str, table_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs);
if (res) if (res)
{ {
/* /* There was a table open error, so set the table type and return */
there was errors during opening tables
*/
const char *error= thd->is_error() ? thd->stmt_da->message() : "";
if (tables->view) if (tables->view)
table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
else if (tables->schema_table) else if (tables->schema_table)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
else else
table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
table->field[20]->store(error, strlen(error), cs);
thd->clear_error(); goto err;
} }
else if (tables->view)
if (tables->view)
{ {
table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
table->field[20]->store(STRING_WITH_LEN("VIEW"), cs); table->field[20]->store(STRING_WITH_LEN("VIEW"), cs);
...@@ -3818,6 +3818,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3818,6 +3818,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
bool is_partitioned= FALSE; bool is_partitioned= FALSE;
#endif #endif
if (share->tmp_table == SYSTEM_TMP_TABLE) if (share->tmp_table == SYSTEM_TMP_TABLE)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
else if (share->tmp_table) else if (share->tmp_table)
...@@ -3831,6 +3832,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3831,6 +3832,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
continue; continue;
table->field[i]->set_notnull(); table->field[i]->set_notnull();
} }
/* Collect table info from the table share */
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
if (share->db_type() == partition_hton && if (share->db_type() == partition_hton &&
share->partition_info_str_len) share->partition_info_str_len)
...@@ -3839,62 +3843,82 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3839,62 +3843,82 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
is_partitioned= TRUE; is_partitioned= TRUE;
} }
#endif #endif
tmp_buff= (char *) ha_resolve_storage_engine_name(tmp_db_type); tmp_buff= (char *) ha_resolve_storage_engine_name(tmp_db_type);
table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); table->field[4]->store(tmp_buff, strlen(tmp_buff), cs);
table->field[5]->store((longlong) share->frm_version, TRUE); table->field[5]->store((longlong) share->frm_version, TRUE);
ptr=option_buff; ptr=option_buff;
if (share->min_rows) if (share->min_rows)
{ {
ptr=strmov(ptr," min_rows="); ptr=strmov(ptr," min_rows=");
ptr=longlong10_to_str(share->min_rows,ptr,10); ptr=longlong10_to_str(share->min_rows,ptr,10);
} }
if (share->max_rows) if (share->max_rows)
{ {
ptr=strmov(ptr," max_rows="); ptr=strmov(ptr," max_rows=");
ptr=longlong10_to_str(share->max_rows,ptr,10); ptr=longlong10_to_str(share->max_rows,ptr,10);
} }
if (share->avg_row_length) if (share->avg_row_length)
{ {
ptr=strmov(ptr," avg_row_length="); ptr=strmov(ptr," avg_row_length=");
ptr=longlong10_to_str(share->avg_row_length,ptr,10); ptr=longlong10_to_str(share->avg_row_length,ptr,10);
} }
if (share->db_create_options & HA_OPTION_PACK_KEYS) if (share->db_create_options & HA_OPTION_PACK_KEYS)
ptr=strmov(ptr," pack_keys=1"); ptr=strmov(ptr," pack_keys=1");
if (share->db_create_options & HA_OPTION_NO_PACK_KEYS) if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
ptr=strmov(ptr," pack_keys=0"); ptr=strmov(ptr," pack_keys=0");
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM) if (share->db_create_options & HA_OPTION_CHECKSUM)
ptr=strmov(ptr," checksum=1"); ptr=strmov(ptr," checksum=1");
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
ptr=strmov(ptr," delay_key_write=1"); ptr=strmov(ptr," delay_key_write=1");
if (share->row_type != ROW_TYPE_DEFAULT) if (share->row_type != ROW_TYPE_DEFAULT)
ptr=strxmov(ptr, " row_format=", ptr=strxmov(ptr, " row_format=",
ha_row_type[(uint) share->row_type], ha_row_type[(uint) share->row_type],
NullS); NullS);
if (share->key_block_size) if (share->key_block_size)
{ {
ptr= strmov(ptr, " KEY_BLOCK_SIZE="); ptr= strmov(ptr, " KEY_BLOCK_SIZE=");
ptr= longlong10_to_str(share->key_block_size, ptr, 10); ptr= longlong10_to_str(share->key_block_size, ptr, 10);
} }
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
if (is_partitioned) if (is_partitioned)
ptr= strmov(ptr, " partitioned"); ptr= strmov(ptr, " partitioned");
#endif #endif
table->field[19]->store(option_buff+1, table->field[19]->store(option_buff+1,
(ptr == option_buff ? 0 : (ptr == option_buff ? 0 :
(uint) (ptr-option_buff)-1), cs); (uint) (ptr-option_buff)-1), cs);
tmp_buff= (share->table_charset ? tmp_buff= (share->table_charset ?
share->table_charset->name : "default"); share->table_charset->name : "default");
table->field[17]->store(tmp_buff, strlen(tmp_buff), cs); table->field[17]->store(tmp_buff, strlen(tmp_buff), cs);
if (share->comment.str) if (share->comment.str)
table->field[20]->store(share->comment.str, share->comment.length, cs); table->field[20]->store(share->comment.str, share->comment.length, cs);
/* Collect table info from the storage engine */
if(file) if(file)
{ {
file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO); /* If info() fails, then there's nothing else to do */
if ((info_error= file->info(HA_STATUS_VARIABLE |
HA_STATUS_TIME |
HA_STATUS_AUTO)) != 0)
goto err;
enum row_type row_type = file->get_row_type(); enum row_type row_type = file->get_row_type();
switch (row_type) { switch (row_type) {
case ROW_TYPE_NOT_USED: case ROW_TYPE_NOT_USED:
...@@ -3923,7 +3947,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3923,7 +3947,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
tmp_buff= "Paged"; tmp_buff= "Paged";
break; break;
} }
table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); table->field[6]->store(tmp_buff, strlen(tmp_buff), cs);
if (!tables->schema_table) if (!tables->schema_table)
{ {
table->field[7]->store((longlong) file->stats.records, TRUE); table->field[7]->store((longlong) file->stats.records, TRUE);
...@@ -3972,6 +3998,26 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ...@@ -3972,6 +3998,26 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
} }
} }
} }
err:
if (res || info_error)
{
/*
If an error was encountered, push a warning, set the TABLE COMMENT
column with the error text, and clear the error so that the operation
can continue.
*/
const char *error= thd->is_error() ? thd->stmt_da->message() : "";
table->field[20]->store(error, strlen(error), cs);
if (thd->is_error())
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
}
}
DBUG_RETURN(schema_table_store_record(thd, table)); DBUG_RETURN(schema_table_store_record(thd, table));
} }
......
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