Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
be01dbf5
Commit
be01dbf5
authored
Feb 08, 2011
by
Vasil Dimov
Browse files
Options
Browse Files
Download
Plain Diff
Merge mysql-5.5-innodb -> mysql-5.5
parents
4e523ad6
1751fd90
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
321 additions
and
187 deletions
+321
-187
mysql-test/valgrind.supp
mysql-test/valgrind.supp
+42
-0
storage/innobase/btr/btr0cur.c
storage/innobase/btr/btr0cur.c
+107
-49
storage/innobase/buf/buf0buf.c
storage/innobase/buf/buf0buf.c
+16
-14
storage/innobase/dict/dict0load.c
storage/innobase/dict/dict0load.c
+1
-1
storage/innobase/fsp/fsp0fsp.c
storage/innobase/fsp/fsp0fsp.c
+4
-4
storage/innobase/ibuf/ibuf0ibuf.c
storage/innobase/ibuf/ibuf0ibuf.c
+4
-4
storage/innobase/include/btr0cur.h
storage/innobase/include/btr0cur.h
+37
-5
storage/innobase/include/buf0buf.h
storage/innobase/include/buf0buf.h
+4
-4
storage/innobase/include/row0upd.h
storage/innobase/include/row0upd.h
+18
-8
storage/innobase/include/univ.i
storage/innobase/include/univ.i
+4
-3
storage/innobase/row/row0ins.c
storage/innobase/row/row0ins.c
+1
-1
storage/innobase/row/row0mysql.c
storage/innobase/row/row0mysql.c
+8
-6
storage/innobase/row/row0purge.c
storage/innobase/row/row0purge.c
+27
-41
storage/innobase/row/row0umod.c
storage/innobase/row/row0umod.c
+11
-24
storage/innobase/row/row0upd.c
storage/innobase/row/row0upd.c
+27
-16
storage/innobase/srv/srv0start.c
storage/innobase/srv/srv0start.c
+8
-5
storage/innobase/trx/trx0roll.c
storage/innobase/trx/trx0roll.c
+2
-2
No files found.
mysql-test/valgrind.supp
View file @
be01dbf5
...
...
@@ -835,3 +835,45 @@
fun:lf_hash_search
}
{
Bug 59874 Valgrind warning in InnoDB compression code
Memcheck:Cond
fun:*
fun:*
fun:deflate
fun:btr_store_big_rec_extern_fields_func
fun:row_ins_index_entry_low
fun:row_ins_index_entry
fun:row_ins_index_entry_step
fun:row_ins
fun:row_ins_step
fun:row_insert_for_mysql
}
{
In page0zip.c we have already checked that the memory is initialized before calling deflate()
Memcheck:Cond
fun:*
fun:*
fun:deflate
fun:page_zip_compress
}
{
In page0zip.c we have already checked that the memory is initialized before calling deflate()
Memcheck:Cond
fun:*
fun:*
fun:deflate
fun:page_zip_compress_deflate
}
{
Bug 59875 Valgrind warning in buf0buddy.c
Memcheck:Addr1
fun:mach_read_from_4
fun:buf_buddy_relocate
fun:buf_buddy_free_low
fun:buf_buddy_free
fun:buf_LRU_block_remove_hashed_page
}
storage/innobase/btr/btr0cur.c
View file @
be01dbf5
...
...
@@ -197,7 +197,7 @@ static
ulint
btr_rec_get_externally_stored_len
(
/*==============================*/
rec_t
*
rec
,
/*!< in: record */
const
rec_t
*
rec
,
/*!< in: record */
const
ulint
*
offsets
);
/*!< in: array returned by rec_get_offsets() */
#endif
/* !UNIV_HOTBACKUP */
...
...
@@ -1886,8 +1886,8 @@ btr_cur_update_in_place(
NOT call it if index is secondary */
if
(
!
dict_index_is_clust
(
index
)
||
row_upd_changes_ord_field_binary
(
NULL
,
NULL
,
index
,
update
))
{
||
row_upd_changes_ord_field_binary
(
index
,
update
,
thr
,
NULL
,
NULL
))
{
/* Remove possible hash index pointer to this record */
btr_search_update_hash_on_delete
(
cursor
);
...
...
@@ -3761,6 +3761,35 @@ btr_estimate_number_of_different_key_vals(
/*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/
/***********************************************************//**
Gets the offset of the pointer to the externally stored part of a field.
@return offset of the pointer to the externally stored part */
static
ulint
btr_rec_get_field_ref_offs
(
/*=======================*/
const
ulint
*
offsets
,
/*!< in: array returned by rec_get_offsets() */
ulint
n
)
/*!< in: index of the external field */
{
ulint
field_ref_offs
;
ulint
local_len
;
ut_a
(
rec_offs_nth_extern
(
offsets
,
n
));
field_ref_offs
=
rec_get_nth_field_offs
(
offsets
,
n
,
&
local_len
);
ut_a
(
local_len
!=
UNIV_SQL_NULL
);
ut_a
(
local_len
>=
BTR_EXTERN_FIELD_REF_SIZE
);
return
(
field_ref_offs
+
local_len
-
BTR_EXTERN_FIELD_REF_SIZE
);
}
/** Gets a pointer to the externally stored part of a field.
@param rec record
@param offsets rec_get_offsets(rec)
@param n index of the externally stored field
@return pointer to the externally stored part */
#define btr_rec_get_field_ref(rec, offsets, n) \
((rec) + btr_rec_get_field_ref_offs(offsets, n))
/***********************************************************//**
Gets the externally stored size of a record, in units of a database page.
@return externally stored part, in units of a database page */
...
...
@@ -3768,28 +3797,27 @@ static
ulint
btr_rec_get_externally_stored_len
(
/*==============================*/
rec_t
*
rec
,
/*!< in: record */
const
rec_t
*
rec
,
/*!< in: record */
const
ulint
*
offsets
)
/*!< in: array returned by rec_get_offsets() */
{
ulint
n_fields
;
byte
*
data
;
ulint
local_len
;
ulint
extern_len
;
ulint
total_extern_len
=
0
;
ulint
i
;
ut_ad
(
!
rec_offs_comp
(
offsets
)
||
!
rec_get_node_ptr_flag
(
rec
));
if
(
!
rec_offs_any_extern
(
offsets
))
{
return
(
0
);
}
n_fields
=
rec_offs_n_fields
(
offsets
);
for
(
i
=
0
;
i
<
n_fields
;
i
++
)
{
if
(
rec_offs_nth_extern
(
offsets
,
i
))
{
data
=
rec_get_nth_field
(
rec
,
offsets
,
i
,
&
local_len
);
local_len
-=
BTR_EXTERN_FIELD_REF_SIZE
;
extern_len
=
mach_read_from_4
(
data
+
local_len
+
BTR_EXTERN_LEN
+
4
);
ulint
extern_len
=
mach_read_from_4
(
btr_rec_get_field_ref
(
rec
,
offsets
,
i
)
+
BTR_EXTERN_LEN
+
4
);
total_extern_len
+=
ut_calc_align
(
extern_len
,
UNIV_PAGE_SIZE
);
...
...
@@ -3819,7 +3847,7 @@ btr_cur_set_ownership_of_extern_field(
ulint
byte_val
;
data
=
rec_get_nth_field
(
rec
,
offsets
,
i
,
&
local_len
);
ut_ad
(
rec_offs_nth_extern
(
offsets
,
i
));
ut_a
(
local_len
>=
BTR_EXTERN_FIELD_REF_SIZE
);
local_len
-=
BTR_EXTERN_FIELD_REF_SIZE
;
...
...
@@ -3829,6 +3857,9 @@ btr_cur_set_ownership_of_extern_field(
if
(
val
)
{
byte_val
=
byte_val
&
(
~
BTR_EXTERN_OWNER_FLAG
);
}
else
{
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
ut_a
(
!
(
byte_val
&
BTR_EXTERN_OWNER_FLAG
));
#endif
/* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
byte_val
=
byte_val
|
BTR_EXTERN_OWNER_FLAG
;
}
...
...
@@ -4067,8 +4098,8 @@ file segment of the index tree.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
UNIV_INTERN
ulint
btr_store_big_rec_extern_fields
(
/*============================*/
btr_store_big_rec_extern_fields
_func
(
/*============================
=====
*/
dict_index_t
*
index
,
/*!< in: index of rec; the index tree
MUST be X-latched */
buf_block_t
*
rec_block
,
/*!< in/out: block containing rec */
...
...
@@ -4077,11 +4108,17 @@ btr_store_big_rec_extern_fields(
the "external storage" flags in offsets
will not correspond to rec when
this function returns */
big_rec_t
*
big_rec_vec
,
/*!< in: vector containing fields
#ifdef UNIV_DEBUG
mtr_t
*
local_mtr
,
/*!< in: mtr containing the
latch to rec and to the tree */
#endif
/* UNIV_DEBUG */
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
ibool
update_in_place
,
/*! in: TRUE if the record is updated
in place (not delete+insert) */
#endif
/* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
const
big_rec_t
*
big_rec_vec
)
/*!< in: vector containing fields
to be stored externally */
mtr_t
*
local_mtr
__attribute__
((
unused
)))
/*!< in: mtr
containing the latch to rec and to the
tree */
{
ulint
rec_page_no
;
byte
*
field_ref
;
...
...
@@ -4099,6 +4136,7 @@ btr_store_big_rec_extern_fields(
z_stream
c_stream
;
ut_ad
(
rec_offs_validate
(
rec
,
index
,
offsets
));
ut_ad
(
rec_offs_any_extern
(
offsets
));
ut_ad
(
mtr_memo_contains
(
local_mtr
,
dict_index_get_lock
(
index
),
MTR_MEMO_X_LOCK
));
ut_ad
(
mtr_memo_contains
(
local_mtr
,
rec_block
,
MTR_MEMO_PAGE_X_FIX
));
...
...
@@ -4130,21 +4168,37 @@ btr_store_big_rec_extern_fields(
ut_a
(
err
==
Z_OK
);
}
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
/* All pointers to externally stored columns in the record
must either be zero or they must be pointers to inherited
columns, owned by this record or an earlier record version. */
for
(
i
=
0
;
i
<
rec_offs_n_fields
(
offsets
);
i
++
)
{
if
(
!
rec_offs_nth_extern
(
offsets
,
i
))
{
continue
;
}
field_ref
=
btr_rec_get_field_ref
(
rec
,
offsets
,
i
);
ut_a
(
!
(
field_ref
[
BTR_EXTERN_LEN
]
&
BTR_EXTERN_OWNER_FLAG
));
/* Either this must be an update in place,
or the BLOB must be inherited, or the BLOB pointer
must be zero (will be written in this function). */
ut_a
(
update_in_place
||
(
field_ref
[
BTR_EXTERN_LEN
]
&
BTR_EXTERN_INHERITED_FLAG
)
||
!
memcmp
(
field_ref
,
field_ref_zero
,
BTR_EXTERN_FIELD_REF_SIZE
));
}
#endif
/* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
/* We have to create a file segment to the tablespace
for each field and put the pointer to the field in rec */
for
(
i
=
0
;
i
<
big_rec_vec
->
n_fields
;
i
++
)
{
ut_ad
(
rec_offs_nth_extern
(
offsets
,
big_rec_vec
->
fields
[
i
].
field_no
));
{
ulint
local_len
;
field_ref
=
rec_get_nth_field
(
rec
,
offsets
,
big_rec_vec
->
fields
[
i
].
field_no
,
&
local_len
);
ut_a
(
local_len
>=
BTR_EXTERN_FIELD_REF_SIZE
);
local_len
-=
BTR_EXTERN_FIELD_REF_SIZE
;
field_ref
+=
local_len
;
}
field_ref
=
btr_rec_get_field_ref
(
rec
,
offsets
,
big_rec_vec
->
fields
[
i
].
field_no
);
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
/* A zero BLOB pointer should have been initially inserted. */
ut_a
(
!
memcmp
(
field_ref
,
field_ref_zero
,
BTR_EXTERN_FIELD_REF_SIZE
));
#endif
/* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
extern_len
=
big_rec_vec
->
fields
[
i
].
len
;
UNIV_MEM_ASSERT_RW
(
big_rec_vec
->
fields
[
i
].
data
,
extern_len
);
...
...
@@ -4426,6 +4480,23 @@ next_zip_page:
mem_heap_free
(
heap
);
}
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
/* All pointers to externally stored columns in the record
must be valid. */
for
(
i
=
0
;
i
<
rec_offs_n_fields
(
offsets
);
i
++
)
{
if
(
!
rec_offs_nth_extern
(
offsets
,
i
))
{
continue
;
}
field_ref
=
btr_rec_get_field_ref
(
rec
,
offsets
,
i
);
/* The pointer must not be zero. */
ut_a
(
0
!=
memcmp
(
field_ref
,
field_ref_zero
,
BTR_EXTERN_FIELD_REF_SIZE
));
/* The column must not be disowned by this record. */
ut_a
(
!
(
field_ref
[
BTR_EXTERN_LEN
]
&
BTR_EXTERN_OWNER_FLAG
));
}
#endif
/* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
return
(
DB_SUCCESS
);
}
...
...
@@ -4448,6 +4519,7 @@ btr_check_blob_fil_page_type(
if
(
UNIV_UNLIKELY
(
type
!=
FIL_PAGE_TYPE_BLOB
))
{
ulint
flags
=
fil_space_get_flags
(
space_id
);
#ifndef UNIV_DEBUG
/* Improve debug test coverage */
if
(
UNIV_LIKELY
((
flags
&
DICT_TF_FORMAT_MASK
)
==
DICT_TF_FORMAT_51
))
{
/* Old versions of InnoDB did not initialize
...
...
@@ -4456,6 +4528,7 @@ btr_check_blob_fil_page_type(
a BLOB page that is in Antelope format.*/
return
;
}
#endif
/* !UNIV_DEBUG */
ut_print_timestamp
(
stderr
);
fprintf
(
stderr
,
...
...
@@ -4505,23 +4578,13 @@ btr_free_externally_stored_field(
ulint
page_no
;
ulint
next_page_no
;
mtr_t
mtr
;
#ifdef UNIV_DEBUG
ut_ad
(
mtr_memo_contains
(
local_mtr
,
dict_index_get_lock
(
index
),
MTR_MEMO_X_LOCK
));
ut_ad
(
mtr_memo_contains_page
(
local_mtr
,
field_ref
,
MTR_MEMO_PAGE_X_FIX
));
ut_ad
(
!
rec
||
rec_offs_validate
(
rec
,
index
,
offsets
));
if
(
rec
)
{
ulint
local_len
;
const
byte
*
f
=
rec_get_nth_field
(
rec
,
offsets
,
i
,
&
local_len
);
ut_a
(
local_len
>=
BTR_EXTERN_FIELD_REF_SIZE
);
local_len
-=
BTR_EXTERN_FIELD_REF_SIZE
;
f
+=
local_len
;
ut_ad
(
f
==
field_ref
);
}
#endif
/* UNIV_DEBUG */
ut_ad
(
!
rec
||
field_ref
==
btr_rec_get_field_ref
(
rec
,
offsets
,
i
));
if
(
UNIV_UNLIKELY
(
!
memcmp
(
field_ref
,
field_ref_zero
,
BTR_EXTERN_FIELD_REF_SIZE
)))
{
...
...
@@ -4684,13 +4747,8 @@ btr_rec_free_externally_stored_fields(
for
(
i
=
0
;
i
<
n_fields
;
i
++
)
{
if
(
rec_offs_nth_extern
(
offsets
,
i
))
{
ulint
len
;
byte
*
data
=
rec_get_nth_field
(
rec
,
offsets
,
i
,
&
len
);
ut_a
(
len
>=
BTR_EXTERN_FIELD_REF_SIZE
);
btr_free_externally_stored_field
(
index
,
data
+
len
-
BTR_EXTERN_FIELD_REF_SIZE
,
index
,
btr_rec_get_field_ref
(
rec
,
offsets
,
i
)
,
rec
,
offsets
,
page_zip
,
i
,
rb_ctx
,
mtr
);
}
}
...
...
storage/innobase/buf/buf0buf.c
View file @
be01dbf5
...
...
@@ -865,9 +865,9 @@ buf_block_init(
block
->
modify_clock
=
0
;
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
block
->
page
.
file_page_was_freed
=
FALSE
;
#endif
/* UNIV_DEBUG_FILE_ACCESSES */
#endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
block
->
check_index_page_at_flush
=
FALSE
;
block
->
index
=
NULL
;
...
...
@@ -2229,7 +2229,7 @@ buf_page_peek_if_search_hashed(
return
(
is_hashed
);
}
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
/********************************************************************//**
Sets file_page_was_freed TRUE if the page is found in the buffer pool.
This function should be called when we free a file page and want the
...
...
@@ -2252,6 +2252,8 @@ buf_page_set_file_page_was_freed(
if
(
bpage
)
{
ut_ad
(
!
buf_pool_watch_is_sentinel
(
buf_pool
,
bpage
));
/* bpage->file_page_was_freed can already hold
when this code is invoked from dict_drop_index_tree() */
bpage
->
file_page_was_freed
=
TRUE
;
}
...
...
@@ -2289,7 +2291,7 @@ buf_page_reset_file_page_was_freed(
return
(
bpage
);
}
#endif
/* UNIV_DEBUG_FILE_ACCESSES */
#endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
/********************************************************************//**
Get read access to a compressed page (usually of type
...
...
@@ -2390,7 +2392,7 @@ got_block:
buf_page_set_accessed_make_young
(
bpage
,
access_time
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ut_a
(
!
bpage
->
file_page_was_freed
);
#endif
...
...
@@ -3058,7 +3060,7 @@ wait_until_unfixed:
buf_page_set_accessed_make_young
(
&
block
->
page
,
access_time
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ut_a
(
!
block
->
page
.
file_page_was_freed
);
#endif
...
...
@@ -3217,7 +3219,7 @@ buf_page_optimistic_get(
ut_a
(
buf_block_get_state
(
block
)
==
BUF_BLOCK_FILE_PAGE
);
#endif
/* UNIV_DEBUG || UNIV_BUF_DEBUG */
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ut_a
(
block
->
page
.
file_page_was_freed
==
FALSE
);
#endif
if
(
UNIV_UNLIKELY
(
!
access_time
))
{
...
...
@@ -3329,7 +3331,7 @@ buf_page_get_known_nowait(
ut_a
(
block
->
page
.
buf_fix_count
>
0
);
ut_a
(
buf_block_get_state
(
block
)
==
BUF_BLOCK_FILE_PAGE
);
#endif
/* UNIV_DEBUG || UNIV_BUF_DEBUG */
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ut_a
(
block
->
page
.
file_page_was_freed
==
FALSE
);
#endif
...
...
@@ -3415,9 +3417,9 @@ buf_page_try_get_func(
ut_a
(
block
->
page
.
buf_fix_count
>
0
);
ut_a
(
buf_block_get_state
(
block
)
==
BUF_BLOCK_FILE_PAGE
);
#endif
/* UNIV_DEBUG || UNIV_BUF_DEBUG */
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ut_a
(
block
->
page
.
file_page_was_freed
==
FALSE
);
#endif
/* UNIV_DEBUG_FILE_ACCESSES */
#endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
buf_block_dbg_add_level
(
block
,
SYNC_NO_ORDER_CHECK
);
buf_pool
->
stat
.
n_page_gets
++
;
...
...
@@ -3446,9 +3448,9 @@ buf_page_init_low(
bpage
->
newest_modification
=
0
;
bpage
->
oldest_modification
=
0
;
HASH_INVALIDATE
(
bpage
,
hash
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
bpage
->
file_page_was_freed
=
FALSE
;
#endif
/* UNIV_DEBUG_FILE_ACCESSES */
#endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
}
/********************************************************************//**
...
...
@@ -3807,9 +3809,9 @@ buf_page_create(
#ifdef UNIV_IBUF_COUNT_DEBUG
ut_a
(
ibuf_count_get
(
space
,
offset
)
==
0
);
#endif
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
block
->
page
.
file_page_was_freed
=
FALSE
;
#endif
/* UNIV_DEBUG_FILE_ACCESSES */
#endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
/* Page can be found in buf_pool */
buf_pool_mutex_exit
(
buf_pool
);
...
...
storage/innobase/dict/dict0load.c
View file @
be01dbf5
...
...
@@ -1552,7 +1552,7 @@ err_len:
"InnoDB: in InnoDB data dictionary"
" has unknown type %lx.
\n
"
,
(
ulong
)
flags
);
return
(
NULL
);
return
(
"incorrect flags in SYS_TABLES"
);
}
}
else
{
flags
=
0
;
...
...
storage/innobase/fsp/fsp0fsp.c
View file @
be01dbf5
...
...
@@ -3435,9 +3435,9 @@ fseg_free_page(
fseg_free_page_low
(
seg_inode
,
space
,
zip_size
,
page
,
mtr
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
buf_page_set_file_page_was_freed
(
space
,
page
);
#endif
#endif
/* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */
}
/**********************************************************************//**
...
...
@@ -3503,13 +3503,13 @@ fseg_free_extent(
fsp_free_extent
(
space
,
zip_size
,
page
,
mtr
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
for
(
i
=
0
;
i
<
FSP_EXTENT_SIZE
;
i
++
)
{
buf_page_set_file_page_was_freed
(
space
,
first_page_in_extent
+
i
);
}
#endif
#endif
/* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */
}
/**********************************************************************//**
...
...
storage/innobase/ibuf/ibuf0ibuf.c
View file @
be01dbf5
...
...
@@ -2276,9 +2276,9 @@ ibuf_remove_free_page(void)
fseg_free_page
(
header_page
+
IBUF_HEADER
+
IBUF_TREE_SEG_HEADER
,
IBUF_SPACE_ID
,
page_no
,
&
mtr
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
buf_page_reset_file_page_was_freed
(
IBUF_SPACE_ID
,
page_no
);
#endif
#endif
/* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */
ibuf_enter
();
...
...
@@ -2322,9 +2322,9 @@ ibuf_remove_free_page(void)
ibuf_bitmap_page_set_bits
(
bitmap_page
,
page_no
,
zip_size
,
IBUF_BITMAP_IBUF
,
FALSE
,
&
mtr
);
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
buf_page_set_file_page_was_freed
(
IBUF_SPACE_ID
,
page_no
);
#endif
#endif
/* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */
mtr_commit
(
&
mtr
);
ibuf_exit
();
...
...
storage/innobase/include/btr0cur.h
View file @
be01dbf5
...
...
@@ -500,8 +500,8 @@ file segment of the index tree.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
UNIV_INTERN
ulint
btr_store_big_rec_extern_fields
(
/*============================*/
btr_store_big_rec_extern_fields
_func
(
/*============================
=====
*/
dict_index_t
*
index
,
/*!< in: index of rec; the index tree
MUST be X-latched */
buf_block_t
*
rec_block
,
/*!< in/out: block containing rec */
...
...
@@ -510,10 +510,42 @@ btr_store_big_rec_extern_fields(
the "external storage" flags in offsets
will not correspond to rec when
this function returns */
big_rec_t
*
big_rec_vec
,
/*!< in: vector containing fields
#ifdef UNIV_DEBUG
mtr_t
*
local_mtr
,
/*!< in: mtr containing the
latch to rec and to the tree */
#endif
/* UNIV_DEBUG */
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
ibool
update_in_place
,
/*! in: TRUE if the record is updated
in place (not delete+insert) */
#endif
/* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
const
big_rec_t
*
big_rec_vec
)
/*!< in: vector containing fields
to be stored externally */
mtr_t
*
local_mtr
);
/*!< in: mtr containing the latch to
rec and to the tree */
__attribute__
((
nonnull
));
/** Stores the fields in big_rec_vec to the tablespace and puts pointers to
them in rec. The extern flags in rec will have to be set beforehand.
The fields are stored on pages allocated from leaf node
file segment of the index tree.
@param index in: clustered index; MUST be X-latched by mtr
@param b in/out: block containing rec; MUST be X-latched by mtr
@param rec in/out: clustered index record
@param offsets in: rec_get_offsets(rec, index);
the "external storage" flags in offsets will not be adjusted
@param mtr in: mini-transaction that holds x-latch on index and b
@param upd in: TRUE if the record is updated in place (not delete+insert)
@param big in: vector containing fields to be stored externally
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
#ifdef UNIV_DEBUG
# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \
btr_store_big_rec_extern_fields_func(index,b,rec,offsets,mtr,upd,big)
#elif defined UNIV_BLOB_LIGHT_DEBUG
# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \
btr_store_big_rec_extern_fields_func(index,b,rec,offsets,upd,big)
#else
# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \
btr_store_big_rec_extern_fields_func(index,b,rec,offsets,big)
#endif
/*******************************************************************//**
Frees the space in an externally stored field to the file space
management if the field in data is owned the externally stored field,
...
...
storage/innobase/include/buf0buf.h
View file @
be01dbf5
...
...
@@ -480,7 +480,7 @@ buf_reset_check_index_page_at_flush(
/*================================*/
ulint
space
,
/*!< in: space id */
ulint
offset
);
/*!< in: page number */
#if
def UNIV_DEBUG_FILE_ACCESSES
#if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
/********************************************************************//**
Sets file_page_was_freed TRUE if the page is found in the buffer pool.
This function should be called when we free a file page and want the
...
...
@@ -505,7 +505,7 @@ buf_page_reset_file_page_was_freed(
/*===============================*/
ulint
space
,
/*!< in: space id */
ulint
offset
);
/*!< in: page number */
#endif
/* UNIV_DEBUG_FILE_ACCESSES */
#endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
/********************************************************************//**
Reads the freed_page_clock of a buffer block.
@return freed_page_clock */
...
...
@@ -1403,11 +1403,11 @@ struct buf_page_struct{
0 if the block was never accessed
in the buffer pool */
/* @} */
# if
def UNIV_DEBUG_FILE_ACCESSES
# if
defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ibool
file_page_was_freed
;
/*!< this is set to TRUE when fsp
frees a page in buffer pool */
# endif
/* UNIV_DEBUG_FILE_ACCESSES */
# endif
/* UNIV_DEBUG_FILE_ACCESSES
|| UNIV_DEBUG
*/
#endif
/* !UNIV_HOTBACKUP */
};
...
...
storage/innobase/include/row0upd.h
View file @
be01dbf5
...
...
@@ -280,19 +280,29 @@ NOTE: we compare the fields as binary strings!
@return TRUE if update vector changes an ordering field in the index record */
UNIV_INTERN
ibool
row_upd_changes_ord_field_binary
(
/*=============================*/
row_upd_changes_ord_field_binary_func
(
/*==================================*/
dict_index_t
*
index
,
/*!< in: index of the record */
const
upd_t
*
update
,
/*!< in: update vector for the row; NOTE: the
field numbers in this MUST be clustered index
positions! */
#ifdef UNIV_DEBUG
const
que_thr_t
*
thr
,
/*!< in: query thread */
#endif
/* UNIV_DEBUG */
const
dtuple_t
*
row
,
/*!< in: old value of row, or NULL if the
row and the data values in update are not
known when this function is called, e.g., at
compile time */
const
row_ext_t
*
ext
,
/*!< NULL, or prefixes of the externally
const
row_ext_t
*
ext
)
/*!< NULL, or prefixes of the externally
stored columns in the old row */
dict_index_t
*
index
,
/*!< in: index of the record */
const
upd_t
*
update
)
/*!< in: update vector for the row; NOTE: the
field numbers in this MUST be clustered index
positions! */
__attribute__
((
nonnull
(
3
,
4
),
warn_unused_result
));
__attribute__
((
nonnull
(
1
,
2
),
warn_unused_result
));
#ifdef UNIV_DEBUG
# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \
row_upd_changes_ord_field_binary_func(index,update,thr,row,ext)
#else
/* UNIV_DEBUG */
# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \
row_upd_changes_ord_field_binary_func(index,update,row,ext)
#endif
/* UNIV_DEBUG */
/***********************************************************//**
Checks if an update vector changes an ordering field of an index record.
This function is fast if the update vector is short or the number of ordering
...
...
storage/innobase/include/univ.i
View file @
be01dbf5
...
...
@@ -184,14 +184,15 @@ command. Not tested on Windows. */
debugging without UNIV_DEBUG */
#
define
UNIV_BUF_DEBUG
/* Enable buffer pool
debugging without UNIV_DEBUG */
#
define
UNIV_BLOB_LIGHT_DEBUG
/* Enable off-page column
debugging without UNIV_DEBUG */
#
define
UNIV_DEBUG
/* Enable ut_ad() assertions
and disable UNIV_INLINE */
#
define
UNIV_DEBUG_LOCK_VALIDATE
/* Enable
ut_ad(lock_rec_validate_page())
assertions. */
#
define
UNIV_DEBUG_FILE_ACCESSES
/* Debug .ibd file access
(field file_page_was_freed
in buf_page_t) */
#
define
UNIV_DEBUG_FILE_ACCESSES
/* Enable freed block access
debugging without UNIV_DEBUG */
#
define
UNIV_LRU_DEBUG
/* debug the buffer pool LRU */
#
define
UNIV_HASH_DEBUG
/* debug HASH_ macros */
#
define
UNIV_LIST_DEBUG
/* debug UT_LIST_ macros */
...
...
storage/innobase/row/row0ins.c
View file @
be01dbf5
...
...
@@ -2122,7 +2122,7 @@ function_exit:
err
=
btr_store_big_rec_extern_fields
(
index
,
btr_cur_get_block
(
&
cursor
),
rec
,
offsets
,
big_rec
,
&
mtr
);
rec
,
offsets
,
&
mtr
,
FALSE
,
big_rec
);
if
(
modify
)
{
dtuple_big_rec_free
(
big_rec
);
...
...
storage/innobase/row/row0mysql.c
View file @
be01dbf5
...
...
@@ -1939,15 +1939,13 @@ err_exit:
err
=
trx
->
error_state
;
if
(
UNIV_UNLIKELY
(
err
!=
DB_SUCCESS
))
{
switch
(
err
)
{
case
DB_SUCCESS
:
break
;
case
DB_OUT_OF_FILE_SPACE
:
trx
->
error_state
=
DB_SUCCESS
;
trx_general_rollback_for_mysql
(
trx
,
NULL
);
/* TO DO: free table? The code below will dereference
table->name, though. */
}
switch
(
err
)
{
case
DB_OUT_OF_FILE_SPACE
:
ut_print_timestamp
(
stderr
);
fputs
(
" InnoDB: Warning: cannot create table "
,
stderr
);
...
...
@@ -1962,9 +1960,13 @@ err_exit:
break
;
case
DB_DUPLICATE_KEY
:
default:
/* We may also get err == DB_ERROR if the .ibd file for the
table already exists */
trx
->
error_state
=
DB_SUCCESS
;
trx_general_rollback_for_mysql
(
trx
,
NULL
);
dict_mem_table_free
(
table
);
break
;
}
...
...
storage/innobase/row/row0purge.c
View file @
be01dbf5
...
...
@@ -489,8 +489,11 @@ Purges an update of an existing record. Also purges an update of a delete
marked record if that record contained an externally stored field. */
static
void
row_purge_upd_exist_or_extern
(
/*==========================*/
row_purge_upd_exist_or_extern_func
(
/*===============================*/
#ifdef UNIV_DEBUG
const
que_thr_t
*
thr
,
/*!< in: query thread */
#endif
/* UNIV_DEBUG */
purge_node_t
*
node
)
/*!< in: row purge node */
{
mem_heap_t
*
heap
;
...
...
@@ -515,8 +518,8 @@ row_purge_upd_exist_or_extern(
while
(
node
->
index
!=
NULL
)
{
index
=
node
->
index
;
if
(
row_upd_changes_ord_field_binary
(
NULL
,
NULL
,
node
->
index
,
node
->
update
))
{
if
(
row_upd_changes_ord_field_binary
(
node
->
index
,
node
->
update
,
thr
,
NULL
,
NULL
))
{
/* Build the older version of the index entry */
entry
=
row_build_index_entry
(
node
->
row
,
NULL
,
index
,
heap
);
...
...
@@ -598,6 +601,14 @@ skip_secondaries:
}
}
#ifdef UNIV_DEBUG
# define row_purge_upd_exist_or_extern(thr,node) \
row_purge_upd_exist_or_extern_func(thr,node)
#else
/* UNIV_DEBUG */
# define row_purge_upd_exist_or_extern(thr,node) \
row_purge_upd_exist_or_extern_func(node)
#endif
/* UNIV_DEBUG */
/***********************************************************//**
Parses the row reference and other info in a modify undo log record.
@return TRUE if purge operation required: NOTE that then the CALLER
...
...
@@ -704,47 +715,32 @@ err_exit:
/***********************************************************//**
Fetches an undo log record and does the purge for the recorded operation.
If none left, or the current purge completed, returns the control to the
parent node, which is always a query thread node.
@return DB_SUCCESS if operation successfully completed, else error code */
static
ulint
parent node, which is always a query thread node. */
static
__attribute__
((
nonnull
))
void
row_purge
(
/*======*/
purge_node_t
*
node
,
/*!< in: row purge node */
que_thr_t
*
thr
)
/*!< in: query thread */
{
roll_ptr_t
roll_ptr
;
ibool
purge_needed
;
ibool
updated_extern
;
trx_t
*
trx
;
ut_ad
(
node
&&
thr
);
trx
=
thr_get_trx
(
thr
);
ut_ad
(
node
);
ut_ad
(
thr
);
node
->
undo_rec
=
trx_purge_fetch_next_rec
(
&
roll_ptr
,
&
(
node
->
reservation
)
,
node
->
undo_rec
=
trx_purge_fetch_next_rec
(
&
node
->
roll_ptr
,
&
node
->
reservation
,
node
->
heap
);
if
(
!
node
->
undo_rec
)
{
/* Purge completed for this query thread */
thr
->
run_node
=
que_node_get_parent
(
node
);
return
(
DB_SUCCESS
);
}
node
->
roll_ptr
=
roll_ptr
;
if
(
node
->
undo_rec
==
&
trx_purge_dummy_rec
)
{
purge_needed
=
FALSE
;
}
else
{
purge_needed
=
row_purge_parse_undo_rec
(
node
,
&
updated_extern
,
thr
);
/* If purge_needed == TRUE, we must also remember to unfreeze
data dictionary! */
return
;
}
if
(
purge_needed
)
{
if
(
node
->
undo_rec
!=
&
trx_purge_dummy_rec
&&
row_purge_parse_undo_rec
(
node
,
&
updated_extern
,
thr
))
{
node
->
found_clust
=
FALSE
;
node
->
index
=
dict_table_get_next_index
(
...
...
@@ -756,14 +752,14 @@ row_purge(
}
else
if
(
updated_extern
||
node
->
rec_type
==
TRX_UNDO_UPD_EXIST_REC
)
{
row_purge_upd_exist_or_extern
(
node
);
row_purge_upd_exist_or_extern
(
thr
,
node
);
}
if
(
node
->
found_clust
)
{
btr_pcur_close
(
&
(
node
->
pcur
));
}
row_mysql_unfreeze_data_dictionary
(
t
rx
);
row_mysql_unfreeze_data_dictionary
(
t
hr_get_trx
(
thr
)
);
}
/* Do some cleanup */
...
...
@@ -771,8 +767,6 @@ row_purge(
mem_heap_empty
(
node
->
heap
);
thr
->
run_node
=
node
;
return
(
DB_SUCCESS
);
}
/***********************************************************//**
...
...
@@ -786,9 +780,6 @@ row_purge_step(
que_thr_t
*
thr
)
/*!< in: query thread */
{
purge_node_t
*
node
;
#ifdef UNIV_DEBUG
ulint
err
;
#endif
/* UNIV_DEBUG */
ut_ad
(
thr
);
...
...
@@ -796,12 +787,7 @@ row_purge_step(
ut_ad
(
que_node_get_type
(
node
)
==
QUE_NODE_PURGE
);
#ifdef UNIV_DEBUG
err
=
#endif
/* UNIV_DEBUG */
row_purge
(
node
,
thr
);
ut_ad
(
err
==
DB_SUCCESS
);
return
(
thr
);
}
storage/innobase/row/row0umod.c
View file @
be01dbf5
...
...
@@ -173,40 +173,26 @@ row_undo_mod_remove_clust_low(
mtr_t
*
mtr
,
/*!< in: mtr */
ulint
mode
)
/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
{
btr_pcur_t
*
pcur
;
btr_cur_t
*
btr_cur
;
ulint
err
;
ibool
success
;
ut_ad
(
node
->
rec_type
==
TRX_UNDO_UPD_DEL_REC
);
pcur
=
&
(
node
->
pcur
);
btr_cur
=
btr_pcur_get_btr_cur
(
pcur
);
success
=
btr_pcur_restore_position
(
mode
,
pcur
,
mtr
);
/* Find out if the record has been purged already
or if we can remove it. */
if
(
!
success
)
{
if
(
!
btr_pcur_restore_position
(
mode
,
&
node
->
pcur
,
mtr
)
||
row_vers_must_preserve_del_marked
(
node
->
new_trx_id
,
mtr
))
{
return
(
DB_SUCCESS
);
}
/* Find out if we can remove the whole clustered index record */
if
(
node
->
rec_type
==
TRX_UNDO_UPD_DEL_REC
&&
!
row_vers_must_preserve_del_marked
(
node
->
new_trx_id
,
mtr
))
{
/* Ok, we can remove */
}
else
{
return
(
DB_SUCCESS
);
}
btr_cur
=
btr_pcur_get_btr_cur
(
&
node
->
pcur
);
if
(
mode
==
BTR_MODIFY_LEAF
)
{
success
=
btr_cur_optimistic_delete
(
btr_cur
,
mtr
);
if
(
success
)
{
err
=
DB_SUCCESS
;
}
else
{
err
=
DB_FAIL
;
}
err
=
btr_cur_optimistic_delete
(
btr_cur
,
mtr
)
?
DB_SUCCESS
:
DB_FAIL
;
}
else
{
ut_ad
(
mode
==
BTR_MODIFY_TREE
);
...
...
@@ -693,8 +679,9 @@ row_undo_mod_upd_exist_sec(
while
(
node
->
index
!=
NULL
)
{
index
=
node
->
index
;
if
(
row_upd_changes_ord_field_binary
(
node
->
row
,
node
->
ext
,
node
->
index
,
node
->
update
))
{
if
(
row_upd_changes_ord_field_binary
(
node
->
index
,
node
->
update
,
thr
,
node
->
row
,
node
->
ext
))
{
/* Build the newest version of the index entry */
entry
=
row_build_index_entry
(
node
->
row
,
node
->
ext
,
...
...
storage/innobase/row/row0upd.c
View file @
be01dbf5
...
...
@@ -1192,25 +1192,31 @@ NOTE: we compare the fields as binary strings!
@return TRUE if update vector changes an ordering field in the index record */
UNIV_INTERN
ibool
row_upd_changes_ord_field_binary
(
/*=============================*/
row_upd_changes_ord_field_binary_func
(
/*==================================*/
dict_index_t
*
index
,
/*!< in: index of the record */
const
upd_t
*
update
,
/*!< in: update vector for the row; NOTE: the
field numbers in this MUST be clustered index
positions! */
#ifdef UNIV_DEBUG
const
que_thr_t
*
thr
,
/*!< in: query thread */
#endif
/* UNIV_DEBUG */
const
dtuple_t
*
row
,
/*!< in: old value of row, or NULL if the
row and the data values in update are not
known when this function is called, e.g., at
compile time */
const
row_ext_t
*
ext
,
/*!< NULL, or prefixes of the externally
const
row_ext_t
*
ext
)
/*!< NULL, or prefixes of the externally
stored columns in the old row */
dict_index_t
*
index
,
/*!< in: index of the record */
const
upd_t
*
update
)
/*!< in: update vector for the row; NOTE: the
field numbers in this MUST be clustered index
positions! */
{
ulint
n_unique
;
ulint
i
;
const
dict_index_t
*
clust_index
;
ut_ad
(
update
);
ut_ad
(
index
);
ut_ad
(
update
);
ut_ad
(
thr
);
ut_ad
(
thr
->
graph
);
ut_ad
(
thr
->
graph
->
trx
);
n_unique
=
dict_index_get_n_unique
(
index
);
...
...
@@ -1263,9 +1269,14 @@ row_upd_changes_ord_field_binary(
if
(
UNIV_LIKELY_NULL
(
buf
))
{
if
(
UNIV_UNLIKELY
(
buf
==
field_ref_zero
))
{
/* This should never happen, but
we try to fail safe here. */
ut_ad
(
0
);
/* The externally stored field
was not written yet. This
record should only be seen by
recv_recovery_rollback_active(),
when the server had crashed before
storing the field. */
ut_ad
(
thr
->
graph
->
trx
->
is_recovered
);
ut_ad
(
trx_is_recv
(
thr
->
graph
->
trx
));
return
(
TRUE
);
}
...
...
@@ -1640,8 +1651,8 @@ row_upd_sec_step(
ut_ad
(
!
dict_index_is_clust
(
node
->
index
));
if
(
node
->
state
==
UPD_NODE_UPDATE_ALL_SEC
||
row_upd_changes_ord_field_binary
(
node
->
row
,
node
->
ext
,
node
->
index
,
node
->
update
))
{
||
row_upd_changes_ord_field_binary
(
node
->
index
,
node
->
update
,
thr
,
node
->
row
,
node
->
ext
))
{
return
(
row_upd_sec_index_entry
(
node
,
thr
));
}
...
...
@@ -1971,7 +1982,7 @@ row_upd_clust_rec(
index
,
btr_cur_get_block
(
btr_cur
),
rec
,
rec_get_offsets
(
rec
,
index
,
offsets_
,
ULINT_UNDEFINED
,
&
heap
),
big_rec
,
mtr
);
mtr
,
TRUE
,
big_rec
);
mtr_commit
(
mtr
);
}
...
...
@@ -2169,8 +2180,8 @@ exit_func:
row_upd_store_row
(
node
);
if
(
row_upd_changes_ord_field_binary
(
node
->
row
,
node
->
ext
,
index
,
node
->
update
))
{
if
(
row_upd_changes_ord_field_binary
(
index
,
node
->
update
,
thr
,
node
->
row
,
node
->
ext
))
{
/* Update causes an ordering field (ordering fields within
the B-tree) of the clustered index record to change: perform
...
...
storage/innobase/srv/srv0start.c
View file @
be01dbf5
...
...
@@ -1327,13 +1327,16 @@ innobase_start_or_create_for_mysql(void)
ut_a
(
srv_n_file_io_threads
<=
SRV_MAX_N_IO_THREADS
);
/* TODO: Investigate if SRV_N_PENDING_IOS_PER_THREAD (32) limit
still applies to windows. */
if
(
!
srv_use_native_aio
)
{
io_limit
=
8
*
SRV_N_PENDING_IOS_PER_THREAD
;
}
else
{
io_limit
=
8
*
SRV_N_PENDING_IOS_PER_THREAD
;
/* On Windows when using native aio the number of aio requests
that a thread can handle at a given time is limited to 32
i.e.: SRV_N_PENDING_IOS_PER_THREAD */
# ifdef __WIN__
if
(
srv_use_native_aio
)
{
io_limit
=
SRV_N_PENDING_IOS_PER_THREAD
;
}
# endif
/* __WIN__ */
os_aio_init
(
io_limit
,
srv_n_read_io_threads
,
...
...
storage/innobase/trx/trx0roll.c
View file @
be01dbf5
...
...
@@ -47,8 +47,8 @@ Created 3/26/1996 Heikki Tuuri
rollback */
#define TRX_ROLL_TRUNC_THRESHOLD 1
/** In crash recovery, the current trx to be rolled back */
static
trx_t
*
trx_roll_crash_recv_trx
=
NULL
;
/** In crash recovery, the current trx to be rolled back
; NULL otherwise
*/
static
const
trx_t
*
trx_roll_crash_recv_trx
=
NULL
;
/** In crash recovery we set this to the undo n:o of the current trx to be
rolled back. Then we can print how many % the rollback has progressed. */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment