Commit 4ea9fe18 authored by marko's avatar marko

branches/zip: Fix various bugs.

btr_root_raise_and_insert(): Because btr_page_set_level() must not
change level from non-zero to zero on compressed pages, invoke
btr_page_set_level() with page_zip==NULL and compress the entire
root page after creating it from the scratch.

btr_attach_half_pages(): Simplify the computation of lower_page_zip
and upper_page_zip.  Invoke btr_node_ptr_set_child_page_no() with
the correct page_zip.

page0page.h: Add __attribute__((const)) to functions testing for
infimum or supremum.

page_zip_dir_delete(): Note that the third parameter may be NULL.
Correct offset errors.

page_zip_available(): Use n_heap instead of n_recs.

page_zip_dir_find(), page_zip_dir_find_free(): Fix off-by-one error.

page_zip_fields_encode(), page_zip_fields_decode(): Encode and decode
index->n_nullable for non-leaf pages.

page_zip_apply_log(): Write REC_NEW_HEAP_NO before calling
rec_offs_make_valid().

page_zip_write_node_ptr(): Correct off-by-one error.

page_cur_search_with_match(): Make use of page_is_leaf().

page_dir_add_slots(): Replaced with page_dir_add_slot().  Use memmove().
parent 4e22f340
...@@ -1064,17 +1064,14 @@ btr_root_raise_and_insert( ...@@ -1064,17 +1064,14 @@ btr_root_raise_and_insert(
moving the root records to the new page, emptying the root, putting moving the root records to the new page, emptying the root, putting
a node pointer to the new page, and then splitting the new page. */ a node pointer to the new page, and then splitting the new page. */
new_page = btr_page_alloc(tree, 0, FSP_NO_DIR, level = btr_page_get_level(root, mtr);
btr_page_get_level(root, mtr), mtr);
btr_page_create(new_page, tree, mtr); new_page = btr_page_alloc(tree, 0, FSP_NO_DIR, level, mtr);
root_page_zip = buf_block_get_page_zip(buf_block_align(root)); btr_page_create(new_page, tree, mtr);
level = btr_page_get_level(root, mtr);
/* Set the levels of the new index page and root page */ /* Set the level of the new index page */
btr_page_set_level(new_page, NULL, level, mtr); btr_page_set_level(new_page, NULL, level, mtr);
btr_page_set_level(root, root_page_zip, level + 1, mtr);
/* Set the next node and previous node fields of new page */ /* Set the next node and previous node fields of new page */
btr_page_set_next(new_page, NULL, FIL_NULL, mtr); btr_page_set_next(new_page, NULL, FIL_NULL, mtr);
...@@ -1084,9 +1081,11 @@ btr_root_raise_and_insert( ...@@ -1084,9 +1081,11 @@ btr_root_raise_and_insert(
new_page_zip = buf_block_get_page_zip(buf_block_align(new_page)); new_page_zip = buf_block_get_page_zip(buf_block_align(new_page));
if (UNIV_UNLIKELY(!page_move_rec_list_end(new_page, new_page_zip, if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_page, new_page_zip,
page_get_infimum_rec(root), root_page_zip, page_get_infimum_rec(root), cursor->index, mtr))) {
cursor->index, mtr))) { /* This should always succeed, as new_page
is created from the scratch and receives
the records in sorted order. */
ut_error; ut_error;
} }
...@@ -1108,13 +1107,11 @@ btr_root_raise_and_insert( ...@@ -1108,13 +1107,11 @@ btr_root_raise_and_insert(
node_ptr = dict_tree_build_node_ptr(tree, rec, new_page_no, heap, node_ptr = dict_tree_build_node_ptr(tree, rec, new_page_no, heap,
level); level);
/* Reorganize the root to get free space */ /* Rebuild the root page to get free space */
if (!btr_page_reorganize_low(FALSE, root, root_page_zip, root_page_zip = buf_block_get_page_zip(buf_block_align(root));
cursor->index, mtr)) { btr_page_set_level(root, NULL, level + 1, mtr);
/* The page should be empty at this point. page_create(root, root_page_zip, mtr, cursor->index);
Thus, the operation should succeed. */ buf_block_align(root)->check_index_page_at_flush = TRUE;
ut_error;
}
page_cursor = btr_cur_get_page_cur(cursor); page_cursor = btr_cur_get_page_cur(cursor);
...@@ -1552,6 +1549,9 @@ btr_attach_half_pages( ...@@ -1552,6 +1549,9 @@ btr_attach_half_pages(
upper_page_no = buf_frame_get_page_no(page); upper_page_no = buf_frame_get_page_no(page);
lower_page = new_page; lower_page = new_page;
upper_page = page; upper_page = page;
lower_page_zip = buf_block_get_page_zip(
buf_block_align(new_page));
upper_page_zip = page_zip;
/* Look from the tree for the node pointer to page */ /* Look from the tree for the node pointer to page */
node_ptr = btr_page_get_father_node_ptr(tree, page, mtr); node_ptr = btr_page_get_father_node_ptr(tree, page, mtr);
...@@ -1559,7 +1559,8 @@ btr_attach_half_pages( ...@@ -1559,7 +1559,8 @@ btr_attach_half_pages(
/* Replace the address of the old child node (= page) with the /* Replace the address of the old child node (= page) with the
address of the new lower half */ address of the new lower half */
btr_node_ptr_set_child_page_no(node_ptr, page_zip, btr_node_ptr_set_child_page_no(node_ptr,
buf_block_get_page_zip(buf_block_align(node_ptr)),
rec_get_offsets(node_ptr, rec_get_offsets(node_ptr,
UT_LIST_GET_FIRST(tree->tree_indexes), UT_LIST_GET_FIRST(tree->tree_indexes),
NULL, ULINT_UNDEFINED, &heap), NULL, ULINT_UNDEFINED, &heap),
...@@ -1570,11 +1571,11 @@ btr_attach_half_pages( ...@@ -1570,11 +1571,11 @@ btr_attach_half_pages(
upper_page_no = buf_frame_get_page_no(new_page); upper_page_no = buf_frame_get_page_no(new_page);
lower_page = page; lower_page = page;
upper_page = new_page; upper_page = new_page;
lower_page_zip = page_zip;
upper_page_zip = buf_block_get_page_zip(
buf_block_align(new_page));
} }
lower_page_zip = buf_block_get_page_zip(buf_block_align(lower_page));
upper_page_zip = buf_block_get_page_zip(buf_block_align(upper_page));
/* Get the level of the split pages */ /* Get the level of the split pages */
level = btr_page_get_level(page, mtr); level = btr_page_get_level(page, mtr);
......
...@@ -447,7 +447,8 @@ ibool ...@@ -447,7 +447,8 @@ ibool
page_rec_is_user_rec_low( page_rec_is_user_rec_low(
/*=====================*/ /*=====================*/
/* out: TRUE if a user record */ /* out: TRUE if a user record */
ulint offset);/* in: record offset on page */ ulint offset) /* in: record offset on page */
__attribute__((const));
/**************************************************************** /****************************************************************
TRUE if the record is the supremum record on a page. */ TRUE if the record is the supremum record on a page. */
UNIV_INLINE UNIV_INLINE
...@@ -455,7 +456,8 @@ ibool ...@@ -455,7 +456,8 @@ ibool
page_rec_is_supremum_low( page_rec_is_supremum_low(
/*=====================*/ /*=====================*/
/* out: TRUE if the supremum record */ /* out: TRUE if the supremum record */
ulint offset);/* in: record offset on page */ ulint offset) /* in: record offset on page */
__attribute__((const));
/**************************************************************** /****************************************************************
TRUE if the record is the infimum record on a page. */ TRUE if the record is the infimum record on a page. */
UNIV_INLINE UNIV_INLINE
...@@ -463,7 +465,8 @@ ibool ...@@ -463,7 +465,8 @@ ibool
page_rec_is_infimum_low( page_rec_is_infimum_low(
/*====================*/ /*====================*/
/* out: TRUE if the infimum record */ /* out: TRUE if the infimum record */
ulint offset);/* in: record offset on page */ ulint offset) /* in: record offset on page */
__attribute__((const));
/**************************************************************** /****************************************************************
TRUE if the record is a user record on the page. */ TRUE if the record is a user record on the page. */
...@@ -472,7 +475,8 @@ ibool ...@@ -472,7 +475,8 @@ ibool
page_rec_is_user_rec( page_rec_is_user_rec(
/*=================*/ /*=================*/
/* out: TRUE if a user record */ /* out: TRUE if a user record */
const rec_t* rec); /* in: record */ const rec_t* rec) /* in: record */
__attribute__((const));
/**************************************************************** /****************************************************************
TRUE if the record is the supremum record on a page. */ TRUE if the record is the supremum record on a page. */
UNIV_INLINE UNIV_INLINE
...@@ -480,7 +484,9 @@ ibool ...@@ -480,7 +484,9 @@ ibool
page_rec_is_supremum( page_rec_is_supremum(
/*=================*/ /*=================*/
/* out: TRUE if the supremum record */ /* out: TRUE if the supremum record */
const rec_t* rec); /* in: record */ const rec_t* rec) /* in: record */
__attribute__((const));
/**************************************************************** /****************************************************************
TRUE if the record is the infimum record on a page. */ TRUE if the record is the infimum record on a page. */
UNIV_INLINE UNIV_INLINE
...@@ -488,7 +494,8 @@ ibool ...@@ -488,7 +494,8 @@ ibool
page_rec_is_infimum( page_rec_is_infimum(
/*================*/ /*================*/
/* out: TRUE if the infimum record */ /* out: TRUE if the infimum record */
const rec_t* rec); /* in: record */ const rec_t* rec) /* in: record */
__attribute__((const));
/******************************************************************* /*******************************************************************
Looks for the record which owns the given record. */ Looks for the record which owns the given record. */
UNIV_INLINE UNIV_INLINE
......
...@@ -112,7 +112,7 @@ page_zip_dir_delete( ...@@ -112,7 +112,7 @@ page_zip_dir_delete(
page_zip_des_t* page_zip,/* in/out: compressed page */ page_zip_des_t* page_zip,/* in/out: compressed page */
const byte* rec, /* in: deleted record */ const byte* rec, /* in: deleted record */
const byte* free) /* in: previous start of the free list */ const byte* free) /* in: previous start of the free list */
__attribute__((nonnull)); __attribute__((nonnull(1,2)));
/************************************************************************** /**************************************************************************
Add a slot to the dense page directory. */ Add a slot to the dense page directory. */
......
...@@ -228,7 +228,7 @@ page_zip_dir_delete( ...@@ -228,7 +228,7 @@ page_zip_dir_delete(
page_zip_des_t* page_zip,/* in/out: compressed page */ page_zip_des_t* page_zip,/* in/out: compressed page */
const byte* rec, /* in: deleted record */ const byte* rec, /* in: deleted record */
const byte* free) /* in: previous start of the free list */ const byte* free) /* in: previous start of the free list */
__attribute__((nonnull)); __attribute__((nonnull(1,2)));
/************************************************************************** /**************************************************************************
Add a slot to the dense page directory. */ Add a slot to the dense page directory. */
......
...@@ -210,7 +210,7 @@ page_zip_available( ...@@ -210,7 +210,7 @@ page_zip_available(
+ REC_NODE_PTR_SIZE; + REC_NODE_PTR_SIZE;
} }
trailer_len = page_get_n_recs((page_t*) page_zip->data) trailer_len = (page_dir_get_n_heap((page_t*) page_zip->data) - 2)
* uncompressed_size * uncompressed_size
+ page_zip->n_blobs + page_zip->n_blobs
* BTR_EXTERN_FIELD_REF_SIZE; * BTR_EXTERN_FIELD_REF_SIZE;
......
...@@ -254,7 +254,7 @@ page_cur_search_with_match( ...@@ -254,7 +254,7 @@ page_cur_search_with_match(
page_check_dir(page); page_check_dir(page);
#ifdef PAGE_CUR_ADAPT #ifdef PAGE_CUR_ADAPT
if ((page_header_get_field(page, PAGE_LEVEL) == 0) if (page_is_leaf(page)
&& (mode == PAGE_CUR_LE) && (mode == PAGE_CUR_LE)
&& (page_header_get_field(page, PAGE_N_DIRECTION) > 3) && (page_header_get_field(page, PAGE_N_DIRECTION) > 3)
&& (page_header_get_ptr(page, PAGE_LAST_INSERT)) && (page_header_get_ptr(page, PAGE_LAST_INSERT))
......
...@@ -1175,39 +1175,27 @@ in the added slots or update n_owned values: this is the responsibility ...@@ -1175,39 +1175,27 @@ in the added slots or update n_owned values: this is the responsibility
of the caller. */ of the caller. */
UNIV_INLINE UNIV_INLINE
void void
page_dir_add_slots( page_dir_add_slot(
/*===============*/ /*==============*/
page_t* page, /* in/out: the index page */ page_t* page, /* in/out: the index page */
page_zip_des_t* page_zip,/* in/out: comprssed page, or NULL */ page_zip_des_t* page_zip,/* in/out: comprssed page, or NULL */
ulint start, /* in: the slot above which the new slots ulint start) /* in: the slot above which the new slots
are added */ are added */
ulint n) /* in: number of slots to add
(currently only n == 1 allowed) */
{ {
page_dir_slot_t* slot; page_dir_slot_t* slot;
ulint n_slots; ulint n_slots;
ulint i;
rec_t* rec;
ut_ad(n == 1);
n_slots = page_dir_get_n_slots(page); n_slots = page_dir_get_n_slots(page);
ut_ad(start < n_slots - 1); ut_ad(start < n_slots - 1);
/* Update the page header */ /* Update the page header */
page_dir_set_n_slots(page, page_zip, n_slots + n); page_dir_set_n_slots(page, page_zip, n_slots + 1);
/* Move slots up */ /* Move slots up */
slot = page_dir_get_nth_slot(page, n_slots);
for (i = n_slots - 1; i > start; i--) { memmove(slot, slot + PAGE_DIR_SLOT_SIZE,
(n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE);
slot = page_dir_get_nth_slot(page, i);
rec = page_dir_slot_get_rec(slot);
slot = page_dir_get_nth_slot(page, i + n);
page_dir_slot_set_rec(slot, rec);
}
} }
/******************************************************************** /********************************************************************
...@@ -1252,7 +1240,7 @@ page_dir_split_slot( ...@@ -1252,7 +1240,7 @@ page_dir_split_slot(
/* 2. We add one directory slot immediately below the slot to be /* 2. We add one directory slot immediately below the slot to be
split. */ split. */
page_dir_add_slots(page, page_zip, slot_no - 1, 1); page_dir_add_slot(page, page_zip, slot_no - 1);
/* The added slot is now number slot_no, and the old slot is /* The added slot is now number slot_no, and the old slot is
now number slot_no + 1 */ now number slot_no + 1 */
......
...@@ -95,7 +95,7 @@ page_zip_dir_find( ...@@ -95,7 +95,7 @@ page_zip_dir_find(
ut_ad(page_zip_simple_validate(page_zip)); ut_ad(page_zip_simple_validate(page_zip));
end = page_zip->data + page_zip->size - PAGE_ZIP_DIR_SLOT_SIZE; end = page_zip->data + page_zip->size;
slot = end - page_zip_dir_user_size(page_zip); slot = end - page_zip_dir_user_size(page_zip);
for (; slot < end; slot += PAGE_ZIP_DIR_SLOT_SIZE) { for (; slot < end; slot += PAGE_ZIP_DIR_SLOT_SIZE) {
...@@ -126,7 +126,7 @@ page_zip_dir_find_free( ...@@ -126,7 +126,7 @@ page_zip_dir_find_free(
slot = end = page_zip->data + page_zip->size; slot = end = page_zip->data + page_zip->size;
slot -= page_zip_dir_size(page_zip); slot -= page_zip_dir_size(page_zip);
end -= PAGE_ZIP_DIR_SLOT_SIZE + page_zip_dir_user_size(page_zip); end -= page_zip_dir_user_size(page_zip);
for (; slot < end; slot += PAGE_ZIP_DIR_SLOT_SIZE) { for (; slot < end; slot += PAGE_ZIP_DIR_SLOT_SIZE) {
if ((mach_read_from_2(slot) & PAGE_ZIP_DIR_SLOT_MASK) if ((mach_read_from_2(slot) & PAGE_ZIP_DIR_SLOT_MASK)
...@@ -287,15 +287,20 @@ page_zip_fields_encode( ...@@ -287,15 +287,20 @@ page_zip_fields_encode(
if (trx_id_pos != ULINT_UNDEFINED) { if (trx_id_pos != ULINT_UNDEFINED) {
/* Write out the position of the trx_id column */ /* Write out the position of the trx_id column */
if (trx_id_col < 128) { i = trx_id_col;
*buf++ = trx_id_col; } else {
} else { /* Write out the number of nullable fields */
*buf++ = 0x80 | trx_id_col >> 8; i = index->n_nullable;
*buf++ = 0xff & trx_id_col;
}
} }
ut_ad((ulint) (buf - buf_start) <= (n + 1) * 2); if (i < 128) {
*buf++ = i;
} else {
*buf++ = 0x80 | i >> 8;
*buf++ = 0xff & i;
}
ut_ad((ulint) (buf - buf_start) <= (n + 2) * 2);
return((ulint) (buf - buf_start)); return((ulint) (buf - buf_start));
} }
...@@ -821,6 +826,7 @@ page_zip_fields_decode( ...@@ -821,6 +826,7 @@ page_zip_fields_decode(
const byte* b; const byte* b;
ulint n; ulint n;
ulint i; ulint i;
ulint val;
dict_table_t* table; dict_table_t* table;
dict_index_t* index; dict_index_t* index;
...@@ -831,16 +837,14 @@ page_zip_fields_decode( ...@@ -831,16 +837,14 @@ page_zip_fields_decode(
} }
} }
n--; /* n_nullable or trx_id */
if (UNIV_UNLIKELY(n > REC_MAX_N_FIELDS) if (UNIV_UNLIKELY(n > REC_MAX_N_FIELDS)
|| UNIV_UNLIKELY(b > end)) { || UNIV_UNLIKELY(b > end)) {
return(NULL); return(NULL);
} }
if (trx_id_col) {
n--;
}
table = dict_mem_table_create("ZIP_DUMMY", DICT_HDR_SPACE, n, TRUE); table = dict_mem_table_create("ZIP_DUMMY", DICT_HDR_SPACE, n, TRUE);
index = dict_mem_index_create("ZIP_DUMMY", "ZIP_DUMMY", index = dict_mem_index_create("ZIP_DUMMY", "ZIP_DUMMY",
DICT_HDR_SPACE, 0, n); DICT_HDR_SPACE, 0, n);
...@@ -851,10 +855,11 @@ page_zip_fields_decode( ...@@ -851,10 +855,11 @@ page_zip_fields_decode(
/* Initialize the fields. */ /* Initialize the fields. */
for (b = buf, i = 0; i < n; i++) { for (b = buf, i = 0; i < n; i++) {
ulint val = *b++;
ulint mtype; ulint mtype;
ulint len; ulint len;
val = *b++;
if (UNIV_UNLIKELY(val & 0x80)) { if (UNIV_UNLIKELY(val & 0x80)) {
/* fixed length > 62 bytes */ /* fixed length > 62 bytes */
val = (val & 0x7f) << 8 | *b++; val = (val & 0x7f) << 8 | *b++;
...@@ -880,13 +885,13 @@ page_zip_fields_decode( ...@@ -880,13 +885,13 @@ page_zip_fields_decode(
dict_table_get_nth_col(table, i), 0); dict_table_get_nth_col(table, i), 0);
} }
val = *b++;
if (UNIV_UNLIKELY(val & 0x80)) {
val = (val & 0x7f) << 8 | *b++;
}
/* Decode the position of the trx_id column. */ /* Decode the position of the trx_id column. */
if (trx_id_col) { if (trx_id_col) {
ulint val = *b++;
if (UNIV_UNLIKELY(val & 0x80)) {
val = (val & 0x7f) << 8 | *b++;
}
if (UNIV_UNLIKELY(val >= n)) { if (UNIV_UNLIKELY(val >= n)) {
page_zip_fields_free(index); page_zip_fields_free(index);
index = NULL; index = NULL;
...@@ -897,6 +902,14 @@ page_zip_fields_decode( ...@@ -897,6 +902,14 @@ page_zip_fields_decode(
} }
*trx_id_col = val; *trx_id_col = val;
} else {
/* Decode the number of nullable fields. */
if (UNIV_UNLIKELY(index->n_nullable > val)) {
page_zip_fields_free(index);
index = NULL;
} else {
index->n_nullable = val;
}
} }
ut_ad(b == end); ut_ad(b == end);
...@@ -1132,13 +1145,15 @@ page_zip_apply_log( ...@@ -1132,13 +1145,15 @@ page_zip_apply_log(
heap_status += 1 << REC_HEAP_NO_SHIFT; heap_status += 1 << REC_HEAP_NO_SHIFT;
} }
mach_write_to_2(rec - REC_NEW_HEAP_NO, hs);
#if REC_STATUS_NODE_PTR != TRUE
# error "REC_STATUS_NODE_PTR != TRUE"
#endif
rec_get_offsets_reverse(data, index, rec_get_offsets_reverse(data, index,
hs & REC_STATUS_NODE_PTR, hs & REC_STATUS_NODE_PTR,
offsets); offsets);
rec_offs_make_valid(rec, index, offsets); rec_offs_make_valid(rec, index, offsets);
mach_write_to_2(rec - REC_NEW_HEAP_NO, hs);
/* Copy the extra bytes (backwards). */ /* Copy the extra bytes (backwards). */
{ {
byte* start = rec_get_start(rec, offsets); byte* start = rec_get_start(rec, offsets);
...@@ -1963,7 +1978,7 @@ page_zip_write_node_ptr( ...@@ -1963,7 +1978,7 @@ page_zip_write_node_ptr(
storage = page_zip->data + page_zip->size storage = page_zip->data + page_zip->size
- (page_dir_get_n_heap(page) - 2) - (page_dir_get_n_heap(page) - 2)
* PAGE_ZIP_DIR_SLOT_SIZE * PAGE_ZIP_DIR_SLOT_SIZE
- (rec_get_heap_no_new(rec) - 2) * REC_NODE_PTR_SIZE; - (rec_get_heap_no_new(rec) - 1) * REC_NODE_PTR_SIZE;
field = rec + size - REC_NODE_PTR_SIZE; field = rec + size - REC_NODE_PTR_SIZE;
#if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG
...@@ -2200,28 +2215,30 @@ page_zip_dir_delete( ...@@ -2200,28 +2215,30 @@ page_zip_dir_delete(
byte* slot_rec; byte* slot_rec;
byte* slot_free; byte* slot_free;
ut_ad(rec);
slot_rec = page_zip_dir_find(page_zip, slot_rec = page_zip_dir_find(page_zip,
ut_align_offset(rec, UNIV_PAGE_SIZE)); ut_align_offset(rec, UNIV_PAGE_SIZE));
slot_free = page_zip_dir_find_free(page_zip,
ut_align_offset(free, UNIV_PAGE_SIZE));
ut_a(slot_rec); ut_a(slot_rec);
if (UNIV_UNLIKELY(!slot_free)) { if (UNIV_UNLIKELY(!free)) {
/* Make the last slot the start of the free list. */ /* Make the last slot the start of the free list. */
slot_free = page_zip->data + page_zip->size slot_free = page_zip->data + page_zip->size
- PAGE_ZIP_DIR_SLOT_SIZE - PAGE_ZIP_DIR_SLOT_SIZE
* page_dir_get_n_heap(page_zip->data); * (page_dir_get_n_heap(page_zip->data) - 2);
} else { } else {
slot_free = page_zip_dir_find_free(page_zip,
ut_align_offset(free, UNIV_PAGE_SIZE));
ut_a(slot_free < slot_rec); ut_a(slot_free < slot_rec);
/* Grow the free list by one slot by moving the start. */ /* Grow the free list by one slot by moving the start. */
slot_free += PAGE_ZIP_DIR_SLOT_SIZE; slot_free += PAGE_ZIP_DIR_SLOT_SIZE;
} }
if (UNIV_LIKELY(slot_free < slot_rec)) { if (UNIV_LIKELY(slot_rec > slot_free)) {
memmove(slot_free + PAGE_ZIP_DIR_SLOT_SIZE, memmove(slot_free + PAGE_ZIP_DIR_SLOT_SIZE,
slot_free, slot_free,
slot_rec - slot_free - PAGE_ZIP_DIR_SLOT_SIZE); slot_rec - slot_free);
} }
/* Write the entry for the deleted record. /* Write the entry for the deleted record.
......
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