Commit 699a6c1d authored by Vasil Dimov's avatar Vasil Dimov

Merge mysql-trunk-innodb from bk-internal into my local tree

parents ed4bd8ef 68ce054f
...@@ -62,32 +62,47 @@ dict_hdr_get( ...@@ -62,32 +62,47 @@ dict_hdr_get(
} }
/**********************************************************************//** /**********************************************************************//**
Returns a new table, index, or tree id. Returns a new table, index, or space id. */
@return the new id */
UNIV_INTERN UNIV_INTERN
dulint void
dict_hdr_get_new_id( dict_hdr_get_new_id(
/*================*/ /*================*/
ulint type) /*!< in: DICT_HDR_ROW_ID, ... */ dulint* table_id, /*!< out: table id (not assigned if NULL) */
dulint* index_id, /*!< out: index id (not assigned if NULL) */
ulint* space_id) /*!< out: space id (not assigned if NULL) */
{ {
dict_hdr_t* dict_hdr; dict_hdr_t* dict_hdr;
dulint id; dulint id;
mtr_t mtr; mtr_t mtr;
ut_ad((type == DICT_HDR_TABLE_ID) || (type == DICT_HDR_INDEX_ID));
mtr_start(&mtr); mtr_start(&mtr);
dict_hdr = dict_hdr_get(&mtr); dict_hdr = dict_hdr_get(&mtr);
id = mtr_read_dulint(dict_hdr + type, &mtr); if (table_id) {
id = mtr_read_dulint(dict_hdr + DICT_HDR_TABLE_ID, &mtr);
id = ut_dulint_add(id, 1); id = ut_dulint_add(id, 1);
mlog_write_dulint(dict_hdr + DICT_HDR_TABLE_ID, id, &mtr);
*table_id = id;
}
mlog_write_dulint(dict_hdr + type, id, &mtr); if (index_id) {
id = mtr_read_dulint(dict_hdr + DICT_HDR_INDEX_ID, &mtr);
id = ut_dulint_add(id, 1);
mlog_write_dulint(dict_hdr + DICT_HDR_INDEX_ID, id, &mtr);
*index_id = id;
}
mtr_commit(&mtr); if (space_id) {
*space_id = mtr_read_ulint(dict_hdr + DICT_HDR_MAX_SPACE_ID,
MLOG_4BYTES, &mtr);
if (fil_assign_new_space_id(space_id)) {
mlog_write_ulint(dict_hdr + DICT_HDR_MAX_SPACE_ID,
*space_id, MLOG_4BYTES, &mtr);
}
}
return(id); mtr_commit(&mtr);
} }
/**********************************************************************//** /**********************************************************************//**
...@@ -151,9 +166,12 @@ dict_hdr_create( ...@@ -151,9 +166,12 @@ dict_hdr_create(
mlog_write_dulint(dict_header + DICT_HDR_INDEX_ID, mlog_write_dulint(dict_header + DICT_HDR_INDEX_ID,
ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr);
/* Obsolete, but we must initialize it to 0 anyway. */ mlog_write_ulint(dict_header + DICT_HDR_MAX_SPACE_ID,
mlog_write_dulint(dict_header + DICT_HDR_MIX_ID, 0, MLOG_4BYTES, mtr);
ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr);
/* Obsolete, but we must initialize it anyway. */
mlog_write_ulint(dict_header + DICT_HDR_MIX_ID_LOW,
DICT_HDR_FIRST_ID, MLOG_4BYTES, mtr);
/* Create the B-tree roots for the clustered indexes of the basic /* Create the B-tree roots for the clustered indexes of the basic
system tables */ system tables */
......
...@@ -239,16 +239,22 @@ dict_build_table_def_step( ...@@ -239,16 +239,22 @@ dict_build_table_def_step(
const char* path_or_name; const char* path_or_name;
ibool is_path; ibool is_path;
mtr_t mtr; mtr_t mtr;
ulint space = 0;
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
table = node->table; table = node->table;
table->id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); dict_hdr_get_new_id(&table->id, NULL,
srv_file_per_table ? &space : NULL);
thr_get_trx(thr)->table_id = table->id; thr_get_trx(thr)->table_id = table->id;
if (srv_file_per_table) { if (srv_file_per_table) {
if (UNIV_UNLIKELY(space == ULINT_UNDEFINED)) {
return(DB_ERROR);
}
/* We create a new single-table tablespace for the table. /* We create a new single-table tablespace for the table.
We initially let it be 4 pages: We initially let it be 4 pages:
- page 0 is the fsp header and an extent descriptor page, - page 0 is the fsp header and an extent descriptor page,
...@@ -257,8 +263,6 @@ dict_build_table_def_step( ...@@ -257,8 +263,6 @@ dict_build_table_def_step(
- page 3 will contain the root of the clustered index of the - page 3 will contain the root of the clustered index of the
table we create here. */ table we create here. */
ulint space = 0; /* reset to zero for the call below */
if (table->dir_path_of_temp_table) { if (table->dir_path_of_temp_table) {
/* We place tables created with CREATE TEMPORARY /* We place tables created with CREATE TEMPORARY
TABLE in the tmp dir of mysqld server */ TABLE in the tmp dir of mysqld server */
...@@ -276,7 +280,7 @@ dict_build_table_def_step( ...@@ -276,7 +280,7 @@ dict_build_table_def_step(
flags = table->flags & ~(~0 << DICT_TF_BITS); flags = table->flags & ~(~0 << DICT_TF_BITS);
error = fil_create_new_single_table_tablespace( error = fil_create_new_single_table_tablespace(
&space, path_or_name, is_path, space, path_or_name, is_path,
flags == DICT_TF_COMPACT ? 0 : flags, flags == DICT_TF_COMPACT ? 0 : flags,
FIL_IBD_FILE_INITIAL_SIZE); FIL_IBD_FILE_INITIAL_SIZE);
table->space = (unsigned int) space; table->space = (unsigned int) space;
...@@ -561,7 +565,7 @@ dict_build_index_def_step( ...@@ -561,7 +565,7 @@ dict_build_index_def_step(
ut_ad((UT_LIST_GET_LEN(table->indexes) > 0) ut_ad((UT_LIST_GET_LEN(table->indexes) > 0)
|| dict_index_is_clust(index)); || dict_index_is_clust(index));
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID); dict_hdr_get_new_id(NULL, &index->id, NULL);
/* Inherit the space id from the table; we store all indexes of a /* Inherit the space id from the table; we store all indexes of a
table in the same tablespace */ table in the same tablespace */
......
...@@ -289,6 +289,10 @@ struct fil_system_struct { ...@@ -289,6 +289,10 @@ struct fil_system_struct {
request */ request */
UT_LIST_BASE_NODE_T(fil_space_t) space_list; UT_LIST_BASE_NODE_T(fil_space_t) space_list;
/*!< list of all file spaces */ /*!< list of all file spaces */
ibool space_id_reuse_warned;
/* !< TRUE if fil_space_create()
has issued a warning about
potential space_id reuse */
}; };
/** The tablespace memory cache. This variable is NULL before the module is /** The tablespace memory cache. This variable is NULL before the module is
...@@ -1210,7 +1214,19 @@ try_again: ...@@ -1210,7 +1214,19 @@ try_again:
space->tablespace_version = fil_system->tablespace_version; space->tablespace_version = fil_system->tablespace_version;
space->mark = FALSE; space->mark = FALSE;
if (purpose == FIL_TABLESPACE && id > fil_system->max_assigned_id) { if (UNIV_LIKELY(purpose == FIL_TABLESPACE)
&& UNIV_UNLIKELY(id > fil_system->max_assigned_id)) {
if (!fil_system->space_id_reuse_warned) {
fil_system->space_id_reuse_warned = TRUE;
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Warning: allocated tablespace %lu,"
" old maximum was %lu\n",
(ulong) id,
(ulong) fil_system->max_assigned_id);
}
fil_system->max_assigned_id = id; fil_system->max_assigned_id = id;
} }
...@@ -1248,19 +1264,25 @@ try_again: ...@@ -1248,19 +1264,25 @@ try_again:
Assigns a new space id for a new single-table tablespace. This works simply by Assigns a new space id for a new single-table tablespace. This works simply by
incrementing the global counter. If 4 billion id's is not enough, we may need incrementing the global counter. If 4 billion id's is not enough, we may need
to recycle id's. to recycle id's.
@return new tablespace id; ULINT_UNDEFINED if could not assign an id */ @return TRUE if assigned, FALSE if not */
static UNIV_INTERN
ulint ibool
fil_assign_new_space_id(void) fil_assign_new_space_id(
/*=========================*/ /*====================*/
ulint* space_id) /*!< in/out: space id */
{ {
ulint id; ulint id;
ibool success;
mutex_enter(&fil_system->mutex); mutex_enter(&fil_system->mutex);
fil_system->max_assigned_id++; id = *space_id;
if (id < fil_system->max_assigned_id) {
id = fil_system->max_assigned_id; id = fil_system->max_assigned_id;
}
id++;
if (id > (SRV_LOG_SPACE_FIRST_ID / 2) && (id % 1000000UL == 0)) { if (id > (SRV_LOG_SPACE_FIRST_ID / 2) && (id % 1000000UL == 0)) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
...@@ -1276,7 +1298,11 @@ fil_assign_new_space_id(void) ...@@ -1276,7 +1298,11 @@ fil_assign_new_space_id(void)
(ulong) SRV_LOG_SPACE_FIRST_ID); (ulong) SRV_LOG_SPACE_FIRST_ID);
} }
if (id >= SRV_LOG_SPACE_FIRST_ID) { success = (id < SRV_LOG_SPACE_FIRST_ID);
if (success) {
*space_id = fil_system->max_assigned_id = id;
} else {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: You have run out of single-table" "InnoDB: You have run out of single-table"
...@@ -1286,14 +1312,12 @@ fil_assign_new_space_id(void) ...@@ -1286,14 +1312,12 @@ fil_assign_new_space_id(void)
" have to dump all your tables and\n" " have to dump all your tables and\n"
"InnoDB: recreate the whole InnoDB installation.\n", "InnoDB: recreate the whole InnoDB installation.\n",
(ulong) id); (ulong) id);
fil_system->max_assigned_id--; *space_id = ULINT_UNDEFINED;
id = ULINT_UNDEFINED;
} }
mutex_exit(&fil_system->mutex); mutex_exit(&fil_system->mutex);
return(id); return(success);
} }
/*******************************************************************//** /*******************************************************************//**
...@@ -1529,7 +1553,7 @@ fil_init( ...@@ -1529,7 +1553,7 @@ fil_init(
ut_a(hash_size > 0); ut_a(hash_size > 0);
ut_a(max_n_open > 0); ut_a(max_n_open > 0);
fil_system = mem_alloc(sizeof(fil_system_t)); fil_system = mem_zalloc(sizeof(fil_system_t));
mutex_create(fil_system_mutex_key, mutex_create(fil_system_mutex_key,
&fil_system->mutex, SYNC_ANY_LATCH); &fil_system->mutex, SYNC_ANY_LATCH);
...@@ -1539,16 +1563,7 @@ fil_init( ...@@ -1539,16 +1563,7 @@ fil_init(
UT_LIST_INIT(fil_system->LRU); UT_LIST_INIT(fil_system->LRU);
fil_system->n_open = 0;
fil_system->max_n_open = max_n_open; fil_system->max_n_open = max_n_open;
fil_system->modification_counter = 0;
fil_system->max_assigned_id = 0;
fil_system->tablespace_version = 0;
UT_LIST_INIT(fil_system->unflushed_spaces);
UT_LIST_INIT(fil_system->space_list);
} }
/*******************************************************************//** /*******************************************************************//**
...@@ -2133,7 +2148,7 @@ fil_op_log_parse_or_replay( ...@@ -2133,7 +2148,7 @@ fil_op_log_parse_or_replay(
fil_create_directory_for_tablename(name); fil_create_directory_for_tablename(name);
if (fil_create_new_single_table_tablespace( if (fil_create_new_single_table_tablespace(
&space_id, name, FALSE, flags, space_id, name, FALSE, flags,
FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) { FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) {
ut_error; ut_error;
} }
...@@ -2580,9 +2595,7 @@ UNIV_INTERN ...@@ -2580,9 +2595,7 @@ UNIV_INTERN
ulint ulint
fil_create_new_single_table_tablespace( fil_create_new_single_table_tablespace(
/*===================================*/ /*===================================*/
ulint* space_id, /*!< in/out: space id; if this is != 0, ulint space_id, /*!< in: space id */
then this is an input parameter,
otherwise output */
const char* tablename, /*!< in: the table name in the usual const char* tablename, /*!< in: the table name in the usual
databasename/tablename format databasename/tablename format
of InnoDB, or a dir path to a temp of InnoDB, or a dir path to a temp
...@@ -2602,6 +2615,8 @@ fil_create_new_single_table_tablespace( ...@@ -2602,6 +2615,8 @@ fil_create_new_single_table_tablespace(
ibool success; ibool success;
char* path; char* path;
ut_a(space_id > 0);
ut_a(space_id < SRV_LOG_SPACE_FIRST_ID);
ut_a(size >= FIL_IBD_FILE_INITIAL_SIZE); ut_a(size >= FIL_IBD_FILE_INITIAL_SIZE);
/* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for /* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for
ROW_FORMAT=COMPACT ROW_FORMAT=COMPACT
...@@ -2659,38 +2674,21 @@ fil_create_new_single_table_tablespace( ...@@ -2659,38 +2674,21 @@ fil_create_new_single_table_tablespace(
return(DB_ERROR); return(DB_ERROR);
} }
buf2 = ut_malloc(3 * UNIV_PAGE_SIZE);
/* Align the memory for file i/o if we might have O_DIRECT set */
page = ut_align(buf2, UNIV_PAGE_SIZE);
ret = os_file_set_size(path, file, size * UNIV_PAGE_SIZE, 0); ret = os_file_set_size(path, file, size * UNIV_PAGE_SIZE, 0);
if (!ret) { if (!ret) {
ut_free(buf2); err = DB_OUT_OF_FILE_SPACE;
os_file_close(file);
os_file_delete(path);
mem_free(path);
return(DB_OUT_OF_FILE_SPACE);
}
if (*space_id == 0) {
*space_id = fil_assign_new_space_id();
}
/* printf("Creating tablespace %s id %lu\n", path, *space_id); */
if (*space_id == ULINT_UNDEFINED) {
ut_free(buf2);
error_exit: error_exit:
os_file_close(file); os_file_close(file);
error_exit2: error_exit2:
os_file_delete(path); os_file_delete(path);
mem_free(path); mem_free(path);
return(DB_ERROR); return(err);
} }
/* printf("Creating tablespace %s id %lu\n", path, space_id); */
/* We have to write the space id to the file immediately and flush the /* We have to write the space id to the file immediately and flush the
file to disk. This is because in crash recovery we must be aware what file to disk. This is because in crash recovery we must be aware what
tablespaces exist and what are their space id's, so that we can apply tablespaces exist and what are their space id's, so that we can apply
...@@ -2700,10 +2698,14 @@ error_exit2: ...@@ -2700,10 +2698,14 @@ error_exit2:
with zeros from the call of os_file_set_size(), until a buffer pool with zeros from the call of os_file_set_size(), until a buffer pool
flush would write to it. */ flush would write to it. */
buf2 = ut_malloc(3 * UNIV_PAGE_SIZE);
/* Align the memory for file i/o if we might have O_DIRECT set */
page = ut_align(buf2, UNIV_PAGE_SIZE);
memset(page, '\0', UNIV_PAGE_SIZE); memset(page, '\0', UNIV_PAGE_SIZE);
fsp_header_init_fields(page, *space_id, flags); fsp_header_init_fields(page, space_id, flags);
mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, *space_id); mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, space_id);
if (!(flags & DICT_TF_ZSSIZE_MASK)) { if (!(flags & DICT_TF_ZSSIZE_MASK)) {
buf_flush_init_for_writing(page, NULL, 0); buf_flush_init_for_writing(page, NULL, 0);
...@@ -2734,6 +2736,7 @@ error_exit2: ...@@ -2734,6 +2736,7 @@ error_exit2:
" to tablespace ", stderr); " to tablespace ", stderr);
ut_print_filename(stderr, path); ut_print_filename(stderr, path);
putc('\n', stderr); putc('\n', stderr);
err = DB_ERROR;
goto error_exit; goto error_exit;
} }
...@@ -2743,22 +2746,20 @@ error_exit2: ...@@ -2743,22 +2746,20 @@ error_exit2:
fputs("InnoDB: Error: file flush of tablespace ", stderr); fputs("InnoDB: Error: file flush of tablespace ", stderr);
ut_print_filename(stderr, path); ut_print_filename(stderr, path);
fputs(" failed\n", stderr); fputs(" failed\n", stderr);
err = DB_ERROR;
goto error_exit; goto error_exit;
} }
os_file_close(file); os_file_close(file);
if (*space_id == ULINT_UNDEFINED) { success = fil_space_create(path, space_id, flags, FIL_TABLESPACE);
goto error_exit2;
}
success = fil_space_create(path, *space_id, flags, FIL_TABLESPACE);
if (!success) { if (!success) {
err = DB_ERROR;
goto error_exit2; goto error_exit2;
} }
fil_node_create(path, size, *space_id, FALSE); fil_node_create(path, size, space_id, FALSE);
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
{ {
...@@ -2769,7 +2770,7 @@ error_exit2: ...@@ -2769,7 +2770,7 @@ error_exit2:
fil_op_write_log(flags fil_op_write_log(flags
? MLOG_FILE_CREATE2 ? MLOG_FILE_CREATE2
: MLOG_FILE_CREATE, : MLOG_FILE_CREATE,
*space_id, space_id,
is_temp ? MLOG_FILE_FLAG_TEMP : 0, is_temp ? MLOG_FILE_FLAG_TEMP : 0,
flags, flags,
tablename, NULL, &mtr); tablename, NULL, &mtr);
......
...@@ -46,13 +46,14 @@ dict_hdr_get( ...@@ -46,13 +46,14 @@ dict_hdr_get(
/*=========*/ /*=========*/
mtr_t* mtr); /*!< in: mtr */ mtr_t* mtr); /*!< in: mtr */
/**********************************************************************//** /**********************************************************************//**
Returns a new row, table, index, or tree id. Returns a new table, index, or space id. */
@return the new id */
UNIV_INTERN UNIV_INTERN
dulint void
dict_hdr_get_new_id( dict_hdr_get_new_id(
/*================*/ /*================*/
ulint type); /*!< in: DICT_HDR_ROW_ID, ... */ dulint* table_id, /*!< out: table id (not assigned if NULL) */
dulint* index_id, /*!< out: index id (not assigned if NULL) */
ulint* space_id); /*!< out: space id (not assigned if NULL) */
/**********************************************************************//** /**********************************************************************//**
Returns a new row id. Returns a new row id.
@return the new id */ @return the new id */
...@@ -119,7 +120,8 @@ dict_create(void); ...@@ -119,7 +120,8 @@ dict_create(void);
#define DICT_HDR_ROW_ID 0 /* The latest assigned row id */ #define DICT_HDR_ROW_ID 0 /* The latest assigned row id */
#define DICT_HDR_TABLE_ID 8 /* The latest assigned table id */ #define DICT_HDR_TABLE_ID 8 /* The latest assigned table id */
#define DICT_HDR_INDEX_ID 16 /* The latest assigned index id */ #define DICT_HDR_INDEX_ID 16 /* The latest assigned index id */
#define DICT_HDR_MIX_ID 24 /* Obsolete, always 0. */ #define DICT_HDR_MAX_SPACE_ID 24 /* The latest assigned space id, or 0*/
#define DICT_HDR_MIX_ID_LOW 28 /* Obsolete,always DICT_HDR_FIRST_ID */
#define DICT_HDR_TABLES 32 /* Root of the table index tree */ #define DICT_HDR_TABLES 32 /* Root of the table index tree */
#define DICT_HDR_TABLE_IDS 36 /* Root of the table index tree */ #define DICT_HDR_TABLE_IDS 36 /* Root of the table index tree */
#define DICT_HDR_COLUMNS 40 /* Root of the column index tree */ #define DICT_HDR_COLUMNS 40 /* Root of the column index tree */
......
...@@ -225,6 +225,16 @@ fil_space_create( ...@@ -225,6 +225,16 @@ fil_space_create(
0 for uncompressed tablespaces */ 0 for uncompressed tablespaces */
ulint purpose);/*!< in: FIL_TABLESPACE, or FIL_LOG if log */ ulint purpose);/*!< in: FIL_TABLESPACE, or FIL_LOG if log */
/*******************************************************************//** /*******************************************************************//**
Assigns a new space id for a new single-table tablespace. This works simply by
incrementing the global counter. If 4 billion id's is not enough, we may need
to recycle id's.
@return TRUE if assigned, FALSE if not */
UNIV_INTERN
ibool
fil_assign_new_space_id(
/*====================*/
ulint* space_id); /*!< in/out: space id */
/*******************************************************************//**
Returns the size of the space in pages. The tablespace must be cached in the Returns the size of the space in pages. The tablespace must be cached in the
memory cache. memory cache.
@return space size, 0 if space not found */ @return space size, 0 if space not found */
...@@ -427,9 +437,7 @@ UNIV_INTERN ...@@ -427,9 +437,7 @@ UNIV_INTERN
ulint ulint
fil_create_new_single_table_tablespace( fil_create_new_single_table_tablespace(
/*===================================*/ /*===================================*/
ulint* space_id, /*!< in/out: space id; if this is != 0, ulint space_id, /*!< in: space id */
then this is an input parameter,
otherwise output */
const char* tablename, /*!< in: the table name in the usual const char* tablename, /*!< in: the table name in the usual
databasename/tablename format databasename/tablename format
of InnoDB, or a dir path to a temp of InnoDB, or a dir path to a temp
......
...@@ -2427,7 +2427,7 @@ row_discard_tablespace_for_mysql( ...@@ -2427,7 +2427,7 @@ row_discard_tablespace_for_mysql(
goto funct_exit; goto funct_exit;
} }
new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); dict_hdr_get_new_id(&new_id, NULL, NULL);
/* Remove all locks except the table-level S and X locks. */ /* Remove all locks except the table-level S and X locks. */
lock_remove_all_on_table(table, FALSE); lock_remove_all_on_table(table, FALSE);
...@@ -2789,10 +2789,11 @@ row_truncate_table_for_mysql( ...@@ -2789,10 +2789,11 @@ row_truncate_table_for_mysql(
dict_index_t* index; dict_index_t* index;
space = 0; dict_hdr_get_new_id(NULL, NULL, &space);
if (fil_create_new_single_table_tablespace( if (space == ULINT_UNDEFINED
&space, table->name, FALSE, flags, || fil_create_new_single_table_tablespace(
space, table->name, FALSE, flags,
FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) { FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
...@@ -2897,7 +2898,7 @@ next_rec: ...@@ -2897,7 +2898,7 @@ next_rec:
mem_heap_free(heap); mem_heap_free(heap);
new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); dict_hdr_get_new_id(&new_id, NULL, NULL);
info = pars_info_create(); info = pars_info_create();
......
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