Commit a9ae7311 authored by marko's avatar marko

branches/zip: Implement the configuration parameter and settable global

variable innodb_file_format.  Implement file format version stamping of
*.ibd files and SYS_TABLES.TYPE.

This change breaks introduces an incompatible change for for
compressed tables.  We can do this, as we have not released yet.

innodb-zip.test: Add tests for stricter KEY_BLOCK_SIZE and ROW_FORMAT
checks.

DICT_TF_COMPRESSED_MASK, DICT_TF_COMPRESSED_SHIFT: Replace with
DICT_TF_ZSSIZE_MASK, DICT_TF_ZSSIZE_SHIFT.

DICT_TF_FORMAT_MASK, DICT_TF_FORMAT_SHIFT, DICT_TF_FORMAT_51,
DICT_TF_FORMAT_ZIP: File format version, stored in table->flags,
in the .ibd file header, and in SYS_TABLES.TYPE.

dict_create_sys_tables_tuple(): Write the table flags to SYS_TABLES.TYPE
if the format is at least DICT_TF_FORMAT_ZIP.  For old formats
(DICT_TF_FORMAT_51), write DICT_TABLE_ORDINARY as the table type.

DB_TABLE_ZIP_NO_IBD: Remove the error code.  The error handling is done
in ha_innodb.cc; as a failsafe measure, dict_build_table_def_step() will
silently clear the compression and format flags instead of returning this
error.

dict_mem_table_create(): Assert that no extra bits are set in the flags.

dict_sys_tables_get_zip_size(): Rename to dict_sys_tables_get_flags().
Check all flag bits, and return ULINT_UNDEFINED if the combination is
unsupported.

dict_boot(): Document the SYS_TABLES columns N_COLS and TYPE.

dict_table_get_format(), dict_table_set_format(),
dict_table_flags_to_zip_size(): New accessors to table->flags.

dtuple_convert_big_rec(): Introduce the auxiliary variables
local_len, local_prefix_len.  Store a 768-byte prefix locally
if the file format is less than DICT_TF_FORMAT_ZIP.

dtuple_convert_back_big_rec(): Restore the columns.

srv_file_format: New variable: innodb_file_format.

fil_create_new_single_table_tablespace(): Replace the parameter zip_size
with table->flags.

fil_open_single_table_tablespace(): Replace the parameter zip_size_in_k
with table->flags.  Check the flags.

fil_space_struct, fil_space_create(), fil_op_write_log():
Replace zip_size with flags.

fil_node_open_file(): Note a TODO item for InnoDB Hot Backup.
Check that the tablespace flags match.

fil_space_get_zip_size(): Rename to fil_space_get_flags().  Add a
wrapper for fil_space_get_zip_size().

fsp_header_get_flags(): New function.

fsp_header_init_fields(): Replace zip_size with flags.

FSP_SPACE_FLAGS: New name for the tablespace flags.  This field used
to be called FSP_PAGE_ZIP_SIZE, or FSP_LOWEST_NO_WRITE.  It has always
been written as 0 in MySQL/InnoDB versions 4.1 to 5.1.

MLOG_ZIP_FILE_CREATE: Rename to MLOG_FILE_CREATE2.  Add a 32-bit
parameter for the tablespace flags.

ha_innobase::create(): Check the table attributes ROW_FORMAT and
KEY_BLOCK_SIZE.  Issue errors if they are inappropriate, or warnings
if the inherited attributes (in ALTER TABLE) will be ignored.

PAGE_ZIP_MIN_SIZE_SHIFT: New constant: the 2-logarithm of PAGE_ZIP_MIN_SIZE.
parent 7d465216
......@@ -561,11 +561,21 @@ dtuple_convert_big_rec(
dict_field_t* ifield;
ulint size;
ulint n_fields;
ulint local_len;
ulint local_prefix_len;
if (UNIV_UNLIKELY(!dict_index_is_clust(index))) {
return(NULL);
}
if (dict_table_get_format(index->table) < DICT_TF_FORMAT_ZIP) {
/* up to MySQL 5.1: store a 768-byte prefix locally */
local_len = BTR_EXTERN_FIELD_REF_SIZE + DICT_MAX_INDEX_COL_LEN;
} else {
/* new-format table: do not store any BLOB prefix locally */
local_len = BTR_EXTERN_FIELD_REF_SIZE;
}
ut_a(dtuple_check_typed_no_assert(entry));
size = rec_get_converted_size(index, entry, *n_ext);
......@@ -601,6 +611,8 @@ dtuple_convert_big_rec(
ulint i;
ulint longest = 0;
ulint longest_i = ULINT_MAX;
byte* data;
big_rec_field_t* b;
for (i = dict_index_get_n_unique_in_tree(index);
i < dtuple_get_n_fields(entry); i++) {
......@@ -615,13 +627,13 @@ dtuple_convert_big_rec(
if (ifield->fixed_len
|| dfield_is_null(dfield)
|| dfield_is_ext(dfield)
|| dfield_get_len(dfield) <= local_len
|| dfield_get_len(dfield)
<= BTR_EXTERN_FIELD_REF_SIZE * 2) {
goto skip_field;
}
savings = dfield_get_len(dfield)
- BTR_EXTERN_FIELD_REF_SIZE;
savings = dfield_get_len(dfield) - local_len;
/* Check that there would be savings */
if (longest >= savings) {
......@@ -651,25 +663,32 @@ skip_field:
dfield = dtuple_get_nth_field(entry, longest_i);
ifield = dict_index_get_nth_field(index, longest_i);
vector->fields[n_fields].field_no = longest_i;
local_prefix_len = local_len - BTR_EXTERN_FIELD_REF_SIZE;
vector->fields[n_fields].len = dfield_get_len(dfield);
b = &vector->fields[n_fields];
b->field_no = longest_i;
b->len = dfield_get_len(dfield) - local_prefix_len;
b->data = (char*) dfield_get_data(dfield) + local_prefix_len;
vector->fields[n_fields].data = dfield_get_data(dfield);
/* Allocate the locally stored part of the column. */
data = mem_heap_alloc(heap, local_len);
/* Set the extern field reference in dfield to zero */
dfield_set_data(dfield,
mem_heap_zalloc(heap,
BTR_EXTERN_FIELD_REF_SIZE),
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_ext(dfield);
/* Copy the local prefix. */
memcpy(data, dfield_get_data(dfield), local_prefix_len);
/* Clear the extern field reference (BLOB pointer). */
memset(data + local_prefix_len, 0, BTR_EXTERN_FIELD_REF_SIZE);
#if 0
/* The following would fail the Valgrind checks in
page_cur_insert_rec_low() and page_cur_insert_rec_zip().
The BLOB pointers in the record will be initialized after
the record and the BLOBs have been written. */
UNIV_MEM_ALLOC(dfield->data, BTR_EXTERN_FIELD_REF_SIZE);
UNIV_MEM_ALLOC(data + local_prefix_len,
BTR_EXTERN_FIELD_REF_SIZE);
#endif
dfield_set_data(dfield, data, local_len);
dfield_set_ext(dfield);
n_fields++;
(*n_ext)++;
ut_ad(n_fields < dtuple_get_n_fields(entry));
......@@ -692,16 +711,26 @@ dtuple_convert_back_big_rec(
big_rec_t* vector) /* in, own: big rec vector; it is
freed in this function */
{
big_rec_field_t* b = vector->fields;
const big_rec_field_t* const end = b + vector->n_fields;
for (; b < end; b++) {
dfield_t* dfield;
ulint i;
ulint local_len;
for (i = 0; i < vector->n_fields; i++) {
dfield = dtuple_get_nth_field(entry, b->field_no);
local_len = dfield_get_len(dfield);
dfield = dtuple_get_nth_field(entry,
vector->fields[i].field_no);
ut_ad(dfield_is_ext(dfield));
ut_ad(local_len >= BTR_EXTERN_FIELD_REF_SIZE);
local_len -= BTR_EXTERN_FIELD_REF_SIZE;
ut_ad(local_len <= DICT_MAX_INDEX_COL_LEN);
dfield_set_data(dfield,
vector->fields[i].data, vector->fields[i].len);
(char*) b->data - local_len,
b->len + local_len);
}
mem_heap_free(vector->heap);
......
......@@ -252,7 +252,10 @@ dict_boot(void)
dict_mem_table_add_col(table, heap, "NAME", DATA_BINARY, 0, 0);
dict_mem_table_add_col(table, heap, "ID", DATA_BINARY, 0, 0);
/* ROW_FORMAT = (N_COLS >> 31) ? COMPACT : REDUNDANT */
dict_mem_table_add_col(table, heap, "N_COLS", DATA_INT, 0, 4);
/* TYPE is either DICT_TABLE_ORDINARY, or (TYPE & DICT_TF_COMPACT)
and (TYPE & DICT_TF_FORMAT_MASK) are nonzero and TYPE = table->flags */
dict_mem_table_add_col(table, heap, "TYPE", DATA_INT, 0, 4);
dict_mem_table_add_col(table, heap, "MIX_ID", DATA_BINARY, 0, 0);
dict_mem_table_add_col(table, heap, "MIX_LEN", DATA_INT, 0, 4);
......
......@@ -77,11 +77,13 @@ dict_create_sys_tables_tuple(
dfield = dtuple_get_nth_field(entry, 3);
ptr = mem_heap_alloc(heap, 4);
if (table->flags & DICT_TF_COMPRESSED_MASK) {
if (table->flags & ~DICT_TF_COMPACT) {
ut_a(table->flags & DICT_TF_COMPACT);
mach_write_to_4(ptr, DICT_TABLE_COMPRESSED_BASE
+ ((table->flags & DICT_TF_COMPRESSED_MASK)
>> DICT_TF_COMPRESSED_SHIFT));
ut_a(dict_table_get_format(table) >= DICT_TF_FORMAT_ZIP);
ut_a((table->flags & DICT_TF_ZSSIZE_MASK)
<= (DICT_TF_ZSSIZE_MAX << DICT_TF_ZSSIZE_SHIFT));
ut_a(!(table->flags & (~0 << DICT_TF_BITS)));
mach_write_to_4(ptr, table->flags);
} else {
mach_write_to_4(ptr, DICT_TABLE_ORDINARY);
}
......@@ -255,9 +257,13 @@ dict_build_table_def_step(
is_path = FALSE;
}
ut_ad(dict_table_get_format(table) <= DICT_TF_FORMAT_MAX);
ut_ad(!dict_table_zip_size(table)
|| dict_table_get_format(table) >= DICT_TF_FORMAT_ZIP);
error = fil_create_new_single_table_tablespace(
&space, path_or_name, is_path,
dict_table_zip_size(table),
table->flags,
FIL_IBD_FILE_INITIAL_SIZE);
table->space = (unsigned int) space;
......@@ -272,10 +278,8 @@ dict_build_table_def_step(
mtr_commit(&mtr);
} else {
/* Create in the system tablespace: disallow compression */
if (table->flags & DICT_TF_COMPRESSED_MASK) {
return(DB_TABLE_ZIP_NO_IBD);
}
/* Create in the system tablespace: disallow new features */
table->flags &= DICT_TF_COMPACT;
}
row = dict_create_sys_tables_tuple(table, node->heap);
......
......@@ -223,11 +223,11 @@ loop:
}
/************************************************************************
Determine the compressed page size of a table described in SYS_TABLES. */
Determine the flags of a table described in SYS_TABLES. */
static
ulint
dict_sys_tables_get_zip_size(
/*=========================*/
dict_sys_tables_get_flags(
/*======================*/
/* out: compressed page size in kilobytes;
or 0 if the tablespace is uncompressed,
ULINT_UNDEFINED on error */
......@@ -236,29 +236,53 @@ dict_sys_tables_get_zip_size(
const byte* field;
ulint len;
ulint n_cols;
ulint table_type;
ulint flags;
field = rec_get_nth_field_old(rec, 5, &len);
ut_a(len == 4);
table_type = mach_read_from_4(field);
flags = mach_read_from_4(field);
if (UNIV_LIKELY(flags == DICT_TABLE_ORDINARY)) {
return(0);
}
field = rec_get_nth_field_old(rec, 4, &len);
n_cols = mach_read_from_4(field);
if (UNIV_EXPECT(n_cols & 0x80000000UL, 0x80000000UL)
&& UNIV_LIKELY(table_type > DICT_TABLE_COMPRESSED_BASE)
&& UNIV_LIKELY(table_type
<= DICT_TABLE_COMPRESSED_BASE + 16)) {
if (UNIV_UNLIKELY(!(n_cols & 0x80000000UL))) {
/* New file formats require ROW_FORMAT=COMPACT. */
return(ULINT_UNDEFINED);
}
switch (flags & (DICT_TF_FORMAT_MASK | DICT_TF_COMPACT)) {
default:
case DICT_TF_FORMAT_51 << DICT_TF_FORMAT_SHIFT:
case DICT_TF_FORMAT_51 << DICT_TF_FORMAT_SHIFT | DICT_TF_COMPACT:
/* flags should be DICT_TABLE_ORDINARY,
or DICT_TF_FORMAT_MASK should be nonzero. */
return(ULINT_UNDEFINED);
return(table_type - DICT_TABLE_COMPRESSED_BASE);
case DICT_TF_FORMAT_ZIP << DICT_TF_FORMAT_SHIFT | DICT_TF_COMPACT:
#if DICT_TF_FORMAT_MAX > DICT_TF_FORMAT_ZIP
# error "missing case labels for DICT_TF_FORMAT_ZIP .. DICT_TF_FORMAT_MAX"
#endif
/* We support this format. */
break;
}
if (UNIV_LIKELY(table_type == DICT_TABLE_ORDINARY)) {
return(0);
if (UNIV_UNLIKELY((flags & DICT_TF_ZSSIZE_MASK)
> (DICT_TF_ZSSIZE_MAX << DICT_TF_ZSSIZE_SHIFT))) {
/* Unsupported compressed page size. */
return(ULINT_UNDEFINED);
}
if (UNIV_UNLIKELY(flags & (~0 << DICT_TF_BITS))) {
/* Some unused bits are set. */
return(ULINT_UNDEFINED);
}
return(flags);
}
/************************************************************************
......@@ -321,14 +345,28 @@ loop:
const byte* field;
ulint len;
ulint space_id;
ulint zip_size_in_k;
ulint flags;
char* name;
field = rec_get_nth_field_old(rec, 0, &len);
name = mem_strdupl((char*) field, len);
zip_size_in_k = dict_sys_tables_get_zip_size(rec);
ut_a(zip_size_in_k != ULINT_UNDEFINED);
flags = dict_sys_tables_get_flags(rec);
if (UNIV_UNLIKELY(flags == ULINT_UNDEFINED)) {
field = rec_get_nth_field_old(rec, 5, &len);
flags = mach_read_from_4(field);
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
ut_print_filename(stderr, name);
fprintf(stderr, "\n"
"InnoDB: in InnoDB data dictionary"
" has unknown type %lx.\n",
(ulong) flags);
goto loop;
}
field = rec_get_nth_field_old(rec, 9, &len);
ut_a(len == 4);
......@@ -352,8 +390,7 @@ loop:
object and check that the .ibd file exists. */
fil_open_single_table_tablespace(FALSE, space_id,
zip_size_in_k * 1024,
name);
flags, name);
}
mem_free(name);
......@@ -784,7 +821,6 @@ dict_load_table(
ulint n_cols;
ulint flags;
ulint err;
ulint zip_size_in_k;
mtr_t mtr;
ut_ad(mutex_own(&(dict_sys->mutex)));
......@@ -833,8 +869,22 @@ err_exit:
/* Check if the tablespace exists and has the right name */
if (space != 0) {
zip_size_in_k = dict_sys_tables_get_zip_size(rec);
ut_a(zip_size_in_k != ULINT_UNDEFINED);
flags = dict_sys_tables_get_flags(rec);
if (UNIV_UNLIKELY(flags == ULINT_UNDEFINED)) {
unknown_table_type:
field = rec_get_nth_field_old(rec, 5, &len);
flags = mach_read_from_4(field);
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
ut_print_filename(stderr, name);
fprintf(stderr, "\n"
"InnoDB: in InnoDB data dictionary"
" has unknown type %lx.\n",
(ulong) flags);
goto err_exit;
}
if (fil_space_for_table_exists_in_mem(space, name, FALSE,
FALSE, FALSE)) {
......@@ -853,7 +903,7 @@ err_exit:
name, (ulong)space);
/* Try to open the tablespace */
if (!fil_open_single_table_tablespace(
TRUE, space, zip_size_in_k << 10, name)) {
TRUE, space, flags, name)) {
/* We failed to find a sensible tablespace
file */
......@@ -861,7 +911,7 @@ err_exit:
}
}
} else {
zip_size_in_k = 0;
flags = 0;
}
ut_a(name_of_col_is(sys_tables, sys_index, 4, "N_COLS"));
......@@ -869,11 +919,12 @@ err_exit:
field = rec_get_nth_field_old(rec, 4, &len);
n_cols = mach_read_from_4(field);
flags = zip_size_in_k << DICT_TF_COMPRESSED_SHIFT;
/* The high-order bit of N_COLS is the "compact format" flag. */
if (n_cols & 0x80000000UL) {
flags |= DICT_TF_COMPACT;
} else if (UNIV_UNLIKELY(flags)) {
/* Only ROW_FORMAT=COMPACT tables can have flags set. */
goto unknown_table_type;
}
table = dict_mem_table_create(name, space, n_cols & ~0x80000000UL,
......@@ -886,17 +937,6 @@ err_exit:
field = rec_get_nth_field_old(rec, 3, &len);
table->id = mach_read_from_8(field);
zip_size_in_k = dict_sys_tables_get_zip_size(rec);
if (UNIV_UNLIKELY(zip_size_in_k == ULINT_UNDEFINED)) {
field = rec_get_nth_field_old(rec, 5, &len);
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: table %s: unknown table type %lu\n",
name, (ulong) mach_read_from_4(field));
goto err_exit;
}
btr_pcur_close(&pcur);
mtr_commit(&mtr);
......
......@@ -42,6 +42,7 @@ dict_mem_table_create(
mem_heap_t* heap;
ut_ad(name);
ut_a(!(flags & (~0 << DICT_TF_BITS)));
heap = mem_heap_create(DICT_HEAP_SIZE);
......
......@@ -167,8 +167,8 @@ struct fil_space_struct {
tablespace whose size we do not know yet;
last incomplete megabytes in data files may be
ignored if space == 0 */
ulint zip_size;/* compressed page size in bytes; 0
if the pages are not compressed */
ulint flags; /* in: compressed page size
and file format, or 0 */
ulint n_reserved_extents;
/* number of reserved free extents for
ongoing operations like B-tree page split */
......@@ -428,8 +428,7 @@ fil_space_get_latch(
/*================*/
/* out: latch protecting storage allocation */
ulint id, /* in: space id */
ulint* zip_size)/* out: compressed page size, or
0 for uncompressed tablespaces */
ulint* flags) /* out: tablespace flags */
{
fil_system_t* system = fil_system;
fil_space_t* space;
......@@ -442,8 +441,8 @@ fil_space_get_latch(
ut_a(space);
if (zip_size) {
*zip_size = space->zip_size;
if (flags) {
*flags = space->flags;
}
mutex_exit(&(system->mutex));
......@@ -616,7 +615,7 @@ fil_node_open_file(
byte* buf2;
byte* page;
ulint space_id;
ulint zip_size;
ulint flags;
#endif /* !UNIV_HOTBACKUP */
ut_ad(mutex_own(&(system->mutex)));
......@@ -654,7 +653,7 @@ fil_node_open_file(
+ (ib_longlong)size_low;
#ifdef UNIV_HOTBACKUP
node->size = (ulint) (size_bytes / UNIV_PAGE_SIZE);
/* TODO: adjust to zip_size, like below? */
#else
ut_a(space->purpose != FIL_LOG);
ut_a(space->id != 0);
......@@ -684,7 +683,7 @@ fil_node_open_file(
success = os_file_read(node->handle, page, 0, 0,
UNIV_PAGE_SIZE);
space_id = fsp_header_get_space_id(page);
zip_size = fsp_header_get_zip_size(page);
flags = fsp_header_get_flags(page);
ut_free(buf2);
......@@ -692,31 +691,32 @@ fil_node_open_file(
os_file_close(node->handle);
if (space_id == ULINT_UNDEFINED || space_id == 0) {
fprintf(stderr,
"InnoDB: Error: tablespace id %lu"
" in file %s is not sensible\n",
(ulong) space_id, node->name);
ut_a(0);
}
if (space_id != space->id) {
if (UNIV_UNLIKELY(space_id != space->id)) {
fprintf(stderr,
"InnoDB: Error: tablespace id is %lu"
" in the data dictionary\n"
"InnoDB: but in file %s it is %lu!\n",
space->id, node->name, space_id);
ut_a(0);
ut_error;
}
if (UNIV_UNLIKELY(zip_size != space->zip_size)) {
if (UNIV_UNLIKELY(space_id == ULINT_UNDEFINED
|| space_id == 0)) {
fprintf(stderr,
"InnoDB: Error: compressed page size is %lu"
"InnoDB: Error: tablespace id %lu"
" in file %s is not sensible\n",
(ulong) space_id, node->name);
ut_error;
}
if (UNIV_UNLIKELY(space->flags != flags)) {
fprintf(stderr,
"InnoDB: Error: table flags are %lx"
" in the data dictionary\n"
"InnoDB: but in file %s it is %lu!\n",
space->zip_size, node->name, zip_size);
"InnoDB: but the flags in file %s are %lx!\n",
space->flags, node->name, flags);
ut_error;
}
......@@ -726,10 +726,12 @@ fil_node_open_file(
size_bytes = ut_2pow_round(size_bytes, 1024 * 1024);
}
if (!zip_size) {
if (!(flags & DICT_TF_ZSSIZE_MASK)) {
node->size = (ulint) (size_bytes / UNIV_PAGE_SIZE);
} else {
node->size = (ulint) (size_bytes / zip_size);
node->size = (ulint)
(size_bytes
/ dict_table_flags_to_zip_size(flags));
}
#endif
space->size += node->size;
......@@ -1071,8 +1073,8 @@ fil_space_create(
/* out: TRUE if success */
const char* name, /* in: space name */
ulint id, /* in: space id */
ulint zip_size,/* in: compressed page size, or
0 for uncompressed tablespaces */
ulint flags, /* in: compressed page size
and file format, or 0 */
ulint purpose)/* in: FIL_TABLESPACE, or FIL_LOG if log */
{
fil_system_t* system = fil_system;
......@@ -1173,7 +1175,7 @@ try_again:
space->is_being_deleted = FALSE;
space->purpose = purpose;
space->size = 0;
space->zip_size = zip_size;
space->flags = flags;
space->n_reserved_extents = 0;
......@@ -1400,20 +1402,19 @@ fil_space_get_size(
}
/***********************************************************************
Returns the compressed page size of the space, or 0 if the space
is not compressed. The tablespace must be cached in the memory cache. */
Returns the flags of the space. The tablespace must be cached
in the memory cache. */
UNIV_INTERN
ulint
fil_space_get_zip_size(
/*===================*/
/* out: compressed page size, ULINT_UNDEFINED
if space not found */
fil_space_get_flags(
/*================*/
/* out: flags, ULINT_UNDEFINED if space not found */
ulint id) /* in: space id */
{
fil_system_t* system = fil_system;
fil_node_t* node;
fil_space_t* space;
ulint size;
ulint flags;
ut_ad(system);
......@@ -1446,11 +1447,34 @@ fil_space_get_zip_size(
fil_node_complete_io(node, system, OS_FILE_READ);
}
size = space->zip_size;
flags = space->flags;
mutex_exit(&(system->mutex));
return(size);
return(flags);
}
/***********************************************************************
Returns the compressed page size of the space, or 0 if the space
is not compressed. The tablespace must be cached in the memory cache. */
UNIV_INTERN
ulint
fil_space_get_zip_size(
/*===================*/
/* out: compressed page size, ULINT_UNDEFINED
if space not found */
ulint id) /* in: space id */
{
ulint flags;
flags = fil_space_get_flags(id);
if (flags && flags != ULINT_UNDEFINED) {
return(dict_table_flags_to_zip_size(flags));
}
return(flags);
}
/***********************************************************************
......@@ -1918,12 +1942,13 @@ void
fil_op_write_log(
/*=============*/
ulint type, /* in: MLOG_FILE_CREATE,
MLOG_ZIP_FILE_CREATE,
MLOG_FILE_CREATE2,
MLOG_FILE_DELETE, or
MLOG_FILE_RENAME */
ulint space_id, /* in: space id */
ulint zip_size, /* in: compressed page size
if type==MLOG_ZIP_FILE_CREATE */
ulint flags, /* in: compressed page size
and file format
if type==MLOG_FILE_CREATE2, or 0 */
const char* name, /* in: table name in the familiar
'databasename/tablename' format, or
the file path in the case of
......@@ -1946,10 +1971,9 @@ fil_op_write_log(
log_ptr = mlog_write_initial_log_record_for_file_op(type, space_id, 0,
log_ptr, mtr);
if (type == MLOG_ZIP_FILE_CREATE) {
ut_a(zip_size && !(zip_size % 1024) && zip_size <= 16384);
mach_write_to_1(log_ptr, zip_size >> 10);
log_ptr++;
if (type == MLOG_FILE_CREATE2) {
mach_write_to_4(log_ptr, flags);
log_ptr += 4;
}
/* Let us store the strings as null-terminated for easier readability
and handling */
......@@ -2007,16 +2031,16 @@ fil_op_log_parse_or_replay(
ulint new_name_len;
const char* name;
const char* new_name = NULL;
ulint zip_size = 0;
ulint flags = 0;
if (type == MLOG_ZIP_FILE_CREATE) {
if (end_ptr < ptr + 1) {
if (type == MLOG_FILE_CREATE2) {
if (end_ptr < ptr + 4) {
return(NULL);
}
zip_size = mach_read_from_1(ptr) << 10;
ptr++;
flags = mach_read_from_4(ptr);
ptr += 4;
}
if (end_ptr < ptr + 2) {
......@@ -2115,7 +2139,7 @@ fil_op_log_parse_or_replay(
break;
case MLOG_FILE_CREATE:
case MLOG_ZIP_FILE_CREATE:
case MLOG_FILE_CREATE2:
if (fil_tablespace_exists_in_mem(space_id)) {
/* Do nothing */
} else if (fil_get_space_id_for_table(name)
......@@ -2127,7 +2151,7 @@ fil_op_log_parse_or_replay(
fil_create_directory_for_tablename(name);
if (fil_create_new_single_table_tablespace(
&space_id, name, FALSE, zip_size,
&space_id, name, FALSE, flags,
FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) {
ut_error;
}
......@@ -2582,8 +2606,8 @@ fil_create_new_single_table_tablespace(
table */
ibool is_temp, /* in: TRUE if a table created with
CREATE TEMPORARY TABLE */
ulint zip_size, /* in: compressed page size,
or 0 if uncompressed tablespace */
ulint flags, /* in: compressed page size and
file format version, or 0 */
ulint size) /* in: the initial size of the
tablespace file in pages,
must be >= FIL_IBD_FILE_INITIAL_SIZE */
......@@ -2644,7 +2668,7 @@ fil_create_new_single_table_tablespace(
return(DB_ERROR);
}
buf2 = ut_malloc(2 * UNIV_PAGE_SIZE + zip_size);
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);
......@@ -2687,14 +2711,20 @@ error_exit2:
memset(page, '\0', UNIV_PAGE_SIZE);
fsp_header_init_fields(page, *space_id, zip_size);
fsp_header_init_fields(page, *space_id, flags);
mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, *space_id);
if (!zip_size) {
if (!(flags & DICT_TF_ZSSIZE_MASK)) {
buf_flush_init_for_writing(page, NULL, 0);
ret = os_file_write(path, file, page, 0, 0, UNIV_PAGE_SIZE);
} else {
page_zip_des_t page_zip;
ulint zip_size;
zip_size = ((PAGE_ZIP_MIN_SIZE >> 1)
<< ((flags & DICT_TF_ZSSIZE_MASK)
>> DICT_TF_ZSSIZE_SHIFT));
page_zip_set_size(&page_zip, zip_size);
page_zip.data = page + UNIV_PAGE_SIZE;
#ifdef UNIV_DEBUG
......@@ -2731,7 +2761,7 @@ error_exit2:
goto error_exit2;
}
success = fil_space_create(path, *space_id, zip_size, FIL_TABLESPACE);
success = fil_space_create(path, *space_id, flags, FIL_TABLESPACE);
if (!success) {
goto error_exit2;
......@@ -2745,10 +2775,10 @@ error_exit2:
mtr_start(&mtr);
fil_op_write_log(zip_size
? MLOG_ZIP_FILE_CREATE
fil_op_write_log(flags
? MLOG_FILE_CREATE2
: MLOG_FILE_CREATE,
*space_id, zip_size,
*space_id, flags,
tablename, NULL, &mtr);
mtr_commit(&mtr);
......@@ -2943,8 +2973,7 @@ fil_open_single_table_tablespace(
faster (the OS caches them) than
accessing the first page of the file */
ulint id, /* in: space id */
ulint zip_size, /* in: compressed page size,
or 0 if uncompressed tablespace */
ulint flags, /* in: tablespace flags */
const char* name) /* in: table name in the
databasename/tablename format */
{
......@@ -2954,6 +2983,7 @@ fil_open_single_table_tablespace(
byte* buf2;
byte* page;
ulint space_id;
ulint space_flags;
ibool ret = TRUE;
filepath = fil_make_ibd_name(name, FALSE);
......@@ -3002,19 +3032,21 @@ fil_open_single_table_tablespace(
success = os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE);
/* We have to read the tablespace id from the file */
/* We have to read the tablespace id and flags from the file. */
space_id = fsp_header_get_space_id(page);
space_flags = fsp_header_get_flags(page);
ut_free(buf2);
if (space_id != id) {
if (UNIV_UNLIKELY(space_id != id || space_flags != flags)) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: tablespace id in file ", stderr);
fputs(" InnoDB: Error: tablespace id and flags in file ",
stderr);
ut_print_filename(stderr, filepath);
fprintf(stderr, " is %lu, but in the InnoDB\n"
"InnoDB: data dictionary it is %lu.\n"
fprintf(stderr, " are %lu and %lu, but in the InnoDB\n"
"InnoDB: data dictionary they are %lu and %lu.\n"
"InnoDB: Have you moved InnoDB .ibd files"
" around without using the\n"
"InnoDB: commands DISCARD TABLESPACE and"
......@@ -3023,7 +3055,8 @@ fil_open_single_table_tablespace(
"InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
"innodb-troubleshooting.html\n"
"InnoDB: for how to resolve the issue.\n",
(ulong) space_id, (ulong) id);
(ulong) space_id, (ulong) space_flags,
(ulong) id, (ulong) flags);
ret = FALSE;
......@@ -3031,8 +3064,7 @@ fil_open_single_table_tablespace(
}
skip_check:
success = fil_space_create(filepath, space_id, zip_size,
FIL_TABLESPACE);
success = fil_space_create(filepath, space_id, flags, FIL_TABLESPACE);
if (!success) {
goto func_exit;
......@@ -3088,7 +3120,7 @@ fil_load_single_table_tablespace(
byte* buf2;
byte* page;
ulint space_id;
ulint zip_size;
ulint flags;
ulint size_low;
ulint size_high;
ib_longlong size;
......@@ -3239,10 +3271,10 @@ fil_load_single_table_tablespace(
/* We have to read the tablespace id from the file */
space_id = fsp_header_get_space_id(page);
zip_size = fsp_header_get_zip_size(page);
flags = fsp_header_get_flags(page);
} else {
space_id = ULINT_UNDEFINED;
zip_size = 0;
flags = 0;
}
#ifndef UNIV_HOTBACKUP
......@@ -3319,8 +3351,7 @@ fil_load_single_table_tablespace(
}
mutex_exit(&(fil_system->mutex));
#endif
success = fil_space_create(filepath, space_id, zip_size,
FIL_TABLESPACE);
success = fil_space_create(filepath, space_id, flags, FIL_TABLESPACE);
if (!success) {
......@@ -3838,7 +3869,7 @@ fil_extend_space_to_desired_size(
return(TRUE);
}
page_size = space->zip_size;
page_size = dict_table_flags_to_zip_size(space->flags);
if (!page_size) {
page_size = UNIV_PAGE_SIZE;
}
......
......@@ -60,9 +60,7 @@ descriptor page, but used only in the first. */
about the first extent, but have not
physically allocted those pages to the
file */
#define FSP_PAGE_ZIP_SIZE 16 /* The size of the compressed page
in bytes, or 0 for uncompressed
tablespaces */
#define FSP_SPACE_FLAGS 16 /* table->flags & ~DICT_TF_COMPACT */
#define FSP_FRAG_N_USED 20 /* number of used pages in the
FSP_FREE_FRAG list */
#define FSP_FREE 24 /* list of free extents */
......@@ -351,7 +349,8 @@ fsp_get_space_header(
buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
#endif /* UNIV_SYNC_DEBUG */
ut_ad(id == mach_read_from_4(FSP_SPACE_ID + header));
ut_ad(zip_size == mach_read_from_4(FSP_PAGE_ZIP_SIZE + header));
ut_ad(zip_size == dict_table_flags_to_zip_size(
mach_read_from_4(FSP_SPACE_FLAGS + header)));
return(header);
}
......@@ -704,7 +703,8 @@ xdes_get_descriptor_with_space_hdr(
/* Read free limit and space size */
limit = mach_read_from_4(sp_header + FSP_FREE_LIMIT);
size = mach_read_from_4(sp_header + FSP_SIZE);
zip_size = mach_read_from_4(sp_header + FSP_PAGE_ZIP_SIZE);
zip_size = dict_table_flags_to_zip_size(
mach_read_from_4(sp_header + FSP_SPACE_FLAGS));
/* If offset is >= size or > limit, return NULL */
......@@ -909,13 +909,13 @@ fsp_header_init_fields(
/*===================*/
page_t* page, /* in/out: first page in the space */
ulint space_id, /* in: space id */
ulint zip_size) /* in: compressed page size in bytes;
0 for uncompressed pages */
ulint flags) /* in: compressed page size and
file format version, or 0 */
{
mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page,
space_id);
mach_write_to_4(FSP_HEADER_OFFSET + FSP_PAGE_ZIP_SIZE + page,
zip_size);
mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page,
flags);
}
/**************************************************************************
......@@ -932,12 +932,14 @@ fsp_header_init(
fsp_header_t* header;
buf_block_t* block;
page_t* page;
ulint flags;
ulint zip_size;
ut_ad(mtr);
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
zip_size = dict_table_flags_to_zip_size(flags);
block = buf_page_create(space, 0, zip_size, mtr);
buf_page_get(space, zip_size, 0, RW_X_LATCH, mtr);
#ifdef UNIV_SYNC_DEBUG
......@@ -959,7 +961,7 @@ fsp_header_init(
mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_PAGE_ZIP_SIZE, zip_size,
mlog_write_ulint(header + FSP_SPACE_FLAGS, flags,
MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr);
......@@ -1008,6 +1010,20 @@ fsp_header_get_space_id(
return(id);
}
/**************************************************************************
Reads the space flags from the first page of a tablespace. */
UNIV_INTERN
ulint
fsp_header_get_flags(
/*=================*/
/* out: flags */
const page_t* page) /* in: first page of a tablespace */
{
ut_ad(!page_offset(page));
return(mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page));
}
/**************************************************************************
Reads the compressed page size from the first page of a tablespace. */
UNIV_INTERN
......@@ -1018,7 +1034,9 @@ fsp_header_get_zip_size(
or 0 if uncompressed */
const page_t* page) /* in: first page of a tablespace */
{
return(mach_read_from_4(FSP_HEADER_OFFSET + FSP_PAGE_ZIP_SIZE + page));
ulint flags = fsp_header_get_flags(page);
return(dict_table_flags_to_zip_size(flags));
}
/**************************************************************************
......@@ -1033,13 +1051,15 @@ fsp_header_inc_size(
{
fsp_header_t* header;
ulint size;
ulint zip_size;
ulint flags;
ut_ad(mtr);
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
header = fsp_get_space_header(space, zip_size, mtr);
header = fsp_get_space_header(space,
dict_table_flags_to_zip_size(flags),
mtr);
size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
......@@ -1172,7 +1192,8 @@ fsp_try_extend_data_file(
}
size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
zip_size = mach_read_from_4(header + FSP_PAGE_ZIP_SIZE);
zip_size = dict_table_flags_to_zip_size(
mach_read_from_4(header + FSP_SPACE_FLAGS));
old_size = size;
......@@ -1294,7 +1315,8 @@ fsp_fill_free_list(
size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr);
zip_size = mach_read_from_4(FSP_PAGE_ZIP_SIZE + header);
zip_size = dict_table_flags_to_zip_size(
mach_read_from_4(FSP_SPACE_FLAGS + header));
ut_a(ut_is_2pow(zip_size));
ut_a(zip_size <= UNIV_PAGE_SIZE);
ut_a(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE);
......@@ -1840,8 +1862,8 @@ fsp_alloc_seg_inode_page(
ulint i;
space = page_get_space_id(page_align(space_header));
zip_size = mtr_read_ulint(space_header + FSP_PAGE_ZIP_SIZE,
MLOG_4BYTES, mtr);
zip_size = dict_table_flags_to_zip_size(
mach_read_from_4(FSP_SPACE_FLAGS + space_header));
page_no = fsp_alloc_free_page(space, zip_size, 0, mtr);
......@@ -1907,7 +1929,8 @@ fsp_alloc_seg_inode(
page_no = flst_get_first(space_header + FSP_SEG_INODES_FREE, mtr).page;
zip_size = mach_read_from_4(space_header + FSP_PAGE_ZIP_SIZE);
zip_size = dict_table_flags_to_zip_size(
mach_read_from_4(FSP_SPACE_FLAGS + space_header));
block = buf_page_get(page_get_space_id(page_align(space_header)),
zip_size, page_no, RW_X_LATCH, mtr);
#ifdef UNIV_SYNC_DEBUG
......@@ -2153,6 +2176,7 @@ fseg_create_general(
operation */
mtr_t* mtr) /* in: mtr */
{
ulint flags;
ulint zip_size;
fsp_header_t* space_header;
fseg_inode_t* inode;
......@@ -2168,7 +2192,8 @@ fseg_create_general(
ut_ad(byte_offset + FSEG_HEADER_SIZE
<= UNIV_PAGE_SIZE - FIL_PAGE_DATA_END);
latch = fil_space_get_latch(space, &zip_size);
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
if (page != 0) {
block = buf_page_get(space, zip_size, page, RW_X_LATCH, mtr);
......@@ -2327,15 +2352,18 @@ fseg_n_reserved_pages(
ulint ret;
fseg_inode_t* inode;
ulint space;
ulint flags;
ulint zip_size;
rw_lock_t* latch;
space = page_get_space_id(page_align(header));
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_ad(!mutex_own(&kernel_mutex)
|| mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
MTR_MEMO_X_LOCK));
|| mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(latch, mtr);
inode = fseg_inode_get(header, space, zip_size, mtr);
......@@ -2674,8 +2702,8 @@ fseg_alloc_free_page_low(
can be obtained immediately with buf_page_get without need
for a disk read */
buf_block_t* block;
ulint zip_size = mach_read_from_4(FSP_PAGE_ZIP_SIZE
+ space_header);
ulint zip_size = dict_table_flags_to_zip_size(
mach_read_from_4(FSP_SPACE_FLAGS + space_header));
block = buf_page_create(space, ret_page, zip_size, mtr);
#ifdef UNIV_SYNC_DEBUG
......@@ -2733,6 +2761,7 @@ fseg_alloc_free_page_general(
{
fseg_inode_t* inode;
ulint space;
ulint flags;
ulint zip_size;
rw_lock_t* latch;
ibool success;
......@@ -2741,7 +2770,9 @@ fseg_alloc_free_page_general(
space = page_get_space_id(page_align(seg_header));
latch = fil_space_get_latch(space, &zip_size);
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_ad(!mutex_own(&kernel_mutex)
|| mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
......@@ -2882,6 +2913,7 @@ fsp_reserve_free_extents(
ulint n_free_list_ext;
ulint free_limit;
ulint size;
ulint flags;
ulint zip_size;
ulint n_free;
ulint n_free_up;
......@@ -2892,7 +2924,9 @@ fsp_reserve_free_extents(
ut_ad(mtr);
*n_reserved = n_ext;
latch = fil_space_get_latch(space, &zip_size);
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_ad(!mutex_own(&kernel_mutex)
|| mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
......@@ -2988,6 +3022,7 @@ fsp_get_available_space_in_free_extents(
ulint n_free_list_ext;
ulint free_limit;
ulint size;
ulint flags;
ulint zip_size;
ulint n_free;
ulint n_free_up;
......@@ -2999,7 +3034,8 @@ fsp_get_available_space_in_free_extents(
mtr_start(&mtr);
latch = fil_space_get_latch(space, &zip_size);
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
mtr_x_lock(latch, &mtr);
......@@ -3268,14 +3304,18 @@ fseg_free_page(
ulint page, /* in: page offset */
mtr_t* mtr) /* in: mtr handle */
{
ulint flags;
ulint zip_size;
fseg_inode_t* seg_inode;
rw_lock_t* latch;
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_ad(!mutex_own(&kernel_mutex)
|| mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
MTR_MEMO_X_LOCK));
|| mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(latch, mtr);
seg_inode = fseg_inode_get(seg_header, space, zip_size, mtr);
......@@ -3378,17 +3418,21 @@ fseg_free_step(
xdes_t* descr;
fseg_inode_t* inode;
ulint space;
ulint flags;
ulint zip_size;
ulint header_page;
rw_lock_t* latch;
space = page_get_space_id(page_align(header));
header_page = page_get_page_no(page_align(header));
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_ad(!mutex_own(&kernel_mutex)
|| mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
MTR_MEMO_X_LOCK));
|| mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(latch, mtr);
descr = xdes_get_descriptor(space, zip_size, header_page, mtr);
......@@ -3454,16 +3498,20 @@ fseg_free_step_not_header(
xdes_t* descr;
fseg_inode_t* inode;
ulint space;
ulint flags;
ulint zip_size;
ulint page_no;
rw_lock_t* latch;
space = page_get_space_id(page_align(header));
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_ad(!mutex_own(&kernel_mutex)
|| mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
MTR_MEMO_X_LOCK));
|| mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(latch, mtr);
inode = fseg_inode_get(header, space, zip_size, mtr);
......@@ -3618,10 +3666,12 @@ fseg_validate_low(
node_addr = flst_get_first(inode + FSEG_FREE, mtr2);
while (!fil_addr_is_null(node_addr)) {
ulint flags;
ulint zip_size;
mtr_start(&mtr);
mtr_x_lock(fil_space_get_latch(space, &zip_size), &mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
zip_size = dict_table_flags_to_zip_size(flags);
descr = xdes_lst_get_descriptor(space, zip_size,
node_addr, &mtr);
......@@ -3640,10 +3690,12 @@ fseg_validate_low(
node_addr = flst_get_first(inode + FSEG_NOT_FULL, mtr2);
while (!fil_addr_is_null(node_addr)) {
ulint flags;
ulint zip_size;
mtr_start(&mtr);
mtr_x_lock(fil_space_get_latch(space, &zip_size), &mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
zip_size = dict_table_flags_to_zip_size(flags);
descr = xdes_lst_get_descriptor(space, zip_size,
node_addr, &mtr);
......@@ -3665,10 +3717,12 @@ fseg_validate_low(
node_addr = flst_get_first(inode + FSEG_FULL, mtr2);
while (!fil_addr_is_null(node_addr)) {
ulint flags;
ulint zip_size;
mtr_start(&mtr);
mtr_x_lock(fil_space_get_latch(space, &zip_size), &mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
zip_size = dict_table_flags_to_zip_size(flags);
descr = xdes_lst_get_descriptor(space, zip_size,
node_addr, &mtr);
......@@ -3695,20 +3749,22 @@ fseg_validate(
/*==========*/
/* out: TRUE if ok */
fseg_header_t* header, /* in: segment header */
mtr_t* mtr2) /* in: mtr */
mtr_t* mtr) /* in: mtr */
{
fseg_inode_t* inode;
ibool ret;
ulint space;
ulint flags;
ulint zip_size;
space = page_get_space_id(page_align(header));
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr2);
mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
zip_size = dict_table_flags_to_zip_size(flags);
inode = fseg_inode_get(header, space, zip_size, mtr2);
inode = fseg_inode_get(header, space, zip_size, mtr);
ret = fseg_validate_low(inode, mtr2);
ret = fseg_validate_low(inode, mtr);
return(ret);
}
......@@ -3777,11 +3833,13 @@ fseg_print(
{
fseg_inode_t* inode;
ulint space;
ulint flags;
ulint zip_size;
space = page_get_space_id(page_align(header));
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
zip_size = dict_table_flags_to_zip_size(flags);
inode = fseg_inode_get(header, space, zip_size, mtr);
......@@ -3803,6 +3861,7 @@ fsp_validate(
page_t* seg_inode_page;
rw_lock_t* latch;
ulint size;
ulint flags;
ulint zip_size;
ulint free_limit;
ulint frag_n_used;
......@@ -3819,7 +3878,8 @@ fsp_validate(
ulint seg_inode_len_free;
ulint seg_inode_len_full;
latch = fil_space_get_latch(space, &zip_size);
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
ut_a(ut_is_2pow(zip_size));
ut_a(zip_size <= UNIV_PAGE_SIZE);
ut_a(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE);
......@@ -4054,6 +4114,7 @@ fsp_print(
fseg_inode_t* seg_inode;
page_t* seg_inode_page;
rw_lock_t* latch;
ulint flags;
ulint zip_size;
ulint size;
ulint free_limit;
......@@ -4071,7 +4132,8 @@ fsp_print(
mtr_t mtr;
mtr_t mtr2;
latch = fil_space_get_latch(space, &zip_size);
latch = fil_space_get_latch(space, &flags);
zip_size = dict_table_flags_to_zip_size(flags);
/* Start first a mini-transaction mtr2 to lock out all other threads
from the fsp system */
......
......@@ -5030,11 +5030,6 @@ create_table_def(
innodb_check_for_record_too_big_error(flags & DICT_TF_COMPACT, error);
if (error == DB_TABLE_ZIP_NO_IBD) {
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
innobase_hton_name, "KEY_BLOCK_SIZE");
}
error = convert_error_code_to_mysql(error, NULL);
DBUG_RETURN(error);
......@@ -5236,6 +5231,7 @@ ha_innobase::create(
THD* thd = ha_thd();
ib_longlong auto_inc_value;
ulint flags;
const ulint file_format = srv_file_format;
DBUG_ENTER("ha_innobase::create");
......@@ -5291,14 +5287,68 @@ ha_innobase::create(
flags = 0;
switch (create_info->key_block_size) {
case 0:
if (form->s->row_type != ROW_TYPE_REDUNDANT) {
flags |= DICT_TF_COMPACT;
}
switch (create_info->key_block_size) {
case 1: case 2: case 4: case 8: case 16:
flags |= create_info->key_block_size
<< DICT_TF_COMPRESSED_SHIFT;
goto key_block_size_ok;
case 1:
flags |= 1 << DICT_TF_ZSSIZE_SHIFT | DICT_TF_COMPACT;
break;
case 2:
flags |= 2 << DICT_TF_ZSSIZE_SHIFT | DICT_TF_COMPACT;
break;
case 4:
flags |= 3 << DICT_TF_ZSSIZE_SHIFT | DICT_TF_COMPACT;
break;
case 8:
flags |= 4 << DICT_TF_ZSSIZE_SHIFT | DICT_TF_COMPACT;
break;
case 16:
flags |= 5 << DICT_TF_ZSSIZE_SHIFT | DICT_TF_COMPACT;
break;
#if DICT_TF_ZSSIZE_MAX != 5
# error "DICT_TF_ZSSIZE_MAX != 5"
#endif
key_block_size_wrong:
default:
if (create_info->used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE) {
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
innobase_hton_name, "KEY_BLOCK_SIZE");
error = -1;
goto cleanup;
} else {
sql_print_warning("InnoDB: ignoring"
" KEY_BLOCK_SIZE=%lu\n",
create_info->key_block_size);
flags &= ~DICT_TF_ZSSIZE_MASK;
goto key_block_size_ok;
}
}
if (!srv_file_per_table || file_format < DICT_TF_FORMAT_ZIP) {
goto key_block_size_wrong;
}
key_block_size_ok:
if (UNIV_EXPECT(flags & DICT_TF_COMPACT, DICT_TF_COMPACT)) {
/* New formats are only available if ROW_FORMAT=COMPACT. */
flags |= file_format << DICT_TF_FORMAT_SHIFT;
}
if ((create_info->used_fields & HA_CREATE_USED_ROW_FORMAT)
&& form->s->row_type != ((flags & DICT_TF_COMPACT)
? ROW_TYPE_COMPACT
: ROW_TYPE_REDUNDANT)) {
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
innobase_hton_name, "ROW_FORMAT");
error = -1;
goto cleanup;
}
error = create_table_def(trx, form, norm_name,
......@@ -8323,6 +8373,11 @@ static MYSQL_SYSVAR_BOOL(file_per_table, srv_file_per_table,
"Stores each InnoDB table to an .ibd file in the database dir.",
NULL, NULL, FALSE);
static MYSQL_SYSVAR_UINT(file_format, srv_file_format,
PLUGIN_VAR_RQCMDARG,
"File format to use for new tables in .ibd files.",
NULL, NULL, DICT_TF_FORMAT_51, DICT_TF_FORMAT_51, DICT_TF_FORMAT_MAX, 0);
static MYSQL_SYSVAR_ULONG(flush_log_at_trx_commit, srv_flush_log_at_trx_commit,
PLUGIN_VAR_OPCMDARG,
"Set to 0 (write and flush once per second),"
......@@ -8500,6 +8555,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(fast_shutdown),
MYSQL_SYSVAR(file_io_threads),
MYSQL_SYSVAR(file_per_table),
MYSQL_SYSVAR(file_format),
MYSQL_SYSVAR(flush_log_at_trx_commit),
MYSQL_SYSVAR(flush_method),
MYSQL_SYSVAR(force_recovery),
......
......@@ -1718,6 +1718,7 @@ ibuf_add_free_page(
{
mtr_t mtr;
page_t* header_page;
ulint flags;
ulint zip_size;
ulint page_no;
page_t* page;
......@@ -1730,7 +1731,8 @@ ibuf_add_free_page(
/* Acquire the fsp latch before the ibuf header, obeying the latching
order */
mtr_x_lock(fil_space_get_latch(space, &zip_size), &mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
zip_size = dict_table_flags_to_zip_size(flags);
header_page = ibuf_header_page_get(space, &mtr);
......@@ -1807,6 +1809,7 @@ ibuf_remove_free_page(
mtr_t mtr;
mtr_t mtr2;
page_t* header_page;
ulint flags;
ulint zip_size;
ulint page_no;
page_t* page;
......@@ -1819,7 +1822,8 @@ ibuf_remove_free_page(
/* Acquire the fsp latch before the ibuf header, obeying the latching
order */
mtr_x_lock(fil_space_get_latch(space, &zip_size), &mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
zip_size = dict_table_flags_to_zip_size(flags);
header_page = ibuf_header_page_get(space, &mtr);
......
......@@ -74,8 +74,6 @@ enum db_err {
DB_PRIMARY_KEY_IS_NULL, /* a column in the PRIMARY KEY
was found to be NULL */
DB_TABLE_ZIP_NO_IBD, /* trying to create a compressed
table in the system tablespace */
/* The following are partial failure codes */
DB_FAIL = 1000,
......
......@@ -631,6 +631,32 @@ dict_table_is_comp(
compact page format */
const dict_table_t* table); /* in: table */
/************************************************************************
Determine the file format of a table. */
UNIV_INLINE
ulint
dict_table_get_format(
/*==================*/
/* out: file format version */
const dict_table_t* table); /* in: table */
/************************************************************************
Set the file format of a table. */
UNIV_INLINE
void
dict_table_set_format(
/*==================*/
dict_table_t* table, /* in/out: table */
ulint format);/* in: file format version */
/************************************************************************
Extract the compressed page size from table flags. */
UNIV_INLINE
ulint
dict_table_flags_to_zip_size(
/*=========================*/
/* out: compressed page size,
or 0 if not compressed */
ulint flags) /* in: flags */
__attribute__((const));
/************************************************************************
Check whether the table uses the compressed compact page format. */
UNIV_INLINE
ulint
......
......@@ -349,6 +349,57 @@ dict_table_is_comp(
return(UNIV_LIKELY(table->flags & DICT_TF_COMPACT));
}
/************************************************************************
Determine the file format of a table. */
UNIV_INLINE
ulint
dict_table_get_format(
/*==================*/
/* out: file format version */
const dict_table_t* table) /* in: table */
{
ut_ad(table);
return((table->flags & DICT_TF_FORMAT_MASK) >> DICT_TF_FORMAT_SHIFT);
}
/************************************************************************
Determine the file format of a table. */
UNIV_INLINE
void
dict_table_set_format(
/*==================*/
dict_table_t* table, /* in/out: table */
ulint format) /* in: file format version */
{
ut_ad(table);
table->flags = (table->flags & ~DICT_TF_FORMAT_MASK)
| (format << DICT_TF_FORMAT_SHIFT);
}
/************************************************************************
Extract the compressed page size from table flags. */
UNIV_INLINE
ulint
dict_table_flags_to_zip_size(
/*=========================*/
/* out: compressed page size,
or 0 if not compressed */
ulint flags) /* in: flags */
{
ulint zip_size = flags & DICT_TF_ZSSIZE_MASK;
if (UNIV_UNLIKELY(zip_size)) {
zip_size = ((PAGE_ZIP_MIN_SIZE >> 1)
<< (zip_size >> DICT_TF_ZSSIZE_SHIFT));
ut_ad(zip_size <= UNIV_PAGE_SIZE);
}
return(zip_size);
}
/************************************************************************
Check whether the table uses the compressed compact page format. */
UNIV_INLINE
......@@ -361,8 +412,7 @@ dict_table_zip_size(
{
ut_ad(table);
return(UNIV_UNLIKELY((table->flags & DICT_TF_COMPRESSED_MASK)
<< (10 - DICT_TF_COMPRESSED_SHIFT)));
return(dict_table_flags_to_zip_size(table->flags));
}
/************************************************************************
......
......@@ -40,12 +40,32 @@ combination of types */
#define DICT_TABLE_CLUSTER 3 /* this means that the table is
really a cluster definition */
#endif
#define DICT_TABLE_COMPRESSED_BASE 0x8000 /* compressed tablespace */
/* Table flags */
#define DICT_TF_COMPACT 1 /* compact page format */
#define DICT_TF_COMPRESSED_MASK 62 /* compressed page size, KiB */
#define DICT_TF_COMPRESSED_SHIFT 1
/* Table flags. All unused bits must be 0. */
#define DICT_TF_COMPACT 1 /* Compact page format.
This must be set for
new file formats
(later than
DICT_TF_FORMAT_51). */
/* compressed page size (0=uncompressed, up to 15 compressed sizes) */
#define DICT_TF_ZSSIZE_SHIFT 1
#define DICT_TF_ZSSIZE_MASK (15 << DICT_TF_ZSSIZE_SHIFT)
#define DICT_TF_ZSSIZE_MAX (UNIV_PAGE_SIZE_SHIFT - PAGE_ZIP_MIN_SIZE_SHIFT + 1)
#define DICT_TF_FORMAT_SHIFT 5 /* file format */
#define DICT_TF_FORMAT_MASK (127 << DICT_TF_FORMAT_SHIFT)
#define DICT_TF_FORMAT_51 0 /* InnoDB/MySQL up to 5.1 */
#define DICT_TF_FORMAT_ZIP 1 /* InnoDB plugin for 5.1:
compressed tables,
new BLOB treatment */
#define DICT_TF_FORMAT_MAX DICT_TF_FORMAT_ZIP
#define DICT_TF_BITS 6 /* number of flag bits */
#if (1 << (DICT_TF_BITS - DICT_TF_FORMAT_SHIFT)) <= DICT_TF_FORMAT_MAX
# error "DICT_TF_BITS is insufficient for DICT_TF_FORMAT_MAX"
#endif
/**************************************************************************
Creates a table memory object. */
......@@ -314,6 +334,7 @@ struct dict_table_struct{
unsigned space:32;
/* space where the clustered index of the
table is placed */
unsigned flags:DICT_TF_BITS;/* DICT_TF_COMPACT, ... */
unsigned ibd_file_missing:1;
/* TRUE if this is in a single-table
tablespace and the .ibd file is missing; then
......@@ -326,7 +347,6 @@ struct dict_table_struct{
TABLESPACE */
unsigned cached:1;/* TRUE if the table object has been added
to the dictionary cache */
unsigned flags:8;/* DICT_TF_COMPACT, ... */
unsigned n_def:10;/* number of columns defined so far */
unsigned n_cols:10;/* number of columns */
dict_col_t* cols; /* array of column descriptions */
......
......@@ -222,6 +222,15 @@ fil_space_get_size(
/* out: space size, 0 if space not found */
ulint id); /* in: space id */
/***********************************************************************
Returns the flags of the space. The tablespace must be cached
in the memory cache. */
UNIV_INTERN
ulint
fil_space_get_flags(
/*================*/
/* out: flags, ULINT_UNDEFINED if space not found */
ulint id); /* in: space id */
/***********************************************************************
Returns the compressed page size of the space, or 0 if the space
is not compressed. The tablespace must be cached in the memory cache. */
UNIV_INTERN
......@@ -413,8 +422,8 @@ fil_create_new_single_table_tablespace(
table */
ibool is_temp, /* in: TRUE if a table created with
CREATE TEMPORARY TABLE */
ulint zip_size, /* in: compressed page size,
or 0 if uncompressed tablespace */
ulint flags, /* in: compressed page size and
file format version, or 0 */
ulint size); /* in: the initial size of the
tablespace file in pages,
must be >= FIL_IBD_FILE_INITIAL_SIZE */
......@@ -440,8 +449,7 @@ fil_open_single_table_tablespace(
faster (the OS caches them) than
accessing the first page of the file */
ulint id, /* in: space id */
ulint zip_size, /* in: compressed page size,
or 0 if uncompressed tablespace */
ulint flags, /* in: tablespace flags */
const char* name); /* in: table name in the
databasename/tablename format */
/************************************************************************
......
......@@ -82,6 +82,14 @@ fsp_header_get_space_id(
/* out: space id, ULINT UNDEFINED if error */
const page_t* page); /* in: first page of a tablespace */
/**************************************************************************
Reads the space flags from the first page of a tablespace. */
UNIV_INTERN
ulint
fsp_header_get_flags(
/*=================*/
/* out: flags */
const page_t* page); /* in: first page of a tablespace */
/**************************************************************************
Reads the compressed page size from the first page of a tablespace. */
UNIV_INTERN
ulint
......@@ -100,8 +108,8 @@ fsp_header_init_fields(
/*===================*/
page_t* page, /* in/out: first page in the space */
ulint space_id, /* in: space id */
ulint zip_size); /* in: compressed page size in bytes;
0 for uncompressed pages */
ulint flags); /* in: compressed page size and
file format version, or 0 */
/**************************************************************************
Initializes the space header of a new created space and creates also the
insert buffer tree root if space == 0. */
......
......@@ -130,8 +130,8 @@ flag value must give the length also! */
/* copy compact record list end
to a new created index page */
#define MLOG_COMP_PAGE_REORGANIZE ((byte)46) /* reorganize an index page */
#define MLOG_ZIP_FILE_CREATE ((byte)47) /* log record about creating a
compressed .ibd file */
#define MLOG_FILE_CREATE2 ((byte)47) /* log record about creating
an .ibd file, with format */
#define MLOG_ZIP_WRITE_NODE_PTR ((byte)48) /* write the node pointer of
a record on a compressed
non-leaf B-tree page */
......
......@@ -46,7 +46,8 @@ struct page_zip_des_struct
PAGE_ZIP_MIN_SIZE << (ssize - 1). */
};
#define PAGE_ZIP_MIN_SIZE 1024 /* smallest page_zip_des_struct.size */
#define PAGE_ZIP_MIN_SIZE_SHIFT 10 /* log2 of smallest compressed size */
#define PAGE_ZIP_MIN_SIZE (1 << PAGE_ZIP_MIN_SIZE_SHIFT)
/** Number of page compressions, indexed by page_zip_des_t::ssize */
extern ulint page_zip_compress_count[8];
......
......@@ -107,7 +107,7 @@ page_zip_get_size(
return(0);
}
size = 512 << page_zip->ssize;
size = (PAGE_ZIP_MIN_SIZE >> 1) << page_zip->ssize;
ut_ad(size >= PAGE_ZIP_MIN_SIZE);
ut_ad(size <= UNIV_PAGE_SIZE);
......
......@@ -60,6 +60,8 @@ extern char* srv_arch_dir;
/* store to its own file each table created by an user; data
dictionary tables are in the system tablespace 0 */
extern my_bool srv_file_per_table;
/* The file format to use on new *.ibd files. */
extern uint srv_file_format;
/* Place locks to records only i.e. do not use next-key locking except
on duplicate key checking and foreign key checking */
extern ibool srv_locks_unsafe_for_binlog;
......
......@@ -962,7 +962,7 @@ recv_parse_or_apply_log_rec_body(
case MLOG_FILE_CREATE:
case MLOG_FILE_RENAME:
case MLOG_FILE_DELETE:
case MLOG_ZIP_FILE_CREATE:
case MLOG_FILE_CREATE2:
ptr = fil_op_log_parse_or_replay(ptr, end_ptr, type, 0);
break;
case MLOG_ZIP_WRITE_NODE_PTR:
......@@ -1990,7 +1990,7 @@ loop:
#endif/* UNIV_LOG_DEBUG */
} else if (type == MLOG_FILE_CREATE
|| type == MLOG_ZIP_FILE_CREATE
|| type == MLOG_FILE_CREATE2
|| type == MLOG_FILE_RENAME
|| type == MLOG_FILE_DELETE) {
ut_a(space);
......
set global innodb_file_per_table=1;
set global innodb_file_per_table=off;
set global innodb_file_format=0;
create table t1(a int primary key) engine=innodb row_format=dynamic;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'ROW_FORMAT'
create table t1(a int primary key) engine=innodb row_format=redundant;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT
drop table t1;
create table t1(a int primary key) engine=innodb row_format=compact;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT
drop table t1;
create table t1(a int primary key) engine=innodb key_block_size=9;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'KEY_BLOCK_SIZE'
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=redundant;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'KEY_BLOCK_SIZE'
set global innodb_file_per_table=on;
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=redundant;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'KEY_BLOCK_SIZE'
set global innodb_file_format=1;
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=redundant;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'ROW_FORMAT'
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=compact;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT KEY_BLOCK_SIZE=1
drop table t1;
create table t1(a int primary key) engine=innodb
key_block_size=1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 KEY_BLOCK_SIZE=1
drop table t1;
create table t1(a int primary key) engine=innodb key_block_size=9;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'KEY_BLOCK_SIZE'
create table t1(a int not null, b text, index(b(10))) engine=innodb
key_block_size=1;
insert into t1 values (1,1);
......@@ -14,3 +66,4 @@ a left(b,40) is_equal
1 1 1
drop table t1;
set global innodb_file_per_table=0;
set global innodb_file_format=0;
-- source include/have_innodb.inc
let $default=`select @@innodb_file_per_table`;
set global innodb_file_per_table=1;
let $per_table=`select @@innodb_file_per_table`;
let $format=`select @@innodb_file_format`;
set global innodb_file_per_table=off;
set global innodb_file_format=0;
--error 1478
create table t1(a int primary key) engine=innodb row_format=dynamic;
create table t1(a int primary key) engine=innodb row_format=redundant;
show create table t1;
drop table t1;
create table t1(a int primary key) engine=innodb row_format=compact;
show create table t1;
drop table t1;
--error 1478
create table t1(a int primary key) engine=innodb key_block_size=9;
--error 1478
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=redundant;
set global innodb_file_per_table=on;
--error 1478
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=redundant;
set global innodb_file_format=1;
--error 1478
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=redundant;
create table t1(a int primary key) engine=innodb
key_block_size=1 row_format=compact;
show create table t1;
drop table t1;
create table t1(a int primary key) engine=innodb
key_block_size=1;
show create table t1;
drop table t1;
--error 1478
create table t1(a int primary key) engine=innodb key_block_size=9;
create table t1(a int not null, b text, index(b(10))) engine=innodb
key_block_size=1;
......@@ -31,4 +69,5 @@ disconnect a;
disconnect b;
drop table t1;
eval set global innodb_file_per_table=$default;
eval set global innodb_file_per_table=$per_table;
eval set global innodb_file_format=$format;
......@@ -2559,8 +2559,7 @@ row_import_tablespace_for_mysql(
ibuf_delete_for_discarded_space(table->space);
success = fil_open_single_table_tablespace(TRUE, table->space,
dict_table_zip_size(table),
table->name);
table->flags, table->name);
if (success) {
table->ibd_file_missing = FALSE;
table->tablespace_discarded = FALSE;
......
......@@ -88,9 +88,15 @@ UNIV_INTERN char* srv_arch_dir = NULL;
/* store to its own file each table created by an user; data
dictionary tables are in the system tablespace 0 */
UNIV_INTERN my_bool srv_file_per_table;
/* The file format to use on new *.ibd files. */
UNIV_INTERN uint srv_file_format;
#if DICT_TF_FORMAT_51
# error "DICT_TF_FORMAT_51 must be 0!"
#endif
/* Place locks to records only i.e. do not use next-key locking except
on duplicate key checking and foreign key checking */
UNIV_INTERN ibool srv_locks_unsafe_for_binlog = FALSE;
UNIV_INTERN ulint srv_n_data_files = 0;
UNIV_INTERN char** srv_data_file_names = NULL;
/* size in database pages */
......
......@@ -665,7 +665,7 @@ open_or_create_log_file(
if (k == 0 && i == 0) {
arch_space_id = 2 * k + 1 + SRV_LOG_SPACE_FIRST_ID;
fil_space_create("arch_log_space", arch_space_id, FIL_LOG);
fil_space_create("arch_log_space", arch_space_id, 0, FIL_LOG);
} else {
arch_space_id = ULINT_UNDEFINED;
}
......
......@@ -243,11 +243,13 @@ trx_rseg_create(
ulint* id, /* out: rseg id */
mtr_t* mtr) /* in: mtr */
{
ulint flags;
ulint zip_size;
ulint page_no;
trx_rseg_t* rseg;
mtr_x_lock(fil_space_get_latch(space, &zip_size), mtr);
mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
zip_size = dict_table_flags_to_zip_size(flags);
mutex_enter(&kernel_mutex);
page_no = trx_rseg_header_create(space, zip_size, max_size, id, mtr);
......
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