diff --git a/include/thr_lock.h b/include/thr_lock.h index 363babeaa63..99bb7545006 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -121,6 +121,7 @@ typedef struct st_thr_lock { void (*get_status)(void*, int); /* When one gets a lock */ void (*copy_status)(void*,void*); void (*update_status)(void*); /* Before release of write */ + void (*restore_status)(void*); /* Before release of read */ my_bool (*check_status)(void *); } THR_LOCK; diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 5a506a010df..8560efd4ee1 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -922,6 +922,27 @@ SET @@myisam_repair_threads=1; SHOW VARIABLES LIKE 'myisam_repair%'; Variable_name Value myisam_repair_threads 1 +CREATE TABLE t1(a VARCHAR(16)); +INSERT INTO t1 VALUES('aaaaaaaa'),(NULL); +UPDATE t1 AS ta1, t1 AS ta2 SET ta1.a='aaaaaaaaaaaaaaaa'; +SELECT * FROM t1; +a +aaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaa +DROP TABLE t1; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1),(2); +UPDATE t1,t1 AS t2 SET t1.a=t1.a+2 WHERE t1.a=t2.a-1; +SELECT * FROM t1 ORDER BY a; +a +2 +3 +DROP TABLE t1; +CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=4100100100; +SHOW TABLE STATUS LIKE 't1'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 10 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4100100100 avg_row_length=70100 +DROP TABLE t1; End of 4.1 tests set storage_engine=MyISAM; drop table if exists t1,t2,t3; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 0898f7d9896..132886fa89a 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -847,6 +847,33 @@ DROP TABLE t1; SET @@myisam_repair_threads=1; SHOW VARIABLES LIKE 'myisam_repair%'; +# +# BUG#21310 - Trees in SQL causing a "crashed" table with MyISAM storage +# engine +# + +# A simplified test case that reflect crashed table issue. +CREATE TABLE t1(a VARCHAR(16)); +INSERT INTO t1 VALUES('aaaaaaaa'),(NULL); +UPDATE t1 AS ta1, t1 AS ta2 SET ta1.a='aaaaaaaaaaaaaaaa'; +SELECT * FROM t1; +DROP TABLE t1; + +# A test case that reflect wrong result set. +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1),(2); +UPDATE t1,t1 AS t2 SET t1.a=t1.a+2 WHERE t1.a=t2.a-1; +SELECT * FROM t1 ORDER BY a; +DROP TABLE t1; + +# +# Bug#24607 - MyISAM pointer size determined incorrectly +# +CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=4100100100; +--replace_column 5 X 6 X 7 X 9 X 10 X 11 X 12 X 13 X 14 X 16 X +SHOW TABLE STATUS LIKE 't1'; +DROP TABLE t1; + --echo End of 4.1 tests diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 2a1b94a363e..5ce7e99fbdc 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -757,8 +757,16 @@ void thr_unlock(THR_LOCK_DATA *data) } else lock->write.last=data->prev; - if (lock_type >= TL_WRITE_CONCURRENT_INSERT && lock->update_status) - (*lock->update_status)(data->status_param); + if (lock_type >= TL_WRITE_CONCURRENT_INSERT) + { + if (lock->update_status) + (*lock->update_status)(data->status_param); + } + else + { + if (lock->restore_status) + (*lock->restore_status)(data->status_param); + } if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count--; data->type=TL_UNLOCK; /* Mark unlocked */ diff --git a/scripts/fill_help_tables.sh b/scripts/fill_help_tables.sh index 1dff7edd268..e600d24032b 100644 --- a/scripts/fill_help_tables.sh +++ b/scripts/fill_help_tables.sh @@ -521,6 +521,7 @@ print < ¬_used_values, for (i=0 ; i < table_count ; i++) set_if_bigger(max_fields, fields_for_table[i]->elements); copy_field= new Copy_field[max_fields]; - - /* - Mark all copies of tables that are updates to ensure that - init_read_record() will not try to enable a cache on them - - The problem is that for queries like - - UPDATE t1, t1 AS t2 SET t1.b=t2.c WHERE t1.a=t2.a; - - the row buffer may contain things that doesn't match what is on disk - which will cause an error when reading a row. - (This issue is mostly relevent for MyISAM tables) - */ - for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) - { - TABLE *table=table_ref->table; - if ((tables_to_update & table->map) && - unique_table(thd, table_ref, update_tables)) - table->no_cache= 1; // Disable row cache - } DBUG_RETURN(thd->is_fatal_error != 0); } +/* + Check if table is safe to update on fly + + SYNOPSIS + safe_update_on_fly() + thd Thread handler + join_tab How table is used in join + all_tables List of tables + + NOTES + We can update the first table in join on the fly if we know that + a row in this table will never be read twice. This is true under + the following conditions: + + - We are doing a table scan and the data is in a separate file (MyISAM) or + if we don't update a clustered key. + + - We are doing a range scan and we don't update the scan key or + the primary key for a clustered table handler. + + - Table is not joined to itself. + + This function gets information about fields to be updated from + the TABLE::write_set bitmap. + + WARNING + This code is a bit dependent of how make_join_readinfo() works. + + RETURN + 0 Not safe to update + 1 Safe to update +*/ + +static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, + TABLE_LIST *table_ref, TABLE_LIST *all_tables) +{ + TABLE *table= join_tab->table; + if (unique_table(thd, table_ref, all_tables)) + return 0; + switch (join_tab->type) { + case JT_SYSTEM: + case JT_CONST: + case JT_EQ_REF: + return TRUE; // At most one matching row + case JT_REF: + case JT_REF_OR_NULL: + return !is_key_used(table, join_tab->ref.key, table->write_set); + case JT_ALL: + /* If range search on index */ + if (join_tab->quick) + return !join_tab->quick->is_keys_used(table->write_set); + /* If scanning in clustered key */ + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + table->s->primary_key < MAX_KEY) + return !is_key_used(table, table->s->primary_key, table->write_set); + return TRUE; + default: + break; // Avoid compler warning + } + return FALSE; + +} + + /* Initialize table for multi table @@ -1220,7 +1262,7 @@ multi_update::initialize_tables(JOIN *join) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (table == main_table) // First table in join { - if (safe_update_on_fly(join->join_tab)) + if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables)) { table_to_update= main_table; // Update table on the fly continue; @@ -1272,61 +1314,6 @@ multi_update::initialize_tables(JOIN *join) DBUG_RETURN(0); } -/* - Check if table is safe to update on fly - - SYNOPSIS - safe_update_on_fly - join_tab How table is used in join - - NOTES - We can update the first table in join on the fly if we know that - a row in this table will never be read twice. This is true under - the following conditions: - - - We are doing a table scan and the data is in a separate file (MyISAM) or - if we don't update a clustered key. - - - We are doing a range scan and we don't update the scan key or - the primary key for a clustered table handler. - - This function gets information about fields to be updated from - the TABLE::write_set bitmap. - - WARNING - This code is a bit dependent of how make_join_readinfo() works. - - RETURN - 0 Not safe to update - 1 Safe to update -*/ - -static bool safe_update_on_fly(JOIN_TAB *join_tab) -{ - TABLE *table= join_tab->table; - switch (join_tab->type) { - case JT_SYSTEM: - case JT_CONST: - case JT_EQ_REF: - return TRUE; // At most one matching row - case JT_REF: - case JT_REF_OR_NULL: - return !is_key_used(table, join_tab->ref.key, table->write_set); - case JT_ALL: - /* If range search on index */ - if (join_tab->quick) - return !join_tab->quick->is_keys_used(table->write_set); - /* If scanning in clustered key */ - if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && - table->s->primary_key < MAX_KEY) - return !is_key_used(table, table->s->primary_key, table->write_set); - return TRUE; - default: - break; // Avoid compler warning - } - return FALSE; -} - multi_update::~multi_update() { diff --git a/sql/table.cc b/sql/table.cc index d4289aa1dd3..cf2eb1705a5 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -780,6 +780,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, memcpy(comment_pos, disk_buff+read_length-com_length, com_length); fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); + if (share->fieldnames.count != share->fields) + goto err; fix_type_pointers(&interval_array, share->intervals, interval_count, &names); diff --git a/storage/myisam/mi_create.c b/storage/myisam/mi_create.c index 83f899dbbda..2b8cbcc7da5 100644 --- a/storage/myisam/mi_create.c +++ b/storage/myisam/mi_create.c @@ -850,18 +850,19 @@ uint mi_get_pointer_length(ulonglong file_length, uint def) if (file_length) /* If not default */ { #ifdef NOT_YET_READY_FOR_8_BYTE_POINTERS - if (file_length >= (longlong) 1 << 56) + if (file_length >= ULL(1) << 56) def=8; + else #endif - if (file_length >= (longlong) 1 << 48) + if (file_length >= ULL(1) << 48) def=7; - if (file_length >= (longlong) 1 << 40) + else if (file_length >= ULL(1) << 40) def=6; - else if (file_length >= (longlong) 1 << 32) + else if (file_length >= ULL(1) << 32) def=5; - else if (file_length >= (1L << 24)) + else if (file_length >= ULL(1) << 24) def=4; - else if (file_length >= (1L << 16)) + else if (file_length >= ULL(1) << 16) def=3; else def=2; diff --git a/storage/myisam/mi_dynrec.c b/storage/myisam/mi_dynrec.c index cae8f5b448c..642efbd4389 100644 --- a/storage/myisam/mi_dynrec.c +++ b/storage/myisam/mi_dynrec.c @@ -254,7 +254,7 @@ int _mi_write_blob_record(MI_INFO *info, const byte *record) #endif if (!(rec_buff=(byte*) my_alloca(reclength))) { - my_errno=ENOMEM; + my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(-1); } reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER), @@ -288,7 +288,7 @@ int _mi_update_blob_record(MI_INFO *info, my_off_t pos, const byte *record) #endif if (!(rec_buff=(byte*) my_alloca(reclength))) { - my_errno=ENOMEM; + my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(-1); } reclength=_mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER), diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c index 44aa02f6431..e822ea9e6da 100644 --- a/storage/myisam/mi_locking.c +++ b/storage/myisam/mi_locking.c @@ -338,6 +338,15 @@ void mi_update_status(void* param) } } + +void mi_restore_status(void *param) +{ + MI_INFO *info= (MI_INFO*) param; + info->state= &info->s->state.state; + info->append_insert_at_end= 0; +} + + void mi_copy_status(void* to,void *from) { ((MI_INFO*) to)->state= &((MI_INFO*) from)->save_state; diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index 7b08ddf27cc..afed5d05963 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -334,7 +334,13 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) for (j=0 ; j < share->keyinfo[i].keysegs; j++,pos++) { disk_pos=mi_keyseg_read(disk_pos, pos); - + if (pos->flag & HA_BLOB_PART && + ! (share->options & (HA_OPTION_COMPRESS_RECORD | + HA_OPTION_PACK_RECORD))) + { + my_errno= HA_ERR_CRASHED; + goto err; + } if (pos->type == HA_KEYTYPE_TEXT || pos->type == HA_KEYTYPE_VARTEXT1 || pos->type == HA_KEYTYPE_VARTEXT2) @@ -453,6 +459,13 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) offset+=share->rec[i].length; } share->rec[i].type=(int) FIELD_LAST; /* End marker */ + if (offset > share->base.reclength) + { + /* purecov: begin inspected */ + my_errno= HA_ERR_CRASHED; + goto err; + /* purecov: end */ + } if (! lock_error) { @@ -518,6 +531,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) share->lock.get_status=mi_get_status; share->lock.copy_status=mi_copy_status; share->lock.update_status=mi_update_status; + share->lock.restore_status= mi_restore_status; share->lock.check_status=mi_check_status; } } diff --git a/storage/myisam/mi_update.c b/storage/myisam/mi_update.c index b35c27d75ad..bea457d2e9a 100644 --- a/storage/myisam/mi_update.c +++ b/storage/myisam/mi_update.c @@ -196,7 +196,8 @@ err: save_errno=my_errno; if (changed) key_changed|= HA_STATE_CHANGED; - if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL) + if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_OUT_OF_MEM || + my_errno == HA_ERR_RECORD_FILE_FULL) { info->errkey= (int) i; flag=0; diff --git a/storage/myisam/mi_write.c b/storage/myisam/mi_write.c index 184270a0364..57c054f2de8 100644 --- a/storage/myisam/mi_write.c +++ b/storage/myisam/mi_write.c @@ -180,7 +180,7 @@ int mi_write(MI_INFO *info, byte *record) err: save_errno=my_errno; if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL || - my_errno == HA_ERR_NULL_IN_SPATIAL) + my_errno == HA_ERR_NULL_IN_SPATIAL || my_errno == HA_ERR_OUT_OF_MEM) { if (info->bulk_insert) { diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index a9cf4163c9e..dceccd10ae2 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -753,6 +753,7 @@ int mi_unique_comp(MI_UNIQUEDEF *def, const byte *a, const byte *b, my_bool null_are_equal); void mi_get_status(void* param, int concurrent_insert); void mi_update_status(void* param); +void mi_restore_status(void* param); void mi_copy_status(void* to,void *from); my_bool mi_check_status(void* param); void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);