Commit 75af3821 authored by aivanov@mysql.com's avatar aivanov@mysql.com

Merge aivanov@bk-internal.mysql.com:/home/bk/mysql-4.1

into  mysql.com:/home/alexi/innodb-ss/mysql-4.1-ss11
parents 2a3d35fa 7ae2d469
......@@ -2077,8 +2077,11 @@ dict_foreign_find_index(
dict_table_t* table, /* in: table */
const char** columns,/* in: array of column names */
ulint n_cols, /* in: number of columns */
dict_index_t* types_idx)/* in: NULL or an index to whose types the
column types must match */
dict_index_t* types_idx, /* in: NULL or an index to whose types the
column types must match */
ibool check_charsets) /* in: whether to check charsets.
only has an effect if types_idx !=
NULL. */
{
dict_index_t* index;
const char* col_name;
......@@ -2107,7 +2110,8 @@ dict_foreign_find_index(
if (types_idx && !cmp_types_are_equal(
dict_index_get_nth_type(index, i),
dict_index_get_nth_type(types_idx, i))) {
dict_index_get_nth_type(types_idx, i),
check_charsets)) {
break;
}
......@@ -2178,7 +2182,8 @@ dict_foreign_add_to_cache(
/*======================*/
/* out: DB_SUCCESS or error code */
dict_foreign_t* foreign, /* in, own: foreign key constraint */
ibool check_types) /* in: TRUE=check type compatibility */
ibool check_charsets) /* in: TRUE=check charset
compatibility */
{
dict_table_t* for_table;
dict_table_t* ref_table;
......@@ -2214,16 +2219,10 @@ dict_foreign_add_to_cache(
}
if (for_in_cache->referenced_table == NULL && ref_table) {
dict_index_t* types_idx;
if (check_types) {
types_idx = for_in_cache->foreign_index;
} else {
types_idx = NULL;
}
index = dict_foreign_find_index(ref_table,
(const char**) for_in_cache->referenced_col_names,
for_in_cache->n_fields,
types_idx);
for_in_cache->foreign_index, check_charsets);
if (index == NULL) {
dict_foreign_error_report(ef, for_in_cache,
......@@ -2247,16 +2246,10 @@ dict_foreign_add_to_cache(
}
if (for_in_cache->foreign_table == NULL && for_table) {
dict_index_t* types_idx;
if (check_types) {
types_idx = for_in_cache->referenced_index;
} else {
types_idx = NULL;
}
index = dict_foreign_find_index(for_table,
(const char**) for_in_cache->foreign_col_names,
for_in_cache->n_fields,
types_idx);
for_in_cache->referenced_index, check_charsets);
if (index == NULL) {
dict_foreign_error_report(ef, for_in_cache,
......@@ -3033,7 +3026,7 @@ col_loop1:
/* Try to find an index which contains the columns
as the first fields and in the right order */
index = dict_foreign_find_index(table, column_names, i, NULL);
index = dict_foreign_find_index(table, column_names, i, NULL, TRUE);
if (!index) {
mutex_enter(&dict_foreign_err_mutex);
......@@ -3298,8 +3291,7 @@ try_find_index:
if (referenced_table) {
index = dict_foreign_find_index(referenced_table,
column_names, i,
foreign->foreign_index);
column_names, i, foreign->foreign_index, TRUE);
if (!index) {
dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex);
......
......@@ -1094,7 +1094,7 @@ dict_load_foreign(
/* out: DB_SUCCESS or error code */
const char* id, /* in: foreign constraint id as a
null-terminated string */
ibool check_types)/* in: TRUE=check type compatibility */
ibool check_charsets)/* in: TRUE=check charset compatibility */
{
dict_foreign_t* foreign;
dict_table_t* sys_foreign;
......@@ -1205,7 +1205,7 @@ dict_load_foreign(
a new foreign key constraint but loading one from the data
dictionary. */
return(dict_foreign_add_to_cache(foreign, check_types));
return(dict_foreign_add_to_cache(foreign, check_charsets));
}
/***************************************************************************
......@@ -1220,7 +1220,8 @@ dict_load_foreigns(
/*===============*/
/* out: DB_SUCCESS or error code */
const char* table_name, /* in: table name */
ibool check_types) /* in: TRUE=check type compatibility */
ibool check_charsets) /* in: TRUE=check charset
compatibility */
{
btr_pcur_t pcur;
mem_heap_t* heap;
......@@ -1319,7 +1320,7 @@ loop:
/* Load the foreign constraint definition to the dictionary cache */
err = dict_load_foreign(id, check_types);
err = dict_load_foreign(id, check_charsets);
if (err != DB_SUCCESS) {
btr_pcur_close(&pcur);
......
......@@ -197,7 +197,8 @@ dict_foreign_add_to_cache(
/*======================*/
/* out: DB_SUCCESS or error code */
dict_foreign_t* foreign, /* in, own: foreign key constraint */
ibool check_types); /* in: TRUE=check type compatibility */
ibool check_charsets);/* in: TRUE=check charset
compatibility */
/*************************************************************************
Checks if a table is referenced by foreign keys. */
......
......@@ -82,7 +82,8 @@ dict_load_foreigns(
/*===============*/
/* out: DB_SUCCESS or error code */
const char* table_name, /* in: table name */
ibool check_types); /* in: TRUE=check type compatibility */
ibool check_charsets);/* in: TRUE=check charsets
compatibility */
/************************************************************************
Prints to the standard output information on all tables found in the data
dictionary system table. */
......
......@@ -182,7 +182,7 @@ Creates a temporary file. */
FILE*
os_file_create_tmpfile(void);
/*========================*/
/* out: temporary file handle (never NULL) */
/* out: temporary file handle, or NULL on error */
/***************************************************************************
The os_file_opendir() function opens a directory stream corresponding to the
directory named by the dirname argument. The directory stream is positioned
......
......@@ -24,7 +24,8 @@ cmp_types_are_equal(
/* out: TRUE if the types are considered
equal in comparisons */
dtype_t* type1, /* in: type 1 */
dtype_t* type2); /* in: type 2 */
dtype_t* type2, /* in: type 2 */
ibool check_charsets); /* in: whether to check charsets */
/*****************************************************************
This function is used to compare two data fields for which we know the
data type. */
......
......@@ -34,6 +34,12 @@ extern ibool srv_lower_case_table_names;
extern mutex_t srv_monitor_file_mutex;
/* Temporary file for innodb monitor output */
extern FILE* srv_monitor_file;
/* Mutex for locking srv_dict_tmpfile.
This mutex has a very high rank; threads reserving it should not
be holding any InnoDB latches. */
extern mutex_t srv_dict_tmpfile_mutex;
/* Temporary file for output from the data dictionary */
extern FILE* srv_dict_tmpfile;
/* Server parameters which are read from the initfile */
......
......@@ -98,7 +98,8 @@ cmp_types_are_equal(
/* out: TRUE if the types are considered
equal in comparisons */
dtype_t* type1, /* in: type 1 */
dtype_t* type2) /* in: type 2 */
dtype_t* type2, /* in: type 2 */
ibool check_charsets) /* in: whether to check charsets */
{
if (dtype_is_non_binary_string_type(type1->mtype, type1->prtype)
&& dtype_is_non_binary_string_type(type2->mtype, type2->prtype)) {
......@@ -106,12 +107,12 @@ cmp_types_are_equal(
/* Both are non-binary string types: they can be compared if
and only if the charset-collation is the same */
if (dtype_get_charset_coll(type1->prtype)
== dtype_get_charset_coll(type2->prtype)) {
if (check_charsets) {
return(dtype_get_charset_coll(type1->prtype)
== dtype_get_charset_coll(type2->prtype));
} else {
return(TRUE);
}
return(FALSE);
}
if (dtype_is_binary_string_type(type1->mtype, type1->prtype)
......
......@@ -522,7 +522,7 @@ row_ins_cascade_calc_update_vec(
&& ufield->new_val.len
< dtype_get_fixed_size(type)) {
ulint cset;
ulint cset;
ufield->new_val.data =
mem_heap_alloc(heap,
......@@ -530,42 +530,42 @@ row_ins_cascade_calc_update_vec(
ufield->new_val.len =
dtype_get_fixed_size(type);
/* Handle UCS2 strings differently.
As no new collations will be
introduced in 4.1, we hardcode the
charset-collation codes here.
In 5.0, the logic is based on
mbminlen. */
cset = dtype_get_charset_coll(
dtype_get_prtype(type));
if (cset == 35/*ucs2_general_ci*/
|| cset == 90/*ucs2_bin*/
|| (cset >= 128/*ucs2_unicode_ci*/
&& cset <= 144
/*ucs2_persian_ci*/)) {
/* space=0x0020 */
ulint i;
for (i = 0;
i < ufield->new_val.len;
i += 2) {
mach_write_to_2(((byte*)
ufield->new_val.data)
+ i, 0x0020);
}
} else {
ut_a(dtype_get_pad_char(type)
!= ULINT_UNDEFINED);
memset(ufield->new_val.data,
(byte)dtype_get_pad_char(
type),
ufield->new_val.len);
}
memcpy(ufield->new_val.data,
parent_ufield->new_val.data,
parent_ufield->new_val.len);
/* Handle UCS2 strings differently.
As no new collations will be
introduced in 4.1, we hardcode the
charset-collation codes here.
In 5.0, the logic is based on
mbminlen. */
cset = dtype_get_charset_coll(
dtype_get_prtype(type));
if (cset == 35/*ucs2_general_ci*/
|| cset == 90/*ucs2_bin*/
|| (cset >= 128/*ucs2_unicode_ci*/
&& cset <= 144
/*ucs2_persian_ci*/)) {
/* space=0x0020 */
ulint i;
for (i = 0;
i < ufield->new_val.len;
i += 2) {
mach_write_to_2(((byte*)
ufield->new_val.data)
+ i, 0x0020);
}
} else {
ut_a(dtype_get_pad_char(type)
!= ULINT_UNDEFINED);
memset(ufield->new_val.data,
(byte)dtype_get_pad_char(
type),
ufield->new_val.len);
}
memcpy(ufield->new_val.data,
parent_ufield->new_val.data,
parent_ufield->new_val.len);
}
ufield->extern_storage = FALSE;
......
......@@ -1804,7 +1804,7 @@ row_table_add_foreign_constraints(
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
err = dict_load_foreigns(name, trx->check_foreigns);
err = dict_load_foreigns(name, TRUE);
}
if (err != DB_SUCCESS) {
......@@ -2963,7 +2963,8 @@ row_rename_table_for_mysql(
mem_heap_t* heap = NULL;
const char** constraints_to_drop = NULL;
ulint n_constraints_to_drop = 0;
ibool recovering_temp_table = FALSE;
ibool recovering_temp_table = FALSE;
ibool old_is_tmp, new_is_tmp;
ulint len;
ulint i;
ibool success;
......@@ -3003,6 +3004,9 @@ row_rename_table_for_mysql(
trx->op_info = "renaming table";
trx_start_if_not_started(trx);
old_is_tmp = row_is_mysql_tmp_table_name(old_name);
new_is_tmp = row_is_mysql_tmp_table_name(new_name);
if (row_mysql_is_recovered_tmp_table(new_name)) {
recovering_temp_table = TRUE;
......@@ -3047,7 +3051,7 @@ row_rename_table_for_mysql(
len = (sizeof str1) + (sizeof str2) + (sizeof str3) + (sizeof str5) - 4
+ ut_strlenq(new_name, '\'') + ut_strlenq(old_name, '\'');
if (row_is_mysql_tmp_table_name(new_name)) {
if (new_is_tmp) {
db_name_len = dict_get_db_name_len(old_name) + 1;
/* MySQL is doing an ALTER TABLE command and it renames the
......@@ -3200,7 +3204,7 @@ row_rename_table_for_mysql(
the table is stored in a single-table tablespace */
success = dict_table_rename_in_cache(table, new_name,
!row_is_mysql_tmp_table_name(new_name));
!new_is_tmp);
if (!success) {
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
......@@ -3217,19 +3221,16 @@ row_rename_table_for_mysql(
goto funct_exit;
}
err = dict_load_foreigns(new_name, trx->check_foreigns);
if (row_is_mysql_tmp_table_name(old_name)) {
/* We only want to switch off some of the type checking in
an ALTER, not in a RENAME. */
err = dict_load_foreigns(new_name,
old_is_tmp ? trx->check_foreigns : TRUE);
/* MySQL is doing an ALTER TABLE command and it
renames the created temporary table to the name
of the original table. In the ALTER TABLE we maybe
created some FOREIGN KEY constraints for the temporary
table. But we want to load also the foreign key
constraint definitions for the original table name. */
if (err != DB_SUCCESS) {
ut_print_timestamp(stderr);
if (err != DB_SUCCESS) {
ut_print_timestamp(stderr);
if (old_is_tmp) {
fputs(" InnoDB: Error: in ALTER TABLE ",
stderr);
ut_print_name(stderr, trx, new_name);
......@@ -3237,36 +3238,23 @@ row_rename_table_for_mysql(
"InnoDB: has or is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n",
stderr);
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
trx->error_state = DB_SUCCESS;
}
} else {
if (err != DB_SUCCESS) {
ut_print_timestamp(stderr);
} else {
fputs(
" InnoDB: Error: in RENAME TABLE table ",
stderr);
ut_print_name(stderr, trx, new_name);
fputs("\n"
"InnoDB: is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n",
"InnoDB: is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n",
stderr);
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
trx->error_state = DB_SUCCESS;
}
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
trx->error_state = DB_SUCCESS;
}
}
funct_exit:
......
......@@ -334,6 +334,12 @@ mutex_t srv_innodb_monitor_mutex;
mutex_t srv_monitor_file_mutex;
/* Temporary file for innodb monitor output */
FILE* srv_monitor_file;
/* Mutex for locking srv_dict_tmpfile.
This mutex has a very high rank; threads reserving it should not
be holding any InnoDB latches. */
mutex_t srv_dict_tmpfile_mutex;
/* Temporary file for output from the data dictionary */
FILE* srv_dict_tmpfile;
ulint srv_main_thread_process_no = 0;
ulint srv_main_thread_id = 0;
......
......@@ -1178,6 +1178,13 @@ NetWare. */
}
}
mutex_create(&srv_dict_tmpfile_mutex);
mutex_set_level(&srv_dict_tmpfile_mutex, SYNC_DICT_OPERATION);
srv_dict_tmpfile = os_file_create_tmpfile();
if (!srv_dict_tmpfile) {
return(DB_ERROR);
}
/* Restrict the maximum number of file i/o threads */
if (srv_n_file_io_threads > SRV_MAX_N_IO_THREADS) {
......@@ -1804,8 +1811,13 @@ innobase_shutdown_for_mysql(void)
mem_free(srv_monitor_file_name);
}
}
if (srv_dict_tmpfile) {
fclose(srv_dict_tmpfile);
srv_dict_tmpfile = 0;
}
mutex_free(&srv_monitor_file_mutex);
mutex_free(&srv_dict_tmpfile_mutex);
/* 3. Free all InnoDB's own mutexes and the os_fast_mutexes inside
them */
......
......@@ -1722,3 +1722,35 @@ checksum table test_checksum;
Table Checksum
test.test_checksum 2050879373
drop table test_checksum;
set foreign_key_checks=0;
create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
ERROR HY000: Can't create table './test/t1.frm' (errno: 150)
set foreign_key_checks=1;
drop table t2;
set foreign_key_checks=0;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8;
ERROR HY000: Can't create table './test/t2.frm' (errno: 150)
set foreign_key_checks=1;
drop table t1;
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb;
create table t1(a varchar(10) primary key) engine = innodb;
alter table t1 modify column a int;
Got one of the listed errors
set foreign_key_checks=1;
drop table t2,t1;
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
alter table t1 convert to character set utf8;
set foreign_key_checks=1;
drop table t2,t1;
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8;
rename table t3 to t1;
ERROR HY000: Error on rename of './test/t3' to './test/t1' (errno: 150)
set foreign_key_checks=1;
drop table t2,t3;
......@@ -1280,4 +1280,53 @@ connection a;
checksum table test_checksum;
drop table test_checksum;
# tests for bugs #9802 and #13778
# test that FKs between invalid types are not accepted
set foreign_key_checks=0;
create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb;
-- error 1005
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
set foreign_key_checks=1;
drop table t2;
# test that FKs between different charsets are not accepted in CREATE even
# when f_k_c is 0
set foreign_key_checks=0;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
-- error 1005
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8;
set foreign_key_checks=1;
drop table t1;
# test that invalid datatype conversions with ALTER are not allowed
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb;
create table t1(a varchar(10) primary key) engine = innodb;
-- error 1025,1025
alter table t1 modify column a int;
set foreign_key_checks=1;
drop table t2,t1;
# test that charset conversions with ALTER are allowed when f_k_c is 0
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
alter table t1 convert to character set utf8;
set foreign_key_checks=1;
drop table t2,t1;
# test that RENAME does not allow invalid charsets when f_k_c is 0
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8;
-- error 1025
rename table t3 to t1;
set foreign_key_checks=1;
drop table t2,t3;
# End of 4.1 tests
......@@ -4729,6 +4729,7 @@ ha_innobase::update_table_comment(
uint length = strlen(comment);
char* str;
row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
long flen;
/* We do not know if MySQL can call this function before calling
external_lock(). To be safe, update the thd of the current table
......@@ -4748,43 +4749,43 @@ ha_innobase::update_table_comment(
trx_search_latch_release_if_reserved(prebuilt->trx);
str = NULL;
if (FILE* file = os_file_create_tmpfile()) {
long flen;
/* output the data to a temporary file */
/* output the data to a temporary file */
fprintf(file, "InnoDB free: %lu kB",
mutex_enter_noninline(&srv_dict_tmpfile_mutex);
rewind(srv_dict_tmpfile);
fprintf(srv_dict_tmpfile, "InnoDB free: %lu kB",
(ulong) fsp_get_available_space_in_free_extents(
prebuilt->table->space));
dict_print_info_on_foreign_keys(FALSE, file,
dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile,
prebuilt->trx, prebuilt->table);
flen = ftell(file);
if (flen < 0) {
flen = 0;
} else if (length + flen + 3 > 64000) {
flen = 64000 - 3 - length;
}
flen = ftell(srv_dict_tmpfile);
if (flen < 0) {
flen = 0;
} else if (length + flen + 3 > 64000) {
flen = 64000 - 3 - length;
}
/* allocate buffer for the full string, and
read the contents of the temporary file */
/* allocate buffer for the full string, and
read the contents of the temporary file */
str = my_malloc(length + flen + 3, MYF(0));
str = my_malloc(length + flen + 3, MYF(0));
if (str) {
char* pos = str + length;
if(length) {
memcpy(str, comment, length);
*pos++ = ';';
*pos++ = ' ';
}
rewind(file);
flen = fread(pos, 1, flen, file);
pos[flen] = 0;
if (str) {
char* pos = str + length;
if (length) {
memcpy(str, comment, length);
*pos++ = ';';
*pos++ = ' ';
}
fclose(file);
rewind(srv_dict_tmpfile);
flen = (uint) fread(pos, 1, flen, srv_dict_tmpfile);
pos[flen] = 0;
}
mutex_exit_noninline(&srv_dict_tmpfile_mutex);
prebuilt->trx->op_info = (char*)"";
return(str ? str : (char*) comment);
......@@ -4802,6 +4803,7 @@ ha_innobase::get_foreign_key_create_info(void)
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
char* str = 0;
long flen;
ut_a(prebuilt != NULL);
......@@ -4811,47 +4813,42 @@ ha_innobase::get_foreign_key_create_info(void)
update_thd(current_thd);
if (FILE* file = os_file_create_tmpfile()) {
long flen;
prebuilt->trx->op_info = (char*)"getting info on foreign keys";
prebuilt->trx->op_info = (char*)"getting info on foreign keys";
/* In case MySQL calls this in the middle of a SELECT query,
release possible adaptive hash latch to avoid
deadlocks of threads */
/* In case MySQL calls this in the middle of a SELECT query,
release possible adaptive hash latch to avoid
deadlocks of threads */
trx_search_latch_release_if_reserved(prebuilt->trx);
trx_search_latch_release_if_reserved(prebuilt->trx);
mutex_enter_noninline(&srv_dict_tmpfile_mutex);
rewind(srv_dict_tmpfile);
/* output the data to a temporary file */
dict_print_info_on_foreign_keys(TRUE, file,
/* output the data to a temporary file */
dict_print_info_on_foreign_keys(TRUE, srv_dict_tmpfile,
prebuilt->trx, prebuilt->table);
prebuilt->trx->op_info = (char*)"";
flen = ftell(file);
if (flen < 0) {
flen = 0;
} else if(flen > 64000 - 1) {
flen = 64000 - 1;
}
prebuilt->trx->op_info = (char*)"";
/* allocate buffer for the string, and
read the contents of the temporary file */
flen = ftell(srv_dict_tmpfile);
if (flen < 0) {
flen = 0;
} else if (flen > 64000 - 1) {
flen = 64000 - 1;
}
str = my_malloc(flen + 1, MYF(0));
/* allocate buffer for the string, and
read the contents of the temporary file */
if (str) {
rewind(file);
flen = fread(str, 1, flen, file);
str[flen] = 0;
}
str = my_malloc(flen + 1, MYF(0));
fclose(file);
} else {
/* unable to create temporary file */
str = my_strdup(
"/* Error: cannot display foreign key constraints */", MYF(0));
if (str) {
rewind(srv_dict_tmpfile);
flen = (uint) fread(str, 1, flen, srv_dict_tmpfile);
str[flen] = 0;
}
mutex_exit_noninline(&srv_dict_tmpfile_mutex);
return(str);
}
......
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