diff options
Diffstat (limited to 'sql')
78 files changed, 3874 insertions, 451 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 7b56acbb773..a8fe9fc233f 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -42,9 +42,10 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h item_uniq.h \ item_create.h mysql_priv.h \ procedure.h sql_class.h sql_lex.h sql_list.h \ - sql_map.h sql_string.h unireg.h \ - field.h handler.h ha_isammrg.h ha_isam.h ha_myisammrg.h\ - ha_heap.h ha_myisam.h ha_berkeley.h\ + sql_manager.h sql_map.h sql_string.h unireg.h \ + field.h handler.h \ + ha_isammrg.h ha_isam.h ha_myisammrg.h\ + ha_heap.h ha_myisam.h ha_berkeley.h ha_innobase.h \ opt_range.h opt_ft.h \ sql_select.h structs.h table.h sql_udf.h hash_filo.h\ lex.h lex_symbol.h sql_acl.h sql_crypt.h md5.h \ @@ -55,7 +56,7 @@ mysqld_SOURCES = sql_lex.cc \ thr_malloc.cc item_create.cc \ field.cc key.cc sql_class.cc sql_list.cc \ net_serv.cc violite.c net_pkg.cc lock.cc my_lock.c \ - sql_string.cc sql_map.cc \ + sql_string.cc sql_manager.cc sql_map.cc \ mysqld.cc password.c hash_filo.cc hostname.cc \ convert.cc sql_parse.cc sql_yacc.yy \ sql_base.cc table.cc sql_select.cc sql_insert.cc \ @@ -66,6 +67,7 @@ mysqld_SOURCES = sql_lex.cc \ records.cc filesort.cc handler.cc \ ha_isam.cc ha_isammrg.cc ha_heap.cc \ ha_myisam.cc ha_myisammrg.cc ha_berkeley.cc \ + ha_innobase.cc \ sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 60830f4df37..35e503751af 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -37,12 +37,15 @@ transaction ?) - When using ALTER TABLE IGNORE, we should not start an transaction, but do everything wthout transactions. + - When we do rollback, we need to subtract the number of changed rows + from the updated tables. Testing of: - - ALTER TABLE - LOCK TABLES - - CHAR keys - BLOBS + - Mark tables that participate in a transaction so that they are not + closed during the transaction. We need to test what happens if + MySQL closes a table that is updated by a not commit transaction. */ @@ -57,19 +60,28 @@ #include <assert.h> #include <hash.h> #include "ha_berkeley.h" +#include "sql_manager.h" +#include <stdarg.h> #define HA_BERKELEY_ROWS_IN_TABLE 10000 /* to get optimization right */ #define HA_BERKELEY_RANGE_COUNT 100 #define HA_BERKELEY_MAX_ROWS 10000000 /* Max rows in table */ +/* extra rows for estimate_number_of_rows() */ +#define HA_BERKELEY_EXTRA_ROWS 100 + +/* Bits for share->status */ +#define STATUS_PRIMARY_KEY_INIT 1 +#define STATUS_ROW_COUNT_INIT 2 +#define STATUS_BDB_ANALYZE 4 const char *ha_berkeley_ext=".db"; -bool berkeley_skip=0; -u_int32_t berkeley_init_flags=0,berkeley_lock_type=DB_LOCK_DEFAULT; +bool berkeley_skip=0,berkeley_shared_data=0; +u_int32_t berkeley_init_flags= DB_PRIVATE, berkeley_lock_type=DB_LOCK_DEFAULT; ulong berkeley_cache_size; char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir; long berkeley_lock_scan_time=0; ulong berkeley_trans_retry=5; -ulong berkeley_lock_max; +ulong berkeley_max_lock; pthread_mutex_t bdb_mutex; static DB_ENV *db_env; @@ -85,8 +97,11 @@ TYPELIB berkeley_lock_typelib= {array_elements(berkeley_lock_names),"", static void berkeley_print_error(const char *db_errpfx, char *buffer); static byte* bdb_get_key(BDB_SHARE *share,uint *length, my_bool not_used __attribute__((unused))); -static BDB_SHARE *get_share(const char *table_name); -static void free_share(BDB_SHARE *share); +static BDB_SHARE *get_share(const char *table_name, TABLE *table); +static void free_share(BDB_SHARE *share, TABLE *table); +static void update_status(BDB_SHARE *share, TABLE *table); +static void berkeley_noticecall(DB_ENV *db_env, db_notices notice); + /* General functions */ @@ -106,6 +121,7 @@ bool berkeley_init(void) DBUG_RETURN(1); db_env->set_errcall(db_env,berkeley_print_error); db_env->set_errpfx(db_env,"bdb"); + db_env->set_noticecall(db_env, berkeley_noticecall); db_env->set_tmp_dir(db_env, berkeley_tmpdir); db_env->set_data_dir(db_env, mysql_data_home); if (berkeley_logdir) @@ -118,17 +134,19 @@ bool berkeley_init(void) db_env->set_cachesize(db_env, 0, berkeley_cache_size, 0); db_env->set_lk_detect(db_env, berkeley_lock_type); - if (berkeley_lock_max) - db_env->set_lk_max(db_env, berkeley_lock_max); + if (berkeley_max_lock) + db_env->set_lk_max(db_env, berkeley_max_lock); + if (db_env->open(db_env, berkeley_home, berkeley_init_flags | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | - DB_CREATE | DB_THREAD | DB_PRIVATE, 0666)) + DB_CREATE | DB_THREAD, 0666)) { db_env->close(db_env,0); db_env=0; } + (void) hash_init(&bdb_open_tables,32,0,0, (hash_get_key) bdb_get_key,0,0); pthread_mutex_init(&bdb_mutex,NULL); @@ -196,6 +214,48 @@ static void berkeley_print_error(const char *db_errpfx, char *buffer) sql_print_error("%s: %s",db_errpfx,buffer); } +static void berkeley_noticecall(DB_ENV *db_env, db_notices notice) +{ + switch (notice) + { + case DB_NOTICE_LOGFILE_CHANGED: + pthread_mutex_lock(&LOCK_manager); + manager_status |= MANAGER_BERKELEY_LOG_CLEANUP; + pthread_mutex_unlock(&LOCK_manager); + pthread_cond_signal(&COND_manager); + break; + } +} + +void berkeley_cleanup_log_files(void) +{ + DBUG_ENTER("berkeley_cleanup_log_files"); + char **names; + int error; + + /* XXX: Probably this should be done somewhere else, and + * should be tunable by the user. */ + if ((error = txn_checkpoint(db_env, 0, 0, 0))) + my_error(ER_ERROR_DURING_CHECKPOINT, MYF(0), error); + + if ((error = log_archive(db_env, &names, DB_ARCH_ABS, NULL)) != 0) + { + DBUG_PRINT("error", ("log_archive failed (error %d)", error)); + db_env->err(db_env, error, "log_archive: DB_ARCH_ABS"); + DBUG_VOID_RETURN; + } + + if (names) + { + char **np; + for (np = names; *np; ++np) + my_delete(*np, MYF(MY_WME)); + + free(names); + } + + DBUG_VOID_RETURN; +} /***************************************************************************** @@ -289,7 +349,7 @@ berkeley_key_cmp(TABLE *table, KEY *key_info, const char *key, uint key_length) if (*key != (table->record[0][key_part->null_offset] & key_part->null_bit) ? 0 : 1) return 1; - if (!*key++) // Null value + if (!*key++) // Null value continue; } if ((cmp=key_part->field->pack_cmp(key,key_part->length))) @@ -298,7 +358,7 @@ berkeley_key_cmp(TABLE *table, KEY *key_info, const char *key, uint key_length) key+=length; key_length-=length; } - return 0; + return 0; // Identical keys } @@ -340,7 +400,7 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) } /* Init table lock structure */ - if (!(share=get_share(name))) + if (!(share=get_share(name,table))) { my_free(rec_buff,MYF(0)); my_free(alloc_ptr,MYF(0)); @@ -350,7 +410,7 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) if ((error=db_create(&file, db_env, 0))) { - free_share(share); + free_share(share,table); my_free(rec_buff,MYF(0)); my_free(alloc_ptr,MYF(0)); my_errno=error; @@ -366,7 +426,7 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) 2 | 4), "main", DB_BTREE, open_mode,0)))) { - free_share(share); + free_share(share,table); my_free(rec_buff,MYF(0)); my_free(alloc_ptr,MYF(0)); my_errno=error; @@ -412,7 +472,6 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) } } } - /* Calculate pack_length of primary key */ if (!hidden_primary_key) { @@ -423,12 +482,9 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) ref_length+= key_part->field->max_packed_col_length(key_part->length); fixed_length_primary_key= (ref_length == table->key_info[primary_key].key_length); + share->status|=STATUS_PRIMARY_KEY_INIT; } - else - { - if (!share->primary_key_inited) - update_auto_primary_key(); - } + get_status(); DBUG_RETURN(0); } @@ -444,7 +500,7 @@ int ha_berkeley::close(void) if (key_file[i] && (error=key_file[i]->close(key_file[i],0))) result=error; } - free_share(share); + free_share(share,table); my_free(rec_buff,MYF(MY_ALLOW_ZERO_PTR)); my_free(alloc_ptr,MYF(MY_ALLOW_ZERO_PTR)); if (result) @@ -585,8 +641,8 @@ void ha_berkeley::unpack_key(char *record, DBT *key, uint index) This will never fail as the key buffer is pre allocated. */ -DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff, - const byte *record) +DBT *ha_berkeley::create_key(DBT *key, uint keynr, char *buff, + const byte *record, int key_length) { bzero((char*) key,sizeof(*key)); @@ -600,11 +656,11 @@ DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff, KEY *key_info=table->key_info+keynr; KEY_PART_INFO *key_part=key_info->key_part; KEY_PART_INFO *end=key_part+key_info->key_parts; - DBUG_ENTER("pack_key"); + DBUG_ENTER("create_key"); key->data=buff; - for ( ; key_part != end ; key_part++) + for ( ; key_part != end && key_length > 0; key_part++) { if (key_part->null_bit) { @@ -619,6 +675,7 @@ DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff, } buff=key_part->field->pack(buff,record + key_part->offset, key_part->length); + key_length-=key_part->length; } key->size= (buff - (char*) key->data); DBUG_DUMP("key",(char*) key->data, key->size); @@ -682,8 +739,8 @@ int ha_berkeley::write_row(byte * record) if (table->keys == 1) { - error=file->put(file, transaction, pack_key(&prim_key, primary_key, - key_buff, record), + error=file->put(file, transaction, create_key(&prim_key, primary_key, + key_buff, record), &row, key_type[primary_key]); } else @@ -695,8 +752,8 @@ int ha_berkeley::write_row(byte * record) if ((error=txn_begin(db_env, transaction, &sub_trans, 0))) break; DBUG_PRINT("trans",("starting subtransaction")); - if (!(error=file->put(file, sub_trans, pack_key(&prim_key, primary_key, - key_buff, record), + if (!(error=file->put(file, sub_trans, create_key(&prim_key, primary_key, + key_buff, record), &row, key_type[primary_key]))) { for (keynr=0 ; keynr < table->keys ; keynr++) @@ -704,8 +761,8 @@ int ha_berkeley::write_row(byte * record) if (keynr == primary_key) continue; if ((error=key_file[keynr]->put(key_file[keynr], sub_trans, - pack_key(&key, keynr, key_buff2, - record), + create_key(&key, keynr, key_buff2, + record), &prim_key, key_type[keynr]))) { last_dup_key=keynr; @@ -736,6 +793,8 @@ int ha_berkeley::write_row(byte * record) } if (error == DB_KEYEXIST) error=HA_ERR_FOUND_DUPP_KEY; + else if (!error) + changed_rows++; DBUG_RETURN(error); } @@ -791,7 +850,7 @@ int ha_berkeley::update_primary_key(DB_TXN *trans, bool primary_key_changed, { // Primary key changed or we are updating a key that can have duplicates. // Delete the old row and add a new one - pack_key(&old_key, primary_key, key_buff2, old_row); + create_key(&old_key, primary_key, key_buff2, old_row); if ((error=remove_key(trans, primary_key, old_row, (DBT *) 0, &old_key))) DBUG_RETURN(error); // This should always succeed if ((error=pack_row(&row, new_row, 0))) @@ -846,10 +905,10 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) } else { - pack_key(&prim_key, primary_key, key_buff, new_row); + create_key(&prim_key, primary_key, key_buff, new_row); if ((primary_key_changed=key_cmp(primary_key, old_row, new_row))) - pack_key(&old_prim_key, primary_key, primary_key_buff, old_row); + create_key(&old_prim_key, primary_key, primary_key_buff, old_row); else old_prim_key=prim_key; } @@ -874,7 +933,7 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) if ((error=remove_key(sub_trans, keynr, old_row, (DBT*) 0, &old_prim_key)) || (error=key_file[keynr]->put(key_file[keynr], sub_trans, - pack_key(&key, keynr, key_buff2, + create_key(&key, keynr, key_buff2, new_row), &prim_key, key_type[keynr]))) { @@ -933,7 +992,7 @@ int ha_berkeley::remove_key(DB_TXN *sub_trans, uint keynr, const byte *record, error=key_file[keynr]->del(key_file[keynr], sub_trans, keynr == primary_key ? prim_key : - pack_key(&key, keynr, key_buff2, record), + create_key(&key, keynr, key_buff2, record), 0); } else @@ -950,7 +1009,7 @@ int ha_berkeley::remove_key(DB_TXN *sub_trans, uint keynr, const byte *record, if (!(error=cursor->c_get(cursor, (keynr == primary_key ? prim_key : - pack_key(&key, keynr, key_buff2, record)), + create_key(&key, keynr, key_buff2, record)), (keynr == primary_key ? packed_record : prim_key), DB_GET_BOTH))) @@ -999,7 +1058,7 @@ int ha_berkeley::delete_row(const byte * record) if ((error=pack_row(&row, record, 0))) DBUG_RETURN((error)); - pack_key(&prim_key, primary_key, key_buff, record); + create_key(&prim_key, primary_key, key_buff, record); if (hidden_primary_key) keys|= (key_map) 1 << primary_key; @@ -1031,7 +1090,9 @@ int ha_berkeley::delete_row(const byte * record) if (error != DB_LOCK_DEADLOCK) break; } - DBUG_RETURN(0); + if (!error) + changed_rows--; + DBUG_RETURN(error); } @@ -1043,7 +1104,7 @@ int ha_berkeley::index_init(uint keynr) dbug_assert(cursor == 0); if ((error=file->cursor(key_file[keynr], transaction, &cursor, table->reginfo.lock_type > TL_WRITE_ALLOW_READ ? - 0 : 0))) + DB_RMW : 0))) cursor=0; // Safety bzero((char*) &last_key,sizeof(last_key)); DBUG_RETURN(error); @@ -1289,7 +1350,7 @@ void ha_berkeley::position(const byte *record) memcpy_fixed(ref, (char*) current_ident, BDB_HIDDEN_PRIMARY_KEY_LENGTH); } else - pack_key(&key, primary_key, ref, record); + create_key(&key, primary_key, ref, record); } @@ -1298,9 +1359,18 @@ void ha_berkeley::info(uint flag) DBUG_ENTER("info"); if (flag & HA_STATUS_VARIABLE) { - records = estimate_number_of_rows(); // Just to get optimisations right + records = share->rows; // Just to get optimisations right deleted = 0; } + if ((flag & HA_STATUS_CONST) || version != share->version) + { + version=share->version; + for (uint i=0 ; i < table->keys ; i++) + { + table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= + share->rec_per_key[i]; + } + } else if (flag & HA_STATUS_ERRKEY) errkey=last_dup_key; DBUG_VOID_RETURN; @@ -1377,6 +1447,7 @@ int ha_berkeley::external_lock(THD *thd, int lock_type) } } transaction= (DB_TXN*) thd->transaction.stmt.bdb_tid; + changed_rows=0; } else { @@ -1390,6 +1461,7 @@ int ha_berkeley::external_lock(THD *thd, int lock_type) current_row.data=0; } } + thread_safe_add(share->rows, changed_rows, &share->mutex); current_row.data=0; if (!--thd->transaction.bdb_lock_count) { @@ -1560,6 +1632,142 @@ ha_rows ha_berkeley::records_in_range(int keynr, DBUG_RETURN(rows <= 1.0 ? (ha_rows) 1 : (ha_rows) rows); } + +longlong ha_berkeley::get_auto_increment() +{ + longlong nr=1; // Default if error or new key + int error; + (void) ha_berkeley::extra(HA_EXTRA_KEYREAD); + ha_berkeley::index_init(table->next_number_index); + + if (!table->next_number_key_offset) + { // Autoincrement at key-start + error=ha_berkeley::index_last(table->record[1]); + } + else + { + DBT row; + bzero((char*) &row,sizeof(row)); + uint key_len; + KEY *key_info= &table->key_info[active_index]; + + /* Reading next available number for a sub key */ + ha_berkeley::create_key(&last_key, active_index, + key_buff, table->record[0], + table->next_number_key_offset); + /* Store for compare */ + memcpy(key_buff2, key_buff, (key_len=last_key.size)); + key_info->handler.bdb_return_if_eq= -1; + error=read_row(cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE), + table->record[1], active_index, &row, (DBT*) 0, 0); + key_info->handler.bdb_return_if_eq= 0; + if (!error && !berkeley_key_cmp(table, key_info, key_buff2, key_len)) + { + /* + Found matching key; Now search after next key, go one step back + and then we should have found the biggest key with the given + prefix + */ + (void) read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT_NODUP), + table->record[1], active_index, &row, (DBT*) 0, 0); + if (read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV), + table->record[1], active_index, &row, (DBT*) 0, 0) || + berkeley_key_cmp(table, key_info, key_buff2, key_len)) + error=1; // Something went wrong + } + } + nr=(longlong) + table->next_number_field->val_int_offset(table->rec_buff_length)+1; + ha_berkeley::index_end(); + (void) ha_berkeley::extra(HA_EXTRA_NO_KEYREAD); + return nr; +} + + +/**************************************************************************** + Analyzing, checking, and optimizing tables +****************************************************************************/ + + +static void print_msg(THD *thd, const char *table_name, const char *op_name, + const char *msg_type, const char *fmt, ...) +{ + String* packet = &thd->packet; + packet->length(0); + char msgbuf[256]; + msgbuf[0] = 0; + va_list args; + va_start(args,fmt); + + my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia + + DBUG_PRINT(msg_type,("message: %s",msgbuf)); + + net_store_data(packet, table_name); + net_store_data(packet, op_name); + net_store_data(packet, msg_type); + net_store_data(packet, msgbuf); + if (my_net_write(&thd->net, (char*)thd->packet.ptr(), + thd->packet.length())) + thd->killed=1; +} + + +int ha_berkeley::analyze(THD* thd, HA_CHECK_OPT* check_opt) +{ + DB_BTREE_STAT stat; + uint i; + + for (i=0 ; i < table->keys ; i++) + { + file->stat(key_file[i], (void*) &stat, 0, 0); + share->rec_per_key[i]= stat.bt_ndata / stat.bt_nkeys; + } + /* If hidden primary key */ + if (hidden_primary_key) + file->stat(file, (void*) &stat, 0, 0); + pthread_mutex_lock(&share->mutex); + share->rows=stat.bt_ndata; + share->status|=STATUS_BDB_ANALYZE; // Save status on close + share->version++; // Update stat in table + pthread_mutex_unlock(&share->mutex); + update_status(share,table); // Write status to file + return ((share->status & STATUS_BDB_ANALYZE) ? HA_ADMIN_FAILED : + HA_ADMIN_OK); +} + +int ha_berkeley::optimize(THD* thd, HA_CHECK_OPT* check_opt) +{ + return ha_berkeley::analyze(thd,check_opt); +} + + +int ha_berkeley::check(THD* thd, HA_CHECK_OPT* check_opt) +{ + char name_buff[FN_REFLEN]; + int error; + fn_format(name_buff,share->table_name,"", ha_berkeley_ext, 2 | 4); + if ((error=file->verify(file, name_buff, NullS, (FILE*) 0, + hidden_primary_key ? 0 : DB_NOORDERCHK))) + { + print_msg(thd, table->real_name, "check", "error", + "Got error %d checking file structure",error); + return HA_ADMIN_CORRUPT; + } + for (uint i=0 ; i < table->keys ; i++) + { + if ((error=file->verify(key_file[i], name_buff, NullS, (FILE*) 0, + DB_ORDERCHKONLY))) + { + print_msg(thd, table->real_name, "check", "error", + "Key %d was not in order",error); + return HA_ADMIN_CORRUPT; + } + } + return HA_ADMIN_OK; +} + /**************************************************************************** Handling the shared BDB_SHARE structure that is needed to provide table locking. @@ -1572,19 +1780,21 @@ static byte* bdb_get_key(BDB_SHARE *share,uint *length, return (byte*) share->table_name; } -static BDB_SHARE *get_share(const char *table_name) +static BDB_SHARE *get_share(const char *table_name, TABLE *table) { BDB_SHARE *share; pthread_mutex_lock(&bdb_mutex); uint length=(uint) strlen(table_name); if (!(share=(BDB_SHARE*) hash_search(&bdb_open_tables, table_name, length))) { - if ((share=(BDB_SHARE *) my_malloc(sizeof(*share)+length+1, + if ((share=(BDB_SHARE *) my_malloc(sizeof(*share)+length+1 + + sizeof(ha_rows)* table->keys, MYF(MY_WME | MY_ZEROFILL)))) { share->table_name_length=length; share->table_name=(char*) (share+1); strmov(share->table_name,table_name); + share->rec_per_key= (ha_rows*) (share+1); if (hash_insert(&bdb_open_tables, (char*) share)) { pthread_mutex_unlock(&bdb_mutex); @@ -1600,11 +1810,14 @@ static BDB_SHARE *get_share(const char *table_name) return share; } -static void free_share(BDB_SHARE *share) +static void free_share(BDB_SHARE *share, TABLE *table) { pthread_mutex_lock(&bdb_mutex); if (!--share->use_count) { + update_status(share,table); + if (share->status_block) + share->status_block->close(share->status_block,0); hash_delete(&bdb_open_tables, (gptr) share); thr_lock_delete(&share->lock); pthread_mutex_destroy(&share->mutex); @@ -1613,20 +1826,124 @@ static void free_share(BDB_SHARE *share) pthread_mutex_unlock(&bdb_mutex); } +/* + Get status information that is stored in the 'status' sub database + and the max used value for the hidden primary key. +*/ -void ha_berkeley::update_auto_primary_key() +void ha_berkeley::get_status() { - pthread_mutex_lock(&share->mutex); - if (!share->primary_key_inited) + if (!test_all_bits(share->status,(STATUS_PRIMARY_KEY_INIT | + STATUS_ROW_COUNT_INIT))) { - (void) extra(HA_EXTRA_KEYREAD); - index_init(primary_key); - if (!index_last(table->record[1])) - share->auto_ident=uint5korr(current_ident); - index_end(); - (void) extra(HA_EXTRA_NO_KEYREAD); + pthread_mutex_lock(&share->mutex); + if (!(share->status & STATUS_PRIMARY_KEY_INIT)) + { + (void) extra(HA_EXTRA_KEYREAD); + index_init(primary_key); + if (!index_last(table->record[1])) + share->auto_ident=uint5korr(current_ident); + index_end(); + (void) extra(HA_EXTRA_NO_KEYREAD); + } + if (! share->status_block) + { + char name_buff[FN_REFLEN]; + uint open_mode= (((table->db_stat & HA_READ_ONLY) ? DB_RDONLY : 0) + | DB_THREAD); + fn_format(name_buff, share->table_name,"", ha_berkeley_ext, 2 | 4); + if (!db_create(&share->status_block, db_env, 0)) + { + if (!share->status_block->open(share->status_block, name_buff, + "status", DB_BTREE, open_mode, 0)) + { + share->status_block->close(share->status_block, 0); + share->status_block=0; + } + } + } + if (!(share->status & STATUS_ROW_COUNT_INIT) && share->status_block) + { + share->org_rows=share->rows= + table->max_rows ? table->max_rows : HA_BERKELEY_MAX_ROWS; + if (!file->cursor(share->status_block, 0, &cursor, 0)) + { + DBT row; + char rec_buff[64],*pos=rec_buff; + bzero((char*) &row,sizeof(row)); + bzero((char*) &last_key,sizeof(last_key)); + row.data=rec_buff; + row.size=sizeof(rec_buff); + row.flags=DB_DBT_USERMEM; + if (!cursor->c_get(cursor, &last_key, &row, DB_FIRST)) + { + uint i; + share->org_rows=share->rows=uint4korr(pos); pos+=4; + for (i=0 ; i < table->keys ; i++) + { + share->rec_per_key[i]=uint4korr(pos); pos+=4; + } + } + cursor->c_close(cursor); + } + cursor=0; // Safety + } + share->status|= STATUS_PRIMARY_KEY_INIT | STATUS_ROW_COUNT_INIT; + pthread_mutex_unlock(&share->mutex); } - pthread_mutex_unlock(&share->mutex); +} + + +static void update_status(BDB_SHARE *share, TABLE *table) +{ + DBUG_ENTER("update_status"); + if (share->rows != share->org_rows || + (share->status & STATUS_BDB_ANALYZE)) + { + pthread_mutex_lock(&share->mutex); + if (!share->status_block) + { + /* + Create sub database 'status' if it doesn't exist from before + (This '*should*' always exist for table created with MySQL) + */ + + char name_buff[FN_REFLEN]; + if (db_create(&share->status_block, db_env, 0)) + goto end; + share->status_block->set_flags(share->status_block,0); + if (share->status_block->open(share->status_block, + fn_format(name_buff,share->table_name,"", + ha_berkeley_ext,2 | 4), + "status", DB_BTREE, + DB_THREAD | DB_CREATE, my_umask)) + goto end; + } + { + uint i; + DBT row,key; + char rec_buff[4+MAX_KEY*sizeof(ulong)], *pos=rec_buff; + const char *key_buff="status"; + + bzero((char*) &row,sizeof(row)); + bzero((char*) &key,sizeof(key)); + row.data=rec_buff; + key.data=(void*) key_buff; + key.size=sizeof(key_buff); + row.flags=key.flags=DB_DBT_USERMEM; + int4store(pos,share->rows); pos+=4; + for (i=0 ; i < table->keys ; i++) + { + int4store(pos,share->rec_per_key[i]); pos+=4; + } + row.size=(uint) (pos-rec_buff); + (void) share->status_block->put(share->status_block, 0, &key, &row, 0); + share->status&= ~STATUS_BDB_ANALYZE; + } +end: + pthread_mutex_unlock(&share->mutex); + } + DBUG_VOID_RETURN; } /* @@ -1636,14 +1953,7 @@ void ha_berkeley::update_auto_primary_key() ha_rows ha_berkeley::estimate_number_of_rows() { - ulonglong max_ident; - ulonglong max_rows=table->max_rows ? table->max_rows : HA_BERKELEY_MAX_ROWS; - if (!hidden_primary_key) - return (ha_rows) max_rows; - pthread_mutex_lock(&share->mutex); - max_ident=share->auto_ident+EXTRA_RECORDS; - pthread_mutex_unlock(&share->mutex); - return (ha_rows) min(max_ident,max_rows); + return share->rows + HA_BERKELEY_EXTRA_ROWS; } #endif /* HAVE_BERKELEY_DB */ diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index 3cfcab82ecf..4ee682664a5 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -27,11 +27,13 @@ typedef struct st_berkeley_share { ulonglong auto_ident; + ha_rows rows, org_rows, *rec_per_key; THR_LOCK lock; pthread_mutex_t mutex; char *table_name; + DB *status_block; uint table_name_length,use_count; - bool primary_key_inited; + uint status,version; } BDB_SHARE; @@ -49,7 +51,8 @@ class ha_berkeley: public handler BDB_SHARE *share; ulong int_option_flag; ulong alloced_rec_buff_length; - uint primary_key,last_dup_key, hidden_primary_key; + ulong changed_rows; + uint primary_key,last_dup_key, hidden_primary_key, version; bool fixed_length_row, fixed_length_primary_key, key_read; bool fix_rec_buff_for_blob(ulong length); byte current_ident[BDB_HIDDEN_PRIMARY_KEY_LENGTH]; @@ -58,7 +61,8 @@ class ha_berkeley: public handler int pack_row(DBT *row,const byte *record, bool new_row); void unpack_row(char *record, DBT *row); void ha_berkeley::unpack_key(char *record, DBT *key, uint index); - DBT *pack_key(DBT *key, uint keynr, char *buff, const byte *record); + DBT *create_key(DBT *key, uint keynr, char *buff, const byte *record, + int key_length = MAX_KEY_LENGTH); DBT *pack_key(DBT *key, uint keynr, char *buff, const byte *key_ptr, uint key_length); int remove_key(DB_TXN *trans, uint keynr, const byte *record, @@ -79,8 +83,9 @@ class ha_berkeley: public handler HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | HA_LONGLONG_KEYS | HA_NULL_KEY | HA_HAVE_KEY_READ_ONLY | HA_BLOB_KEY | HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE), - last_dup_key((uint) -1) + HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | + HA_AUTO_PART_KEY), + last_dup_key((uint) -1),version(0) { } ~ha_berkeley() {} @@ -123,6 +128,10 @@ class ha_berkeley: public handler int reset(void); int external_lock(THD *thd, int lock_type); void position(byte *record); + int analyze(THD* thd,HA_CHECK_OPT* check_opt); + int optimize(THD* thd, HA_CHECK_OPT* check_opt); + int check(THD* thd, HA_CHECK_OPT* check_opt); + ha_rows records_in_range(int inx, const byte *start_key,uint start_key_len, enum ha_rkey_function start_search_flag, @@ -135,7 +144,7 @@ class ha_berkeley: public handler THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); - void update_auto_primary_key(); + void get_status(); inline void get_auto_primary_key(byte *to) { ulonglong tmp; @@ -144,11 +153,12 @@ class ha_berkeley: public handler int5store(to,share->auto_ident); pthread_mutex_unlock(&share->mutex); } + longlong ha_berkeley::get_auto_increment(); }; -extern bool berkeley_skip; +extern bool berkeley_skip, berkeley_shared_data; extern u_int32_t berkeley_init_flags,berkeley_lock_type,berkeley_lock_types[]; -extern ulong berkeley_cache_size, berkeley_lock_max; +extern ulong berkeley_cache_size, berkeley_max_lock; extern char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir; extern long berkeley_lock_scan_time; extern TYPELIB berkeley_lock_typelib; diff --git a/sql/ha_innobase.cc b/sql/ha_innobase.cc new file mode 100644 index 00000000000..58e3d540d04 --- /dev/null +++ b/sql/ha_innobase.cc @@ -0,0 +1,2431 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + & Innobase Oy + + - This file is modified from ha_berkeley.cpp of the MySQL distribution - + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* This file defines the Innobase handler: the interface between MySQL and +Innobase */ + +/* TODO list for the Innobase handler: + - How to check for deadlocks if Innobase tables are used alongside + other MySQL table types? Should MySQL communicate the locking + information also to Innobase of any object it locks, or should + we use a timeout to detect a deadlock? Solution: there is no problem, + because MySQL requires that all table-level locks are reserved + at transaction startup (conservative locking). Deadlocks cannot + occur because of these locks. + - Innobase cmp function should call MySQL cmp for most datatypes? + Except currently for binary strings and 32-bit integers? + Solution: MySQL has conversion functions which currently convert + any datatype to a binary string which can be compared as binary + strings, except for character strings where we must identify + lower case with upper case. + - MySQL parser should know SELECT FOR UPDATE and SELECT WITH SHARED LOCKS + for Innobase interface. We probably will make the non-locking + consistent read the default in Innobase like in Oracle. + - Does function next_same require matching of the whole last field, + or it is enough that the common prefix of the last field matches? + Answer: it is enough that the common prefix matches. + - Is the 'ref' field in handle pre-allocated to be big enough? Primary key + values can be very long! Answer: we can reallocate it to be long enough. + - DELETE FROM TABLE must not drop the table like it does now, because + consistent read will not work then! Answer: there is probably a flag + in MySQL which we can use to prevent dropping of a table in this case. +-------Oct 24, 2000 + - Update trx pointers in 'prebuilt' when the transaction object of + the handle changes. Answer: in 'external_lock' we always set the pointers + to point to the trx of the current user. Note that if a user has + disconnected, then another thd at exactly the same machine address may + be created: just comparing the thd pointers does not really tell if it + actually is the same user using the handle! + - ANSI SQL specifies that if an SQL statement fails because of + an error (like duplicate key, division by zero), the whole statement + must be rolled back. Currently an error like this only rolls + back a single insert of a row, or a single row update. +-------Oct 25, 2000 + - There are some autonomous threads within Innobase, like purge (= gc), + ibuf merge, and recovery threads, which may have to open tables. + Then they need type information for the table columns from MySQL. + Could they call 'openfrm' in MySQL? Then they should be properly + initialized pthreads, I presume. +-------Oct 30, 2000 + - Dropping of table in Innobase fails if there is a lock set on it: + Innobase then gives an error number to MySQL but MySQL seems to drop + the table from its own data dictionary anyway, causing incoherence + between the two databases. +-------Oct 31, 2000 + - In sql_table.cpp in quick_rm_table, the path has to be 'unpacked' + also after the latter sprintf to change / to \ in the path name. + - Innobase currently includes the path to a table name: the path should + actually be dropped off, because we may move a whole database to a new + directory. +-------Nov 1, 2000 + - Ask from Monty what error codes 'record not found' and 'end of table' + exactly mean and when read and fetch operations should return them. +-------Nov 2, 2000 + - Find out why in 'test-ATIS' in 'bench' directory, the client does + not seem to receive rows sent by the server: maybe Innobase does not + handle 'decimal' type correctly. Answer: Innobase did not return + 'record not found' and 'end of table' with right meanings. +-------Nov 3, 2000 + - 'pack' adds field length in front of string type fields: fields of + different length are not correctly alphabetically ordered. + - 'pack' removes leading (or was it, trailing) spaces from string type + fields: maybe it would be better to store them as they are, if the + field is not declared as varchar. + - MySQL 'read_last_with_key' does not allow Innobase to return + HA_ERR_KEY_NOT_FOUND, even when we try to read from an empty + table. +-------Nov 4, 2000 + - MySQL should know when row id is added as uniquefier to a table for + update and delete to work. + - Innobase does not really support MySQL varchar type yet. +-------Nov 16, 2000 + - We use memcpy to store float and double types to Innobase: this + makes database files not portable between big-endian and little-endian + machines. +-------Nov 17, 2000 + - We added call of innobase_close_connection to THD::~THD in sql_class.cpp. +-------Nov 21, 2000 + - In mysql_delete, in sql_delete.cpp, we must be able to prevent + MySQL from using generate_table to do a delete: consistent read does + not allow this. Currently, MySQL uses generate_table in DELETE FROM ... + if autocommit is on. +-------Nov 24, 2000 + - Make the SELECT in an update a locking read. + - Add a deadlock error message to MySQL. + - Add 'cannot drop locked table' error message to MySQL. +-------Nov 26, 2000 + - Find out why MySQL sometimes prints error message about read locks and + write locks associated with a handle. + - Find out why MySQL at shutdown prints error message 'Error on delete of + ......pid (Errcode : 2). +-------Nov 30, 2000 + - MySQL calls innobase_end (shutdown) before it knows that all handles + have been closed. It declares MySQL shutdown complete before Innobase + shutdown is complete. +*/ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#ifdef HAVE_INNOBASE_DB +#include <m_ctype.h> +#include <assert.h> +#include <hash.h> +#include <myisampack.h> + + +/* Include necessary Innobase headers */ +extern "C" { +#include <univmysql.i> +#include <srv0start.h> +#include <srv0srv.h> +#include <trx0roll.h> +#include <trx0trx.h> +#include <row0ins.h> +#include <row0mysql.h> +#include <row0sel.h> +#include <row0upd.h> +#include <log0log.h> +#include <dict0crea.h> +#include <btr0cur.h> +#include <btr0btr.h> +} +#include "ha_innobase.h" + + +#define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */ +#define HA_INNOBASE_RANGE_COUNT 100 + +const char* ha_innobase_ext = ".ib"; + +bool innobase_skip = 0; +uint innobase_init_flags = 0; +ulong innobase_cache_size = 0; + +long innobase_mirrored_log_groups, innobase_mirrored_log_groups, + innobase_log_file_size, innobase_log_buffer_size, + innobase_buffer_pool_size, innobase_additional_mem_pool_size, + innobase_file_io_threads; + +char *innobase_data_home_dir, *innobase_data_file_path; +char *innobase_log_group_home_dir, *innobase_log_arch_dir; +bool innobase_flush_log_at_trx_commit,innobase_log_archive; + +/* innobase_data_file_path=ibdata:15,idata2:1,... */ + +/* The following counter is used to convey information to Innobase +about server activity: in selects it is not sensible to call +srv_active_wake_master_thread after each fetch or search, we only do +it every INNOBASE_WAKE_INTERVAL'th step. */ + +ulong innobase_select_counter = 0; +#define INNOBASE_WAKE_INTERVAL 32 + + +char* innobase_home = NULL; + +pthread_mutex_t innb_mutex; + +static HASH innb_open_tables; + +static byte* innobase_get_key(INNOBASE_SHARE *share,uint *length, + my_bool not_used __attribute__((unused))); +static INNOBASE_SHARE *get_share(const char *table_name); +static void free_share(INNOBASE_SHARE *share); +static void innobase_print_error(const char* db_errpfx, char* buffer); + +/* General functions */ + +/************************************************************************ +Converts an Innobase error code to a MySQL error code. */ +static +int +convert_error_code_to_mysql( +/*========================*/ + /* out: MySQL error code */ + int error) /* in: Innobase error code */ +{ + if (error == DB_SUCCESS) { + + return(0); + + } else if (error == (int) DB_DUPLICATE_KEY) { + + return(HA_ERR_FOUND_DUPP_KEY); + + } else if (error == (int) DB_RECORD_NOT_FOUND) { + + return(HA_ERR_NO_ACTIVE_RECORD); + + } else if (error == (int) DB_ERROR) { + + return(HA_ERR_NO_ACTIVE_RECORD); + + } else if (error == (int) DB_DEADLOCK) { + + return(HA_ERR_UNSUPPORTED); + + } else if (error == (int) DB_OUT_OF_FILE_SPACE) { + + return(HA_ERR_RECORD_FILE_FULL); + + } else if (error == (int) DB_TABLE_IS_BEING_USED) { + + return(HA_ERR_WRONG_COMMAND); + + } else if (error == (int) DB_TABLE_NOT_FOUND) { + + return(HA_ERR_KEY_NOT_FOUND); + + } else if (error == (int) DB_TOO_BIG_RECORD) { + + return(HA_ERR_TO_BIG_ROW); + + } else { + assert(0); + + return(0); + } +} + +/************************************************************************* +Gets the Innobase transaction handle for a MySQL handler object, creates +an Innobase transaction struct if the corresponding MySQL thread struct still +lacks one. */ +inline +trx_t* +check_trx_exists( +/*=============*/ + /* out: Innobase transaction handle */ + THD* thd) /* in: user thread handle */ +{ + trx_t* trx; + + assert(thd != NULL); + + trx = (trx_t*) thd->transaction.innobase_trx_handle; + + if (trx == NULL) { + trx = trx_allocate_for_mysql(); + + thd->transaction.innobase_trx_handle = trx; + } + + return(trx); +} + +/************************************************************************* +Updates the user_thd field in a handle and also allocates a new Innobase +transaction handle if needed, and updates the transaction fields in the +prebuilt struct. */ + +int +ha_innobase::update_thd( +/*====================*/ + /* out: 0 or error code */ + THD* thd) /* in: thd to use the handle */ +{ + trx_t* trx; + + trx = check_trx_exists(thd); + + if (innobase_prebuilt != NULL) { + + row_update_prebuilt_trx((row_prebuilt_t*) innobase_prebuilt, + trx); + } + + user_thd = thd; + + return(0); +} + +/************************************************************************* +Opens an Innobase database. */ + +bool +innobase_init(void) +/*===============*/ + /* out: TRUE if error */ +{ + int err; + + DBUG_ENTER("innobase_init"); + + if (!innobase_home) { + innobase_home = mysql_real_data_home; + + printf("Innobase home is %s\n", innobase_home); + } + + err = innobase_start_or_create_for_mysql(innobase_home); + + if (err != DB_SUCCESS) { + + return(1); + } + (void) hash_init(&innobase_open_tables,32,0,0, + (hash_get_key) innobase_get_key,0,0); + pthread_mutex_init(&innobase_mutex,NULL); + return(0); +} + +/*********************************************************************** +Closes an Innobase database. */ + +bool +innobase_end(void) +/*==============*/ + /* out: TRUE if error */ +{ + int err; + + DBUG_ENTER("innobase_end"); + + err = innobase_shutdown_for_mysql(); + + if (err != DB_SUCCESS) { + + return(1); + } + + return(0); +} + +/******************************************************************** +Flushes Innobase logs to disk and makes a checkpoint. Really, a commit +flushes logs, and the name of this function should be innobase_checkpoint. */ + +bool +innobase_flush_logs(void) +/*=====================*/ + /* out: TRUE if error */ +{ + bool result = 0; + + DBUG_ENTER("innobase_flush_logs"); + + log_make_checkpoint_at(ut_dulint_max, TRUE); + + DBUG_RETURN(result); +} + +/********************************************************************* +Commits a transaction in an Innobase database. */ + +int +innobase_commit( +/*============*/ + /* out: 0 or error number */ + THD* thd) /* in: MySQL thread handle of the user for whom + the transaction should be committed */ +{ + int error = 0; + + DBUG_ENTER("innobase_commit"); + DBUG_PRINT("trans", ("ending transaction")); + + check_trx_exists(thd); + + trx_commit_for_mysql((trx_t*)(thd->transaction.innobase_trx_handle)); + +#ifndef DBUG_OFF + if (error) { + DBUG_PRINT("error", ("error: %d", error)); + } +#endif + /* Tell Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + DBUG_RETURN(error); +} + +/********************************************************************* +Rolls back a transaction in an Innobase database. */ + +int +innobase_rollback( +/*==============*/ + /* out: 0 or error number */ + THD* thd) /* in: handle to the MySQL thread of the user + whose transaction should be rolled back */ +{ + int error = 0; + + DBUG_ENTER("innobase_rollback"); + DBUG_PRINT("trans", ("aborting transaction")); + + check_trx_exists(thd); + + error = trx_rollback_for_mysql((trx_t*) + (thd->transaction.innobase_trx_handle)); + + DBUG_RETURN(convert_error_code_to_mysql(error)); +} + +/********************************************************************* +Frees a possible Innobase trx object associated with the current +THD. */ + +int +innobase_close_connection( +/*======================*/ + /* out: 0 or error number */ + THD* thd) /* in: handle to the MySQL thread of the user + whose transaction should be rolled back */ +{ + if (NULL != thd->transaction.innobase_trx_handle) { + + trx_free_for_mysql((trx_t*) + (thd->transaction.innobase_trx_handle)); + } + + return(0); +} + +/********************************************************************** +Prints an error message. */ +static +void +innobase_print_error( +/*=================*/ + const char* db_errpfx, /* in: error prefix text */ + char* buffer) /* in: error text */ +{ + sql_print_error("%s: %s", db_errpfx, buffer); +} + + +/***************************************************************************** +** Innobase database tables +*****************************************************************************/ + +/******************************************************************** +??????????????? */ + +const char** +ha_innobase::bas_ext() const +/*========================*/ + /* out: ?????????? */ +{ + static const char* ext[] = {ha_innobase_ext, NullS}; + return(ext); +} + +/********************************************************************* +Creates and opens a handle to a table which already exists in an Innnobase +database. */ + +int +ha_innobase::open( +/*==============*/ + /* out: 1 if error, 0 if success */ + const char* name, /* in: table name */ + int mode, /* in: not used */ + int test_if_locked) /* in: not used */ +{ + int error = 0; + uint buff_len; + + DBUG_ENTER("ha_innobase::open"); + + UT_NOT_USED(mode); + UT_NOT_USED(test_if_locked); + + user_thd = NULL; + + if (!(share=get_share(name))) + DBUG_RETURN(1); + + /* Create buffers for packing the fields of a record; + Why table->reclength did not work here? + obviously, because char fields when packed actually became + 1 byte longer, when we also stored the string length as + the first byte. */ + + buff_len = table->reclength + table->max_key_length + + MAX_REF_PARTS * 2; + + if (!(byte*) my_multi_malloc(MYF(MY_WME), + &rec_buff, buff_len, + &upd_buff, buff_len, + &key_val_buff, buff_len, + NullS)) + { + free_share(share); + DBUG_RETURN(1); + } + + /* MySQL allocates the buffer for ref */ + + ref_length = buff_len; + + /* Get pointer to a table object in Innobase dictionary cache */ + + if (NULL == (innobase_table_handle + = dict_table_get((char*)name, NULL))) { + + free_share(share); + my_free((char*) rec_buff, MYF(0)); + my_errno = ENOENT; + DBUG_RETURN(1); + } + + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + + /* Allocate a persistent cursor struct */ + + innobase_prebuilt = row_create_prebuilt((dict_table_t*) + innobase_table_handle); + primary_key = 0; + + if (!row_table_got_default_clust_index((dict_table_t*) + innobase_table_handle)) { + /* If we automatically created the clustered index, + then MySQL does not know about it and it must not be aware + of the index used on scan, to avoid checking if we update + the column of the index. The column is the row id in + the automatical case, and it will not be updated. */ + + key_used_on_scan = primary_key; + } else { + assert(key_used_on_scan == MAX_KEY); + } + + /* Init table lock structure */ + thr_lock_data_init(&share->lock,&lock,(void*) 0); + DBUG_RETURN(0); +} + +/********************************************************************* +Does nothing. */ + +void +ha_innobase::initialize(void) +/*=========================*/ +{ +} + +/********************************************************************** +Closes a handle to an Innobase table. */ + +int +ha_innobase::close(void) +/*====================*/ + /* out: error number */ +{ + DBUG_ENTER("ha_innobase::close"); + + row_prebuilt_free((row_prebuilt_t*) innobase_prebuilt); + + my_free((char*) rec_buff, MYF(0)); + free_share(share); + + /* Tell Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + DBUG_RETURN(0); +} + +/* The following accessor functions should really be inside MySQL code! */ + +/****************************************************************** +Gets field offset for a field in a table. */ +inline +uint +get_field_offset( +/*=============*/ + /* out: offset */ + TABLE* table, /* in: MySQL table object */ + Field* field) /* in: MySQL field object */ +{ + return((uint) (field->ptr - (char*) table->record[0])); +} + +/****************************************************************** +Checks if a field in a record is SQL NULL. Uses the record format +information in table to track the null bit in record. */ +inline +uint +field_in_record_is_null( +/*====================*/ + /* out: 1 if NULL, 0 otherwise */ + TABLE* table, /* in: MySQL table object */ + Field* field, /* in: MySQL field object */ + char* record) /* in: a row in MySQL format */ +{ + int null_offset; + + if (!field->null_ptr) { + + return(0); + } + + null_offset = (uint) ((char*) field->null_ptr + - (char*) table->record[0]); + + if (record[null_offset] & field->null_bit) { + + return(1); + } + + return(0); +} + +/****************************************************************** +Sets a field in a record to SQL NULL. Uses the record format +information in table to track the null bit in record. */ +inline +void +set_field_in_record_to_null( +/*========================*/ + TABLE* table, /* in: MySQL table object */ + Field* field, /* in: MySQL field object */ + char* record) /* in: a row in MySQL format */ +{ + int null_offset; + + null_offset = (uint) ((char*) field->null_ptr + - (char*) table->record[0]); + + record[null_offset] = record[null_offset] | field->null_bit; +} + +/****************************************************************** +Resets SQL NULL bits in a record to zero. */ +inline +void +reset_null_bits( +/*============*/ + TABLE* table, /* in: MySQL table object */ + char* record) /* in: a row in MySQL format */ +{ + bzero(record, table->null_bytes); +} + +extern "C" { +/***************************************************************** +This function is used to compare two data fields for which the data type +is such that we must use MySQL code to compare them. */ + +int +innobase_mysql_cmp( +/*===============*/ + /* out: 1, 0, -1, if a is greater, + equal, less than b, respectively */ + int mysql_type, /* in: MySQL type */ + unsigned char* a, /* in: data field */ + unsigned int a_length, /* in: data field length, + not UNIV_SQL_NULL */ + unsigned char* b, /* in: data field */ + unsigned int b_length) /* in: data field length, + not UNIV_SQL_NULL */ +{ + float f_1; + float f_2; + double d_1; + double d_2; + int swap_flag = 1; + enum_field_types mysql_tp; + + assert(a_length != UNIV_SQL_NULL); + assert(b_length != UNIV_SQL_NULL); + + mysql_tp = (enum_field_types) mysql_type; + + switch (mysql_tp) { + + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + return(my_sortncmp((const char*) a, a_length, + (const char*) b, b_length)); + case FIELD_TYPE_FLOAT: + memcpy(&f_1, a, sizeof(float)); + memcpy(&f_2, b, sizeof(float)); + + if (f_1 > f_2) { + return(1); + } else if (f_2 > f_1) { + return(-1); + } + + return(0); + + case FIELD_TYPE_DOUBLE: + memcpy(&d_1, a, sizeof(double)); + memcpy(&d_2, b, sizeof(double)); + + if (d_1 > d_2) { + return(1); + } else if (d_2 > d_1) { + return(-1); + } + + return(0); + + case FIELD_TYPE_DECIMAL: + /* Remove preceding spaces */ + for (; a_length && *a == ' '; a++, a_length--); + for (; b_length && *b == ' '; b++, b_length--); + + if (*a == '-') { + if (*b != '-') { + return(-1); + } + + a++; b++; + a_length--; + b_length--; + + swap_flag = -1; + + } else if (*b == '-') { + + return(1); + } + + while (a_length > 0 && (*a == '+' || *a == '0')) { + a++; a_length--; + } + + while (b_length > 0 && (*b == '+' || *b == '0')) { + b++; b_length--; + } + + if (a_length != b_length) { + if (a_length < b_length) { + return(-swap_flag); + } + + return(swap_flag); + } + + while (a_length > 0 && *a == *b) { + + a++; b++; a_length--; + } + + if (a_length == 0) { + + return(0); + } + + if (*a > *b) { + return(swap_flag); + } + + return(-swap_flag); + default: + assert(0); + } + + return(0); +} +} + +/****************************************************************** +Decides of MySQL types whether Innobase can internally compare them +using its own comparison functions, or whether Innobase must call MySQL +cmp function to compare them. */ +inline +ulint +innobase_cmp_type( +/*==============*/ + /* out: DATA_BINARY, DATA_VARCHAR, or DATA_MYSQL */ + Field* field) /* in: MySQL field */ +{ + /* The following asserts check that MySQL type code fits in + one byte: this is used in ibuf */ + + assert((ulint)FIELD_TYPE_STRING < 256); + assert((ulint)FIELD_TYPE_VAR_STRING < 256); + assert((ulint)FIELD_TYPE_DOUBLE < 256); + assert((ulint)FIELD_TYPE_FLOAT < 256); + assert((ulint)FIELD_TYPE_DECIMAL < 256); + + switch (field->type()) { + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_STRING: if (field->flags & BINARY_FLAG) { + + return(DATA_BINARY); + } else if (strcmp( + default_charset_info->name, + "latin1") == 0) { + return(DATA_VARCHAR); + } else { + return(DATA_MYSQL); + } + case FIELD_TYPE_LONG: + case FIELD_TYPE_LONGLONG: + case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_INT24: + case FIELD_TYPE_DATE: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_YEAR: + case FIELD_TYPE_NEWDATE: + case FIELD_TYPE_ENUM: + case FIELD_TYPE_SET: + return(DATA_BINARY); + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_DECIMAL: + return(DATA_MYSQL); + default: + assert(0); + } + + return(0); +} + +/****************************************************************** +Packs a non-SQL-NULL field data for storage in Innobase. Usually this +'packing' is just memcpy, but for an integer it is also converted +to a big-endian, sign bit negated form. */ +inline +byte* +pack_for_ib( +/*========*/ + /* out: pointer to the end of the packed data + in the buffer */ + byte* buf, /* in/out: pointer to buffer where to pack */ + Field* field, /* in: MySQL field object */ + byte* data) /* in: data to pack */ +{ + uint len; + uint i; + + switch (field->type()) { + case FIELD_TYPE_LONG: + case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_INT24: + case FIELD_TYPE_LONGLONG: + len = field->pack_length(); break; + case FIELD_TYPE_VAR_STRING: + len = field->field_length; + + /* Scan away trailing spaces */ + for (i = 0; i < len; i++) { + if (data[len - i - 1] != ' ') { + break; + } + } + + memcpy(buf, data, len - i); + return(buf + len - i); + case FIELD_TYPE_STRING: + /* We store character strings with no + conversion */ + len = field->field_length; + memcpy(buf, data, len); + return(buf + len); + case FIELD_TYPE_DOUBLE: + memcpy(buf, data, sizeof(double)); + + return(buf + sizeof(double)); + case FIELD_TYPE_FLOAT: + memcpy(buf, data, sizeof(float)); + + return(buf + sizeof(float)); + default: + return((byte*) field->pack((char*) buf, + (const char*) data)); + } + + /* Store integer data in Innobase in a big-endian format, sign + bit negated */ + + for (i = 0; i < len; i++) { + buf[len - 1 - i] = data[i]; + } + + buf[0] = buf[0] ^ 128; + + return(buf + len); +} + +/****************************************************************** +Packs a non-SQL-NULL field data in a key value for storage in Innobase. +TODO: find out what is the difference between keypack and pack. */ +inline +byte* +keypack_for_ib( +/*===========*/ + /* out: pointer to the end of the packed data + in the buffer */ + byte* buf, /* in/out: buffer where to pack */ + Field* field, /* in: field object */ + byte* data, /* in: data to pack */ + uint len) /* in: length of the data to pack */ +{ + switch (field->type()) { + case FIELD_TYPE_LONG: + case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_INT24: + case FIELD_TYPE_LONGLONG: + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_FLOAT: + return(pack_for_ib(buf, field, data)); + default: + return((byte*) field->keypack((char*) buf, + (const char*) data, len)); + } +} + +/****************************************************************** +Unpacks a non-SQL-NULL field data stored in Innobase. */ +inline +void +unpack_for_ib( +/*==========*/ + byte* dest, /* in/out: buffer where to unpack */ + Field* field, /* in: field object */ + byte* ptr, /* in: data to unpack */ + uint data_len)/* in: length of the data */ +{ + uint len; + uint i; + + switch (field->type()) { + case FIELD_TYPE_LONG: + case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_INT24: + case FIELD_TYPE_LONGLONG: + len = field->pack_length(); break; + case FIELD_TYPE_VAR_STRING: + len = field->field_length; + /* Pad trailing characters with spaces */ + for (i = data_len; i < len; i++) { + dest[i] = ' '; + } + memcpy(dest, ptr, data_len); + + return; + case FIELD_TYPE_STRING: + /* We store character strings with no + conversion */ + len = field->field_length; + memcpy(dest, ptr, len); + + return; + case FIELD_TYPE_DOUBLE: + memcpy(dest, ptr, sizeof(double)); + + return; + case FIELD_TYPE_FLOAT: + memcpy(dest, ptr, sizeof(float)); + + return; + default: + field->unpack((char*) dest, (const char*) ptr); + + return; + } + + /* Get integer data from Innobase in a little-endian format, sign + bit restored to normal */ + + for (i = 0; i < len; i++) { + dest[len - 1 - i] = ptr[i]; + } + + dest[len - 1] = dest[len - 1] ^ 128; +} + +/*********************************************************************** +Stores a key value for a row to a buffer. */ + +uint +ha_innobase::store_key_val_for_row( +/*===============================*/ + /* out: key value length as stored in buff */ + uint keynr, /* in: key number */ + char* buff, /* in/out: buffer for the key value (in MySQL + format) */ + const byte* record) /* in: row in MySQL format */ +{ + KEY* key_info = table->key_info + keynr; + KEY_PART_INFO* key_part = key_info->key_part; + KEY_PART_INFO* end = key_part + key_info->key_parts; + char* buff_start = buff; + + DBUG_ENTER("store_key_val_for_row"); + + for (; key_part != end; key_part++) { + + if (key_part->null_bit) { + /* Store 0 if the key part is a NULL part */ + + if (record[key_part->null_offset] + & key_part->null_bit) { + *buff++ =0; + continue; + } + + *buff++ = 1; + } + + memcpy(buff, record + key_part->offset, key_part->length); + buff += key_part->length; + } + + DBUG_RETURN(buff - buff_start); +} + +/****************************************************************** +Convert a row in MySQL format to a row in Innobase format. Uses rec_buff +of the handle. */ + +void +ha_innobase::convert_row_to_innobase( +/*=================================*/ + dtuple_t* row, /* in/out: row in Innobase format */ + char* record) /* in: row in MySQL format */ +{ + Field* field; + dfield_t* dfield; + uint n_fields; + ulint len; + byte* old_ptr; + byte* ptr; + uint i; + + n_fields = table->fields; + + ptr = rec_buff; + + for (i = 0; i < n_fields; i++) { + field = table->field[i]; + dfield = dtuple_get_nth_field_noninline(row, i); + + old_ptr = ptr; + + if (field->null_ptr && field_in_record_is_null(table, + field, record)) { + len = UNIV_SQL_NULL; + } else { + ptr = pack_for_ib(ptr, field, (byte*) record + + get_field_offset(table, + field)); + len = ptr - old_ptr; + } + + dfield_set_data_noninline(dfield, old_ptr, len); + } +} + +/****************************************************************** +Convert a row in Innobase format to a row in MySQL format. */ + +void +ha_innobase::convert_row_to_mysql( +/*==============================*/ + char* record, /* in/out: row in MySQL format */ + dtuple_t* row) /* in: row in Innobase format */ +{ + Field* field; + dfield_t* dfield; + byte* ptr; + uint n_fields; + uint len; + uint i; + + reset_null_bits(table, record); + + n_fields = table->fields; + + for (i = 0; i < n_fields; i++) { + field = table->field[i]; + dfield = dtuple_get_nth_field_noninline(row, i); + + len = dfield_get_len_noninline(dfield); + + if (len != UNIV_SQL_NULL) { + + ptr = (byte*) dfield_get_data_noninline(dfield); + + unpack_for_ib((byte*) + record + get_field_offset(table, field), + field, ptr, len); + } else { + set_field_in_record_to_null(table, field, record); + } + } +} + +/******************************************************************** +Converts a key value stored in MySQL format to an Innobase dtuple. +The last field of the key value may be just a prefix of a fixed length +field: hence the parameter key_len. */ + +dtuple_t* +ha_innobase::convert_key_to_innobase( +/*=================================*/ + dtuple_t* tuple, /* in/out: an Innobase dtuple which + must contain enough fields to be + able to store the key value */ + byte* buf, /* in/out: buffer where to store converted + field data */ + dict_index_t* index, /* in: Innobase index handle */ + KEY* key, /* in: MySQL key definition */ + byte* key_ptr,/* in: MySQL key value */ + int key_len)/* in: MySQL key value length */ +{ + KEY_PART_INFO* key_part = key->key_part; + KEY_PART_INFO* end = key_part + key->key_parts; + uint offset; + dfield_t* dfield; + byte* old_buf; + ulint n_fields = 0; + + DBUG_ENTER("convert_key_to_innobase"); + + /* Permit us to access any field in the tuple (ULINT_MAX): */ + + dtuple_set_n_fields(tuple, ULINT_MAX); + + dfield = dtuple_get_nth_field_noninline(tuple, 0); + + for (; key_part != end && key_len > 0; key_part++) { + n_fields++; + + offset = 0; + + if (key_part->null_bit) { + offset = 1; + if (*key_ptr != 0) { + dfield_set_data_noninline(dfield, NULL, + UNIV_SQL_NULL); + goto next_part; + } + + /* Is there a bug in ha_berkeley.cpp here? There + key_ptr is advanced one byte here. ???????????? */ + } + + old_buf = buf; + buf = keypack_for_ib(buf, key_part->field, key_ptr + offset, + key_part->length); + + dfield_set_data_noninline(dfield, old_buf, + (ulint) (buf - old_buf)); + next_part: + key_ptr += key_part->store_length; + key_len -= key_part->store_length; + + if (key_len < 0) { + /* The last field in key was not a complete + field but a prefix of it */ + + assert(dfield_get_len_noninline(dfield) + != UNIV_SQL_NULL); + assert((int)(buf - old_buf) + key_len >= 0); + + dfield_set_data_noninline(dfield, old_buf, + (ulint) ((buf - old_buf) + key_len)); + } + + dfield++; + } + + dict_index_copy_types(tuple, index, n_fields); + + /* We set the length of tuple to n_fields: we assume that + the memory area allocated for it is big enough (usually + bigger than n_fields). */ + + dtuple_set_n_fields(tuple, n_fields); + + DBUG_RETURN(tuple); +} + +/************************************************************************ +Stores a row in an Innobase database, to the table specified in this +handle. */ + +int +ha_innobase::write_row( +/*===================*/ + /* out: error code */ + byte* record) /* in: a row in MySQL format */ +{ + trx_t* trx; + dtuple_t* row; + int error; + + DBUG_ENTER("write_row"); + + statistic_increment(ha_write_count, &LOCK_status); + + if (table->time_stamp) { + update_timestamp(record + table->time_stamp - 1); + } + if (table->next_number_field && record == table->record[0]) { + update_auto_increment(); + } + + assert(user_thd->transaction.innobase_trx_handle); + trx = check_trx_exists(user_thd); + + /* Convert the MySQL row into an Innobase dtuple format */ + + row = row_get_prebuilt_insert_row( + (row_prebuilt_t*) innobase_prebuilt, + (dict_table_t*) innobase_table_handle, trx); + + convert_row_to_innobase(row, (char*) record); + + error = row_insert_for_mysql((row_prebuilt_t*)innobase_prebuilt, trx); + + error = convert_error_code_to_mysql(error); + + /* Tell Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + DBUG_RETURN(error); +} + +/************************************************************************** +Checks which fields have changed in a row and stores information +of them to an update vector. */ + +int +ha_innobase::calc_row_difference( +/*=============================*/ + /* out: error number or 0 */ + upd_t* uvect, /* in/out: update vector */ + byte* old_row, /* in: old row in MySQL format */ + byte* new_row) /* in: new row in MySQL format */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + Field* field; + uint n_fields; + ulint o_len; + ulint n_len; + byte* o_ptr; + byte* n_ptr; + byte* old_ptr; + byte* ptr; + uint i; + upd_field_t* ufield; + ulint n_changed = 0; + + n_fields = table->fields; + + /* We use upd_buff to pack changed fields */ + ptr = upd_buff; + + for (i = 0; i < n_fields; i++) { + field = table->field[i]; + + o_ptr = old_row + get_field_offset(table, field); + n_ptr = new_row + get_field_offset(table, field); + o_len = field->pack_length(); + n_len = field->pack_length(); + + if (field->null_ptr) { + if (field_in_record_is_null(table, field, + (char*) old_row)) { + o_len = UNIV_SQL_NULL; + } + if (field_in_record_is_null(table, field, + (char*) new_row)) { + n_len = UNIV_SQL_NULL; + } + } + + if (o_len != n_len || (o_len != UNIV_SQL_NULL && + 0 != memcmp(o_ptr, n_ptr, o_len))) { + /* The field has changed */ + + if (n_len != UNIV_SQL_NULL) { + old_ptr = ptr; + ptr = pack_for_ib(ptr, field, n_ptr); + n_len = ptr - old_ptr; + } + + ufield = uvect->fields + n_changed; + + dfield_set_data_noninline(&(ufield->new_val), old_ptr, + n_len); + ufield->exp = NULL; + ufield->field_no = dict_table_get_nth_col_pos( + (dict_table_t*) + innobase_table_handle, i); + n_changed++; + } + } + + uvect->n_fields = n_changed; + uvect->info_bits = 0; + + return(0); +} + +/************************************************************************** +Updates a row given as a parameter to a new value. Note that we are given +whole rows, not just the fields which are updated: this incurs some +overhead for CPU when we check which fields are actually updated. +TODO: currently Innobase does not prevent the 'Halloween problem': +in a searched update a single row can get updated several times +if its index columns are updated! */ + +int +ha_innobase::update_row( +/*====================*/ + /* out: error number or 0 */ + const byte* old_row, /* in: old row in MySQL format */ + byte* new_row) /* in: new row in MySQL format */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + trx_t* trx; + upd_t* uvect; + int error = 0; + + DBUG_ENTER("update_row"); + + assert(user_thd->transaction.innobase_trx_handle); + trx = check_trx_exists(user_thd); + + uvect = row_get_prebuilt_update_vector( + prebuilt, + (dict_table_t*) innobase_table_handle, trx); + + /* Build old row in the Innobase format (uses rec_buff of the + handle) */ + + convert_row_to_innobase(prebuilt->row_tuple, (char*) old_row); + + /* Build an update vector from the modified fields in the rows + (uses upd_buff of the handle) */ + + calc_row_difference(uvect, (byte*) old_row, new_row); + + /* This is not a delete */ + prebuilt->upd_node->is_delete = FALSE; + + error = row_update_for_mysql((row_prebuilt_t*) innobase_prebuilt, + (dict_table_t*) innobase_table_handle, trx); + + error = convert_error_code_to_mysql(error); + + /* Tell Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + DBUG_RETURN(error); +} + +/************************************************************************** +Deletes a row given as the parameter. */ + +int +ha_innobase::delete_row( +/*====================*/ + /* out: error number or 0 */ + const byte* record) /* in: a row in MySQL format */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + trx_t* trx; + upd_t* uvect; + int error = 0; + + DBUG_ENTER("update_row"); + + assert(user_thd->transaction.innobase_trx_handle); + trx = check_trx_exists(user_thd); + + uvect = row_get_prebuilt_update_vector( + prebuilt, + (dict_table_t*) innobase_table_handle, trx); + + /* Build old row in the Innobase format (uses rec_buff of the + handle) */ + + convert_row_to_innobase(prebuilt->row_tuple, (char*) record); + + /* This is a delete */ + + prebuilt->upd_node->is_delete = TRUE; + + error = row_update_for_mysql((row_prebuilt_t*) innobase_prebuilt, + (dict_table_t*) innobase_table_handle, trx); + + error = convert_error_code_to_mysql(error); + + /* Tell the Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + DBUG_RETURN(error); +} + +/********************************************************************** +Initializes a handle to use an index. */ + +int +ha_innobase::index_init( +/*====================*/ + /* out: 0 or error number */ + uint keynr) /* in: key (index) number */ +{ + int error = 0; + DBUG_ENTER("index_init"); + + change_active_index(keynr); + + DBUG_RETURN(error); +} + +/********************************************************************** +?????????????????????????????????? */ + +int +ha_innobase::index_end(void) +/*========================*/ +{ + int error = 0; + DBUG_ENTER("index_end"); + + /* Tell Innobase server that there might be work for utility + threads: */ + + srv_active_wake_master_thread(); + + DBUG_RETURN(error); +} + +/************************************************************************* +Converts a search mode flag understood by MySQL to a flag understood +by Innobase. */ +inline +ulint +convert_search_mode_to_innobase( +/*============================*/ + enum ha_rkey_function find_flag) +{ + switch (find_flag) { + case HA_READ_KEY_EXACT: return(PAGE_CUR_GE); + /* the above does not require the index to be UNIQUE */ + case HA_READ_KEY_OR_NEXT: return(PAGE_CUR_GE); + case HA_READ_KEY_OR_PREV: return(PAGE_CUR_LE); + case HA_READ_AFTER_KEY: return(PAGE_CUR_G); + case HA_READ_BEFORE_KEY: return(PAGE_CUR_L); + case HA_READ_PREFIX: return(PAGE_CUR_GE); + case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE); + /* the above PREFIX flags mean that the last + field in the key value may just be a prefix + of the complete fixed length field */ + default: assert(0); + } + + return(0); +} + +/************************************************************************** +Positions an index cursor to the index specified in the handle. Fetches the +row if any. */ + +int +ha_innobase::index_read( +/*====================*/ + /* out: 0, HA_ERR_KEY_NOT_FOUND, + or error number */ + byte* buf, /* in/out: buffer for the returned + row */ + const byte* key_ptr, /* in: key value; if this is NULL + we position the cursor at the + start or end of index */ + uint key_len, /* in: key value length */ + enum ha_rkey_function find_flag)/* in: search flags from my_base.h */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + ulint mode; + dict_index_t* index; + btr_pcur_t* pcur; + KEY* key; + ulint match_mode = 0; + int error; + ulint ret; + trx_t* trx; + mtr_t mtr; + + DBUG_ENTER("index_read"); + statistic_increment(ha_read_key_count, &LOCK_status); + + /* TODO: currently we assume all reads perform consistent read! */ + /* prebuilt->consistent_read = TRUE; */ + + assert(user_thd->transaction.innobase_trx_handle); + trx = check_trx_exists(user_thd); + + pcur = prebuilt->pcur; + + key = table->key_info + active_index; + + index = prebuilt->index; + + if (key_ptr) { + convert_key_to_innobase(prebuilt->search_tuple, key_val_buff, + index, key, (unsigned char*) key_ptr, + (int) key_len); + } else { + /* We position the cursor to the last or the first entry + in the index */ + + dtuple_set_n_fields(prebuilt->search_tuple, 0); + } + + mode = convert_search_mode_to_innobase(find_flag); + + match_mode = 0; + + if (find_flag == HA_READ_KEY_EXACT) { + match_mode = ROW_SEL_EXACT; + + } else if (find_flag == HA_READ_PREFIX + || find_flag == HA_READ_PREFIX_LAST) { + match_mode = ROW_SEL_EXACT_PREFIX; + } + + last_match_mode = match_mode; + + /* Start an Innobase mini-transaction, which carries the + latch information of the read operation */ + + mtr_start_noninline(&mtr); + + ret = row_search_for_mysql(prebuilt->row_tuple, + mode, prebuilt, match_mode, + trx, &mtr, 0); + + if (ret == DB_SUCCESS) { + convert_row_to_mysql((char*) buf, prebuilt->row_tuple); + error = 0; + table->status = 0; + + } else if (ret == DB_RECORD_NOT_FOUND) { + error = HA_ERR_KEY_NOT_FOUND; + table->status = STATUS_NOT_FOUND; + + } else if (ret == DB_END_OF_INDEX) { + error = HA_ERR_KEY_NOT_FOUND; + table->status = STATUS_NOT_FOUND; + } else { + error = convert_error_code_to_mysql(ret); + table->status = STATUS_NOT_FOUND; + } + + mtr_commit(&mtr); + + innobase_select_counter++; + + if (innobase_select_counter % INNOBASE_WAKE_INTERVAL == 0) { + srv_active_wake_master_thread(); + } + + DBUG_RETURN(error); +} + +/************************************************************************ +Changes the active index of a handle. */ + +int +ha_innobase::change_active_index( +/*=============================*/ + /* out: 0 or error code */ + uint keynr) /* in: use this index */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + KEY* key; + + statistic_increment(ha_read_key_count, &LOCK_status); + + DBUG_ENTER("index_read_idx"); + + active_index = keynr; + + if (table->keys > 0) { + key = table->key_info + active_index; + + prebuilt->index = dict_table_get_index_noninline( + (dict_table_t*) innobase_table_handle, + key->name); + } else { + assert(keynr == 0); + prebuilt->index = dict_table_get_first_index_noninline( + (dict_table_t*) innobase_table_handle); + } + + assert(prebuilt->index); + + return(0); +} + +/************************************************************************** +Positions an index cursor to the index specified in keynr. Fetches the +row if any. */ +/* ??? This is only used to read whole keys ??? */ + +int +ha_innobase::index_read_idx( +/*========================*/ + /* out: error number or 0 */ + byte* buf, /* in/out: buffer for the returned + row */ + uint keynr, /* in: use this index */ + const byte* key, /* in: key value; if this is NULL + we position the cursor at the + start or end of index */ + uint key_len, /* in: key value length */ + enum ha_rkey_function find_flag)/* in: search flags from my_base.h */ +{ + change_active_index(keynr); + + return(index_read(buf, key, key_len, find_flag)); +} + +/*************************************************************************** +Reads the next or previous row from a cursor, which must have previously been +positioned using index_read. */ + +int +ha_innobase::general_fetch( +/*=======================*/ + /* out: 0, HA_ERR_END_OF_FILE, or error + number */ + byte* buf, /* in/out: buffer for next row in MySQL + format */ + uint direction, /* in: ROW_SEL_NEXT or ROW_SEL_PREV */ + uint match_mode) /* in: 0, ROW_SEL_EXACT, or + ROW_SEL_EXACT_PREFIX */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + ulint ret; + trx_t* trx; + int error = 0; + mtr_t mtr; + + DBUG_ENTER("general_fetch"); + statistic_increment(ha_read_next_count, &LOCK_status); + + trx = check_trx_exists(user_thd); + + mtr_start_noninline(&mtr); + + ret = row_search_for_mysql(prebuilt->row_tuple, 0, prebuilt, + match_mode, trx, &mtr, direction); + if (ret == DB_SUCCESS) { + convert_row_to_mysql((char*) buf, prebuilt->row_tuple); + error = 0; + table->status = 0; + + } else if (ret == DB_RECORD_NOT_FOUND) { + error = HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + + } else if (ret == DB_END_OF_INDEX) { + error = HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + } else { + error = convert_error_code_to_mysql(ret); + table->status = STATUS_NOT_FOUND; + } + + mtr_commit(&mtr); + + innobase_select_counter++; + + if (innobase_select_counter % INNOBASE_WAKE_INTERVAL == 0) { + srv_active_wake_master_thread(); + } + + DBUG_RETURN(error); +} + +/*************************************************************************** +Reads the next row from a cursor, which must have previously been +positioned using index_read. */ + +int +ha_innobase::index_next( +/*====================*/ + /* out: 0, HA_ERR_END_OF_FILE, or error + number */ + byte* buf) /* in/out: buffer for next row in MySQL + format */ +{ + return(general_fetch(buf, ROW_SEL_NEXT, 0)); +} + +/*********************************************************************** +Reads the next row matching to the key value given as the parameter. */ + +int +ha_innobase::index_next_same( +/*=========================*/ + /* out: 0, HA_ERR_END_OF_FILE, or error + number */ + byte* buf, /* in/out: buffer for the row */ + const byte* key, /* in: key value */ + uint keylen) /* in: key value length */ +{ + assert(last_match_mode != 0); + + return(general_fetch(buf, ROW_SEL_NEXT, last_match_mode)); +} + +/*************************************************************************** +Reads the previous row from a cursor, which must have previously been +positioned using index_read. */ + +int +ha_innobase::index_prev( +/*====================*/ + /* out: 0, HA_ERR_END_OF_FILE, or error + number */ + byte* buf) /* in/out: buffer for previous row in MySQL + format */ +{ + return(general_fetch(buf, ROW_SEL_PREV, 0)); +} + +/************************************************************************ +Positions a cursor on the first record in an index and reads the +corresponding row to buf. */ + +int +ha_innobase::index_first( +/*=====================*/ + /* out: 0, HA_ERR_KEY_NOT_FOUND, or error code */ + byte* buf) /* in/out: buffer for the row */ +{ + int error; + + DBUG_ENTER("index_first"); + statistic_increment(ha_read_first_count, &LOCK_status); + + error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY); + + DBUG_RETURN(error); +} + +/************************************************************************ +Positions a cursor on the last record in an index and reads the +corresponding row to buf. */ + +int +ha_innobase::index_last( +/*====================*/ + /* out: 0, HA_ERR_END_OF_FILE, or error code */ + byte* buf) /* in/out: buffer for the row */ +{ + int error; + + DBUG_ENTER("index_first"); + statistic_increment(ha_read_first_count, &LOCK_status); + + error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY); + + /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */ + + if (error == HA_ERR_KEY_NOT_FOUND) { + error = HA_ERR_END_OF_FILE; + } + + DBUG_RETURN(error); +} + +/******************************************************************** +Initialize a table scan. */ + +int +ha_innobase::rnd_init( +/*==================*/ + /* out: 0 or error number */ + bool scan) /* in: ???????? */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + + change_active_index(primary_key); + + prebuilt->start_of_scan = TRUE; + + return(0); +} + +/********************************************************************* +Ends a table scan ???????????????? */ + +int +ha_innobase::rnd_end(void) +/*======================*/ + /* out: 0 or error number */ +{ + return(index_end()); +} + +/********************************************************************* +Reads the next row in a table scan (also used to read the FIRST row +in a table scan). */ + +int +ha_innobase::rnd_next( +/*==================*/ + /* out: 0, HA_ERR_END_OF_FILE, or error number */ + byte* buf) /* in/out: returns the row in this buffer, + in MySQL format */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + int error; + + DBUG_ENTER("rnd_next"); + statistic_increment(ha_read_rnd_next_count, &LOCK_status); + + if (prebuilt->start_of_scan) { + error = index_first(buf); + if (error == HA_ERR_KEY_NOT_FOUND) { + error = HA_ERR_END_OF_FILE; + } + prebuilt->start_of_scan = FALSE; + } else { + error = index_next(buf); + } + + DBUG_RETURN(error); +} + +/************************************************************************** +Fetches a row from the table based on a reference. TODO: currently we use +'ref_stored_len' of the handle as the key length. This may change. */ + +int +ha_innobase::rnd_pos( +/*=================*/ + /* out: 0, HA_ERR_KEY_NOT_FOUND, or error code */ + byte* buf, /* in/out: buffer for the row */ + byte* pos) /* in: primary key value in MySQL format */ +{ + int error; + + DBUG_ENTER("rnd_pos"); + statistic_increment(ha_read_rnd_count, &LOCK_status); + + assert(table->keys > 0); + + /* The following assert states that the cursor is positioned + to the primary index in this function: this cannot be used to + position the cursor to a secondary index! */ + + assert(active_index == primary_key); + + error = index_read(buf, pos, ref_stored_len, HA_READ_KEY_EXACT); + + DBUG_RETURN(error); +} + +/************************************************************************* +Stores a reference to a given row to 'ref' field of the handle. */ + +void +ha_innobase::position( +/*==================*/ + const byte* record) /* in: row in MySQL format */ +{ + uint len; + + assert(table->keys > 0); + + len = store_key_val_for_row(primary_key, (char*) ref, record); + + assert(len <= ref_length); + + ref_stored_len = len; +} + +/************************************************************************* +Returns various information to MySQL interpreter, in various fields +of the handle object. */ + +void +ha_innobase::info( +/*==============*/ + uint flag) /* in: what information MySQL requests */ +{ + DBUG_ENTER("info"); + + if (flag & HA_STATUS_VARIABLE) { + records = HA_INNOBASE_ROWS_IN_TABLE; // Just to get optimisations right + deleted = 0; + + } else if (flag & HA_STATUS_ERRKEY) { + + errkey = -1; /* TODO: get the key number from Innobase */ + } + + DBUG_VOID_RETURN; +} + +int ha_innobase::extra(enum ha_extra_function operation) +{ + return 0; +} + +int ha_innobase::reset(void) +{ + return 0; +} + +/********************************************************************** +As MySQL will execute an external lock for every new table it uses +we can use this to store the pointer to the THD in the handle. We use this +also in explicit locking of tables by request of the user. */ + +int +ha_innobase::external_lock( +/*=======================*/ + THD* thd, /* in: handle to the user thread */ + int lock_type) /* in: lock type */ +{ + int error = 0; + + DBUG_ENTER("ha_innobase::external_lock"); + + update_thd(thd); + + DBUG_RETURN(error); +} + +/* Currently, the following does nothing in Innobase: */ +THR_LOCK_DATA **ha_innobase::store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type) +{ + /*??????????????????*/ + + *to++= &lock; + + return(to); +} + +/********************************************************************* +Creates a table definition to an Innobase database. */ +static +int +create_table_def( +/*=============*/ + trx_t* trx, /* in: Innobase transaction handle */ + TABLE* form, /* in: information on table + columns and indexes */ + const char* table_name) /* in: table name */ +{ + Field* field; + dict_table_t* table; + ulint n_cols; + int error; + ulint col_type; + ulint i; + + DBUG_ENTER("create_table_def"); + DBUG_PRINT("enter", ("table_name: %s", table_name)); + + n_cols = form->fields; + + /* The '0' below specifies that everything is currently + created in tablespace 0 */ + + table = dict_mem_table_create((char*) table_name, 0, n_cols); + + for (i = 0; i < n_cols; i++) { + field = form->field[i]; + + col_type = innobase_cmp_type(field); + + dict_mem_table_add_col(table, (char*) field->field_name, + col_type, (ulint)field->type(), + field->pack_length(), 0); + } + + error = row_create_table_for_mysql(table, trx); + + error = convert_error_code_to_mysql(error); + + DBUG_RETURN(error); +} + +/********************************************************************* +Creates an index in an Innobase database. */ +static +int +create_sub_table( +/*=============*/ + trx_t* trx, /* in: Innobase transaction handle */ + TABLE* form, /* in: information on table + columns and indexes */ + const char* table_name, /* in: table name */ + uint key_num) /* in: index number */ +{ + dict_index_t* index; + int error; + ulint n_fields; + KEY* key; + KEY_PART_INFO* key_part; + ulint ind_type; + ulint i; + + DBUG_ENTER("create_sub_table"); + + key = form->key_info + key_num; + + n_fields = key->key_parts; + + ind_type = 0; + + if (key_num == 0) { + /* We assume that the clustered index is always + created first: */ + ind_type = ind_type | DICT_CLUSTERED; + } + + if (key->flags & HA_NOSAME ) { + ind_type = ind_type | DICT_UNIQUE; + } + + /* The '0' below specifies that everything in Innobase is currently + created in tablespace 0 */ + + index = dict_mem_index_create((char*) table_name, key->name, 0, + ind_type, n_fields); + for (i = 0; i < n_fields; i++) { + key_part = key->key_part + i; + + /* We assume all fields should be sorted in ascending + order, hence the '0': */ + dict_mem_index_add_field(index, + (char*) key_part->field->field_name, 0); + } + + error = row_create_index_for_mysql(index, trx); + + error = convert_error_code_to_mysql(error); + + DBUG_RETURN(error); +} + +/********************************************************************* +Creates an index to an Innobase table when the user has defined no +index. */ +static +int +create_index_when_no_index( +/*=======================*/ + trx_t* trx, /* in: Innobase transaction handle */ + const char* table_name) /* in: table name */ +{ + dict_index_t* index; + int error; + + DBUG_ENTER("create_index_when_no_index"); + + /* The first '0' below specifies that everything in Innobase is + currently created in file space 0 */ + + index = dict_mem_index_create((char*) table_name, "GEN_CLUST_INDEX", + 0, DICT_CLUSTERED, 0); + error = row_create_index_for_mysql(index, trx); + + error = convert_error_code_to_mysql(error); + + DBUG_RETURN(error); +} + +/********************************************************************* +Creates a new table to an Innobase database. */ + +int +ha_innobase::create( +/*================*/ + /* out: error number */ + const char* name, /* in: table name */ + TABLE* form, /* in: information on table + columns and indexes */ + HA_CREATE_INFO* create_info) /* in: ??????? */ +{ + int error; + dict_table_t* innobase_table; + uint name_len; + trx_t* trx; + char name2[1000]; + + DBUG_ENTER("ha_innobase::create"); + + trx = trx_allocate_for_mysql(); + + name_len = strlen(name); + + assert(name_len < 1000); + assert(name_len > 4); + + memcpy(name2, name, name_len); + + /* Erase the .frm end from table name: */ + + name2[name_len - 4] = '\0'; + + /* Create the table definition in Innobase */ + + if (error = create_table_def(trx, form, name2)) { + + trx_commit_for_mysql(trx); + + trx_free_for_mysql(trx); + + DBUG_RETURN(error); + } + + /* Create the keys */ + + if (form->keys == 0) { + /* Create a single index which is used as the clustered + index; order the rows by their row id generated internally + by Innobase */ + + error = create_index_when_no_index(trx, name2); + + if (error) { + trx_commit_for_mysql(trx); + + trx_free_for_mysql(trx); + + DBUG_RETURN(error); + } + } else { + for (uint i = 0; i < form->keys; i++) { + + if (error = create_sub_table(trx, form, name2, i)) { + + trx_commit_for_mysql(trx); + + trx_free_for_mysql(trx); + + DBUG_RETURN(error); + } + } + } + + trx_commit_for_mysql(trx); + + innobase_table = dict_table_get((char*)name2, NULL); + + assert(innobase_table); + + /* Tell the Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + trx_free_for_mysql(trx); + + DBUG_RETURN(0); +} + +/********************************************************************* +Drops a table from an Innobase database. No one is allowed to have +locks on the table, not even the calling user when the table is +dropped. */ + +int +ha_innobase::delete_table( +/*======================*/ + /* out: error number */ + const char* name) /* in: table name */ +{ + ulint name_len; + int error; + trx_t* trx; + + DBUG_ENTER("ha_innobase::delete_table"); + + trx = trx_allocate_for_mysql(); + + name_len = strlen(name); + + assert(name_len < 1000); + assert(name_len > 4); + + /* Strangely, MySQL passes the table name without the '.frm' + extension, in contrast to ::create */ + + /* Drop the table in Innobase */ + + error = row_drop_table_for_mysql((char*) name, trx, FALSE); + + /* Tell the Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + trx_free_for_mysql(trx); + + error = convert_error_code_to_mysql(error); + + DBUG_RETURN(error); +} + +/************************************************************************* +Renames an Innobase table. */ + +int +ha_innobase::rename_table( +/*======================*/ + /* out: 0 or error code */ + const char* from, /* in: old name of the table */ + const char* to) /* in: new name of the table */ +{ + ulint name_len1; + ulint name_len2; + int error; + trx_t* trx; + + DBUG_ENTER("ha_innobase::rename_table"); + + trx = trx_allocate_for_mysql(); + + name_len1 = strlen(from); + name_len2 = strlen(to); + + assert(name_len1 < 1000); + assert(name_len1 > 4); + assert(name_len2 < 1000); + assert(name_len2 > 4); + + /* TODO: what name extensions MySQL passes here? */ + + /* Rename the table in Innobase */ + + error = row_rename_table_for_mysql((char*) from, (char*) to, trx); + + /* Tell the Innobase server that there might be work for + utility threads: */ + + srv_active_wake_master_thread(); + + trx_free_for_mysql(trx); + + error = convert_error_code_to_mysql(error); + + DBUG_RETURN(error); +} + +/************************************************************************* +How many seeks it will take to read through the table. This is to be +comparable to the number returned by records_in_range so that we can +decide if we should scan the table or use keys. */ + +double +ha_innobase::scan_time() +/*====================*/ + /* out: estimated time measured in disk seeks */ +{ + dict_table_t* table = (dict_table_t*) innobase_table_handle; + + /* In the following formula we assume that scanning 5 pages + takes the same time as a disk seek: */ + + return((double) (btr_get_size( + dict_table_get_first_index_noninline(table), + BTR_N_LEAF_PAGES) / 5)); +} + +/************************************************************************* +Estimates the number of index records in a range. */ + +ha_rows +ha_innobase::records_in_range( +/*==========================*/ + /* out: estimated number of rows, + currently 32-bit int or uint */ + int keynr, /* in: index number */ + const byte* start_key, /* in: start key value of the + range, may also be empty */ + uint start_key_len, /* in: start key val len, may + also be 0 */ + enum ha_rkey_function start_search_flag,/* in: start search condition + e.g., 'greater than' */ + const byte* end_key, /* in: range end key val, may + also be empty */ + uint end_key_len, /* in: range end key val len, + may also be 0 */ + enum ha_rkey_function end_search_flag)/* in: range end search cond */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + KEY* key; + dict_index_t* index; + byte* key_val_buff2 = (byte*) my_malloc(table->reclength, + MYF(MY_WME)); + dtuple_t* range_end; + mem_heap_t* heap; + ulint n_rows; + ulint mode1; + ulint mode2; + + DBUG_ENTER("records_in_range"); + + active_index = keynr; + + key = table->key_info + active_index; + + index = dict_table_get_index_noninline( + (dict_table_t*) innobase_table_handle, + key->name); + + /* In converting the first key value we make use of the buffers + in our handle: */ + + convert_key_to_innobase(prebuilt->search_tuple, key_val_buff, index, + key, (byte*) start_key, (int) start_key_len); + + /* For the second key value we have to use allocated buffers: */ + + heap = mem_heap_create(100); + + range_end = dtuple_create(heap, key->key_parts); + + convert_key_to_innobase(range_end, key_val_buff2, index, + key, (byte*) end_key, (int) end_key_len); + + mode1 = convert_search_mode_to_innobase(start_search_flag); + mode2 = convert_search_mode_to_innobase(end_search_flag); + + n_rows = btr_estimate_n_rows_in_range(index, prebuilt->search_tuple, + mode1, range_end, mode2); + mem_heap_free(heap); + my_free((char*) key_val_buff2, MYF(0)); + + DBUG_RETURN((ha_rows) n_rows); +} + +/**************************************************************************** + Handling the shared INNOBASE_SHARE structure that is needed to provide table + locking. +****************************************************************************/ + +static byte* innobase_get_key(INNOBASE_SHARE *share,uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=share->table_name_length; + return (byte*) share->table_name; +} + +static INNOBASE_SHARE *get_share(const char *table_name) +{ + INNOBASE_SHARE *share; + pthread_mutex_lock(&innobase_mutex); + uint length=(uint) strlen(table_name); + if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables, table_name, + length))) + { + if ((share=(INNOBASE_SHARE *) my_malloc(sizeof(*share)+length+1, + MYF(MY_WME | MY_ZEROFILL)))) + { + share->table_name_length=length; + share->table_name=(char*) (share+1); + strmov(share->table_name,table_name); + if (hash_insert(&innobase_open_tables, (char*) share)) + { + pthread_mutex_unlock(&innobase_mutex); + my_free((gptr) share,0); + return 0; + } + thr_lock_init(&share->lock); + pthread_mutex_init(&share->mutex,NULL); + } + } + share->use_count++; + pthread_mutex_unlock(&innobase_mutex); + return share; +} + +static void free_share(INNOBASE_SHARE *share) +{ + pthread_mutex_lock(&innobase_mutex); + if (!--share->use_count) + { + hash_delete(&innobase_open_tables, (gptr) share); + thr_lock_delete(&share->lock); + pthread_mutex_destroy(&share->mutex); + my_free((gptr) share, MYF(0)); + } + pthread_mutex_unlock(&innobase_mutex); +} +#endif /* HAVE_INNOBASE_DB */ diff --git a/sql/ha_innobase.h b/sql/ha_innobase.h new file mode 100644 index 00000000000..4bd6cc4a0c6 --- /dev/null +++ b/sql/ha_innobase.h @@ -0,0 +1,182 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + && Innobase Oy + + -This file is modified from ha_berkeley.h of MySQL distribution- + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +/* This file defines the Innobase handler: the interface between MySQL and +Innobase */ + +extern "C" { +#include <data0types.h> +#include <dict0types.h> +#include <row0types.h> +} + +typedef struct st_innobase_share { + THR_LOCK lock; + pthread_mutex_t mutex; + char *table_name; + uint table_name_length,use_count; +} INNOBASE_SHARE; + + +/* The class defining a handle to an Innobase table */ +class ha_innobase: public handler +{ + void* innobase_table_handle; /* (dict_table_t*) pointer to a table + in Innobase data dictionary cache */ + void* innobase_prebuilt; /* (row_prebuilt_t*) prebuilt + structs in Innobase, used to save + CPU */ + THD* user_thd; /* the thread handle of the user + currently using the handle; this is + set in external_lock function */ + THR_LOCK_DATA lock; + INNOBASE_SHARE *share; + + gptr alloc_ptr; + byte* rec_buff; /* buffer used in converting + rows from MySQL format + to Innobase format */ + byte* upd_buff; /* buffer used in updates */ + byte* key_val_buff; /* buffer used in converting + search key values from MySQL format + to Innobase format */ + uint ref_stored_len; /* length of the key value stored to + 'ref' buffer of the handle, if any */ + ulong int_option_flag; + uint primary_key; + uint last_dup_key; + + uint last_match_mode;/* match mode of the latest search: + ROW_SEL_EXACT or + ROW_SEL_EXACT_PREFIX or undefined */ + + ulong max_row_length(const byte *buf); + + uint store_key_val_for_row(uint keynr, char* buff, const byte* record); + void convert_row_to_innobase(dtuple_t* row, char* record); + void convert_row_to_mysql(char* record, dtuple_t* row); + dtuple_t* convert_key_to_innobase(dtuple_t* tuple, byte* buf, + dict_index_t* index, + KEY* key, byte* key_ptr, int key_len); + int calc_row_difference(upd_t* uvect, byte* old_row, byte* new_row); + int update_thd(THD* thd); + int change_active_index(uint keynr); + int general_fetch(byte* buf, uint direction, uint match_mode); + + /* Init values for the class: */ + public: + ha_innobase(TABLE *table): handler(table), + rec_buff(0), + int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | + HA_REC_NOT_IN_SEQ | + HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | + HA_LONGLONG_KEYS | HA_NULL_KEY | HA_NO_BLOBS | + HA_NOT_EXACT_COUNT | + HA_NO_WRITE_DELAYED | + HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE), + last_dup_key((uint) -1) + { + } + ~ha_innobase() {} + + const char* table_type() const { return("Innobase");} + const char** bas_ext() const; + ulong option_flag() const { return int_option_flag; } + uint max_record_length() const { return HA_MAX_REC_LENGTH; } + uint max_keys() const { return MAX_KEY; } + uint max_key_parts() const { return MAX_REF_PARTS; } + uint max_key_length() const { return MAX_KEY_LENGTH; } + bool fast_key_read() { return 1;} + bool has_transactions() { return 1;} + + int open(const char *name, int mode, int test_if_locked); + void initialize(void); + int close(void); + double scan_time(); + + int write_row(byte * buf); + int update_row(const byte * old_data, byte * new_data); + int delete_row(const byte * buf); + + int index_init(uint index); + int index_end(); + int index_read(byte * buf, const byte * key, + uint key_len, enum ha_rkey_function find_flag); + int index_read_idx(byte * buf, uint index, const byte * key, + uint key_len, enum ha_rkey_function find_flag); + int index_next(byte * buf); + int index_next_same(byte * buf, const byte *key, uint keylen); + int index_prev(byte * buf); + int index_first(byte * buf); + int index_last(byte * buf); + + int rnd_init(bool scan=1); + int rnd_end(); + int rnd_next(byte *buf); + int rnd_pos(byte * buf, byte *pos); + + void position(const byte *record); + void info(uint); + int extra(enum ha_extra_function operation); + int reset(void); + int external_lock(THD *thd, int lock_type); + void position(byte *record); + ha_rows records_in_range(int inx, + const byte *start_key,uint start_key_len, + enum ha_rkey_function start_search_flag, + const byte *end_key,uint end_key_len, + enum ha_rkey_function end_search_flag); + + int create(const char *name, register TABLE *form, + HA_CREATE_INFO *create_info); + int delete_table(const char *name); + int rename_table(const char* from, const char* to); + + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type); +}; + +extern bool innobase_skip; +extern uint innobase_init_flags, innobase_lock_type; +extern ulong innobase_cache_size; +extern char *innobase_home, *innobase_tmpdir, *innobase_logdir; +extern long innobase_lock_scan_time; +extern long innobase_mirrored_log_groups, innobase_mirrored_log_groups; +extern long innobase_log_file_size, innobase_log_buffer_size; +extern long innobase_buffer_pool_size, innobase_additional_mem_pool_size; +extern long innobase_file_io_threads; +extern char *innobase_data_home_dir, *innobase_data_file_path; +extern char *innobase_log_group_home_dir, *innobase_log_arch_dir; +extern bool innobase_flush_log_at_trx_commit,innobase_log_archive; + +extern TYPELIB innobase_lock_typelib; + +bool innobase_init(void); +bool innobase_end(void); +bool innobase_flush_logs(void); + +int innobase_commit(THD *thd); +int innobase_rollback(THD *thd); +int innobase_close_connection(THD *thd); + diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 229c24df107..860418a3fe4 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -236,10 +236,12 @@ int ha_myisam::write_row(byte * buf) int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) { if (!file) return HA_ADMIN_INTERNAL_ERROR; - int error ; + int error; MI_CHECK param; MYISAM_SHARE* share = file->s; + const char *old_proc_info=thd->proc_info; + thd->proc_info="Checking table"; myisamchk_init(¶m); param.thd = thd; param.op_name = (char*)"check"; @@ -306,6 +308,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) } check_opt->retry_without_quick=param.retry_without_quick; + thd->proc_info=old_proc_info; return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; } @@ -483,6 +486,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) char fixed_name[FN_REFLEN]; const char *old_proc_info=thd->proc_info; MYISAM_SHARE* share = file->s; + ha_rows rows= file->state->records; DBUG_ENTER("ha_myisam::repair"); param.table_name = table->table_name; @@ -559,6 +563,13 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) T_STATISTICS ? UPDATE_STAT : 0)); info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE | HA_STATUS_CONST); + if (rows != file->state->records) + { + char llbuff[22],llbuff2[22]; + mi_check_print_warning(¶m,"Number of rows changed from %s to %s", + llstr(rows,llbuff), + llstr(file->state->records,llbuff2)); + } } else { @@ -1094,7 +1105,17 @@ int ha_myisam::ft_read(byte * buf) if (error=ft_read_next((FT_DOCLIST *) ft_handler,(char*) buf)) ft_handler=NULL; // Magic here ! See Item_func_match::val() - + // and ha_myisam::index_init() table->status=error ? STATUS_NOT_FOUND: 0; return error; } + +int ha_myisam::index_init(uint idx) +{ + if (idx != active_index) + ft_handler=NULL; // Magic here ! + + active_index=idx; + return 0; +} + diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index d9f322fe9ca..c72b29ed3c7 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -71,6 +71,7 @@ class ha_myisam: public handler int index_first(byte * buf); int index_last(byte * buf); int index_next_same(byte *buf, const byte *key, uint keylen); + int index_init(uint idx); int ft_init() { if(!ft_handler) return 1; ft_reinit_search(ft_handler); return 0; } void *ft_init_ext(uint inx,const byte *key, uint keylen, bool presort) diff --git a/sql/handler.cc b/sql/handler.cc index 6f3d243394e..7c6a3e32ff2 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -174,6 +174,14 @@ int ha_panic(enum ha_panic_function flag) } /* ha_panic */ +void ha_close_connection(THD* thd) +{ +#ifdef HAVE_INNOBASE_DB + if (!innobase_skip) + innobase_close_connection(THD* thd); +#endif +} + /* This is used to commit or rollback a single statement depending on the value of error @@ -183,8 +191,7 @@ int ha_autocommit_or_rollback(THD *thd, int error) { DBUG_ENTER("ha_autocommit_or_rollback"); #ifdef USING_TRANSACTIONS - if (!(thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) && - !thd->locked_tables) + if (!(thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { if (!error) { @@ -203,6 +210,16 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) { int error=0; DBUG_ENTER("ha_commit"); +#ifdef USING_TRANSACTIONS + /* Update the binary log if we have cached some queries */ + if (trans == &thd->transaction.all && mysql_bin_log.is_open() && + my_b_tell(&thd->transaction.trans_log)) + { + mysql_bin_log.write(&thd->transaction.trans_log); + reinit_io_cache(&thd->transaction.trans_log, + WRITE_CACHE, (my_off_t) 0, 0, 1); + thd->transaction.trans_log.end_of_file= max_binlog_cache_size; + } #ifdef HAVE_BERKELEY_DB if (trans->bdb_tid) { @@ -216,7 +233,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) #endif #ifdef HAVE_INNOBASE_DB { - if ((error=innobase_commit(thd,trans->innobase_tid)) + if ((error=innobase_commit(thd,trans->innobase_tid))) { my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); error=1; @@ -224,6 +241,9 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) trans->innobase_tid=0; } #endif + if (error && trans == &thd->transaction.all && mysql_bin_log.is_open()) + sql_print_error("Error: Got error during commit; Binlog is not up to date!"); +#endif DBUG_RETURN(error); } @@ -253,6 +273,12 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) trans->innobase_tid=0; } #endif +#ifdef USING_TRANSACTIONS + if (trans == &thd->transaction.all) + reinit_io_cache(&thd->transaction.trans_log, + WRITE_CACHE, (my_off_t) 0, 0, 1); + thd->transaction.trans_log.end_of_file= max_binlog_cache_size; +#endif DBUG_RETURN(error); } diff --git a/sql/handler.h b/sql/handler.h index 259a3ad6405..571825c36ef 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -315,6 +315,7 @@ my_off_t ha_get_ptr(byte *ptr, uint pack_length); void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos); int ha_init(void); int ha_panic(enum ha_panic_function flag); +void ha_close_connection(THD* thd); enum db_type ha_checktype(enum db_type database_type); int ha_create_table(const char *name, HA_CREATE_INFO *create_info, bool update_create_info); diff --git a/sql/hostname.cc b/sql/hostname.cc index 47953020ffb..db8f8349446 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -180,20 +180,21 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors) VOID(pthread_mutex_lock(&hostname_cache->lock)); if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET))) { - DBUG_PRINT("error",("gethostbyaddr returned %d",errno)); VOID(pthread_mutex_unlock(&hostname_cache->lock)); - add_wrong_ip(in); - DBUG_RETURN(0); + DBUG_PRINT("error",("gethostbyaddr returned %d",errno)); + goto err; } - if (!hp->h_name[0]) + if (!hp->h_name[0]) // Don't allow empty hostnames { VOID(pthread_mutex_unlock(&hostname_cache->lock)); DBUG_PRINT("error",("Got an empty hostname")); - add_wrong_ip(in); - DBUG_RETURN(0); // Don't allow empty hostnames + goto err; } if (!(name=my_strdup(hp->h_name,MYF(0)))) + { + VOID(pthread_mutex_unlock(&hostname_cache->lock)); DBUG_RETURN(0); // out of memory + } check=gethostbyname(name); VOID(pthread_mutex_unlock(&hostname_cache->lock)); if (!check) @@ -214,8 +215,7 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors) { DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'")); my_free(name,MYF(0)); - add_wrong_ip(in); - DBUG_RETURN(0); + goto err; } } @@ -230,6 +230,8 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors) } DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname")); my_free(name,MYF(0)); + +err: add_wrong_ip(in); DBUG_RETURN(0); } diff --git a/sql/item_func.cc b/sql/item_func.cc index 3d002e5c9d9..bad789479b2 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1840,9 +1840,12 @@ err: double Item_func_match::val() { - // Don't know how to return an error from val(), so NULL will be returned - if ((null_value=(ft_handler==NULL))) - return 0.0; + /* If called uninitialized we should return neither NULL nor 0 (important + for const_tables) so, let's return -1, which is obviously incorrect + for normal operation, and could be easily spotted */ + + if (ft_handler==NULL) + return -1.0; if (join_key) { @@ -1985,7 +1988,7 @@ bool Item_func_match::fix_index() ft_to_key[mkeys]=ft_to_key[key]; continue; } - if (ft_cnt[key] == max_cnt) + if (max_cnt && ft_cnt[key] == max_cnt) { mkeys++; ft_cnt[mkeys]=ft_cnt[key]; diff --git a/sql/log.cc b/sql/log.cc index 2dda594ea59..1c377ebc3a5 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -16,6 +16,7 @@ /* logging of commands */ +/* TODO: Abort logging when we get an error in reading or writing log files */ #include "mysql_priv.h" #include "sql_acl.h" @@ -523,14 +524,12 @@ void MYSQL_LOG::new_file() } -void MYSQL_LOG::write(THD *thd,enum enum_server_command command, +bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, const char *format,...) { if (is_open() && (what_to_log & (1L << (uint) command))) { - va_list args; - va_start(args,format); - char buff[32]; + int error=0; VOID(pthread_mutex_lock(&LOCK_log)); /* Test if someone closed after the is_open test */ @@ -538,14 +537,17 @@ void MYSQL_LOG::write(THD *thd,enum enum_server_command command, { time_t skr; ulong id; - int error=0; + va_list args; + va_start(args,format); + char buff[32]; + if (thd) { // Normal thread if ((thd->options & OPTION_LOG_OFF) && (thd->master_access & PROCESS_ACL)) { VOID(pthread_mutex_unlock(&LOCK_log)); - return; // No logging + return 0; // No logging } id=thd->thread_id; if (thd->user_time || !(skr=thd->query_start())) @@ -593,115 +595,184 @@ void MYSQL_LOG::write(THD *thd,enum enum_server_command command, write_error=1; sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); } + va_end(args); + VOID(pthread_mutex_unlock(&LOCK_log)); } - va_end(args); VOID(pthread_mutex_unlock(&LOCK_log)); + return error != 0; } + return 0; } /* Write to binary log in a format to be used for replication */ -void MYSQL_LOG::write(Query_log_event* event_info) +bool MYSQL_LOG::write(Query_log_event* event_info) { + /* In most cases this is only called if 'is_open()' is true */ + bool error=1; + VOID(pthread_mutex_lock(&LOCK_log)); if (is_open()) { - VOID(pthread_mutex_lock(&LOCK_log)); - if (is_open()) + THD *thd=event_info->thd; + IO_CACHE *file = (event_info->cache_stmt ? &thd->transaction.trans_log : + &log_file); + if ((!(thd->options & OPTION_BIN_LOG) && + thd->master_access & PROCESS_ACL) || + !db_ok(event_info->db, binlog_do_db, binlog_ignore_db)) { - THD *thd=event_info->thd; - if ((!(thd->options & OPTION_BIN_LOG) && - thd->master_access & PROCESS_ACL) || - !db_ok(event_info->db, binlog_do_db, binlog_ignore_db)) - { - VOID(pthread_mutex_unlock(&LOCK_log)); - return; - } + VOID(pthread_mutex_unlock(&LOCK_log)); + return 0; + } - if (thd->last_insert_id_used) - { - Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->last_insert_id); - if (e.write(&log_file)) - { - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - goto err; - } - } - if (thd->insert_id_used) - { - Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id); - if (e.write(&log_file)) - { - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - goto err; - } - } - if (thd->convert_set) + if (thd->last_insert_id_used) + { + Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->last_insert_id); + if (e.write(file)) + goto err; + } + if (thd->insert_id_used) + { + Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id); + if (e.write(file)) + goto err; + } + if (thd->convert_set) + { + char buf[1024] = "SET CHARACTER SET "; + char* p = strend(buf); + p = strmov(p, thd->convert_set->name); + int save_query_length = thd->query_length; + // just in case somebody wants it later + thd->query_length = (uint)(p - buf); + Query_log_event e(thd, buf); + if (e.write(file)) + goto err; + thd->query_length = save_query_length; // clean up + } + if (event_info->write(file) || + file == &log_file && flush_io_cache(file)) + goto err; + error=0; + +err: + if (error) + { + if (my_errno == EFBIG) + my_error(ER_TRANS_CACHE_FULL, MYF(0)); + else + my_error(ER_ERROR_ON_WRITE, MYF(0), name, errno); + write_error=1; + } + if (file == &log_file) + VOID(pthread_cond_broadcast(&COND_binlog_update)); + } + else + error=0; + VOID(pthread_mutex_unlock(&LOCK_log)); + return error; +} + +/* + Write a cached log entry to the binary log + We only come here if there is something in the cache. + 'cache' needs to be reinitialized after this functions returns. +*/ + +bool MYSQL_LOG::write(IO_CACHE *cache) +{ + VOID(pthread_mutex_lock(&LOCK_log)); + bool error=1; + if (is_open()) + { + uint length; + my_off_t start_pos=my_b_tell(&log_file); + + if (reinit_io_cache(cache, WRITE_CACHE, 0, 0, 0)) + { + if (!write_error) + sql_print_error(ER(ER_ERROR_ON_WRITE), cache->file_name, errno); + goto err; + } + while ((length=my_b_fill(cache))) + { + if (my_b_write(&log_file, cache->rc_pos, length)) { - char buf[1024] = "SET CHARACTER SET "; - char* p = strend(buf); - p = strmov(p, thd->convert_set->name); - int save_query_length = thd->query_length; - // just in case somebody wants it later - thd->query_length = (uint)(p - buf); - Query_log_event e(thd, buf); - if (e.write(&log_file)) - { + if (!write_error) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - goto err; - } - thd->query_length = save_query_length; // clean up + goto err; } - if (event_info->write(&log_file) || flush_io_cache(&log_file)) - { + cache->rc_pos=cache->rc_end; // Mark buffer used up + } + if (flush_io_cache(&log_file)) + { + if (!write_error) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } - err: - VOID(pthread_cond_broadcast(&COND_binlog_update)); + goto err; + } + if (cache->error) // Error on read + { + if (!write_error) + sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno); + goto err; } - VOID(pthread_mutex_unlock(&LOCK_log)); } + error=0; + +err: + if (error) + write_error=1; + else + VOID(pthread_cond_broadcast(&COND_binlog_update)); + + VOID(pthread_mutex_unlock(&LOCK_log)); + return error; } -void MYSQL_LOG::write(Load_log_event* event_info) + +bool MYSQL_LOG::write(Load_log_event* event_info) { + bool error=0; + VOID(pthread_mutex_lock(&LOCK_log)); if (is_open()) { - VOID(pthread_mutex_lock(&LOCK_log)); - if (is_open()) + THD *thd=event_info->thd; + if ((thd->options & OPTION_BIN_LOG) || + !(thd->master_access & PROCESS_ACL)) { - THD *thd=event_info->thd; - if ((thd->options & OPTION_BIN_LOG) || - !(thd->master_access & PROCESS_ACL)) + if (event_info->write(&log_file) || flush_io_cache(&log_file)) { - if (event_info->write(&log_file) || flush_io_cache(&log_file)) + if (!write_error) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + error=write_error=1; } + VOID(pthread_cond_broadcast(&COND_binlog_update)); } - VOID(pthread_mutex_unlock(&LOCK_log)); } + VOID(pthread_mutex_unlock(&LOCK_log)); + return error; } /* Write update log in a format suitable for incremental backup */ -void MYSQL_LOG::write(THD *thd,const char *query, uint query_length, +bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, time_t query_start) { + bool error=0; if (is_open()) { time_t current_time; VOID(pthread_mutex_lock(&LOCK_log)); if (is_open()) { // Safety agains reopen - int error=0; + int tmp_errno=0; char buff[80],*end; end=buff; if (!(thd->options & OPTION_UPDATE_LOG) && (thd->master_access & PROCESS_ACL)) { VOID(pthread_mutex_unlock(&LOCK_log)); - return; + return 0; } if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start) { @@ -722,14 +793,14 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length, start->tm_min, start->tm_sec); if (my_b_write(&log_file, (byte*) buff,24)) - error=errno; + tmp_errno=errno; } if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n", thd->priv_user, thd->user, thd->host ? thd->host : "", thd->ip ? thd->ip : "") == (uint) -1) - error=errno; + tmp_errno=errno; } if (query_start) { @@ -739,12 +810,12 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length, (ulong) (current_time - query_start), (ulong) (thd->time_after_lock - query_start), (ulong) thd->sent_row_count) == (uint) -1) - error=errno; + tmp_errno=errno; } if (thd->db && strcmp(thd->db,db)) { // Database changed if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1) - error=errno; + tmp_errno=errno; strmov(db,thd->db); } if (thd->last_insert_id_used) @@ -777,7 +848,7 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length, *end=0; if (my_b_write(&log_file, (byte*) "SET ",4) || my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff)-1)) - error=errno; + tmp_errno=errno; } if (!query) { @@ -787,29 +858,22 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length, if (my_b_write(&log_file, (byte*) query,query_length) || my_b_write(&log_file, (byte*) ";\n",2) || flush_io_cache(&log_file)) - error=errno; - if (error && ! write_error) + tmp_errno=errno; + if (tmp_errno) { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); + error=1; + if (! write_error) + { + write_error=1; + sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); + } } } VOID(pthread_mutex_unlock(&LOCK_log)); } + return error; } -#ifdef TO_BE_REMOVED -void MYSQL_LOG::flush() -{ - if (is_open()) - if (flush_io_cache(log_file) && ! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno); - } -} -#endif - void MYSQL_LOG::close(bool exiting) { // One can't set log_type here! diff --git a/sql/log_event.h b/sql/log_event.h index c956cfde396..3d307000b13 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -115,19 +115,21 @@ public: // otherwise, set it to 0, in which case, we compute it with strlen() uint32 db_len; uint16 error_code; - int thread_id; + ulong thread_id; #if !defined(MYSQL_CLIENT) THD* thd; - Query_log_event(THD* thd_arg, const char* query_arg): - Log_event(thd_arg->start_time,0,0,thd_arg->server_id), data_buf(0), + bool cache_stmt; + Query_log_event(THD* thd_arg, const char* query_arg, bool using_trans=0): + Log_event(thd_arg->start_time,0,1,thd_arg->server_id), data_buf(0), query(query_arg), db(thd_arg->db), q_len(thd_arg->query_length), error_code(thd_arg->net.last_errno), - thread_id(thd_arg->thread_id), thd(thd_arg) + thread_id(thd_arg->thread_id), thd(thd_arg), + cache_stmt(using_trans && + (thd_arg->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { time_t end_time; time(&end_time); exec_time = (ulong) (end_time - thd->start_time); - valid_exec_time = 1; db_len = (db) ? (uint32) strlen(db) : 0; } #endif @@ -186,7 +188,7 @@ protected: void copy_log_event(const char *buf, ulong data_len); public: - int thread_id; + ulong thread_id; uint32 table_name_len; uint32 db_len; uint32 fname_len; diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index f723e35ca93..cddacaa820f 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -121,7 +121,7 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, } /* end_of_file may be changed by user later */ info->end_of_file= ((type == READ_NET || type == READ_FIFO ) ? 0 - : MY_FILEPOS_ERROR); + : ~(my_off_t) 0); info->type=type; info->error=0; info->read_function=(type == READ_NET) ? _my_b_net_read : _my_b_read; /* net | file */ @@ -176,6 +176,8 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, DBUG_ENTER("reinit_io_cache"); info->seek_not_done= test(info->file >= 0); /* Seek not done */ + + /* If the whole file is in memory, avoid flushing to disk */ if (! clear_cache && seek_offset >= info->pos_in_file && seek_offset <= info->pos_in_file + @@ -186,8 +188,12 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, info->rc_end=info->rc_pos; info->end_of_file=my_b_tell(info); } - else if (info->type == READ_CACHE && type == WRITE_CACHE) - info->rc_end=info->buffer+info->buffer_length; + else if (type == WRITE_CACHE) + { + if (info->type == READ_CACHE) + info->rc_end=info->buffer+info->buffer_length; + info->end_of_file = ~(my_off_t) 0; + } info->rc_pos=info->rc_request_pos+(seek_offset-info->pos_in_file); #ifdef HAVE_AIOWAIT my_aiowait(&info->aio_result); /* Wait for outstanding req */ @@ -195,11 +201,20 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, } else { + /* + If we change from WRITE_CACHE to READ_CACHE, assume that everything + after the current positions should be ignored + */ if (info->type == WRITE_CACHE && type == READ_CACHE) info->end_of_file=my_b_tell(info); - if (flush_io_cache(info)) + /* No need to flush cache if we want to reuse it */ + if ((type != WRITE_CACHE || !clear_cache) && flush_io_cache(info)) DBUG_RETURN(1); - info->pos_in_file=seek_offset; + if (info->pos_in_file != seek_offset) + { + info->pos_in_file=seek_offset; + info->seek_not_done=1; + } info->rc_request_pos=info->rc_pos=info->buffer; if (type == READ_CACHE || type == READ_NET || type == READ_FIFO) { @@ -210,7 +225,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, info->rc_end=info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1)); info->end_of_file= ((type == READ_NET || type == READ_FIFO) ? 0 : - MY_FILEPOS_ERROR); + ~(my_off_t) 0); } } info->type=type; @@ -536,6 +551,11 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) Buffer+=rest_length; Count-=rest_length; info->rc_pos+=rest_length; + if (info->pos_in_file+info->buffer_length > info->end_of_file) + { + my_errno=errno=EFBIG; + return info->error = -1; + } if (flush_io_cache(info)) return 1; if (Count >= IO_SIZE) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index f2f9e48e889..7c3b1cfadef 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -403,7 +403,7 @@ void close_temporary_tables(THD *thd); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name); void close_temporary(TABLE *table, bool delete_table=1); -bool rename_temporary_table(TABLE *table, const char *new_db, +bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); void remove_db_from_cache(const my_string db); void flush_tables(); @@ -422,6 +422,17 @@ int mysql_load(THD *thd,sql_exchange *ex, TABLE_LIST *table_list, List<Item> &fields, enum enum_duplicates handle_duplicates, bool local_file,thr_lock_type lock_type); int write_record(TABLE *table,COPY_INFO *info); + +/* sql_manager.cc */ +/* bits set in manager_status */ +#define MANAGER_BERKELEY_LOG_CLEANUP (1L << 0) +extern ulong volatile manager_status; +extern bool volatile manager_thread_in_use; +extern pthread_t manager_thread; +extern pthread_mutex_t LOCK_manager; +extern pthread_cond_t COND_manager; +pthread_handler_decl(handle_manager, arg); + /* sql_test.cc */ #ifndef DBUG_OFF void print_where(COND *cond,const char *info); @@ -458,7 +469,8 @@ extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables, extern ulong filesort_rows, filesort_range_count, filesort_scan_count; extern ulong filesort_merge_passes; extern ulong select_range_check_count, select_range_count, select_scan_count; -extern ulong select_full_range_join_count,select_full_join_count; +extern ulong select_full_range_join_count,select_full_join_count, + slave_open_temp_tables; extern uint test_flags,select_errors,mysql_port,ha_open_options; extern ulong thd_startup_options, slow_launch_threads, slow_launch_time; extern time_t start_time; @@ -495,9 +507,9 @@ extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, net_read_timeout,net_write_timeout, what_to_log,flush_time, max_tmp_tables,max_heap_table_size,query_buff_size, - lower_case_table_names,thread_stack,thread_stack_min; -extern ulong specialflag; -extern ulong current_pid; + lower_case_table_names,thread_stack,thread_stack_min, + binlog_cache_size, max_binlog_cache_size; +extern ulong specialflag, current_pid; extern bool low_priority_updates; extern bool opt_sql_bin_update; extern char language[LIBLEN],reg_ext[FN_EXTLEN],blob_newline; diff --git a/sql/mysqlbinlog.cc b/sql/mysqlbinlog.cc index 21a740ca1f4..087d1029afb 100644 --- a/sql/mysqlbinlog.cc +++ b/sql/mysqlbinlog.cc @@ -361,8 +361,7 @@ static void dump_local_log_entries(const char* logname) die("Could not read entry at offset %ld : Error in log format or \ read error", my_b_tell(file)); - else - die("Could not construct event object"); + // file->error == 0 means EOF, that's OK, we break in this case break; } if (rec_count >= offset) @@ -389,7 +388,9 @@ int main(int argc, char** argv) if(use_remote) { +#ifndef __WIN__ init_thr_alarm(10); // need to do this manually +#endif mysql = safe_connect(); } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 78fcc2f563c..1122a770523 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -154,7 +154,6 @@ static my_string opt_logname=0,opt_update_logname=0, opt_binlog_index_name = 0,opt_slow_logname=0; static char mysql_home[FN_REFLEN],pidfile_name[FN_REFLEN]; static pthread_t select_thread; -static pthread_t flush_thread; // Used when debugging static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl, opt_disable_networking=0, opt_bootstrap=0,opt_skip_show_db=0, opt_ansi_mode=0,opt_myisam_log=0, opt_large_files=sizeof(my_off_t) > 4; @@ -171,6 +170,7 @@ static struct rand_struct sql_rand; static int cleanup_done; static char **defaults_argv,time_zone[30]; static const char *default_table_type_name; +static char glob_hostname[FN_REFLEN]; #ifdef HAVE_OPENSSL static bool opt_use_ssl = FALSE; @@ -200,11 +200,11 @@ ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, thread_stack_min,net_wait_timeout,what_to_log= ~ (1L << (uint) COM_TIME), query_buff_size, lower_case_table_names, mysqld_net_retry_count, net_interactive_timeout, slow_launch_time = 2L, - net_read_timeout,net_write_timeout; -ulong thread_cache_size=0; + net_read_timeout,net_write_timeout,slave_open_temp_tables=0; +ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; volatile ulong cached_thread_count=0; -// replication parameters, if master_host is not NULL, we are slaving off the master +// replication parameters, if master_host is not NULL, we are a slave my_string master_user = (char*) "test", master_password = 0, master_host=0, master_info_file = (char*) "master.info"; const char *localhost=LOCAL_HOST; @@ -214,7 +214,7 @@ ulong max_tmp_tables,max_heap_table_size; ulong bytes_sent = 0L, bytes_received = 0L; bool opt_endinfo,using_udf_functions,low_priority_updates, locked_in_memory; -bool volatile abort_loop,select_thread_in_use,flush_thread_in_use,grant_option; +bool volatile abort_loop,select_thread_in_use,grant_option; bool volatile ready_to_exit,shutdown_in_progress; ulong refresh_version=1L,flush_version=1L; /* Increments on each reload */ ulong query_id=1L,long_query_count,long_query_time,aborted_threads, @@ -257,10 +257,10 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_mapped_file, LOCK_status, LOCK_grant, LOCK_error_log, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, - LOCK_flush, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, + LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, LOCK_binlog_update, LOCK_slave, LOCK_server_id; -pthread_cond_t COND_refresh,COND_thread_count,COND_flush, COND_binlog_update, +pthread_cond_t COND_refresh,COND_thread_count,COND_binlog_update, COND_slave_stopped; pthread_cond_t COND_thread_cache,COND_flush_thread_cache; pthread_t signal_thread; @@ -289,7 +289,6 @@ static pthread_handler_decl(handle_connections_namedpipes,arg); #ifdef __WIN__ static int get_service_parameters(); #endif -static pthread_handler_decl(handle_flush,arg); extern pthread_handler_decl(handle_slave,arg); #ifdef SET_RLIMIT_NOFILE static uint set_maximum_open_files(uint max_file_limit); @@ -313,13 +312,13 @@ static void close_connections(void) flush_thread_cache(); /* kill flush thread */ - (void) pthread_mutex_lock(&LOCK_flush); - if (flush_thread_in_use) + (void) pthread_mutex_lock(&LOCK_manager); + if (manager_thread_in_use) { - DBUG_PRINT("quit",("killing flush thread: %lx",flush_thread)); - (void) pthread_cond_signal(&COND_flush); + DBUG_PRINT("quit",("killing manager thread: %lx",manager_thread)); + (void) pthread_cond_signal(&COND_manager); } - (void) pthread_mutex_unlock(&LOCK_flush); + (void) pthread_mutex_unlock(&LOCK_manager); /* kill connection thread */ #if !defined(__WIN__) && !defined(__EMX__) @@ -358,6 +357,7 @@ static void close_connections(void) #if defined(AIX_3_2) || defined(HAVE_DEC_3_2_THREADS) if (ip_sock != INVALID_SOCKET) { + DBUG_PRINT("error",("closing TCP/IP and socket files")); VOID(shutdown(ip_sock,2)); VOID(closesocket(ip_sock)); VOID(shutdown(unix_sock,2)); @@ -609,12 +609,15 @@ void clean_up(void) free_defaults(defaults_argv); my_free(mysql_tmpdir,MYF(0)); x_free(opt_bin_logname); +#ifndef __WIN__ (void) my_delete(pidfile_name,MYF(0)); // This may not always exist - my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); +#endif + my_thread_end(); /* Tell main we are ready */ (void) pthread_mutex_lock(&LOCK_thread_count); ready_to_exit=1; + /* do the broadcast inside the lock to ensure that my_end() is not called */ (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); } /* clean_up */ @@ -928,8 +931,8 @@ void end_thread(THD *thd, bool put_in_cache) DBUG_PRINT("info", ("sending a broadcast")) /* Tell main we are ready */ - (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); + (void) pthread_cond_broadcast(&COND_thread_count); DBUG_PRINT("info", ("unlocked thread_count mutex")) #ifdef ONE_THREAD if (!(test_flags & TEST_NO_THREADS)) // For debugging under Linux @@ -1310,7 +1313,6 @@ int main(int argc, char **argv) #endif { DEBUGGER_OFF; - char hostname[FN_REFLEN]; my_umask=0660; // Default umask for new files my_umask_dir=0700; // Default umask for new directories @@ -1334,9 +1336,9 @@ int main(int argc, char **argv) #endif #endif - if (gethostname(hostname,sizeof(hostname)-4) < 0) - strmov(hostname,"mysql"); - strmov(pidfile_name,hostname); + if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0) + strmov(glob_hostname,"mysql"); + strmov(pidfile_name,glob_hostname); strmov(strcend(pidfile_name,'.'),".pid"); // Add extension #ifdef DEMO_VERSION strcat(server_version,"-demo"); @@ -1398,8 +1400,8 @@ int main(int argc, char **argv) (void) pthread_cond_init(&COND_refresh,NULL); (void) pthread_cond_init(&COND_thread_cache,NULL); (void) pthread_cond_init(&COND_flush_thread_cache,NULL); - (void) pthread_cond_init(&COND_flush,NULL); - (void) pthread_mutex_init(&LOCK_flush,NULL); + (void) pthread_cond_init(&COND_manager,NULL); + (void) pthread_mutex_init(&LOCK_manager,NULL); (void) pthread_mutex_init(&LOCK_crypt,NULL); (void) pthread_mutex_init(&LOCK_bytes_sent,NULL); (void) pthread_mutex_init(&LOCK_bytes_received,NULL); @@ -1490,31 +1492,29 @@ int main(int argc, char **argv) /* Setup log files */ if (opt_log) - open_log(&mysql_log, hostname, opt_logname, ".log", LOG_NORMAL); + open_log(&mysql_log, glob_hostname, opt_logname, ".log", LOG_NORMAL); if (opt_update_log) - open_log(&mysql_update_log, hostname, opt_update_logname, "", + open_log(&mysql_update_log, glob_hostname, opt_update_logname, "", LOG_NEW); + + if (!server_id) + server_id= !master_host ? 1 : 2; if (opt_bin_log) { - if(server_id) - { - if (!opt_bin_logname) - { - char tmp[FN_REFLEN]; - strnmov(tmp,hostname,FN_REFLEN-5); - strmov(strcend(tmp,'.'),"-bin"); - opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); - } - mysql_bin_log.set_index_file_name(opt_binlog_index_name); - open_log(&mysql_bin_log, hostname, opt_bin_logname, "-bin", - LOG_BIN); - } - else - sql_print_error("Server id is not set - binary logging disabled"); + if (!opt_bin_logname) + { + char tmp[FN_REFLEN]; + strnmov(tmp,glob_hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-bin"); + opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); + } + mysql_bin_log.set_index_file_name(opt_binlog_index_name); + open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", + LOG_BIN); } if (opt_slow_log) - open_log(&mysql_slow_log, hostname, opt_slow_logname, "-slow.log", + open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", LOG_NORMAL); if (ha_init()) { @@ -1606,27 +1606,26 @@ int main(int argc, char **argv) } #endif - if (flush_time && flush_time != ~(ulong) 0L) + if ( +#ifdef HAVE_BERKELEY_DB + !berkeley_skip || +#endif + (flush_time && flush_time != ~(ulong) 0L)) { pthread_t hThread; - if (pthread_create(&hThread,&connection_attrib,handle_flush,0)) - sql_print_error("Warning: Can't create thread to handle flush"); + if (pthread_create(&hThread,&connection_attrib,handle_manager,0)) + sql_print_error("Warning: Can't create thread to manage maintenance"); } // slave thread - if(master_host) + if (master_host) { - if(server_id) - { - pthread_t hThread; - if(!opt_skip_slave_start && - pthread_create(&hThread, &connection_attrib, handle_slave, 0)) - sql_print_error("Warning: Can't create thread to handle slave"); - else if(opt_skip_slave_start) - init_master_info(&glob_mi); - } - else - sql_print_error("Server id is not set, slave thread will not be started"); + pthread_t hThread; + if(!opt_skip_slave_start && + pthread_create(&hThread, &connection_attrib, handle_slave, 0)) + sql_print_error("Warning: Can't create thread to handle slave"); + else if(opt_skip_slave_start) + init_master_info(&glob_mi); } printf(ER(ER_READY),my_progname,server_version,""); @@ -1709,10 +1708,7 @@ int main(int argc, char **argv) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); } (void) pthread_mutex_unlock(&LOCK_thread_count); -#ifndef __WIN__ - (void) my_delete(pidfile_name,MYF(0)); // Not neaded anymore -#endif - my_thread_end(); + my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(0); return(0); /* purecov: deadcode */ } @@ -2087,8 +2083,8 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) #ifdef __NT__ pthread_mutex_lock(&LOCK_thread_count); handler_count--; - pthread_cond_signal(&COND_handler_count); pthread_mutex_unlock(&LOCK_thread_count); + pthread_cond_signal(&COND_handler_count); #endif DBUG_RETURN(0); } @@ -2177,41 +2173,6 @@ pthread_handler_decl(handle_connections_namedpipes,arg) } #endif /* __NT__ */ -/**************************************************************************** -** Create thread that automaticly flush all tables after a given time -****************************************************************************/ - -pthread_handler_decl(handle_flush,arg __attribute__((unused))) -{ - my_thread_init(); - DBUG_ENTER("handle_flush"); - - pthread_detach_this_thread(); - flush_thread=pthread_self(); - flush_thread_in_use=1; - - pthread_mutex_lock(&LOCK_flush); - while (flush_time) - { - struct timespec abstime; -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time(NULL)+flush_time; // Bsd 2.1 - abstime.ts_nsec=0; -#else - abstime.tv_sec=time(NULL)+flush_time; // Linux or Solairs - abstime.tv_nsec=0; -#endif - (void) pthread_cond_timedwait(&COND_flush,&LOCK_flush, &abstime); - if (abort_loop) - break; - flush_tables(); - } - flush_thread_in_use=0; - pthread_mutex_unlock(&LOCK_flush); - my_thread_end(); - DBUG_RETURN(0); -} - /****************************************************************************** ** handle start options @@ -2237,7 +2198,8 @@ enum options { OPT_BDB_HOME, OPT_BDB_LOG, OPT_BDB_TMP, OPT_BDB_NOSYNC, OPT_BDB_LOCK, OPT_BDB_SKIP, - OPT_BDB_RECOVER, OPT_MASTER_HOST, + OPT_BDB_RECOVER, OPT_BDB_SHARED, + OPT_MASTER_HOST, OPT_MASTER_USER, OPT_MASTER_PASSWORD, OPT_MASTER_PORT, OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY, OPT_SQL_BIN_UPDATE_SAME, @@ -2249,7 +2211,11 @@ enum options { OPT_SKIP_INNOBASE,OPT_SAFEMALLOC_MEM_LIMIT, OPT_REPLICATE_DO_TABLE, OPT_REPLICATE_IGNORE_TABLE, OPT_REPLICATE_WILD_DO_TABLE, OPT_REPLICATE_WILD_IGNORE_TABLE, - OPT_DISCONNECT_SLAVE_EVENT_COUNT + OPT_DISCONNECT_SLAVE_EVENT_COUNT, OPT_ABORT_SLAVE_EVENT_COUNT, + OPT_INNOBASE_DATA_HOME_DIR,OPT_INNOBASE_DATA_FILE_PATH, + OPT_INNOBASE_LOG_GROUP_HOME_DIR, + OPT_INNOBASE_LOG_ARCH_DIR, OPT_INNOBASE_LOG_ARCHIVE, + OPT_INNOBASE_FLUSH_LOG_AT_TRX_COMMIT }; static struct option long_options[] = { @@ -2261,6 +2227,7 @@ static struct option long_options[] = { {"bdb-logdir", required_argument, 0, (int) OPT_BDB_LOG}, {"bdb-recover", no_argument, 0, (int) OPT_BDB_RECOVER}, {"bdb-no-sync", no_argument, 0, (int) OPT_BDB_NOSYNC}, + {"bdb-shared-data", required_argument, 0, (int) OPT_BDB_SHARED}, {"bdb-tmpdir", required_argument, 0, (int) OPT_BDB_TMP}, #endif {"big-tables", no_argument, 0, (int) OPT_BIG_TABLES}, @@ -2285,6 +2252,20 @@ static struct option long_options[] = { {"enable-locking", no_argument, 0, (int) OPT_ENABLE_LOCK}, {"exit-info", optional_argument, 0, 'T'}, {"flush", no_argument, 0, (int) OPT_FLUSH}, +#ifdef HAVE_INNOBASE_DB + {"innobase_data_home_dir", required_argument, 0, + OPT_INNOBASE_DATA_HOME_DIR}, + {"innobase_data_file_path", required_argument, 0, + OPT_INNOBASE_DATA_FILE_PATH}, + {"innobase_log_group_home_dir", required_argument, 0, + OPT_INNOBASE_LOG_GROUP_HOME_DIR}, + {"innobase_log_arch_dir", required_argument, 0, + OPT_INNOBASE_LOG_ARCH_DIR}, + {"innobase_log_archive", optional_argument, 0, + OPT_INNOBASE_LOG_ARCHIVE}, + {"innobase_flush_log_at_trx_commit", optional_argument, 0, + OPT_INNOBASE_FLUSH_LOG_AT_TRX_COMMIT}, +#endif {"help", no_argument, 0, '?'}, {"init-file", required_argument, 0, (int) OPT_INIT_FILE}, {"log", optional_argument, 0, 'l'}, @@ -2309,6 +2290,8 @@ static struct option long_options[] = { // is a no-op {"disconnect-slave-event-count", required_argument, 0, (int) OPT_DISCONNECT_SLAVE_EVENT_COUNT}, + {"abort-slave-event-count", required_argument, 0, + (int) OPT_ABORT_SLAVE_EVENT_COUNT}, #if !defined(DBUG_OFF) && defined(SAFEMALLOC) {"safemalloc-mem-limit", required_argument, 0, (int) OPT_SAFEMALLOC_MEM_LIMIT}, @@ -2335,7 +2318,7 @@ static struct option long_options[] = { (int) OPT_REPLICATE_REWRITE_DB}, {"safe-mode", no_argument, 0, (int) OPT_SAFE}, {"socket", required_argument, 0, (int) OPT_SOCKET}, - {"server-id", required_argument, 0, (int)OPT_SERVER_ID}, + {"server-id", required_argument, 0, (int) OPT_SERVER_ID}, {"set-variable", required_argument, 0, 'O'}, #ifdef HAVE_BERKELEY_DB {"skip-bdb", no_argument, 0, (int) OPT_BDB_SKIP}, @@ -2375,9 +2358,14 @@ CHANGEABLE_VAR changeable_vars[] = { #ifdef HAVE_BERKELEY_DB { "bdb_cache_size", (long*) &berkeley_cache_size, KEY_CACHE_SIZE, 20*1024, (long) ~0, 0, IO_SIZE }, - { "bdb_lock_max", (long*) &berkeley_lock_max, + { "bdb_max_lock", (long*) &berkeley_max_lock, + 1000, 0, (long) ~0, 0, 1 }, + /* QQ: The following should be removed soon! */ + { "bdb_lock_max", (long*) &berkeley_max_lock, 1000, 0, (long) ~0, 0, 1 }, #endif + { "binlog_cache_size", (long*) &binlog_cache_size, + 32*1024L, IO_SIZE, ~0L, 0, IO_SIZE }, { "connect_timeout", (long*) &connect_timeout, CONNECT_TIMEOUT, 2, 65535, 0, 1 }, { "delayed_insert_timeout", (long*) &delayed_insert_timeout, @@ -2388,6 +2376,26 @@ CHANGEABLE_VAR changeable_vars[] = { DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1 }, { "flush_time", (long*) &flush_time, FLUSH_TIME, 0, ~0L, 0, 1 }, +#ifdef HAVE_INNOBASE_DB + {"innobase_mirrored_log_groups", + (long*) &innobase_mirrored_log_groups, 1, 1, 10, 0, 1}, + {"innobase_log_files_in_group", + (long*) &innobase_mirrored_log_groups, 2, 2, 100, 0, 1}, + {"innobase_log_file_size", + (long*) &innobase_log_file_size, 5*1024*1024L, 1*1024*1024L, + ~0L, 0, 1024*1024L}, + {"innobase_log_buffer_size", + (long*) &innobase_log_buffer_size, 1024*1024L, 256*1024L, + ~0L, 0, 1024}, + {"innobase_buffer_pool_size", + (long*) &innobase_buffer_pool_size, 8*1024*1024L, 1024*1024L, + ~0L, 0, 1024*1024L}, + {"innobase_additional_mem_pool_size", + (long*) &innobase_additional_mem_pool_size, 1*1024*1024L, 512*1024L, + ~0L, 0, 1024}, + {"innobase_file_io_threads", + (long*) &innobase_file_io_threads, 9, 4, 64, 0, 1}, +#endif { "interactive_timeout", (long*) &net_interactive_timeout, NET_WAIT_TIMEOUT, 1, 31*24*60*60, 0, 1 }, { "join_buffer_size", (long*) &join_buff_size, @@ -2400,6 +2408,8 @@ CHANGEABLE_VAR changeable_vars[] = { IF_WIN(1,0), 0, 1, 0, 1 }, { "max_allowed_packet", (long*) &max_allowed_packet, 1024*1024L, 80, 17*1024*1024L, MALLOC_OVERHEAD, 1024 }, + { "max_binlog_cache_size", (long*) &max_binlog_cache_size, + ~0L, IO_SIZE, ~0L, 0, IO_SIZE }, { "max_connections", (long*) &max_connections, 100, 1, 16384, 0, 1 }, { "max_connect_errors", (long*) &max_connect_errors, @@ -2457,10 +2467,12 @@ struct show_var_st init_vars[]= { #ifdef HAVE_BERKELEY_DB {"bdb_cache_size", (char*) &berkeley_cache_size, SHOW_LONG}, {"bdb_home", (char*) &berkeley_home, SHOW_CHAR_PTR}, - {"bdb_lock_max", (char*) &berkeley_lock_max, SHOW_LONG}, + {"bdb_max_lock", (char*) &berkeley_max_lock, SHOW_LONG}, {"bdb_logdir", (char*) &berkeley_logdir, SHOW_CHAR_PTR}, + {"bdb_shared_data", (char*) &berkeley_shared_data, SHOW_BOOL}, {"bdb_tmpdir", (char*) &berkeley_tmpdir, SHOW_CHAR_PTR}, #endif + {"binlog_cache_size", (char*) &binlog_cache_size, SHOW_LONG}, {"character_set", default_charset, SHOW_CHAR}, {"character_sets", (char*) &charsets_list, SHOW_CHAR_PTR}, {"concurrent_insert", (char*) &myisam_concurrent_insert, SHOW_MY_BOOL}, @@ -2489,6 +2501,7 @@ struct show_var_st init_vars[]= { {"low_priority_updates", (char*) &low_priority_updates, SHOW_BOOL}, {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_LONG}, {"max_allowed_packet", (char*) &max_allowed_packet, SHOW_LONG}, + {"max_binlog_cache_size", (char*) &max_binlog_cache_size, SHOW_LONG}, {"max_connections", (char*) &max_connections, SHOW_LONG}, {"max_connect_errors", (char*) &max_connect_errors, SHOW_LONG}, {"max_delayed_threads", (char*) &max_insert_delayed_threads, SHOW_LONG}, @@ -2573,6 +2586,7 @@ struct show_var_st status_vars[]= { {"Select_range_check", (char*) &select_range_check_count, SHOW_LONG}, {"Select_scan", (char*) &select_scan_count, SHOW_LONG}, {"Slave_running", (char*) &slave_running, SHOW_BOOL}, + {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG}, {"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG}, {"Slow_queries", (char*) &long_query_count, SHOW_LONG}, {"Sort_merge_passes", (char*) &filesort_merge_passes, SHOW_LONG}, @@ -2702,8 +2716,9 @@ static void usage(void) --bdb-lock-detect=# Berkeley lock detect\n\ (DEFAULT, OLDEST, RANDOM or YOUNGEST, # sec)\n\ --bdb-logdir=directory Berkeley DB log file directory\n\ - --bdb-nosync Don't synchronously flush logs\n\ + --bdb-no-sync Don't synchronously flush logs\n\ --bdb-recover Start Berkeley DB in recover mode\n\ + --bdb-shared-data Start Berkeley DB in multi-process mode\n\ --bdb-tmpdir=directory Berkeley DB tempfile name\n\ --skip-bdb Don't use berkeley db (will save memory)\n\ "); @@ -2916,6 +2931,11 @@ static void get_options(int argc,char **argv) disconnect_slave_event_count = atoi(optarg); #endif break; + case (int)OPT_ABORT_SLAVE_EVENT_COUNT: +#ifndef DBUG_OFF + abort_slave_event_count = atoi(optarg); +#endif + break; case (int) OPT_LOG_SLAVE_UPDATES: opt_log_slave_updates = 1; break; @@ -3210,6 +3230,10 @@ static void get_options(int argc,char **argv) } break; } + case OPT_BDB_SHARED: + berkeley_init_flags&= ~(DB_PRIVATE); + berkeley_shared_data=1; + break; case OPT_BDB_SKIP: berkeley_skip=1; break; @@ -3218,6 +3242,24 @@ static void get_options(int argc,char **argv) case OPT_INNOBASE_SKIP: innobase_skip=1; break; + case OPT_INNOBASE_DATA_HOME_DIR: + innobase_data_home_dir=optarg; + break; + case OPT_INNOBASE_DATA_FILE_PATH: + innobase_data_file_path=optarg; + break; + case OPT_INNOBASE_LOG_GROUP_HOME_DIR: + innobase_log_group_home_dir=optarg; + break; + case OPT_INNOBASE_LOG_ARCH_DIR: + innobase_log_arch_dir=optarg; + break; + case OPT_INNOBASE_LOG_ARCHIVE: + innobase_log_archive= optarg ? test(atoi(optarg)) : 1; + break; + case OPT_INNOBASE_FLUSH_LOG_AT_TRX_COMMIT: + innobase_flush_log_at_trx_commit= optarg ? test(atoi(optarg)) : 1; + break; #endif case OPT_MYISAM_RECOVER: { diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 442597bbfad..dfd45933999 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -393,8 +393,8 @@ QUICK_SELECT::QUICK_SELECT(TABLE *table,uint key_nr,bool no_alloc) else bzero((char*) &alloc,sizeof(alloc)); file=head->file; - error=file->index_init(index); record=head->record[0]; + init(); } QUICK_SELECT::~QUICK_SELECT() @@ -403,13 +403,16 @@ QUICK_SELECT::~QUICK_SELECT() free_root(&alloc,MYF(0)); } +int QUICK_SELECT::init() +{ + return error=file->index_init(index); +} QUICK_RANGE::QUICK_RANGE() :min_key(0),max_key(0),min_length(0),max_length(0), flag(NO_MIN_RANGE | NO_MAX_RANGE) {} - SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc() { type=arg.type; diff --git a/sql/opt_range.h b/sql/opt_range.h index 586c35f397f..2005773eca7 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -73,7 +73,7 @@ public: QUICK_SELECT(TABLE *table,uint index_arg,bool no_alloc=0); virtual ~QUICK_SELECT(); void reset(void) { next=0; it.rewind(); } - virtual int init() { return 0; } + virtual int init(); virtual int get_next(); int cmp_next(QUICK_RANGE *range); bool unique_key_range(); diff --git a/sql/share/czech/errmsg.sys b/sql/share/czech/errmsg.sys Binary files differindex 8fb8d9630c2..68ce82eeb9e 100644 --- a/sql/share/czech/errmsg.sys +++ b/sql/share/czech/errmsg.sys diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index ae0369e3f17..c10d59dc21c 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -1,18 +1,13 @@ -/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB - This file is public domain and comes with NO WARRANTY of any kind */ - -/* Modifikoval Petr -B¹najdr, snajdr@pvt.net, snajdr@cpress.cz v.0.01 -A - ISO LATIN-8852-2 - Upravil Jan Pazdziora, adelton@fi.muni.cz - Tue Nov 18 17:53:55 MET 1997 verze 0.02 - Roz-B¹íøeno podle 3.21.15c Jan Pazdziora, adelton@fi.muni.cz-A - Tue Dec 2 19:08:54 MET 1997 verze 0.03 - Roz-B¹íøeno podle 3.21.29 Jan Pazdziora, adelton@fi.muni.cz-A - Thu May 7 17:40:49 MET DST 1998 verze 0.04 - Podle verze 3.22.20 upravil Jan Pazdziora, adelton@fi.muni.cz - Thu Apr 1 20:49:57 CEST 1999 - Podle verze 3.23.2 upravil Jan Pazdziora, adelton@fi.muni.cz - Mon Aug 9 13:30:09 MET DST 1999 +/* + Modifikoval Petr -B©najdr, snajdr@pvt.net, snajdr@cpress.cz v.0.01 -A + ISO LATIN-8852-2 + Dal-B¹í verze Jan Pazdziora, adelton@fi.muni.cz-A + Tue Nov 18 17:53:55 MET 1997 + Tue Dec 2 19:08:54 MET 1997 podle 3.21.15c + Thu May 7 17:40:49 MET DST 1998 podle 3.21.29 + Thu Apr 1 20:49:57 CEST 1999 podle 3.22.20 + Mon Aug 9 13:30:09 MET DST 1999 podle 3.23.2 + Thu Nov 30 14:02:52 MET 2000 podle 3.23.28 */ "hashchk", @@ -207,8 +202,9 @@ "S-Bí»ová chyba pøi ètení z masteru",-A "S-Bí»ová chyba pøi zápisu na master",-A "-B®ádný sloupec nemá vytvoøen fulltextový index",-A -"Can't execute the given command because you have active locked tables or an active transaction", -"Unknown system variable '%-.64'", -"Table '%-.64s' is marked as crashed and should be repaired", -"Table '%-.64s' is marked as crashed and last (automatic?) repair failed", +"Nemohu prov-Bést zadaný pøíkaz, proto¾e existují aktivní zamèené tabulky nebo aktivní transakce",-A +"Nezn-Bámá systémová promìnná '%-.64'",-A +"Tabulka '%-.64s' je ozna-Bèena jako poru¹ená a mìla by být opravena",-A +"Tabulka '%-.64s' je ozna-Bèena jako poru¹ená a poslední (automatická?) oprava se nezdaøila",-A "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/danish/errmsg.sys b/sql/share/danish/errmsg.sys Binary files differindex d55c0ada743..1e358664792 100644 --- a/sql/share/danish/errmsg.sys +++ b/sql/share/danish/errmsg.sys diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index b1faff2fffb..79d7ae5efc4 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -201,3 +201,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/dutch/errmsg.sys b/sql/share/dutch/errmsg.sys Binary files differindex 7a35b102cf1..c00dc0f3c0c 100644 --- a/sql/share/dutch/errmsg.sys +++ b/sql/share/dutch/errmsg.sys diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 18029c32a97..1cfdbee8d43 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -198,3 +198,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/english/errmsg.sys b/sql/share/english/errmsg.sys Binary files differindex 903f2958129..af1b051e1cd 100644 --- a/sql/share/english/errmsg.sys +++ b/sql/share/english/errmsg.sys diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 6395180f4fd..4900e4bd7ad 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -198,3 +198,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/estonian/errmsg.sys b/sql/share/estonian/errmsg.sys Binary files differindex fe9b221ce58..f27359c76e0 100644 --- a/sql/share/estonian/errmsg.sys +++ b/sql/share/estonian/errmsg.sys diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index b90b5d205ec..cb609f66432 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -202,3 +202,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/french/errmsg.sys b/sql/share/french/errmsg.sys Binary files differindex 75c1c8328f1..6bac623b236 100644 --- a/sql/share/french/errmsg.sys +++ b/sql/share/french/errmsg.sys diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index e5d90a93733..0e1b9cf8e01 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -198,3 +198,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/german/errmsg.sys b/sql/share/german/errmsg.sys Binary files differindex 46a68a5ef7f..d9f835e8dbe 100644 --- a/sql/share/german/errmsg.sys +++ b/sql/share/german/errmsg.sys diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 4ebd1ffe751..4d36544fedc 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -201,3 +201,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/greek/errmsg.sys b/sql/share/greek/errmsg.sys Binary files differindex bed966bdab4..d831fac812d 100644 --- a/sql/share/greek/errmsg.sys +++ b/sql/share/greek/errmsg.sys diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index d5ff3d1253f..eb43678ddcf 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -198,3 +198,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/hungarian/errmsg.sys b/sql/share/hungarian/errmsg.sys Binary files differindex da976080e27..6c2d550e7ac 100644 --- a/sql/share/hungarian/errmsg.sys +++ b/sql/share/hungarian/errmsg.sys diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index f864fe7ff0d..eacc8a967a9 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -200,3 +200,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/italian/errmsg.sys b/sql/share/italian/errmsg.sys Binary files differindex 045e64ce093..e63175d0230 100644 --- a/sql/share/italian/errmsg.sys +++ b/sql/share/italian/errmsg.sys diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index a88df6dcc0e..f5e333696cf 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -194,7 +194,8 @@ "Errore di rete inviando al master", "Impossibile trovare un indice FULLTEXT che corrisponda all'elenco delle colonne", "Can't execute the given command because you have active locked tables or an active transaction", -"Unknown system variable '%-.64'", -"Table '%-.64s' is marked as crashed and should be repaired", -"Table '%-.64s' is marked as crashed and last (automatic?) repair failed", +"Variabile di sistema '%-.64' sconosciuta", +"La tabella '%-.64s' e' segnalata come rovinata e deve essere riparata", +"La tabella '%-.64s' e' segnalata come rovinata e l'ultima ricostruzione (automatica?) e' fallita", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/japanese/errmsg.sys b/sql/share/japanese/errmsg.sys Binary files differindex 41618a4ffea..d0d8cfe1c41 100644 --- a/sql/share/japanese/errmsg.sys +++ b/sql/share/japanese/errmsg.sys diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index c2650178002..4b7a3fd30e1 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -200,3 +200,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/korean/errmsg.sys b/sql/share/korean/errmsg.sys Binary files differindex c94649378f4..a47cab2da19 100644 --- a/sql/share/korean/errmsg.sys +++ b/sql/share/korean/errmsg.sys diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index e49eead2210..d772f8c46d5 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -198,3 +198,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 0dd18ba9ecb..d6c29026c0b 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -200,3 +200,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index eb12dafbd6d..4d0c1ef87f0 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -200,3 +200,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/polish/errmsg.sys b/sql/share/polish/errmsg.sys Binary files differindex 551ff4d81a6..6246eac84f9 100644 --- a/sql/share/polish/errmsg.sys +++ b/sql/share/polish/errmsg.sys diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 84991513129..a77f64b8b37 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -202,3 +202,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/portuguese/errmsg.sys b/sql/share/portuguese/errmsg.sys Binary files differindex 5a6e038c1ce..df54263f7d7 100644 --- a/sql/share/portuguese/errmsg.sys +++ b/sql/share/portuguese/errmsg.sys diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index dbb881f21d5..946a64df5d0 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -198,3 +198,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/romanian/errmsg.sys b/sql/share/romanian/errmsg.sys Binary files differindex 1638246579c..a8336a034a8 100644 --- a/sql/share/romanian/errmsg.sys +++ b/sql/share/romanian/errmsg.sys diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 4b48d4347a9..7c325b9c662 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -202,3 +202,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/russian/errmsg.sys b/sql/share/russian/errmsg.sys Binary files differindex e2497d5e356..e7dbcac4f0e 100644 --- a/sql/share/russian/errmsg.sys +++ b/sql/share/russian/errmsg.sys diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 9f9d34b9d05..7a83573e0ff 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -163,7 +163,7 @@ "ðÏÌÕÞÅÎ ÐÁËÅÔ × ÎÅÐÒÁ×ÉÌØÎÏÍ ÐÏÒÑÄËÅ", "îÅ ÍÏÇÕ ÒÁÓÐÁËÏ×ÁÔØ ÐÁËÅÔ", "ïÛÉÂËÁ ÐÒÉ ÞÔÅÎÉÉ ÐÁËÅÔÏ×" -"Timeout ÐÒÉ ÞÔÅÎÉÉ ÐÁËÅÔÏ×", +"ôÁÊÍÁÕÔ ÐÒÉ ÞÔÅÎÉÉ ÐÁËÅÔÏ×", "ïÛÉÂËÁ ÐÒÉ ÏÔÐÒÁ×ËÅ ÐÁËÅÔÏ×", "ïÛÉÂËÁ ÐÒÉ ÏÔÐÒÁ×ËÅ ÐÁËÅÔÏ×", "òÅÚÕÌØÔÉÒÕÀÝÁÑ ÓÔÒÏËÁ ÂÏÌØÛÅ ÞÅÍ max_allowed_packet", @@ -171,25 +171,25 @@ "éÓÐÏÌØÚÕÅÍÁÑ ÔÁÂÌÉÃÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÏÌÑ AUTO_INCREMENT", "INSERT DELAYED ÎÅ ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØÓÑ Ó ÔÁÂÌÉÃÅÊ '%-.64s', ÏÎÁ ÚÁÎÑÔÁ ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ LOCK TABLES", "îÅ×ÅÒÎÏÅ ÉÍÑ ÐÏÌÑ '%-.100s'", -"éÓÐÏÌØÚÕÅÍÙÊ table handler ÎÅ ÍÏÖÅÔ ÉÎÄÅËÓÉÒÏ×ÁÔØ ÐÏÌÅ '%-.64s'", -"All tables in the MERGE table are not defined identically", -"Can't write, because of unique constraint, to table '%-.64s'", -"BLOB column '%-.64s' used in key specification without a key length", -"All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead", -"Result consisted of more than one row", -"This table type requires a primary key", -"This version of MySQL is not compiled with RAID support", +"ôÁÂÌÉÃÁ ÉÓÐÏÌØÚÕÅÍÏÇÏ ÔÉÐÁ ÎÅ ÍÏÖÅÔ ÉÎÄÅËÓÉÒÏ×ÁÔØ ÐÏÌÅ '%-.64s'", +"îÅ ×ÓÅ ÔÁÂÌÉÃÙ × MERGE ÏÐÒÅÄÅÌÅÎÙ ÏÄÉÎÁËÏ×Ï", +"îÅ ÍÏÇÕ ÐÉÓÁÔØ × ÔÁÂÌÉÃÕ '%-.64s' ÉÚ-ÚÁ UNIQUE ÕÓÌÏ×ÉÊ", +"ðÏÌÅ ÔÉÐÁ BLOB '%-.64s' × ÏÐÒÅÄÅÌÅÎÉÉ ÉÎÄÅËÓÁ ÂÅÚ ÕËÁÚÁÎÉÑ ÄÌÉÎÙ", +"÷ÓÅ ÞÁÓÔÉ PRIMARY KEY ÄÏÌÖÎÙ ÂÙÔØ NOT NULL; ÅÓÌÉ NULL × ÉÎÄÅËÓÅ ÎÅÏÂÈÏÄÉÍ, ÉÓÐÏÌØÚÕÊÔÅ UNIQUE", +"òÅÚÕÌØÔÁÔ ÓÏÄÅÒÖÉÔ ÂÏÌØÛÅ ÏÄÎÏÊ ÓÔÒÏËÉ", +"ôÁÂÌÉÃÁ ÜÔÏÇÏ ÔÉÐÁ ÏÂÑÚÁÎÁ ÉÍÅÔØ PRIMARY KEY", +"üÔÁ ËÏÐÉÑ MySQL ÓËÏÍÐÉÌÉÒÏ×ÁÎÁ ÂÅÚ ÐÏÄÄÅÒÖËÉ RAID", "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", -"Key '%-.64s' doesn't exist in table '%-.64s'", -"Can't open table", -"The handler for the table doesn't support check/repair", -"You are not allowed to execute this command in a transaction", -"Got error %d during COMMIT", -"Got error %d during ROLLBACK", -"Got error %d during FLUSH_LOGS", -"Got error %d during CHECKPOINT", -"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.64s' (%-.64s)", -"The handler for the table does not support binary table dump", +"éÎÄÅËÓ '%-.64s' ÎÅ ÎÁÊÄÅÎ × ÔÁÂÌÉÃÅ '%-.64s'", +"îÅ ÍÏÇÕ ÏÔËÒÙÔØ ÔÁÂÌÉÃÕ", +"äÁÎÎÙÊ ÔÉÐ ÔÁÂÌÉà ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ check/repair", +"üÔÁ ËÏÍÁÎÄÁ ×ÎÕÔÒÉ ÔÒÁÎÚÁËÃÉÉ ÚÁÐÒÅÝÅÎÁ", +"ïÛÉÂËÁ %d ×Ï ×ÒÅÍÑ COMMIT", +"ïÛÉÂËÁ %d ×Ï ×ÒÅÍÑ ROLLBACK", +"ïÛÉÂËÁ %d ×Ï ×ÒÅÍÑ FLUSH_LOGS", +"ïÛÉÂËÁ %d ×Ï ×ÒÅÍÑ CHECKPOINT", +"ðÒÅÒ×ÁÎÎÏÅ ÓÏÅÄÉÎÅÎÉÅ %ld Ë ÂÁÚÅ ÄÁÎÎÙÈ: '%-.64s' ÐÏÌØÚÏ×ÁÔÅÌØ: '%-.32s' ÈÏÓÔ: `%-.64s' (%-.64s)", +"üÔÏÔ ÔÉÐ ÔÁÂÌÉà ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ binary table dump", "Binlog closed while trying to FLUSH MASTER", "Failed rebuilding the index of dumped table '%-.64s'", "Error from master: '%-.64s'", @@ -197,7 +197,8 @@ "Net error writing to master", "FULLTEXT ÉÎÄÅËÓ, ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÊ ÚÁÄÁÎÎÏÍÕ ÓÐÉÓËÕ ÓÔÏÌÂÃÏ×, ÎÅ ÎÁÊÄÅÎ", "Can't execute the given command because you have active locked tables or an active transaction", -"Unknown system variable '%-.64'", -"Table '%-.64s' is marked as crashed and should be repaired", -"Table '%-.64s' is marked as crashed and last (automatic?) repair failed", +"îÅÉÚ×ÅÓÔÎÁÑ ÓÉÓÔÅÍÎÁÑ ÐÅÒÅÍÅÎÎÁÑ '%-.64'", +"ôÁÂÌÉÃÁ '%-.64s' ÐÏÍÅÞÅÎÁ ËÁË ÉÓÐÏÒÞÅÎÎÁÑ É ÄÏÌÖÎÁ ÂÙÔØ ÉÓÐÒÁ×ÌÅÎÁ", +"ôÁÂÌÉÃÁ '%-.64s' ÐÏÍÅÞÅÎÁ ËÁË ÉÓÐÏÒÞÅÎÎÁÑ É ÐÏÓÌÅÄÎÑÑ ÐÏÐÙÔËÁ ÉÓÐÒÁ×ÌÅÎÉÑ (Á×ÔÏÍÁÔÉÞÅÓËÁÑ?) ÎÅ ÕÄÁÌÁÓØ", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/slovak/errmsg.sys b/sql/share/slovak/errmsg.sys Binary files differindex 8e0c6ab4b79..2a8f977d3ea 100644 --- a/sql/share/slovak/errmsg.sys +++ b/sql/share/slovak/errmsg.sys diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 1ca1632bd57..774047be656 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -206,3 +206,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/spanish/errmsg.sys b/sql/share/spanish/errmsg.sys Binary files differindex bb301f89d3e..d651bd4c555 100644 --- a/sql/share/spanish/errmsg.sys +++ b/sql/share/spanish/errmsg.sys diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 7a76c438bad..0003e435183 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -199,3 +199,4 @@ "Table '%-.64s' is marked as crashed and should be repaired", "Table '%-.64s' is marked as crashed and last (automatic?) repair failed", "Warning: Some non-transactional changed tables couldn't be rolled back", +"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again', diff --git a/sql/share/swedish/errmsg.OLD b/sql/share/swedish/errmsg.OLD index f1a8062e390..ea8c2b78e3f 100644 --- a/sql/share/swedish/errmsg.OLD +++ b/sql/share/swedish/errmsg.OLD @@ -197,5 +197,6 @@ "Okänd system variabel '%-.64'", "Tabell '%-.64s' är crashad och bör repareras med REPAIR TABLE", "Tabell '%-.64s' är crashad och senast (automatiska?) reparation misslyckades", -#ER_WARNING_NOT_COMPLETE_ROLLBACK "Warning: Några icke transaktionella tabeller kunde inte återställas vid ROLLBACK", +#ER_TRANS_CACHE_FULL +"Transaktionen krävde mera än 'max_binlog_cache_size' minne. Utöka denna mysqld variabel och försök på nytt", diff --git a/sql/share/swedish/errmsg.sys b/sql/share/swedish/errmsg.sys Binary files differindex 011968008e8..3cf1b59746a 100644 --- a/sql/share/swedish/errmsg.sys +++ b/sql/share/swedish/errmsg.sys diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 78dfa51a806..ee3d913e51a 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -198,3 +198,4 @@ "Tabell '%-.64s' är crashad och bör repareras med REPAIR TABLE", "Tabell '%-.64s' är crashad och senast (automatiska?) reparation misslyckades", "Warning: Några icke transaktionella tabeller kunde inte återställas vid ROLLBACK", +"Transaktionen krävde mera än 'max_binlog_cache_size' minne. Utöka denna mysqld variabel och försök på nytt", diff --git a/sql/slave.cc b/sql/slave.cc index f2636ab5380..bea48af02af 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -33,9 +33,13 @@ DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; bool do_table_inited = 0, ignore_table_inited = 0; bool wild_do_table_inited = 0, wild_ignore_table_inited = 0; bool table_rules_on = 0; + +// when slave thread exits, we need to remember the temporary tables so we +// can re-use them on slave start +static TABLE* save_temporary_tables = 0; #ifndef DBUG_OFF -int disconnect_slave_event_count = 0; -static int events_till_disconnect = -1; +int disconnect_slave_event_count = 0, abort_slave_event_count = 0; +static int events_till_disconnect = -1, events_till_abort = -1; static int stuck_count = 0; #endif @@ -43,8 +47,8 @@ static int stuck_count = 0; static inline void skip_load_data_infile(NET* net); static inline bool slave_killed(THD* thd); static int init_slave_thread(THD* thd); -static void safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); -static void safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); +static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); +static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); static int safe_sleep(THD* thd, int sec); static int request_table_dump(MYSQL* mysql, char* db, char* table); static int create_table_from_dump(THD* thd, NET* net, const char* db, @@ -131,7 +135,7 @@ int tables_ok(THD* thd, TABLE_LIST* tables) int add_table_rule(HASH* h, const char* table_spec) { - char* dot = strchr(table_spec, '.'); + const char* dot = strchr(table_spec, '.'); if(!dot) return 1; uint len = (uint)strlen(table_spec); if(!len) return 1; @@ -148,7 +152,7 @@ int add_table_rule(HASH* h, const char* table_spec) int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec) { - char* dot = strchr(table_spec, '.'); + const char* dot = strchr(table_spec, '.'); if(!dot) return 1; uint len = (uint)strlen(table_spec); if(!len) return 1; @@ -761,6 +765,7 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) thd->query_error = 0; // clear error thd->net.last_errno = 0; thd->net.last_error[0] = 0; + thd->slave_proxy_id = qev->thread_id; // for temp tables mysql_parse(thd, thd->query, q_len); int expected_error,actual_error; if((expected_error = qev->error_code) != @@ -781,24 +786,17 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) thd->convert_set = 0; // assume no convert for next query // unless set explictly close_thread_tables(thd); - free_root(&thd->mem_root,0); - if (thd->query_error) + if (thd->query_error || thd->fatal_error) { sql_print_error("Slave: error running query '%s' ", qev->query); + free_root(&thd->mem_root,0); delete ev; return 1; } - + free_root(&thd->mem_root,0); delete ev; - - if(thd->fatal_error) - { - sql_print_error("Slave: Fatal error running query '%s' ", - thd->query); - return 1; - } mi->inc_pos(event_len); flush_master_info(mi); @@ -874,6 +872,7 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) List<Item> fields; lev->set_fields(fields); + thd->slave_proxy_id = thd->thread_id; thd->net.vio = net->vio; // mysql_load will use thd->net to read the file thd->net.pkt_nr = net->pkt_nr; @@ -919,11 +918,13 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) } case START_EVENT: + close_temporary_tables(thd); mi->inc_pos(event_len); flush_master_info(mi); break; case STOP_EVENT: + close_temporary_tables(thd); mi->inc_pos(event_len); flush_master_info(mi); break; @@ -971,7 +972,7 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) pthread_handler_decl(handle_slave,arg __attribute__((unused))) { - THD *thd;; // needs to be first for thread_stack + THD *thd; // needs to be first for thread_stack MYSQL *mysql = NULL ; if(!server_id) @@ -988,6 +989,9 @@ pthread_handler_decl(handle_slave,arg __attribute__((unused))) } slave_running = 1; abort_slave = 0; +#ifndef DBUG_OFF + events_till_abort = abort_slave_event_count; +#endif pthread_mutex_unlock(&LOCK_slave); int error = 1; @@ -1004,7 +1008,7 @@ pthread_handler_decl(handle_slave,arg __attribute__((unused))) if(init_slave_thread(thd) || init_master_info(&glob_mi)) goto err; thd->thread_stack = (char*)&thd; // remember where our stack is - + thd->temporary_tables = save_temporary_tables; // restore temp tables threads.append(thd); DBUG_PRINT("info",("master info: log_file_name=%s, position=%d", @@ -1020,14 +1024,16 @@ pthread_handler_decl(handle_slave,arg __attribute__((unused))) thd->proc_info = "connecting to master"; #ifndef DBUG_OFF sql_print_error("Slave thread initialized"); -#endif - safe_connect(thd, mysql, &glob_mi); - // always report status on startup, even if we are not in debug - sql_print_error("Slave: connected to master '%s@%s:%d',\ - replication started in log '%s' at position %ld", glob_mi.user, +#endif + // we can get killed during safe_connect + if(!safe_connect(thd, mysql, &glob_mi)) + sql_print_error("Slave: connected to master '%s@%s:%d',\ + replication started in log '%s' at position %ld", glob_mi.user, glob_mi.host, glob_mi.port, RPL_LOG_NAME, glob_mi.pos); + else + goto err; while(!slave_killed(thd)) { @@ -1056,8 +1062,7 @@ pthread_handler_decl(handle_slave,arg __attribute__((unused))) sql_print_error("Slave: failed dump request, reconnecting to \ try again, log '%s' at postion %ld", RPL_LOG_NAME, last_failed_pos = glob_mi.pos ); - safe_reconnect(thd, mysql, &glob_mi); - if(slave_killed(thd)) + if(safe_reconnect(thd, mysql, &glob_mi) || slave_killed(thd)) goto err; continue; @@ -1087,8 +1092,7 @@ try again, log '%s' at postion %ld", RPL_LOG_NAME, sql_print_error("Slave: Failed reading log event, \ reconnecting to retry, log '%s' position %ld", RPL_LOG_NAME, last_failed_pos = glob_mi.pos); - safe_reconnect(thd, mysql, &glob_mi); - if(slave_killed(thd)) + if(safe_reconnect(thd, mysql, &glob_mi) || slave_killed(thd)) goto err; break; } @@ -1104,6 +1108,13 @@ reconnecting to retry, log '%s' position %ld", RPL_LOG_NAME, // abort the slave thread, when the problem is fixed, the user // should restart the slave with mysqladmin start-slave } +#ifndef DBUG_OFF + if(abort_slave_event_count && !--events_till_abort) + { + sql_print_error("Slave: debugging abort"); + goto err; + } +#endif // successful exec with offset advance, // the slave repents and his sins are forgiven! @@ -1146,6 +1157,8 @@ position %ld", pthread_mutex_lock(&LOCK_slave); slave_running = 0; abort_slave = 0; + save_temporary_tables = thd->temporary_tables; + thd->temporary_tables = 0; // remove tempation from destructor to close them pthread_cond_broadcast(&COND_slave_stopped); // tell the world we are done pthread_mutex_unlock(&LOCK_slave); delete thd; @@ -1154,44 +1167,55 @@ position %ld", DBUG_RETURN(0); // Can't return anything here } -static void safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) - // will try to connect until successful +static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) + // will try to connect until successful or slave killed { + int slave_was_killed; #ifndef DBUG_OFF events_till_disconnect = disconnect_slave_event_count; #endif - while(!slave_killed(thd) && + while(!(slave_was_killed = slave_killed(thd)) && !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, mi->port, 0, 0)) { - sql_print_error( - "Slave thread: error connecting to master:%s, retry in %d sec", - mc_mysql_error(mysql), mi->connect_retry); + sql_print_error("Slave thread: error connecting to master:%s(%d),\ + retry in %d sec", mc_mysql_error(mysql), errno, mi->connect_retry); safe_sleep(thd, mi->connect_retry); } - mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d", + if(!slave_was_killed) + mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d", mi->user, mi->host, mi->port); + return slave_was_killed; } -// will try to connect until successful +// will try to connect until successful or slave killed -static void safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) { + int slave_was_killed; mi->pending = 0; // if we lost connection after reading a state set event // we will be re-reading it, so pending needs to be cleared #ifndef DBUG_OFF events_till_disconnect = disconnect_slave_event_count; #endif - while(!slave_killed(thd) && mc_mysql_reconnect(mysql)) + while(!(slave_was_killed = slave_killed(thd)) && mc_mysql_reconnect(mysql)) { - sql_print_error("Slave thread: error connecting to master:\ -%s, retry in %d sec", - mc_mysql_error(mysql), mi->connect_retry); + sql_print_error("Slave thread: error re-connecting to master:\ +%s, last_errno=%d, retry in %d sec", + mc_mysql_error(mysql), errno, mi->connect_retry); safe_sleep(thd, mi->connect_retry); } + + if(!slave_was_killed) + sql_print_error("Slave: reconnected to master '%s@%s:%d',\ +replication resumed in log '%s' at position %ld", glob_mi.user, + glob_mi.host, glob_mi.port, + RPL_LOG_NAME, + glob_mi.pos); + return slave_was_killed; } #ifdef __GNUC__ diff --git a/sql/slave.h b/sql/slave.h index fb559e8260c..b4fb16b4b07 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -95,7 +95,7 @@ extern bool do_table_inited, ignore_table_inited, extern bool table_rules_on; #ifndef DBUG_OFF -extern int disconnect_slave_event_count ; +extern int disconnect_slave_event_count, abort_slave_event_count ; #endif // the master variables are defaults read from my.cnf or command line diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 66c4a22ad67..63c17dae740 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -422,11 +422,47 @@ void close_temporary(TABLE *table,bool delete_table) void close_temporary_tables(THD *thd) { TABLE *table,*next; + uint init_query_buf_size = 11, query_buf_size; // "drop table " + char* query, *p; + LINT_INIT(p); + query_buf_size = init_query_buf_size; + + for (table=thd->temporary_tables ; table ; table=table->next) + { + query_buf_size += table->key_length; + + } + + if(query_buf_size == init_query_buf_size) + return; // no tables to close + + if((query = alloc_root(&thd->mem_root, query_buf_size))) + { + memcpy(query, "drop table ", init_query_buf_size); + p = query + init_query_buf_size; + } + for (table=thd->temporary_tables ; table ; table=next) { + if(query) // we might be out of memory, but this is not fatal + { + p = strxmov(p,table->table_cache_key,".", + table->table_name,",", NullS); + // here we assume table_cache_key always starts + // with \0 terminated db name + } next=table->next; close_temporary(table); } + if (query && mysql_bin_log.is_open()) + { + uint save_query_len = thd->query_length; + *--p = 0; + thd->query_length = (uint)(p-query); + Query_log_event qinfo(thd, query); + mysql_bin_log.write(&qinfo); + thd->query_length = save_query_len; + } thd->temporary_tables=0; } @@ -437,6 +473,9 @@ TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) uint key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1; TABLE *table,**prev; + int4store(key+key_length,thd->slave_proxy_id); + key_length += 4; + prev= &thd->temporary_tables; for (table=thd->temporary_tables ; table ; table=table->next) { @@ -457,21 +496,25 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name) table= *prev; *prev= table->next; close_temporary(table); + if(thd->slave_thread) + --slave_open_temp_tables; return 0; } -bool rename_temporary_table(TABLE *table, const char *db, +bool rename_temporary_table(THD* thd, TABLE *table, const char *db, const char *table_name) { char *key; if (!(key=(char*) alloc_root(&table->mem_root, (uint) strlen(db)+ - (uint) strlen(table_name)+2))) + (uint) strlen(table_name)+6))) return 1; /* purecov: inspected */ table->key_length=(uint) (strmov((table->real_name=strmov(table->table_cache_key=key, db)+1), table_name) - table->table_cache_key)+1; + int4store(key+table->key_length,thd->slave_proxy_id); + table->key_length += 4; return 0; } @@ -624,12 +667,13 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, *refresh=0; if (thd->killed) DBUG_RETURN(0); - key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; - + key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1; + int4store(key + key_length, thd->slave_proxy_id); + for (table=thd->temporary_tables; table ; table=table->next) { - if (table->key_length == key_length && - !memcmp(table->table_cache_key,key,key_length)) + if (table->key_length == key_length+4 && + !memcmp(table->table_cache_key,key,key_length+4)) { if (table->query_id == thd->query_id) { @@ -1339,8 +1383,14 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, { TABLE *tmp_table; DBUG_ENTER("open_temporary_table"); + + // the extra size in my_malloc() is for table_cache_key + // 4 bytes for master thread id if we are in the slave + // 1 byte to terminate db + // 1 byte to terminate table_name + // total of 6 extra bytes in my_malloc in addition to table/db stuff if (!(tmp_table=(TABLE*) my_malloc(sizeof(*tmp_table)+(uint) strlen(db)+ - (uint) strlen(table_name)+2, + (uint) strlen(table_name)+6, MYF(MY_WME)))) DBUG_RETURN(0); /* purecov: inspected */ @@ -1361,10 +1411,16 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, tmp_table->key_length= (uint) (strmov(strmov(tmp_table->table_cache_key,db) +1, table_name) - tmp_table->table_cache_key)+1; + int4store(tmp_table->table_cache_key + tmp_table->key_length, + thd->slave_proxy_id); + tmp_table->key_length += 4; + if (link_in_list) { tmp_table->next=thd->temporary_tables; thd->temporary_tables=tmp_table; + if(thd->slave_thread) + ++slave_open_temp_tables; } DBUG_RETURN(tmp_table); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 5be229a2695..b310d0f44bb 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -98,6 +98,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), start_time=(time_t) 0; current_linfo = 0; slave_thread = 0; + slave_proxy_id = 0; last_nx_table = last_nx_db = 0; inactive_timeout=net_wait_timeout; open_options=ha_open_options; @@ -120,8 +121,10 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), #ifdef USING_TRANSACTIONS bzero((char*) &transaction,sizeof(transaction)); if (open_cached_file(&transaction.trans_log, - mysql_tmpdir,LOG_PREFIX,0,MYF(MY_WME))) + mysql_tmpdir, LOG_PREFIX, binlog_cache_size, + MYF(MY_WME))) killed=1; + transaction.trans_log.end_of_file= max_binlog_cache_size; #endif #ifdef __WIN__ @@ -147,6 +150,7 @@ THD::~THD() close_temporary_tables(this); #ifdef USING_TRANSACTIONS close_cached_file(&transaction.trans_log); + ha_close_connection(this); #endif if (global_read_lock) { diff --git a/sql/sql_class.h b/sql/sql_class.h index 83c70f29d19..4943e334aa0 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -74,12 +74,12 @@ public: void open(const char *log_name,enum_log_type log_type, const char *new_name=0); void new_file(void); - void write(THD *thd, enum enum_server_command command,const char *format,...); - void write(THD *thd, const char *query, uint query_length, + bool write(THD *thd, enum enum_server_command command,const char *format,...); + bool write(THD *thd, const char *query, uint query_length, time_t query_start=0); - void write(Query_log_event* event_info); // binary log write - void write(Load_log_event* event_info); - + bool write(Query_log_event* event_info); // binary log write + bool write(Load_log_event* event_info); + bool write(IO_CACHE *cache); int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); @@ -274,6 +274,10 @@ public: // if we do a purge of binary logs, log index info of the threads // that are currently reading it needs to be adjusted. To do that // each thread that is using LOG_INFO needs to adjust the pointer to it + + ulong slave_proxy_id; // in slave thread we need to know in behalf of which + // thread the query is being run to replicate temp tables properly + THD(); ~THD(); bool store_globals(); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 85732863732..89e7faa237c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -158,14 +158,14 @@ exit: are 2 digits (raid directories). */ -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, - uint level) +static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, + uint level) { long deleted=0; ulong found_other_files=0; char filePath[FN_REFLEN]; DBUG_ENTER("mysql_rm_known_files"); - DBUG_PRINT("enter",("path: %s", path)); + DBUG_PRINT("enter",("path: %s", org_path)); /* remove all files with known extensions */ for (uint idx=2 ; @@ -181,7 +181,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, { char newpath[FN_REFLEN]; MY_DIR *new_dirp; - strxmov(newpath,path,"/",file->name,NullS); + strxmov(newpath,org_path,"/",file->name,NullS); + unpack_filename(newpath,newpath); if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT)))) { DBUG_PRINT("my",("New subdir found: %s", newpath)); @@ -198,7 +199,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, found_other_files++; continue; } - strxmov(filePath,path,"/",file->name,NullS); + strxmov(filePath,org_path,"/",file->name,NullS); + unpack_filename(filePath,filePath); if (my_delete(filePath,MYF(MY_WME))) { net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error); @@ -222,6 +224,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, */ if (!found_other_files) { + char tmp_path[FN_REFLEN]; + char *path=unpack_filename(tmp_path,org_path); #ifdef HAVE_READLINK int linkcount = readlink(path,filePath,sizeof(filePath)-1); if (linkcount > 0) // If the path was a symbolic link @@ -238,6 +242,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, path=filePath; } #endif + /* Remove last FN_LIBCHAR to not cause a probelm on OS/2 */ + char *pos=strend(path); + if (pos > path && pos[-1] == FN_LIBCHAR) + *--pos=0; /* Don't give errors if we can't delete 'RAID' directory */ if (rmdir(path) < 0 && !level) { diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 807464552a7..dbcd2640dbc 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -126,7 +126,7 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, SQL_SELECT *select; READ_RECORD info; bool using_limit=limit != HA_POS_ERROR; - bool use_generate_table; + bool use_generate_table,using_transactions; DBUG_ENTER("mysql_delete"); if (!table_list->db) @@ -214,18 +214,20 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, (void) table->file->extra(HA_EXTRA_READCHECK); if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); - if (deleted) + using_transactions=table->file->has_transactions(); + if (deleted && (error == 0 || !using_transactions)) { mysql_update_log.write(thd,thd->query, thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); + Query_log_event qinfo(thd, thd->query, using_transactions); + if (mysql_bin_log.write(&qinfo) && using_transactions) + error=1; } - if (!table->file->has_transactions()) + if (!using_transactions) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (ha_autocommit_or_rollback(thd,error >= 0)) + if (using_transactions && ha_autocommit_or_rollback(thd,error >= 0)) error=1; if (thd->lock) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b1ae918a791..baaf65d4197 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -102,6 +102,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, int error; bool log_on= ((thd->options & OPTION_UPDATE_LOG) || !(thd->master_access & PROCESS_ACL)); + bool using_transactions; uint value_count; uint save_time_stamp; ulong counter = 1; @@ -254,18 +255,21 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, thd->insert_id(id); // For update log else if (table->next_number_field) id=table->next_number_field->val_int(); // Return auto_increment value - if (info.copied || info.deleted) + using_transactions=table->file->has_transactions(); + if ((info.copied || info.deleted) && (error == 0 || !using_transactions)) { mysql_update_log.write(thd, thd->query, thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); + Query_log_event qinfo(thd, thd->query, using_transactions); + if (mysql_bin_log.write(&qinfo) && using_transactions) + error=1; } - if (!table->file->has_transactions()) + if (!using_transactions) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - error=ha_autocommit_or_rollback(thd,error); + if (using_transactions) + error=ha_autocommit_or_rollback(thd,error); if (thd->lock) { mysql_unlock_tables(thd, thd->lock); @@ -1265,7 +1269,8 @@ bool select_insert::send_eof() mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); + Query_log_event qinfo(thd, thd->query, + table->file->has_transactions()); mysql_bin_log.write(&qinfo); } return 0; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 1cb042537fc..7c2d77ce3fc 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -245,10 +245,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (!table->file->has_transactions()) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; - if (!read_file_from_client) + if (!read_file_from_client && mysql_bin_log.is_open()) { ex->skip_lines = save_skip_lines; - Load_log_event qinfo(thd, ex, table->table_name, fields, handle_duplicates); + Load_log_event qinfo(thd, ex, table->table_name, fields, + handle_duplicates); mysql_bin_log.write(&qinfo); } DBUG_RETURN(0); diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc new file mode 100644 index 00000000000..53953c96d0b --- /dev/null +++ b/sql/sql_manager.cc @@ -0,0 +1,101 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + * sql_manager.cc + * This thread manages various maintenance tasks. + * + * o Flushing the tables every flush_time seconds. + * o Berkeley DB: removing unneeded log files. + */ + +#include "mysql_priv.h" +#include "sql_manager.h" + +ulong volatile manager_status; +bool volatile manager_thread_in_use; + +pthread_t manager_thread; +pthread_mutex_t LOCK_manager; +pthread_cond_t COND_manager; + +pthread_handler_decl(handle_manager,arg __attribute__((unused))) +{ + int error = 0; + ulong status; + struct timespec abstime; + bool reset_flush_time = TRUE; + my_thread_init(); + DBUG_ENTER("handle_manager"); + + pthread_detach_this_thread(); + manager_thread = pthread_self(); + manager_status = 0; + manager_thread_in_use = 1; + + for (;;) + { + pthread_mutex_lock(&LOCK_manager); + /* XXX: This will need to be made more general to handle different + * polling needs. */ + if (flush_time) + { + if (reset_flush_time) + { +#ifdef HAVE_TIMESPEC_TS_SEC + abstime.ts_sec = time(NULL)+flush_time; // Bsd 2.1 + abstime.ts_nsec = 0; +#else + abstime.tv_sec = time(NULL)+flush_time; // Linux or Solairs + abstime.tv_nsec = 0; +#endif + reset_flush_time = FALSE; + } + while (!manager_status && !error && !abort_loop) + error = pthread_cond_timedwait(&COND_manager, &LOCK_manager, &abstime); + } + else + while (!manager_status && !error && !abort_loop) + error = pthread_cond_wait(&COND_manager, &LOCK_manager); + status = manager_status; + manager_status = 0; + pthread_mutex_unlock(&LOCK_manager); + + if (abort_loop) + break; + + if (error) /* == ETIMEDOUT */ + { + flush_tables(); + error = 0; + reset_flush_time = TRUE; + } + +#ifdef HAVE_BERKELEY_DB + if (status & MANAGER_BERKELEY_LOG_CLEANUP) + { + berkeley_cleanup_log_files(); + status &= ~MANAGER_BERKELEY_LOG_CLEANUP; + } +#endif + + if (status) + DBUG_PRINT("error", ("manager did not handle something: %lx", status)); + } + manager_thread_in_use = 0; + my_thread_end(); + DBUG_RETURN(NULL); +} diff --git a/sql/sql_manager.h b/sql/sql_manager.h new file mode 100644 index 00000000000..35704705820 --- /dev/null +++ b/sql/sql_manager.h @@ -0,0 +1,19 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef HAVE_BERKELEY_DB +void berkeley_cleanup_log_files(void); +#endif /* HAVE_BERKELEY_DB */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5eba2ab880b..88a9df7543a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -172,7 +172,7 @@ check_connections(THD *thd) vio_description(net->vio))); if (!thd->host) // If TCP/IP connection { - char ip[17]; + char ip[30]; if (vio_peer_addr(net->vio,ip)) return (ER_BAD_HOST_ERROR); @@ -462,7 +462,7 @@ pthread_handler_decl(handle_bootstrap,arg) thd->proc_info=0; thd->version=refresh_version; - thd->priv_user=thd->user="boot"; + thd->priv_user=thd->user=(char*)"boot"; buff= (char*) thd->net.buff; init_sql_alloc(&thd->mem_root,8192,8192); @@ -718,7 +718,7 @@ bool do_command(THD *thd) case COM_DROP_DB: { char *db=thd->strdup(packet+1); - if (check_access(thd,DROP_ACL,db,0,1)) + if (check_access(thd,DROP_ACL,db,0,1) || end_active_trans(thd)) break; mysql_log.write(thd,command,db); mysql_rm_db(thd,db,0); @@ -875,7 +875,7 @@ mysql_execute_command(void) TABLE_LIST *tables=(TABLE_LIST*) lex->table_list.first; DBUG_ENTER("mysql_execute_command"); - if(thd->slave_thread && table_rules_on && tables && !tables_ok(thd,tables)) + if(table_rules_on && thd->slave_thread && tables && !tables_ok(thd,tables)) DBUG_VOID_RETURN; // skip if we are in the slave thread, some table // rules have been given and the table list says the query should not be // replicated @@ -1136,7 +1136,10 @@ mysql_execute_command(void) goto error; /* purecov: inspected */ if (grant_option && check_grant(thd,INDEX_ACL,tables)) goto error; - res = mysql_create_index(thd, tables, lex->key_list); + if (end_active_trans(thd)) + res= -1; + else + res = mysql_create_index(thd, tables, lex->key_list); break; case SQLCOM_SLAVE_START: @@ -1224,7 +1227,9 @@ mysql_execute_command(void) goto error; } } - if (mysql_rename_tables(thd,tables)) + if (end_active_trans(thd)) + res= -1; + else if (mysql_rename_tables(thd,tables)) res= -1; break; } @@ -1404,8 +1409,11 @@ mysql_execute_command(void) #endif break; } - case SQLCOM_DELETE: case SQLCOM_TRUNCATE: + lex->where=0; + lex->select_limit=HA_POS_ERROR; + /* Fall through */ + case SQLCOM_DELETE: { if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) goto error; /* purecov: inspected */ @@ -1425,7 +1433,10 @@ mysql_execute_command(void) { if (check_table_access(thd,DROP_ACL,tables)) goto error; /* purecov: inspected */ - res = mysql_rm_table(thd,tables,lex->drop_if_exists); + if (end_active_trans(thd)) + res= -1; + else + res = mysql_rm_table(thd,tables,lex->drop_if_exists); } break; case SQLCOM_DROP_INDEX: @@ -1435,7 +1446,10 @@ mysql_execute_command(void) goto error; /* purecov: inspected */ if (grant_option && check_grant(thd,INDEX_ACL,tables)) goto error; - res = mysql_drop_index(thd, tables, lex->drop_list); + if (end_active_trans(thd)) + res= -1; + else + res = mysql_drop_index(thd, tables, lex->drop_list); break; case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) @@ -1589,7 +1603,10 @@ mysql_execute_command(void) } } else + { + thd->options&= ~(ulong) (OPTION_STATUS_NO_TRANS_UPDATE); thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; + } } send_ok(&thd->net); break; @@ -1637,7 +1654,8 @@ mysql_execute_command(void) } case SQLCOM_DROP_DB: { - if (check_access(thd,DROP_ACL,lex->name,0,1)) + if (check_access(thd,DROP_ACL,lex->name,0,1) || + end_active_trans(thd)) break; mysql_rm_db(thd,lex->name,lex->drop_if_exists); break; @@ -1755,9 +1773,17 @@ mysql_execute_command(void) } break; case SQLCOM_BEGIN: - thd->options|= OPTION_BEGIN; - thd->server_status|= SERVER_STATUS_IN_TRANS; - send_ok(&thd->net); + if (end_active_trans(thd)) + { + res= -1; + } + else + { + thd->options= ((thd->options & (ulong) (OPTION_STATUS_NO_TRANS_UPDATE)) | + OPTION_BEGIN); + thd->server_status|= SERVER_STATUS_IN_TRANS; + send_ok(&thd->net); + } break; case SQLCOM_COMMIT: /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8856d31645c..31fc3f28099 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5218,8 +5218,10 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields) List_iterator<Item> it(fields); Item *item; while ((item=it++)) - if (item->tmp_table_field()) + { + if (item->tmp_table_field() && ! item->const_item()) field_count++; + } if (!field_count) { // only const items diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a9e43198d5a..367ed9a0acb 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -23,7 +23,8 @@ #include "sql_select.h" // For select_describe #include "sql_acl.h" #include <my_dir.h> -extern "C" pthread_mutex_t THR_LOCK_keycache; + +/* extern "C" pthread_mutex_t THR_LOCK_keycache; */ static const char *grant_names[]={ "select","insert","update","delete","create","drop","reload","shutdown", @@ -602,7 +603,7 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) else net_store_null(packet); net_store_null(packet); // No pack_information yet - net_store_null(packet); // No comments yet + net_store_data(packet,key_info->flags & HA_FULLTEXT ? "FULLTEXT":""); if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) DBUG_RETURN(1); /* purecov: inspected */ } @@ -718,7 +719,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) field->sql_type(type); packet->append(type.ptr(),type.length()); - bool has_default = (field->type() != FIELD_TYPE_BLOB && + bool has_default = (field->type() != FIELD_TYPE_BLOB && field->type() != FIELD_TYPE_TIMESTAMP && field->unireg_check != Field::NEXT_NUMBER); if (flags & NOT_NULL_FLAG) @@ -821,7 +822,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) packet->append(" DELAY_KEY_WRITE=1",18); if(table->comment) { - packet->append(" COMMENT='", 10); + packet->append(" COMMENT='", 10); append_unescaped(packet, table->comment); packet->append('\''); } @@ -988,7 +989,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) if (send_fields(thd,field_list,1)) DBUG_RETURN(1); /* purecov: inspected */ - pthread_mutex_lock(&THR_LOCK_keycache); + /* pthread_mutex_lock(&THR_LOCK_keycache); */ pthread_mutex_lock(&LOCK_status); for (i=0; variables[i].name; i++) { @@ -1037,13 +1038,13 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) } } pthread_mutex_unlock(&LOCK_status); - pthread_mutex_unlock(&THR_LOCK_keycache); + /* pthread_mutex_unlock(&THR_LOCK_keycache); */ send_eof(&thd->net); DBUG_RETURN(0); err: pthread_mutex_unlock(&LOCK_status); - pthread_mutex_unlock(&THR_LOCK_keycache); + /* pthread_mutex_unlock(&THR_LOCK_keycache); */ DBUG_RETURN(1); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f51e1d1abe8..dc1ceb1112b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -178,6 +178,7 @@ int quick_rm_table(enum db_type base,const char *db, if (my_delete(path,MYF(0))) error=1; /* purecov: inspected */ sprintf(path,"%s/%s/%s",mysql_data_home,db,table_name); + unpack_filename(path,path); return ha_delete_table(base,path) || error; } @@ -1426,6 +1427,17 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->count_cuted_fields=0; /* Don`t calc cuted fields */ new_table->time_stamp=save_time_stamp; +#if defined( __WIN__) || defined( __EMX__) + /* + We must do the COMMIT here so that we can close and rename the + temporary table (as windows can't rename open tables) + */ + if (ha_commit_stmt(thd)) + error=1; + if (ha_commit(thd)) + error=1; +#endif + if (table->tmp_table) { /* We changed a temporary table */ @@ -1437,7 +1449,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* Remove link to old table and rename the new one */ close_temporary_table(thd,table->table_cache_key,table_name); - if (rename_temporary_table(new_table, new_db, new_name)) + if (rename_temporary_table(thd, new_table, new_db, new_name)) { // Fatal error close_temporary_table(thd,new_db,tmp_name); my_free((gptr) new_table,MYF(0)); @@ -1543,6 +1555,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err; } } + +#if !(defined( __WIN__) || defined( __EMX__)) /* The ALTER TABLE is always in it's own transaction */ error = ha_commit_stmt(thd); if (ha_commit(thd)) @@ -1553,6 +1567,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_mutex_unlock(&LOCK_open)); goto err; } +#endif thd->proc_info="end"; mysql_update_log.write(thd, thd->query,thd->query_length); @@ -1615,6 +1630,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, (copy_end++)->set(*ptr,def->field,0); } + found_count=delete_count=0; + if(order) { from->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); @@ -1632,7 +1649,6 @@ copy_data_between_tables(TABLE *from,TABLE *to, init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1); - found_count=delete_count=0; next_field=to->next_number_field; while (!(error=info.read_record(&info))) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 25e990e9f62..54f85eb2ec7 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -49,7 +49,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, thr_lock_type lock_type) { bool using_limit=limit != HA_POS_ERROR; - bool used_key_is_modified; + bool used_key_is_modified, using_transactions; int error=0; uint save_time_stamp, used_index; key_map old_used_keys; @@ -237,18 +237,20 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, thd->proc_info="end"; VOID(table->file->extra(HA_EXTRA_READCHECK)); table->time_stamp=save_time_stamp; // Restore auto timestamp pointer - if (updated) + using_transactions=table->file->has_transactions(); + if (updated && (error == 0 || !using_transactions)) { mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); + Query_log_event qinfo(thd, thd->query, using_transactions); + if (mysql_bin_log.write(&qinfo) && using_transactions) + error=1; } - if (!table->file->has_transactions()) + if (!using_transactions) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (ha_autocommit_or_rollback(thd, error >= 0)) + if (using_transactions && ha_autocommit_or_rollback(thd, error >= 0)) error=1; if (thd->lock) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6735498b8f2..e301c290894 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2451,6 +2451,7 @@ user: keyword: ACTION {} | AFTER_SYM {} + | AGAINST {} | AGGREGATE_SYM {} | AUTOCOMMIT {} | AVG_ROW_LENGTH {} diff --git a/sql/unireg.h b/sql/unireg.h index c293433dd42..9f078ef3766 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -41,7 +41,8 @@ #define ERRMAPP 1 /* Errormap f|r my_error */ #define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */ -#define MAX_DBKEY_LENGTH (FN_LEN*2+2) +#define MAX_DBKEY_LENGTH (FN_LEN*2+6) /* extra 4 bytes for slave tmp + * tables */ #define MAX_FIELD_NAME 34 /* Max colum name length +2 */ #define MAX_KEY 32 /* Max used keys */ #define MAX_REF_PARTS 16 /* Max parts used as ref */ |