Commit 02c712aa authored by Sergei Golubchik's avatar Sergei Golubchik

* frm extra2 segment.

* persistent table versions in the extra2
* ha_archive::frm_compare using TABLE_SHARE::tabledef_version
* distinguish between "important" and "optional" extra2 frm values
* write engine-defined attributes (aka "table options") to extra2, not to extra,
  but still read from the old location, if they're found there.
parent f6168bb6
......@@ -318,7 +318,7 @@ enum ha_base_keytype {
#define HA_OPTION_NULL_FIELDS 1024
#define HA_OPTION_PAGE_CHECKSUM 2048
/* .frm has extra create options in linked-list format */
#define HA_OPTION_TEXT_CREATE_OPTIONS (1L << 14)
#define HA_OPTION_TEXT_CREATE_OPTIONS_legacy (1L << 14) /* 5.2 to 5.5, unused since 10.0 */
#define HA_OPTION_TEMP_COMPRESS_RECORD (1L << 15) /* set by isamchk */
#define HA_OPTION_READ_ONLY_DATA (1L << 16) /* Set by isamchk */
#define HA_OPTION_NO_CHECKSUM (1L << 17)
......
......@@ -12701,12 +12701,12 @@ CREATE TABLE t1(a INT, b BLOB) ENGINE=archive;
SELECT DATA_LENGTH, AVG_ROW_LENGTH FROM
INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='t1' AND TABLE_SCHEMA='test';
DATA_LENGTH AVG_ROW_LENGTH
535 15
550 15
INSERT INTO t1 VALUES(1, 'sampleblob1'),(2, 'sampleblob2');
SELECT DATA_LENGTH, AVG_ROW_LENGTH FROM
INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='t1' AND TABLE_SCHEMA='test';
DATA_LENGTH AVG_ROW_LENGTH
569 284
584 292
DROP TABLE t1;
SET @save_join_buffer_size= @@join_buffer_size;
SET @@join_buffer_size= 8192;
......@@ -12818,10 +12818,11 @@ select * from t1;
a b
flush tables;
select * from t1;
a b
1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
ERROR HY000: Table 't1' is marked as crashed and should be repaired
show warnings;
Level Code Message
Warning 127 Got error 127 when reading table `test`.`t1`
Error 1194 Table 't1' is marked as crashed and should be repaired
drop table t1;
create temporary table t1 (a int) engine=archive;
insert t1 values (1),(2),(3);
......
......@@ -1738,7 +1738,7 @@ select * from t1; # open the table to create the frm
flush tables; # and close the table again
--remove_file $MYSQLD_DATADIR/test/t1.ARZ
copy_file std_data/t917689.ARZ $MYSQLD_DATADIR/test/t1.ARZ;
#--error ER_CRASHED_ON_USAGE
--error ER_CRASHED_ON_USAGE
select * from t1;
show warnings;
drop table t1;
......
......@@ -17,6 +17,8 @@ select * from t1;
a
1
2
t1.ARZ
t1.frm
#
# show tables
#
......@@ -28,10 +30,9 @@ show tables;
Tables_in_test
t1
t2
select * from t1;
a
1
2
t1.ARZ
t2.ARZ
t2.frm
#
# show full tables
#
......@@ -40,29 +41,27 @@ show full tables;
Tables_in_test Table_type
t1 BASE TABLE
t2 BASE TABLE
select * from t1;
a
1
2
t1.ARZ
t2.ARZ
t2.frm
#
# discover on truncate
#
flush tables;
truncate table t1;
ERROR HY000: Table storage engine for 't1' doesn't have this option
show tables;
Tables_in_test
t1
t2
t1.ARZ
t1.frm
t2.ARZ
t2.frm
#
# discover on rename
#
flush tables;
rename table t2 to t0;
show tables;
Tables_in_test
t0
t1
t0.ARZ
t1.ARZ
t1.frm
#
# discover on HA_ERR_TABLE_DEF_CHANGED
#
......@@ -77,9 +76,7 @@ t1 CREATE TABLE `t1` (
#
flush tables;
drop table t1;
show tables;
Tables_in_test
t0
t0.ARZ
#
# discover of table non-existance on drop
#
......@@ -89,6 +86,9 @@ flush tables;
select * from t1;
ERROR 42S02: Table 'test.t1' doesn't exist
drop table t0;
show status like 'Handler_discover';
Variable_name Value
Handler_discover 7
#
# Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE
#
......
......@@ -13,6 +13,7 @@ remove_file $mysqld_datadir/test/t1.frm;
flush tables;
insert t1 values (2);
select * from t1;
--list_files $mysqld_datadir/test
--echo #
--echo # show tables
......@@ -22,24 +23,22 @@ select * from t2;
remove_file $mysqld_datadir/test/t1.frm;
flush tables;
show tables;
select * from t1;
--list_files $mysqld_datadir/test
--echo #
--echo # show full tables
--echo #
remove_file $mysqld_datadir/test/t1.frm;
flush tables;
show full tables;
select * from t1;
--list_files $mysqld_datadir/test
--echo #
--echo # discover on truncate
--echo #
remove_file $mysqld_datadir/test/t1.frm;
flush tables;
--error ER_ILLEGAL_HA
truncate table t1;
show tables;
--list_files $mysqld_datadir/test
--echo #
--echo # discover on rename
......@@ -47,7 +46,7 @@ show tables;
remove_file $mysqld_datadir/test/t2.frm;
flush tables;
rename table t2 to t0;
show tables;
--list_files $mysqld_datadir/test
--echo #
--echo # discover on HA_ERR_TABLE_DEF_CHANGED
......@@ -63,7 +62,7 @@ show create table t1;
remove_file $mysqld_datadir/test/t1.frm;
flush tables;
drop table t1;
show tables;
--list_files $mysqld_datadir/test
--echo #
--echo # discover of table non-existance on drop
......@@ -74,6 +73,8 @@ flush tables;
--error ER_NO_SUCH_TABLE
select * from t1;
drop table t0;
--list_files $mysqld_datadir/test
show status like 'Handler_discover';
--echo #
--echo # Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE
......
......@@ -15,10 +15,10 @@ ENGINE = ARCHIVE;
INSERT INTO t1 VALUES(CURRENT_DATE);
SELECT DATA_LENGTH, INDEX_LENGTH FROM information_schema.TABLES WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
520 0
535 0
SELECT DATA_LENGTH, INDEX_LENGTH FROM information_schema.TABLES WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
520 0
535 0
DROP TABLE t1;
drop database if exists db99;
drop table if exists t1;
......
......@@ -123,7 +123,7 @@ void my_uuid_init(ulong seed1, ulong seed2)
Create a global unique identifier (uuid)
@func my_uuid()
@param to Store uuid here. Must be of size MY_uuid_SIZE (16)
@param to Store uuid here. Must be of size MY_UUID_SIZE (16)
*/
void my_uuid(uchar *to)
......
......@@ -1360,6 +1360,7 @@ enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES };
struct HA_CREATE_INFO
{
CHARSET_INFO *table_charset, *default_table_charset;
LEX_CUSTRING tabledef_version;
LEX_STRING connect_string;
const char *password, *tablespace;
LEX_STRING comment;
......@@ -1393,6 +1394,7 @@ struct HA_CREATE_INFO
enum ha_storage_media storage_media; ///< DEFAULT, DISK or MEMORY
enum ha_choice page_checksum; ///< If we have page_checksums
engine_option_value *option_list; ///< list of table create options
/* the following three are only for ALTER TABLE, check_if_incompatible_data() */
ha_table_option_struct *option_struct; ///< structure with parsed table options
ha_field_option_struct **fields_option_struct; ///< array of field option structures
......
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
Copyright (c) 2008-2011 Monty Program Ab
Copyright (c) 2008, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -706,7 +706,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
uint i,j;
bool use_hash;
char *keynames, *names, *comment_pos;
const uchar *forminfo;
const uchar *forminfo, *extra2;
const uchar *frm_image_end = frm_image + frm_length;
uchar *record, *null_flags, *null_pos;
const uchar *disk_buff, *strpos;
......@@ -723,7 +723,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bool null_bits_are_used;
uint vcol_screen_length, UNINIT_VAR(options_len);
char *vcol_screen_pos;
const uchar *UNINIT_VAR(options);
const uchar *options= 0;
KEY first_keyinfo;
uint len;
KEY_PART_INFO *first_key_part= NULL;
......@@ -749,8 +749,60 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
new_frm_ver= (frm_image[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
/* Position of the form in the form file. */
/* Length of the MariaDB extra2 segment in the form file. */
len = uint2korr(frm_image+4);
extra2= frm_image + 64;
if (*extra2 != '/') // old frm had '/' there
{
const uchar *e2end= extra2 + len;
while (extra2 < e2end)
{
uchar type= *extra2++;
size_t length= *extra2++;
if (!length)
{
length= uint2korr(extra2);
extra2+=2;
if (length < 256)
goto err;
}
switch (type) {
case EXTRA2_TABLEDEF_VERSION:
if (tabledef_version.str) // see init_from_sql_statement_string()
{
if (length != tabledef_version.length ||
memcmp(extra2, tabledef_version.str, length))
goto err;
}
else
{
uchar *buf= (uchar*) alloc_root(&mem_root, length);
if (!buf)
goto err;
memcpy(buf, extra2, length);
tabledef_version.str= buf;
tabledef_version.length= length;
}
break;
case EXTRA2_ENGINE_TABLEOPTS:
if (options)
goto err;
/* remember but delay parsing until we have read fields and keys */
options= extra2;
options_len= length;
break;
default:
/* abort frm parsing if it's an unknown but important extra2 value */
if (type >= 128)
goto err;
}
extra2+= length;
}
if (extra2 > e2end)
goto err;
}
if (frm_length < FRM_HEADER_SIZE + len ||
!(pos= uint4korr(frm_image + FRM_HEADER_SIZE + len)))
goto err;
......@@ -1169,12 +1221,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
DBUG_ASSERT(next_chunk <= buff_end);
if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS_legacy)
{
/*
store options position, but skip till the time we will
know number of fields
*/
if (options)
goto err;
options_len= uint4korr(next_chunk);
options= next_chunk + 4;
next_chunk+= options_len + 4;
......@@ -1839,7 +1889,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
null_length, 255);
}
if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
if (options)
{
DBUG_ASSERT(options_len);
if (engine_table_options_frm_read(options, options_len, share))
......@@ -2028,6 +2078,9 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
thd->lex->create_info.db_type= plugin_data(db_plugin, handlerton *);
if (tabledef_version.str)
thd->lex->create_info.tabledef_version= tabledef_version;
file= mysql_create_frm_image(thd, db.str, table_name.str,
&thd->lex->create_info, &thd->lex->alter_info,
0, 0, &frm);
......@@ -3086,7 +3139,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
fileinfo[3]= (uchar) ha_legacy_type(
ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0));
int2store(fileinfo+4,3);
/*
Keep in sync with pack_keys() in unireg.cc
For each key:
......
......@@ -616,6 +616,8 @@ struct TABLE_SHARE
I_P_List <TABLE, TABLE_share> used_tables;
I_P_List <TABLE, TABLE_share> free_tables;
LEX_CUSTRING tabledef_version;
engine_option_value *option_list; /* text options for table */
ha_table_option_struct *option_struct; /* structure with parsed options */
......
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -122,13 +123,6 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
create_fields,
keys, key_info);
DBUG_PRINT("info", ("Options length: %u", options_len));
if (options_len)
{
create_info->table_options|= HA_OPTION_TEXT_CREATE_OPTIONS;
create_info->extra_size+= (options_len + 4);
}
else
create_info->table_options&= ~HA_OPTION_TEXT_CREATE_OPTIONS;
/*
This gives us the byte-position of the character at
......@@ -193,13 +187,31 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
forminfo[46]=(uchar) create_info->comment.length;
}
if (!create_info->tabledef_version.str)
{
uchar *to= (uchar*) thd->alloc(MY_UUID_SIZE);
if (unlikely(!to))
DBUG_RETURN(frm);
my_uuid(to);
create_info->tabledef_version.str= to;
create_info->tabledef_version.length= MY_UUID_SIZE;
}
DBUG_ASSERT(create_info->tabledef_version.length > 0);
DBUG_ASSERT(create_info->tabledef_version.length <= 255);
prepare_frm_header(thd, reclength, fileinfo, create_info, keys, key_info);
/* one byte for a type, one or three for a length */
uint extra2_size= 1 + 1 + create_info->tabledef_version.length;
if (options_len)
extra2_size+= 1 + (options_len > 255 ? 3 : 1) + options_len;
key_buff_length= uint4korr(fileinfo+47);
frm.length= FRM_HEADER_SIZE; // fileinfo;
frm.length+= uint2korr(fileinfo+4) + 4; // "form entry"
frm.length+= extra2_size + 4; // mariadb extra2 frm segment
int2store(fileinfo+4, extra2_size);
int2store(fileinfo+6, frm.length);
frm.length+= key_buff_length;
frm.length+= reclength; // row with default values
......@@ -214,11 +226,34 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
if (!frm_ptr)
DBUG_RETURN(frm);
pos = frm_ptr + uint2korr(fileinfo+6);
key_info_length= pack_keys(pos, keys, key_info, data_offset);
/* write the extra2 segment */
pos = frm_ptr + 64;
*pos++ = EXTRA2_TABLEDEF_VERSION;
*pos++ = create_info->tabledef_version.length;
memcpy(pos, create_info->tabledef_version.str,
create_info->tabledef_version.length);
pos+= create_info->tabledef_version.length;
memcpy(frm_ptr + FRM_HEADER_SIZE, "//", 3);
int4store(frm_ptr + 67, filepos);
if (options_len)
{
*pos++= EXTRA2_ENGINE_TABLEOPTS;
if (options_len < 255)
*pos++= options_len;
else
{
DBUG_ASSERT(options_len <= 65535); // FIXME if necessary
int2store(pos + 1, options_len);
pos+= 3;
}
pos= engine_table_options_frm_image(pos, create_info->option_list,
create_fields, keys, key_info);
}
int4store(pos, filepos); // end of the extra2 segment
pos+= 4;
DBUG_ASSERT(pos == frm_ptr + uint2korr(fileinfo+6));
key_info_length= pack_keys(pos, keys, key_info, data_offset);
maxlength=(uint) next_io_size((ulong) (uint2korr(forminfo)+1000));
int2store(forminfo+2,maxlength);
......@@ -285,19 +320,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
pos+= create_info->comment.length;
}
if (options_len)
{
int4store(pos, options_len);
pos+= 4;
engine_table_options_frm_image(pos,
create_info->option_list,
create_fields,
keys, key_info);
pos+= options_len;
}
memcpy(frm_ptr + filepos, forminfo, FRM_FORMINFO_SIZE);
if (pack_fields(frm_ptr + filepos + FRM_FORMINFO_SIZE, create_fields, data_offset))
memcpy(frm_ptr + filepos, forminfo, 288);
if (pack_fields(frm_ptr + filepos + 288, create_fields, data_offset))
goto err;
{
......@@ -332,14 +356,12 @@ err:
SYNOPSIS
rea_create_table()
thd Thread handler
frm binary frm image of the table to create
path Name of file (including database, without .frm)
db Data base name
table_name Table name
create_info create info parameters
create_fields Fields to create
keys number of keys to create
key_info Keys to create
file Handler to use
file Handler to use or NULL if only frm needs to be created
RETURN
0 ok
......
......@@ -167,6 +167,22 @@
#include "sql_list.h" /* List<> */
#include "field.h" /* Create_field */
/*
Types of values in the MariaDB extra2 frm segment.
Each value is written as
type: 1 byte
length: 1 byte (1..255) or \0 and 2 bytes.
binary value of the 'length' bytes.
Older MariaDB servers can ignore values of unknown types if
the type code is less than 128. Otherwise older servers are required
to report an error.
*/
enum extra2_frm_value_type {
EXTRA2_TABLEDEF_VERSION=0,
EXTRA2_ENGINE_TABLEOPTS=128,
};
int rea_create_table(THD *thd, LEX_CUSTRING *frm,
const char *path, const char *db, const char *table_name,
HA_CREATE_INFO *create_info, handler *file);
......
......@@ -364,6 +364,8 @@ void read_header(azio_stream *s, unsigned char *buffer)
{
if (buffer[0] == az_magic[0] && buffer[1] == az_magic[1])
{
uchar tmp[AZ_FRMVER_LEN + 2];
s->version= (unsigned int)buffer[AZ_VERSION_POS];
s->minor_version= (unsigned int)buffer[AZ_MINOR_VERSION_POS];
s->block_size= 1024 * buffer[AZ_BLOCK_POS];
......@@ -379,6 +381,22 @@ void read_header(azio_stream *s, unsigned char *buffer)
s->comment_start_pos= (unsigned int)uint4korr(buffer + AZ_COMMENT_POS);
s->comment_length= (unsigned int)uint4korr(buffer + AZ_COMMENT_LENGTH_POS);
s->dirty= (unsigned int)buffer[AZ_DIRTY_POS];
/*
we'll hard-code the current frm format for now, to avoid
changing archive table versions.
*/
if (s->frm_length == 0 ||
my_pread(s->file, tmp, sizeof(tmp), s->frm_start_pos + 64, MYF(MY_NABP)) ||
tmp[0] != 0 || tmp[1] != AZ_FRMVER_LEN)
{
s->frmver_length= 0;
}
else
{
s->frmver_length= tmp[1];
memcpy(s->frmver, tmp+2, s->frmver_length);
}
}
else if (buffer[0] == gz_magic[0] && buffer[1] == gz_magic[1])
{
......
......@@ -198,6 +198,7 @@ extern "C" {
#define AZ_BUFSIZE_READ 32768
#define AZ_BUFSIZE_WRITE 16384
#define AZ_FRMVER_LEN 16 /* same as MY_UUID_SIZE in 10.0.2 */
typedef struct azio_stream {
z_stream stream;
......@@ -227,6 +228,8 @@ typedef struct azio_stream {
unsigned char dirty; /* State of file */
unsigned int frm_start_pos; /* Position for start of FRM */
unsigned int frm_length; /* Position for start of FRM */
unsigned char frmver[AZ_FRMVER_LEN];
unsigned int frmver_length;
unsigned int comment_start_pos; /* Position for start of comment */
unsigned int comment_length; /* Position for start of comment */
} azio_stream;
......
......@@ -673,33 +673,12 @@ int ha_archive::frm_copy(azio_stream *src, azio_stream *dst)
int ha_archive::frm_compare(azio_stream *s)
{
int rc= 0;
const uchar *frm_ptr= 0;
uchar *azfrm_ptr= 0;
size_t frm_len;
/* no frm = no discovery. perhaps it's a partitioned table */
if (table->s->read_frm_image(&frm_ptr, &frm_len))
goto err;
if (!(azfrm_ptr= (uchar *) my_malloc(s->frm_length,
MYF(MY_THREAD_SPECIFIC | MY_WME))))
goto err;
rc= 1;
if (!s->frmver_length)
return 0; // Old pre-10.0 archive table. Never rediscover.
if (frm_len != s->frm_length)
goto err;
if (azread_frm(s, azfrm_ptr))
goto err;
rc= memcmp(frm_ptr, azfrm_ptr, frm_len);
err:
my_free(const_cast<uchar*>(frm_ptr));
my_free(azfrm_ptr);
return rc;
LEX_CUSTRING *ver= &table->s->tabledef_version;
return ver->length != s->frmver_length ||
memcmp(ver->str, s->frmver, ver->length);
}
......
--- suite/archive/discover.result 2013-04-08 00:06:37.000000000 +0200
+++ /usr/home/serg/Abk/mysql/10.0-serg/storage/test_sql_discovery/mysql-test/archive/discover.reject 2013-04-08 00:07:02.000000000 +0200
@@ -42,6 +42,7 @@
t1 BASE TABLE
t2 BASE TABLE
t1.ARZ
+t1.frm
t2.ARZ
t2.frm
#
@@ -60,6 +61,7 @@
flush tables;
rename table t2 to t0;
t0.ARZ
+t0.frm
t1.ARZ
t1.frm
#
@@ -77,6 +79,7 @@
flush tables;
drop table t1;
t0.ARZ
+t0.frm
#
# discover of table non-existance on drop
#
@@ -86,7 +89,7 @@
drop table t0;
show status like 'Handler_discover';
Variable_name Value
-Handler_discover 7
+Handler_discover 8
#
# Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE
#
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