diff options
author | unknown <monty@mashka.mysql.fi> | 2003-08-11 22:44:43 +0300 |
---|---|---|
committer | unknown <monty@mashka.mysql.fi> | 2003-08-11 22:44:43 +0300 |
commit | 034b44cb9ff914ceb6d32706a3a284eda00891b3 (patch) | |
tree | 3c0ddcb446b8be099c3ab2616c459a573ee3cf92 /sql | |
parent | f4646c0d063fb541f48dd2824ecccea464a9958d (diff) | |
parent | 6db21de014d603477952c7a16449bcae40a70e5c (diff) | |
download | mariadb-git-034b44cb9ff914ceb6d32706a3a284eda00891b3.tar.gz |
Merge with 4.0.14
BitKeeper/etc/logging_ok:
auto-union
scripts/make_win_src_distribution.old:
Merge rename: scripts/make_win_src_distribution.sh -> scripts/make_win_src_distribution.old
BUILD/compile-pentium-debug-max:
Auto merged
BitKeeper/deleted/.del-sel000001.result~383913ae4505ec86:
Auto merged
BitKeeper/deleted/.del-sel000001.test~9567c1646058cc:
Auto merged
Build-tools/Bootstrap:
Auto merged
Build-tools/Do-compile:
Auto merged
Docs/Makefile.am:
Auto merged
client/get_password.c:
Auto merged
client/mysql.cc:
Auto merged
client/mysqltest.c:
Auto merged
extra/perror.c:
Auto merged
include/config-win.h:
Auto merged
include/my_sys.h:
Auto merged
innobase/btr/btr0cur.c:
Auto merged
innobase/btr/btr0pcur.c:
Auto merged
innobase/buf/buf0buf.c:
Auto merged
innobase/buf/buf0flu.c:
Auto merged
innobase/dict/dict0dict.c:
Auto merged
innobase/dict/dict0load.c:
Auto merged
innobase/include/buf0buf.h:
Auto merged
innobase/include/log0recv.h:
Auto merged
innobase/include/row0sel.h:
Auto merged
innobase/include/srv0srv.h:
Auto merged
innobase/include/ut0mem.h:
Auto merged
innobase/lock/lock0lock.c:
Auto merged
innobase/log/log0log.c:
Auto merged
innobase/mem/mem0pool.c:
Auto merged
innobase/os/os0file.c:
Auto merged
innobase/row/row0mysql.c:
Auto merged
innobase/row/row0sel.c:
Auto merged
innobase/srv/srv0srv.c:
Auto merged
innobase/srv/srv0start.c:
Auto merged
innobase/trx/trx0sys.c:
Auto merged
innobase/trx/trx0trx.c:
Auto merged
innobase/ut/ut0mem.c:
Auto merged
innobase/ut/ut0ut.c:
Auto merged
myisam/ft_boolean_search.c:
Auto merged
myisam/mi_check.c:
Auto merged
myisam/mi_extra.c:
Auto merged
myisam/mi_key.c:
Auto merged
myisam/myisamdef.h:
Auto merged
myisammrg/myrg_queue.c:
Auto merged
mysql-test/mysql-test-run.sh:
Auto merged
mysql-test/r/ctype_latin1_de.result:
Auto merged
mysql-test/r/flush.result:
Auto merged
mysql-test/r/func_time.result:
Auto merged
mysql-test/r/grant_cache.result:
Auto merged
mysql-test/r/join.result:
Auto merged
mysql-test/r/join_outer.result:
Auto merged
mysql-test/r/range.result:
Auto merged
mysql-test/r/rpl000018.result:
Auto merged
mysql-test/r/rpl_insert_id.result:
Auto merged
mysql-test/r/rpl_master_pos_wait.result:
Auto merged
mysql-test/r/rpl_relayspace.result:
Auto merged
mysql-test/r/select_safe.result:
Auto merged
mysql-test/r/symlink.result:
Auto merged
mysql-test/r/type_date.result:
Auto merged
mysql-test/r/type_datetime.result:
Auto merged
mysql-test/t/alias.test:
Auto merged
mysql-test/t/ctype_latin1_de.test:
Auto merged
mysql-test/t/fulltext_left_join.test:
Auto merged
mysql-test/t/func_time.test:
Auto merged
mysql-test/t/handler.test:
Auto merged
mysql-test/t/heap.test:
Auto merged
mysql-test/t/join.test:
Auto merged
mysql-test/t/join_outer.test:
Auto merged
mysql-test/t/order_by.test:
Auto merged
mysql-test/t/range.test:
Auto merged
mysql-test/t/rpl000001.test:
Auto merged
mysql-test/t/rpl000018.test:
Auto merged
mysql-test/t/rpl_insert_id.test:
Auto merged
mysql-test/t/sel000100.test:
Auto merged
mysql-test/t/select_safe.test:
Auto merged
mysql-test/t/type_date.test:
Auto merged
mysql-test/t/type_datetime.test:
Auto merged
mysql-test/t/user_var.test:
Auto merged
mysys/default.c:
Auto merged
mysys/mf_format.c:
Auto merged
mysys/my_getopt.c:
Auto merged
mysys/thr_lock.c:
Auto merged
mysys/tree.c:
Auto merged
scripts/Makefile.am:
Auto merged
scripts/mysql_install_db.sh:
Auto merged
scripts/mysqld_safe.sh:
Auto merged
sql/Makefile.am:
Auto merged
sql/field_conv.cc:
Auto merged
sql/ha_innodb.h:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/ha_myisammrg.h:
Auto merged
sql/handler.cc:
Auto merged
sql/handler.h:
Auto merged
sql/item.h:
Auto merged
sql/item_func.cc:
Auto merged
sql/item_timefunc.cc:
Auto merged
sql/net_serv.cc:
Auto merged
sql/nt_servc.cc:
Auto merged
sql/opt_range.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_cache.h:
Auto merged
sql/sql_db.cc:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_list.h:
Auto merged
sql/sql_load.cc:
Auto merged
sql/sql_rename.cc:
Auto merged
sql/sql_repl.h:
Auto merged
sql/sql_update.cc:
Auto merged
sql/table.cc:
Auto merged
sql/table.h:
Auto merged
sql/uniques.cc:
Auto merged
support-files/mysql.spec.sh:
Auto merged
vio/viosocket.c:
Auto merged
BitKeeper/deleted/.del-ctype-latin1_de.c~c5d8f9208bceb98e:
merge
BitKeeper/deleted/.del-mini_client.cc~8677895ec8169183:
merge
acinclude.m4:
Merge with 4.0 (openssl patch)
client/mysqlbinlog.cc:
Merge with 4.0 in which we had added code from 4.1
(We are basicly only using the 4.1 code here)
configure.in:
Keep 4.1 file
heap/hp_open.c:
merge with 4.0
include/my_base.h:
merge with 4.0
include/my_global.h:
merge with 4.0
include/mysqld_error.h:
merge with 4.0
innobase/ha/ha0ha.c:
merge with 4.0
(Code should be same but we use indentaion from 4.0)
innobase/log/log0recv.c:
merge with 4.0
libmysql/libmysql.c:
Remove with 4.0 code that was ported from 4.1
libmysqld/lib_sql.cc:
merge with 4.0
myisam/mi_open.c:
Remove 4.0 specific code
myisam/myisamchk.c:
merge with 4.0
myisammrg/myrg_rkey.c:
merge with 4.0
mysql-test/r/alter_table.result:
May need to be fixed after merge
mysql-test/r/create.result:
May need to be fixed after merge
mysql-test/r/distinct.result:
May need to be fixed after merge
mysql-test/r/drop.result:
May need to be fixed after merge
mysql-test/r/fulltext.result:
May need to be fixed after merge
mysql-test/r/func_set.result:
May need to be fixed after merge
mysql-test/r/func_str.result:
May need to be fixed after merge
mysql-test/r/func_test.result:
May need to be fixed after merge
mysql-test/r/grant.result:
May need to be fixed after merge
mysql-test/r/group_by.result:
May need to be fixed after merge
mysql-test/r/handler.result:
May need to be fixed after merge
mysql-test/r/heap.result:
May need to be fixed after merge
mysql-test/r/innodb.result:
May need to be fixed after merge
mysql-test/r/insert.result:
May need to be fixed after merge
mysql-test/r/insert_select.result:
May need to be fixed after merge
mysql-test/r/key_diff.result:
May need to be fixed after merge
mysql-test/r/merge.result:
May need to be fixed after merge
mysql-test/r/myisam.result:
May need to be fixed after merge
mysql-test/r/order_by.result:
May need to be fixed after merge
mysql-test/r/query_cache.result:
May need to be fixed after merge
mysql-test/r/rpl_flush_log_loop.result:
May need to be fixed after merge
mysql-test/r/rpl_loaddata.result:
May need to be fixed after merge
mysql-test/r/rpl_log.result:
May need to be fixed after merge
mysql-test/r/rpl_log_pos.result:
May need to be fixed after merge
mysql-test/r/rpl_rotate_logs.result:
May need to be fixed after merge
mysql-test/r/select.result:
May need to be fixed after merge
mysql-test/r/union.result:
May need to be fixed after merge
mysql-test/r/user_var.result:
May need to be fixed after merge
mysql-test/t/alter_table.test:
merge with 4.0
mysql-test/t/create.test:
merge with 4.0
mysql-test/t/distinct.test:
merge with 4.0
mysql-test/t/drop.test:
merge with 4.0
mysql-test/t/flush.test:
merge with 4.0
mysql-test/t/fulltext.test:
merge with 4.0
mysql-test/t/func_set.test:
merge with 4.0
mysql-test/t/func_str.test:
merge with 4.0
mysql-test/t/func_test.test:
merge with 4.0
mysql-test/t/grant.test:
merge with 4.0
mysql-test/t/grant_cache.test:
merge with 4.0
mysql-test/t/innodb.test:
Add back EXPLAIN and SHOW KEYS statements, but make them independent of number of rows returned by InnoDB
mysql-test/t/insert.test:
merge with 4.0
mysql-test/t/insert_select.test:
merge with 4.0
mysql-test/t/merge.test:
merge with 4.0
mysql-test/t/query_cache.test:
merge with 4.0
mysql-test/t/rpl_flush_log_loop.test:
merge with 4.0
mysql-test/t/rpl_loaddata.test:
merge with 4.0
mysql-test/t/rpl_rotate_logs.test:
merge with 4.0
mysql-test/t/select.test:
merge with 4.0
mysql-test/t/symlink.test:
merge with 4.0
mysql-test/t/union.test:
merge with 4.0
mysys/charset.c:
merge with 4.0
scripts/mysql_fix_privilege_tables.sh:
merge with 4.0 (Add quoting for some variables)
sql/field.h:
merge with 4.0
sql/ha_innodb.cc:
merge with 4.0
sql/item_cmpfunc.cc:
merge with 4.0
sql/item_cmpfunc.h:
merge with 4.0
sql/item_func.h:
merge with 4.0
sql/item_strfunc.cc:
merge with 4.0
Fixed null handling with ELT()
sql/item_timefunc.h:
merge with 4.0
sql/lex.h:
merge with 4.0
sql/log.cc:
merge with 4.0
sql/log_event.cc:
Merge with 4.0
Cleanups:
- Indentation
- #endif comments
- Replace strmov() with *pos++= for two byte strings
- Moved variable declarations to start of functions
- Merged identical code (LOAD_EVENT)
- Added casts when subtracting pointers
Did a full diff between this and 4.0 to ensure that the file is correct after merge.
sql/log_event.h:
merge with 4.0
sql/mysql_priv.h:
merge with 4.0
sql/mysqld.cc:
merge with 4.0
sql/repl_failsafe.cc:
merge with 4.0
sql/set_var.cc:
merge with 4.0
sql/set_var.h:
merge with 4.0
sql/share/czech/errmsg.txt:
merge with 4.0
sql/share/danish/errmsg.txt:
merge with 4.0
sql/share/dutch/errmsg.txt:
merge with 4.0
sql/share/english/errmsg.txt:
merge with 4.0
sql/share/estonian/errmsg.txt:
merge with 4.0
sql/share/french/errmsg.txt:
merge with 4.0
sql/share/german/errmsg.txt:
merge with 4.0
sql/share/greek/errmsg.txt:
merge with 4.0
sql/share/hungarian/errmsg.txt:
merge with 4.0
sql/share/italian/errmsg.txt:
merge with 4.0
sql/share/japanese/errmsg.txt:
merge with 4.0
sql/share/korean/errmsg.txt:
merge with 4.0
sql/share/norwegian-ny/errmsg.txt:
merge with 4.0
sql/share/norwegian/errmsg.txt:
merge with 4.0
sql/share/polish/errmsg.txt:
merge with 4.0
sql/share/portuguese/errmsg.txt:
merge with 4.0
sql/share/romanian/errmsg.txt:
merge with 4.0
sql/share/russian/errmsg.txt:
merge with 4.0
sql/share/slovak/errmsg.txt:
merge with 4.0
sql/share/spanish/errmsg.txt:
merge with 4.0
sql/share/swedish/errmsg.txt:
merge with 4.0
sql/share/ukrainian/errmsg.txt:
merge with 4.0
sql/slave.cc:
Merge + some indentation fixes
sql/slave.h:
merge with 4.0
sql/sql_acl.cc:
merge with 4.0
Some end space removal to make it easier to do future merges
sql/sql_acl.h:
merge with 4.0
sql/sql_cache.cc:
merge with 4.0
sql/sql_class.h:
merge with 4.0
sql/sql_handler.cc:
merge with 4.0
sql/sql_lex.cc:
merge with 4.0
sql/sql_lex.h:
merge with 4.0
sql/sql_parse.cc:
merge with 4.0
sql/sql_repl.cc:
merge with 4.0
sql/sql_select.cc:
merge with 4.0
sql/sql_table.cc:
merge with 4.0
sql/sql_union.cc:
Merge with 4.0
Note that I couldn't find out how to merge OPTION_FOUND_ROWS handling so this has to be fixed later
sql/sql_yacc.yy:
merge with 4.0
Removed end space to make merge easier
vio/Makefile.am:
merge with 4.0
Diffstat (limited to 'sql')
74 files changed, 2530 insertions, 1145 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 5781b6181d2..69b9c58dd6d 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -123,8 +123,8 @@ sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS) $(CXXCOMPILE) $(LM_CFLAGS) -c $< lex_hash.h: lex.h gen_lex_hash.cc sql_yacc.h - $(MAKE) gen_lex_hash - ./gen_lex_hash > $@ + $(MAKE) gen_lex_hash$(EXEEXT) + ./gen_lex_hash$(EXEEXT) > $@ # Hack to ensure that lex_hash.h is built early sql_lex.o: lex_hash.h diff --git a/sql/field.h b/sql/field.h index 79c45a99a43..794b4a89542 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1066,6 +1066,7 @@ public: uint offset,pack_flag; create_field() :after(0) {} create_field(Field *field, Field *orig_field); + void create_length_to_internal_length(void); }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 44e30afb880..6510a03f5a6 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL 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 diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 1cf123dbec6..d870d0debfd 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -42,7 +42,9 @@ InnoDB */ pthread_mutex_t innobase_mutex; /* Store MySQL definition of 'byte': in Linux it is char while InnoDB -uses unsigned char */ +uses unsigned char; the header univ.i which we include next defines +'byte' as a macro which expands to 'unsigned char' */ + typedef byte mysql_byte; #define INSIDE_HA_INNOBASE_CC @@ -130,11 +132,44 @@ static void innobase_print_error(const char* db_errpfx, char* buffer); /* General functions */ /********************************************************************** +Save some CPU by testing the value of srv_thread_concurrency in inline +functions. */ +inline +void +innodb_srv_conc_enter_innodb( +/*=========================*/ + trx_t* trx) /* in: transaction handle */ +{ + if (srv_thread_concurrency >= 500) { + + return; + } + + srv_conc_enter_innodb(trx); +} + +/********************************************************************** +Save some CPU by testing the value of srv_thread_concurrency in inline +functions. */ +inline +void +innodb_srv_conc_exit_innodb( +/*========================*/ + trx_t* trx) /* in: transaction handle */ +{ + if (srv_thread_concurrency >= 500) { + + return; + } + + srv_conc_exit_innodb(trx); +} + +/********************************************************************** Releases possible search latch and InnoDB thread FIFO ticket. These should be released at each SQL statement end, and also when mysqld passes the control to the client. It does no harm to release these also in the middle of an SQL statement. */ -static inline void innobase_release_stat_resources( @@ -183,7 +218,9 @@ innobase_active_small(void) } /************************************************************************ -Converts an InnoDB error code to a MySQL error code. */ +Converts an InnoDB error code to a MySQL error code and also tells to MySQL +about a possible transaction rollback inside InnoDB caused by a lock wait +timeout or a deadlock. */ static int convert_error_code_to_mysql( @@ -206,10 +243,10 @@ convert_error_code_to_mysql( } else if (error == (int) DB_ERROR) { - return(HA_ERR_NO_ACTIVE_RECORD); + return(-1); /* unspecified error */ } else if (error == (int) DB_DEADLOCK) { - /* Since we roll back the whole transaction, we must + /* Since we rolled back the whole transaction, we must tell it also to MySQL so that MySQL knows to empty the cached binlog for this transaction */ @@ -221,11 +258,10 @@ convert_error_code_to_mysql( } else if (error == (int) DB_LOCK_WAIT_TIMEOUT) { - /* Since we roll back the whole transaction, we must + /* Since we rolled back the whole transaction, we must tell it also to MySQL so that MySQL knows to empty the cached binlog for this transaction */ - if (thd) { ha_rollback(thd); } @@ -271,6 +307,9 @@ convert_error_code_to_mysql( } else if (error == (int) DB_CORRUPTION) { return(HA_ERR_CRASHED); + } else if (error == (int) DB_NO_SAVEPOINT) { + + return(HA_ERR_NO_SAVEPOINT); } else { return(-1); // Unknown error } @@ -624,6 +663,8 @@ ha_innobase::init_table_handle_for_HANDLER(void) prebuilt = (row_prebuilt_t*)innobase_prebuilt; + innobase_release_stat_resources(prebuilt->trx); + /* If the transaction is not started yet, start it */ trx_start_if_not_started_noninline(prebuilt->trx); @@ -891,7 +932,7 @@ innobase_flush_logs(void) DBUG_ENTER("innobase_flush_logs"); - log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); + log_buffer_flush_to_disk(); DBUG_RETURN(result); } @@ -960,17 +1001,28 @@ innobase_commit( DBUG_ENTER("innobase_commit"); DBUG_PRINT("trans", ("ending transaction")); - /* The flag thd->transaction.all.innodb_active_trans is set to 1 - in ::external_lock and ::start_stmt, and it is only set to 0 in - a commit or a rollback. If it is 0 we know there cannot be resources - to be freed and we can return immediately. */ + trx = check_trx_exists(thd); - if (thd->transaction.all.innodb_active_trans == 0) { + /* Release a possible FIFO ticket and search latch. Since we will + reserve the kernel mutex, we have to release the search system latch + first to obey the latching order. */ - DBUG_RETURN(0); - } + innobase_release_stat_resources(trx); - trx = check_trx_exists(thd); + /* The flag thd->transaction.all.innodb_active_trans is set to 1 in + ::external_lock, ::start_stmt, and innobase_savepoint, and it is only + set to 0 in a commit or a rollback. If it is 0 we know there cannot be + resources to be freed and we could return immediately. For the time + being we play safe and do the cleanup though there should be nothing + to clean up. */ + + if (thd->transaction.all.innodb_active_trans == 0 + && trx->conc_state != TRX_NOT_STARTED) { + + fprintf(stderr, +"InnoDB: Error: thd->transaction.all.innodb_active_trans == 0\n" +"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n"); + } if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { @@ -983,9 +1035,7 @@ innobase_commit( /* If we had reserved the auto-inc lock for some table in this SQL statement we release it now */ - srv_conc_enter_innodb(trx); row_unlock_table_autoinc_for_mysql(trx); - srv_conc_exit_innodb(trx); } /* Store the current undo_no of the transaction so that we know where to roll back if we have to roll back the next @@ -994,9 +1044,6 @@ innobase_commit( trx_mark_sql_stat_end(trx); } - /* Release a possible FIFO ticket and search latch */ - innobase_release_stat_resources(trx); - /* Tell the InnoDB server that there might be work for utility threads: */ @@ -1069,7 +1116,7 @@ innobase_commit_complete( } /********************************************************************* -Rolls back a transaction or the latest SQL statement in an InnoDB database. */ +Rolls back a transaction or the latest SQL statement. */ int innobase_rollback( @@ -1089,54 +1136,138 @@ innobase_rollback( trx = check_trx_exists(thd); + /* Release a possible FIFO ticket and search latch. Since we will + reserve the kernel mutex, we have to release the search system latch + first to obey the latching order. */ + + innobase_release_stat_resources(trx); + if (trx->auto_inc_lock) { - - /* If we had reserved the auto-inc lock for - some table in this SQL statement, we release it now */ - - srv_conc_enter_innodb(trx); + /* If we had reserved the auto-inc lock for some table (if + we come here to roll back the latest SQL statement) we + release it now before a possibly lengthy rollback */ + row_unlock_table_autoinc_for_mysql(trx); - srv_conc_exit_innodb(trx); } - srv_conc_enter_innodb(trx); + if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle + || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { - if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) { error = trx_rollback_for_mysql(trx); - thd->transaction.all.innodb_active_trans=0; + thd->transaction.all.innodb_active_trans = 0; } else { error = trx_rollback_last_sql_stat_for_mysql(trx); } - srv_conc_exit_innodb(trx); + DBUG_RETURN(convert_error_code_to_mysql(error, NULL)); +} + +/********************************************************************* +Rolls back a transaction to a savepoint. */ + +int +innobase_rollback_to_savepoint( +/*===========================*/ + /* out: 0 if success, HA_ERR_NO_SAVEPOINT if + no savepoint with the given name */ + THD* thd, /* in: handle to the MySQL thread of the user + whose transaction should be rolled back */ + char* savepoint_name, /* in: savepoint name */ + my_off_t* binlog_cache_pos)/* out: position which corresponds to the + savepoint in the binlog cache of this + transaction, not defined if error */ +{ + ib_longlong mysql_binlog_cache_pos; + int error = 0; + trx_t* trx; + + DBUG_ENTER("innobase_rollback_to_savepoint"); + + trx = check_trx_exists(thd); + + /* Release a possible FIFO ticket and search latch. Since we will + reserve the kernel mutex, we have to release the search system latch + first to obey the latching order. */ - /* Release a possible FIFO ticket and search latch */ innobase_release_stat_resources(trx); + error = trx_rollback_to_savepoint_for_mysql(trx, savepoint_name, + &mysql_binlog_cache_pos); + + *binlog_cache_pos = (my_off_t)mysql_binlog_cache_pos; + DBUG_RETURN(convert_error_code_to_mysql(error, NULL)); } /********************************************************************* -Frees a possible InnoDB trx object associated with the current -THD. */ +Sets a transaction savepoint. */ + +int +innobase_savepoint( +/*===============*/ + /* out: always 0, that is, always succeeds */ + THD* thd, /* in: handle to the MySQL thread */ + char* savepoint_name, /* in: savepoint name */ + my_off_t binlog_cache_pos)/* in: offset up to which the current + transaction has cached log entries to its + binlog cache, not defined if no transaction + active, or we are in the autocommit state, or + binlogging is not switched on */ +{ + int error = 0; + trx_t* trx; + + DBUG_ENTER("innobase_savepoint"); + + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { + /* In the autocommit state there is no sense to set a + savepoint: we return immediate success */ + DBUG_RETURN(0); + } + + trx = check_trx_exists(thd); + + /* Release a possible FIFO ticket and search latch. Since we will + reserve the kernel mutex, we have to release the search system latch + first to obey the latching order. */ + + innobase_release_stat_resources(trx); + + /* Setting a savepoint starts a transaction inside InnoDB since + it allocates resources for it (memory to store the savepoint name, + for example) */ + + thd->transaction.all.innodb_active_trans = 1; + + error = trx_savepoint_for_mysql(trx, savepoint_name, + (ib_longlong)binlog_cache_pos); + + DBUG_RETURN(convert_error_code_to_mysql(error, NULL)); +} + +/********************************************************************* +Frees a possible InnoDB 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 */ + /* 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.all.innobase_tid) { + trx_t* trx; - trx_rollback_for_mysql((trx_t*) - (thd->transaction.all.innobase_tid)); - trx_free_for_mysql((trx_t*) - (thd->transaction.all.innobase_tid)); - thd->transaction.all.innobase_tid = NULL; - } + trx = (trx_t*)thd->transaction.all.innobase_tid; - return(0); + if (NULL != trx) { + innobase_rollback(thd, (void*)trx); + + trx_free_for_mysql(trx); + + thd->transaction.all.innobase_tid = NULL; + } + + return(0); } /********************************************************************** @@ -1239,7 +1370,6 @@ ha_innobase::open( { dict_table_t* ib_table; int error = 0; - uint buff_len; char norm_name[1000]; DBUG_ENTER("ha_innobase::open"); @@ -1264,11 +1394,11 @@ ha_innobase::open( 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 + upd_and_key_val_buff_len = table->reclength + table->max_key_length + MAX_REF_PARTS * 3; if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME), - &upd_buff, buff_len, - &key_val_buff, buff_len, + &upd_buff, upd_and_key_val_buff_len, + &key_val_buff, upd_and_key_val_buff_len, NullS)) { free_share(share); DBUG_RETURN(1); @@ -1519,8 +1649,12 @@ innobase_mysql_cmp( case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: - // BAR TODO: Discuss with heikki.tuuri@innodb.com - // so that he sends CHARSET_INFO for the field to this function. + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_BLOB: + case FIELD_TYPE_LONG_BLOB: + // BAR TODO: Discuss with heikki.tuuri@innodb.com + // so that he sends CHARSET_INFO for the field to this function. ret = my_strnncoll(default_charset_info, a, a_length, b, b_length); @@ -1548,7 +1682,7 @@ get_innobase_type_from_mysql_type( /* out: DATA_BINARY, DATA_VARCHAR, ... */ Field* field) /* in: MySQL field */ { - /* The following asserts check that MySQL type code fits in + /* The following asserts check that the MySQL type code fits in 8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to the type */ @@ -1559,6 +1693,8 @@ get_innobase_type_from_mysql_type( DBUG_ASSERT((ulint)FIELD_TYPE_DECIMAL < 256); switch (field->type()) { + /* NOTE that we only allow string types in DATA_MYSQL + and DATA_VARMYSQL */ case FIELD_TYPE_VAR_STRING: if (field->binary()) { return(DATA_BINARY); @@ -1612,8 +1748,7 @@ get_innobase_type_from_mysql_type( } /*********************************************************************** -Stores a key value for a row to a buffer. This must currently only be used -to store a row reference to the 'ref' buffer of this table handle! */ +Stores a key value for a row to a buffer. */ uint ha_innobase::store_key_val_for_row( @@ -1621,41 +1756,108 @@ 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); currently this MUST be the 'ref' - buffer! */ + format) */ + uint buff_len,/* in: buffer length */ const mysql_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; + enum_field_types mysql_type; + Field* field; + ulint blob_len; + byte* blob_data; + ibool is_null; DBUG_ENTER("store_key_val_for_row"); + /* The format for storing a key field in MySQL is the following: + + 1. If the column can be NULL, then in the first byte we put 1 if the + field value is NULL, 0 otherwise. + + 2. If the column is of a BLOB type (it must be a column prefix field + in this case), then we put the length of the data in the field to the + next 2 bytes, in the little-endian format. If the field is SQL NULL, + then these 2 bytes are set to 0. Note that the length of data in the + field is <= column prefix length. + + 3. In a column prefix field, prefix_len next bytes are reserved for + data. In a normal field the max field length next bytes are reserved + for data. For a VARCHAR(n) the max field length is n. If the stored + value is the SQL NULL then these data bytes are set to 0. */ + + /* We have to zero-fill the buffer so that MySQL is able to use a + simple memcmp to compare two key values to determine if they are + equal. MySQL does this to compare contents of two 'ref' values. */ + + bzero(buff, buff_len); + for (; key_part != end; key_part++) { + is_null = FALSE; 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++ = 1; - continue; - } - - *buff++ = 0; + *buff = 1; + is_null = TRUE; + } else { + *buff = 0; + } + buff++; } - memcpy(buff, record + key_part->offset, key_part->length); - buff += key_part->length; + field = key_part->field; + mysql_type = field->type(); + + if (mysql_type == FIELD_TYPE_TINY_BLOB + || mysql_type == FIELD_TYPE_MEDIUM_BLOB + || mysql_type == FIELD_TYPE_BLOB + || mysql_type == FIELD_TYPE_LONG_BLOB) { + + ut_a(key_part->key_part_flag & HA_PART_KEY); + + if (is_null) { + buff += key_part->length + 2; + + continue; + } + + blob_data = row_mysql_read_blob_ref(&blob_len, + (byte*) (record + + (ulint)get_field_offset(table, field)), + (ulint) field->pack_length()); + + ut_a(get_field_offset(table, field) + == key_part->offset); + if (blob_len > key_part->length) { + blob_len = key_part->length; + } + + /* MySQL reserves 2 bytes for the length and the + storage of the number is little-endian */ + + ut_a(blob_len < 256); + *((byte*)buff) = (byte)blob_len; + buff += 2; + + memcpy(buff, blob_data, blob_len); + + buff += key_part->length; + } else { + if (is_null) { + buff += key_part->length; + + continue; + } + memcpy(buff, record + key_part->offset, + key_part->length); + buff += key_part->length; + } } - /* - We have to zero-fill the 'ref' buffer so that MySQL is able to - use a simple memcmp to compare two key values to determine if they - are equal - */ - bzero(buff, (ref_length- (uint) (buff - buff_start))); + ut_a(buff <= buff_start + buff_len); DBUG_RETURN((uint)(buff - buff_start)); } @@ -1693,7 +1895,11 @@ build_template( if (prebuilt->read_just_key) { /* MySQL has instructed us that it is enough to - fetch the columns in the key */ + fetch the columns in the key; looks like MySQL + can set this flag also when there is only a + prefix of the column in the key: in that case we + retrieve the whole column from the clustered + index */ fetch_all_in_key = TRUE; } else { @@ -1709,12 +1915,6 @@ build_template( } if (prebuilt->select_lock_type == LOCK_X) { - /* In versions < 3.23.50 we always retrieved the clustered - index record if prebuilt->select_lock_type == LOCK_S, - but there is really not need for that, and in some cases - performance could be seriously degraded because the MySQL - optimizer did not know about our convention! */ - /* We always retrieve the whole clustered index record if we use exclusive row level locks, for example, if the read is done in an UPDATE statement. */ @@ -1723,6 +1923,11 @@ build_template( } if (templ_type == ROW_MYSQL_REC_FIELDS) { + /* In versions < 3.23.50 we always retrieved the clustered + index record if prebuilt->select_lock_type == LOCK_S, + but there is really not need for that, and in some cases + performance could be seriously degraded because the MySQL + optimizer did not know about our convention! */ index = prebuilt->index; } else { index = clust_index; @@ -1768,9 +1973,8 @@ build_template( is fixed!!!!!!!!!!!!!!!!! if (templ_type == ROW_MYSQL_REC_FIELDS - && !(fetch_all_in_key && - ULINT_UNDEFINED != dict_index_get_nth_col_pos( - index, i)) + && !(fetch_all_in_key + && dict_index_contains_col_or_prefix(index, i)) && thd->query_id != field->query_id && thd->query_id != (field->query_id ^ MAX_ULONG_BIT) && thd->query_id != @@ -1942,9 +2146,9 @@ ha_innobase::write_row( The lock is released at each SQL statement's end. */ - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); error = row_lock_table_autoinc_for_mysql(prebuilt); - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); if (error != DB_SUCCESS) { @@ -1955,14 +2159,15 @@ ha_innobase::write_row( dict_table_autoinc_update(prebuilt->table, auto_inc); } else { - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); if (!prebuilt->trx->auto_inc_lock) { error = row_lock_table_autoinc_for_mysql( prebuilt); if (error != DB_SUCCESS) { - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb( + prebuilt->trx); error = convert_error_code_to_mysql( error, user_thd); @@ -1976,7 +2181,7 @@ ha_innobase::write_row( auto_inc = dict_table_autoinc_get(prebuilt->table); incremented_auto_inc_counter = TRUE; - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); /* We can give the new value for MySQL to place in the field */ @@ -1999,11 +2204,11 @@ ha_innobase::write_row( build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW); } - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); error = row_insert_for_mysql((byte*) record, prebuilt); - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); if (error != DB_SUCCESS) { /* If the insert did not succeed we restore the value of @@ -2074,7 +2279,6 @@ innobase_convert_and_store_changed_col( while (len > 0 && data[len - 1] == ' ') { len--; } - } else if (col_type == DATA_INT) { /* Store integer data in InnoDB in a big-endian format, sign bit negated, if signed */ @@ -2112,9 +2316,11 @@ calc_row_difference( struct st_table* table, /* in: table in MySQL data dictionary */ mysql_byte* upd_buff, /* in: buffer to use */ + ulint buff_len, /* in: buffer length */ row_prebuilt_t* prebuilt, /* in: InnoDB prebuilt struct */ THD* thd) /* in: user thread */ { + mysql_byte* original_upd_buff = upd_buff; Field* field; uint n_fields; ulint o_len; @@ -2196,12 +2402,13 @@ calc_row_difference( (prebuilt->table->cols + i)->clust_pos; n_changed++; } - ; } uvect->n_fields = n_changed; uvect->info_bits = 0; + ut_a(buf <= (byte*)original_upd_buff + buff_len); + return(0); } @@ -2250,17 +2457,19 @@ ha_innobase::update_row( (uses upd_buff of the handle) */ calc_row_difference(uvect, (mysql_byte*) old_row, new_row, table, - upd_buff, prebuilt, user_thd); + upd_buff, (ulint)upd_and_key_val_buff_len, + prebuilt, user_thd); + /* This is not a delete */ prebuilt->upd_node->is_delete = FALSE; assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); error = row_update_for_mysql((byte*) old_row, prebuilt); - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); error = convert_error_code_to_mysql(error, user_thd); @@ -2304,11 +2513,11 @@ ha_innobase::delete_row( prebuilt->upd_node->is_delete = TRUE; - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); error = row_update_for_mysql((byte*) record, prebuilt); - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); error = convert_error_code_to_mysql(error, user_thd); @@ -2491,10 +2700,11 @@ ha_innobase::index_read( prebuilt->search_tuple */ row_sel_convert_mysql_key_to_innobase(prebuilt->search_tuple, - (byte*) key_val_buff, - index, - (byte*) key_ptr, - (ulint) key_len); + (byte*) key_val_buff, + (ulint)upd_and_key_val_buff_len, + index, + (byte*) key_ptr, + (ulint) key_len); } else { /* We position the cursor to the last or the first entry in the index */ @@ -2516,11 +2726,11 @@ ha_innobase::index_read( last_match_mode = match_mode; - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); ret = row_search_for_mysql((byte*) buf, mode, prebuilt, match_mode, 0); - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); if (ret == DB_SUCCESS) { error = 0; @@ -2670,11 +2880,11 @@ ha_innobase::general_fetch( ut_a(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); - srv_conc_enter_innodb(prebuilt->trx); + innodb_srv_conc_enter_innodb(prebuilt->trx); ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode, direction); - srv_conc_exit_innodb(prebuilt->trx); + innodb_srv_conc_exit_innodb(prebuilt->trx); if (ret == DB_SUCCESS) { error = 0; @@ -2963,7 +3173,8 @@ ha_innobase::position( memcpy(ref, prebuilt->row_id, len); } else { - len = store_key_val_for_row(primary_key, (char*) ref, record); + len = store_key_val_for_row(primary_key, (char*)ref, + ref_length, record); } /* Since we do not store len to the buffer 'ref', we must assume @@ -2977,7 +3188,6 @@ ha_innobase::position( } } - /********************************************************************* Creates a table definition to an InnoDB database. */ static @@ -2996,6 +3206,8 @@ create_table_def( ulint col_type; ulint nulls_allowed; ulint unsigned_type; + ulint binary_type; + ulint nonlatin1_type; ulint i; DBUG_ENTER("create_table_def"); @@ -3024,9 +3236,24 @@ create_table_def( unsigned_type = 0; } + if (col_type == DATA_BLOB + && strcmp(default_charset_info->name, "latin1") != 0) { + nonlatin1_type = DATA_NONLATIN1; + } else { + nonlatin1_type = 0; + } + + if (field->flags & BINARY_FLAG) { + binary_type = DATA_BINARY_TYPE; + nonlatin1_type = 0; + } else { + binary_type = 0; + } + dict_mem_table_add_col(table, (char*) field->field_name, col_type, (ulint)field->type() - | nulls_allowed | unsigned_type, + | nulls_allowed | unsigned_type + | nonlatin1_type | binary_type, field->pack_length(), 0); } @@ -3049,6 +3276,7 @@ create_index( const char* table_name, /* in: table name */ uint key_num) /* in: index number */ { + Field* field; dict_index_t* index; int error; ulint n_fields; @@ -3058,6 +3286,7 @@ create_index( ulint col_type; ulint prefix_len; ulint i; + ulint j; DBUG_ENTER("create_index"); @@ -3084,31 +3313,63 @@ create_index( for (i = 0; i < n_fields; i++) { key_part = key->key_part + i; - if (key_part->length != key_part->field->pack_length()) { + /* (The flag HA_PART_KEY denotes in MySQL a column prefix + field in an index: we only store a specified number of first + bytes of the column to the index field.) The flag does not + seem to be properly set by MySQL. Let us fall back on testing + the length of the key part versus the column. */ + + field = NULL; + for (j = 0; j < form->fields; j++) { + + field = form->field[j]; + + if (strlen(field->field_name) + == strlen(key_part->field->field_name) + && 0 == ut_cmp_in_lower_case( + (char*)field->field_name, + (char*)key_part->field->field_name, + strlen(field->field_name))) { + /* Found the corresponding column */ + + break; + } + } + + ut_a(j < form->fields); + + col_type = get_innobase_type_from_mysql_type(key_part->field); + + if (DATA_BLOB == col_type + || key_part->length < field->pack_length()) { + prefix_len = key_part->length; - col_type = get_innobase_type_from_mysql_type( - key_part->field); if (col_type == DATA_INT || col_type == DATA_FLOAT || col_type == DATA_DOUBLE || col_type == DATA_DECIMAL) { fprintf(stderr, "InnoDB: error: MySQL is trying to create a column prefix index field\n" -"InnoDB: on an inappropriate data type %lu. Table name %s, column name %s.\n", - col_type, table_name, - key_part->field->field_name); +"InnoDB: on an inappropriate data type. Table name %s, column name %s.\n", + table_name, key_part->field->field_name); prefix_len = 0; } } else { prefix_len = 0; - } + } + + if (prefix_len >= DICT_MAX_COL_PREFIX_LEN) { + DBUG_RETURN(-1); + } /* 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); + (char*) key_part->field->field_name, + 0, prefix_len); } error = row_create_index_for_mysql(index, trx); @@ -3172,6 +3433,13 @@ ha_innobase::create( DBUG_ASSERT(thd != NULL); + if (form->fields > 1000) { + /* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020, + but we play safe here */ + + return(HA_ERR_TO_BIG_ROW); + } + /* Get the transaction associated with the current thd, or create one if not yet created */ @@ -3314,7 +3582,7 @@ ha_innobase::create( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); + log_buffer_flush_to_disk(); innobase_table = dict_table_get(norm_name, NULL); @@ -3389,7 +3657,7 @@ ha_innobase::delete_table( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); + log_buffer_flush_to_disk(); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -3459,7 +3727,7 @@ innobase_drop_database( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); + log_buffer_flush_to_disk(); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -3531,7 +3799,7 @@ ha_innobase::rename_table( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); + log_buffer_flush_to_disk(); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -3574,6 +3842,8 @@ ha_innobase::records_in_range( table->reclength + table->max_key_length + 100, MYF(MY_WME)); + ulint buff2_len = table->reclength + + table->max_key_length + 100; dtuple_t* range_start; dtuple_t* range_end; ib_longlong n_rows; @@ -3610,12 +3880,15 @@ ha_innobase::records_in_range( dict_index_copy_types(range_end, index, key->key_parts); row_sel_convert_mysql_key_to_innobase( - range_start, (byte*) key_val_buff, index, + range_start, (byte*) key_val_buff, + (ulint)upd_and_key_val_buff_len, + index, (byte*) start_key, (ulint) start_key_len); row_sel_convert_mysql_key_to_innobase( - range_end, (byte*) key_val_buff2, index, + range_end, (byte*) key_val_buff2, + buff2_len, index, (byte*) end_key, (ulint) end_key_len); @@ -3825,8 +4098,32 @@ ha_innobase::info( } for (i = 0; i < table->keys; i++) { + if (index == NULL) { + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: Error: table %s contains less indexes inside InnoDB\n" +"InnoDB: than are defined in the MySQL .frm file. Have you mixed up\n" +"InnoDB: .frm files from different installations? See section\n" +"InnoDB: 15.1 at http://www.innodb.com/ibman.html\n", + ib_table->name); + break; + } + for (j = 0; j < table->key_info[i].key_parts; j++) { + if (j + 1 > index->n_uniq) { + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: Error: index %s of %s has %lu columns unique inside InnoDB\n" +"InnoDB: but MySQL is asking statistics for %lu columns. Have you mixed up\n" +"InnoDB: .frm files from different installations? See section\n" +"InnoDB: 15.1 at http://www.innodb.com/ibman.html\n", + index->name, + ib_table->name, index->n_uniq, + j + 1); + break; + } + if (index->stat_n_diff_key_vals[j + 1] == 0) { rec_per_key = records; @@ -3885,6 +4182,12 @@ ha_innobase::analyze( return(0); } + +int ha_innobase::optimize(THD* thd, HA_CHECK_OPT* check_opt) +{ + return ha_innobase::analyze(thd,check_opt); +} + /*********************************************************************** Tries to check that an InnoDB table is not corrupted. If corruption is noticed, prints to stderr information about it. In case of corruption @@ -4094,10 +4397,11 @@ ha_innobase::reset(void) } /********************************************************************** -MySQL calls this function at the start of each SQL statement. Inside LOCK -TABLES the ::external_lock method does not work to mark SQL statement -borders. Note also a special case: if a temporary table is created inside -LOCK TABLES, MySQL has not called external_lock() at all on that table. */ +MySQL calls this function at the start of each SQL statement inside LOCK +TABLES. Inside LOCK TABLES the ::external_lock method does not work to +mark SQL statement borders. Note also a special case: if a temporary table +is created inside LOCK TABLES, MySQL has not called external_lock() at all +on that table. */ int ha_innobase::start_stmt( @@ -4254,6 +4558,12 @@ ha_innobase::external_lock( trx->mysql_n_tables_locked = 0; prebuilt->used_in_HANDLER = FALSE; + /* Release a possible FIFO ticket and search latch. Since we + may reserve the kernel mutex, we have to release the search + system latch first to obey the latching order. */ + + innobase_release_stat_resources(trx); + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { if (thd->transaction.all.innodb_active_trans != 0) { @@ -4269,11 +4579,6 @@ ha_innobase::external_lock( read_view_close_for_mysql(trx); } } - - /* Here we release the search latch and the InnoDB thread FIFO - ticket if they were reserved. */ - - innobase_release_stat_resources(trx); } DBUG_RETURN(error); @@ -4289,7 +4594,9 @@ innodb_show_status( THD* thd) /* in: the MySQL query thread of the caller */ { char* buf; + trx_t* trx; Protocol *protocol= thd->protocol; + DBUG_ENTER("innodb_show_status"); if (innodb_skip) { @@ -4299,12 +4606,16 @@ innodb_show_status( DBUG_RETURN(-1); } - /* We let the InnoDB Monitor to output at most 200 kB of text, add - a safety margin of 10 kB for buffer overruns */ + trx = check_trx_exists(thd); + + innobase_release_stat_resources(trx); + + /* We let the InnoDB Monitor to output at most 60 kB of text, add + a safety margin of 100 kB for buffer overruns */ - buf = (char*)ut_malloc(210 * 1024); + buf = (char*)ut_malloc(160 * 1024); - srv_sprintf_innodb_monitor(buf, 200 * 1024); + srv_sprintf_innodb_monitor(buf, 60 * 1024); List<Item> field_list; @@ -4479,6 +4790,11 @@ ha_innobase::innobase_read_and_init_auto_inc( (trx_t*) current_thd->transaction.all.innobase_tid); ut_a(prebuilt->table); + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); + auto_inc = dict_table_autoinc_read(prebuilt->table); if (auto_inc != 0) { @@ -4488,9 +4804,7 @@ ha_innobase::innobase_read_and_init_auto_inc( return(0); } - srv_conc_enter_innodb(prebuilt->trx); error = row_lock_table_autoinc_for_mysql(prebuilt); - srv_conc_exit_innodb(prebuilt->trx); if (error != DB_SUCCESS) { error = convert_error_code_to_mysql(error, user_thd); diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 9ca8475e75e..13337f466bf 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -32,7 +32,6 @@ typedef struct st_innobase_share { uint table_name_length,use_count; } INNOBASE_SHARE; - /* The class defining a handle to an Innodb table */ class ha_innobase: public handler { @@ -52,6 +51,9 @@ class ha_innobase: public handler byte* key_val_buff; /* buffer used in converting search key values from MySQL format to Innodb format */ + ulong upd_and_key_val_buff_len; + /* the length of each of the previous + two buffers */ ulong int_table_flags; uint primary_key; uint last_dup_key; @@ -73,7 +75,8 @@ class ha_innobase: public handler longlong auto_inc_counter_for_this_stat; ulong max_row_length(const byte *buf); - uint store_key_val_for_row(uint keynr, char* buff, const byte* record); + uint store_key_val_for_row(uint keynr, char* buff, uint buff_len, + const byte* record); int update_thd(THD* thd); int change_active_index(uint keynr); int general_fetch(byte* buf, uint direction, uint match_mode); @@ -83,13 +86,15 @@ class ha_innobase: public handler public: ha_innobase(TABLE *table): handler(table), int_table_flags(HA_REC_NOT_IN_SEQ | - HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | - HA_NULL_KEY | HA_CAN_SQL_HANDLER | + HA_KEYPOS_TO_RNDPOS | + HA_LASTKEY_ORDER | + HA_NULL_KEY | + HA_BLOB_KEY | + HA_CAN_SQL_HANDLER | HA_NOT_EXACT_COUNT | HA_NO_WRITE_DELAYED | HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | - HA_NO_PREFIX_CHAR_KEYS | HA_TABLE_SCAN_ON_INDEX), last_dup_key((uint) -1), start_of_scan(0) @@ -153,6 +158,7 @@ class ha_innobase: public handler void position(const byte *record); void info(uint); int analyze(THD* thd,HA_CHECK_OPT* check_opt); + int optimize(THD* thd,HA_CHECK_OPT* check_opt); int extra(enum ha_extra_function operation); int reset(void); int external_lock(THD *thd, int lock_type); @@ -219,6 +225,14 @@ int innobase_report_binlog_offset_and_commit( int innobase_commit_complete( void* trx_handle); int innobase_rollback(THD *thd, void* trx_handle); +int innobase_rollback_to_savepoint( + THD* thd, + char* savepoint_name, + my_off_t* binlog_cache_pos); +int innobase_savepoint( + THD* thd, + char* savepoint_name, + my_off_t binlog_cache_pos); int innobase_close_connection(THD *thd); int innobase_drop_database(char *path); int innodb_show_status(THD* thd); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 70322cef6fd..e8e4798c2b2 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -572,7 +572,8 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) strmov(fixed_name,file->filename); // Don't lock tables if we have used LOCK TABLE - if (!thd->locked_tables && mi_lock_database(file,F_WRLCK)) + if (!thd->locked_tables && + mi_lock_database(file, table->tmp_table ? F_EXTRA_LCK : F_WRLCK)) { mi_check_print_error(¶m,ER(ER_CANT_LOCK),my_errno); DBUG_RETURN(HA_ADMIN_FAILED); @@ -802,6 +803,7 @@ void ha_myisam::deactivate_non_unique_index(ha_rows rows) } } enable_activate_all_index=1; + info(HA_STATUS_CONST); // Read new key info } else enable_activate_all_index=0; @@ -834,6 +836,7 @@ bool ha_myisam::activate_all_index(THD *thd) param.sort_buffer_length= thd->variables.myisam_sort_buff_size; param.tmpdir=&mysql_tmpdir_list; error=repair(thd,param,0) != HA_ADMIN_OK; + info(HA_STATUS_CONST); thd->proc_info=save_proc_info; } else @@ -1017,8 +1020,9 @@ void ha_myisam::info(uint flag) ref_length=info.reflength; table->db_options_in_use = info.options; block_size=myisam_block_size; - table->keys_in_use&= info.key_map; - table->keys_for_keyread&= info.key_map; + table->keys_in_use= (set_bits(key_map, table->keys) & + (key_map) info.key_map); + table->keys_for_keyread= table->keys_in_use & ~table->read_only_keys; table->db_record_offset=info.record_offset; if (table->key_parts) memcpy((char*) table->key_info[0].rec_per_key, @@ -1087,9 +1091,9 @@ int ha_myisam::delete_table(const char *name) int ha_myisam::external_lock(THD *thd, int lock_type) { - if (!table->tmp_table) - return mi_lock_database(file,lock_type); - return 0; + return mi_lock_database(file, !table->tmp_table ? + lock_type : ((lock_type == F_UNLCK) ? + F_UNLCK : F_EXTRA_LCK)); } diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index 008f5339caf..cfe4b47ef10 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -40,7 +40,8 @@ class ha_myisammrg: public handler } ulong index_flags(uint inx) const { - ulong flags=(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER); + ulong flags=(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | + HA_NOT_READ_PREFIX_LAST); // This - last - flag is ONLY for 4.0 !!! return (flags | ((table->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ? 0 : HA_KEY_READ_ONLY)); } diff --git a/sql/handler.cc b/sql/handler.cc index 90fd754a4c5..b4d370491bb 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -389,7 +389,6 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) trans->innodb_active_trans=0; if (trans == &thd->transaction.all) operation_done= transaction_commited= 1; - } #endif #ifdef HAVE_QUERY_CACHE @@ -457,6 +456,70 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) DBUG_RETURN(error); } + +/* +Rolls the current transaction back to a savepoint. +Return value: 0 if success, 1 if there was not a savepoint of the given +name. +*/ + +int ha_rollback_to_savepoint(THD *thd, char *savepoint_name) +{ + my_off_t binlog_cache_pos=0; + bool operation_done=0; + int error=0; + DBUG_ENTER("ha_rollback_to_savepoint"); +#ifdef USING_TRANSACTIONS + if (opt_using_transactions) + { +#ifdef HAVE_INNOBASE_DB + /* + Retrieve the trans_log binlog cache position corresponding to the + savepoint, and if the rollback is successful inside InnoDB reset the write + position in the binlog cache to what it was at the savepoint. + */ + if ((error=innobase_rollback_to_savepoint(thd, savepoint_name, + &binlog_cache_pos))) + { + my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error); + error=1; + } + else + reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, + binlog_cache_pos, 0, 0); + operation_done=1; +#endif + if (operation_done) + statistic_increment(ha_rollback_count,&LOCK_status); + } +#endif /* USING_TRANSACTIONS */ + + DBUG_RETURN(error); +} + + +/* +Sets a transaction savepoint. +Return value: always 0, that is, succeeds always +*/ + +int ha_savepoint(THD *thd, char *savepoint_name) +{ + my_off_t binlog_cache_pos=0; + int error=0; + DBUG_ENTER("ha_savepoint"); +#ifdef USING_TRANSACTIONS + if (opt_using_transactions) + { + binlog_cache_pos=my_b_tell(&thd->transaction.trans_log); +#ifdef HAVE_INNOBASE_DB + innobase_savepoint(thd,savepoint_name, binlog_cache_pos); +#endif + } +#endif /* USING_TRANSACTIONS */ + DBUG_RETURN(error); +} + bool ha_flush_logs() { bool result=0; @@ -759,6 +822,9 @@ void handler::print_error(int error, myf errflag) int textno=ER_GET_ERRNO; switch (error) { + case EACCES: + textno=ER_OPEN_AS_READONLY; + break; case EAGAIN: textno=ER_FILE_USED; break; diff --git a/sql/handler.h b/sql/handler.h index 08a7c83d328..b1b5cfb02b0 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -192,39 +192,41 @@ class handler :public Sql_alloc { protected: struct st_table *table; /* The table definition */ - uint active_index; public: byte *ref; /* Pointer to current row */ byte *dupp_ref; /* Pointer to dupp row */ - uint ref_length; /* Length of ref (1-8) */ - uint block_size; /* index block size */ - ha_rows records; /* Records i datafilen */ - ha_rows deleted; /* Deleted records */ ulonglong data_file_length; /* Length off data file */ ulonglong max_data_file_length; /* Length off data file */ ulonglong index_file_length; ulonglong max_index_file_length; ulonglong delete_length; /* Free bytes */ ulonglong auto_increment_value; - uint raid_type,raid_chunks; + ha_rows records; /* Records in table */ + ha_rows deleted; /* Deleted records */ ulong raid_chunksize; - uint errkey; /* Last dup key */ - uint sortkey, key_used_on_scan; + ulong mean_rec_length; /* physical reclength */ time_t create_time; /* When table was created */ time_t check_time; time_t update_time; - ulong mean_rec_length; /* physical reclength */ + uint errkey; /* Last dup key */ + uint sortkey, key_used_on_scan; + uint active_index; + /* Length of ref (1-8 or the clustered key length) */ + uint ref_length; + uint block_size; /* index block size */ + uint raid_type,raid_chunks; FT_INFO *ft_handler; bool auto_increment_column_changed; - handler(TABLE *table_arg) : table(table_arg),active_index(MAX_REF_PARTS), - ref(0),ref_length(sizeof(my_off_t)), block_size(0),records(0),deleted(0), - data_file_length(0), max_data_file_length(0), index_file_length(0), - delete_length(0), auto_increment_value(0), raid_type(0), - key_used_on_scan(MAX_KEY), - create_time(0), check_time(0), update_time(0), mean_rec_length(0), - ft_handler(0) + handler(TABLE *table_arg) :table(table_arg), + ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0), + delete_length(0), auto_increment_value(0), + records(0), deleted(0), mean_rec_length(0), + create_time(0), check_time(0), update_time(0), + key_used_on_scan(MAX_KEY), active_index(MAX_REF_PARTS), + ref_length(sizeof(my_off_t)), block_size(0), + raid_type(0), ft_handler(0) {} virtual ~handler(void) {} int ha_open(const char *name, int mode, int test_if_locked); @@ -234,7 +236,7 @@ public: uint get_dup_key(int error); void change_table_ptr(TABLE *table_arg) { table=table_arg; } virtual double scan_time() - { return ulonglong2double(data_file_length) / IO_SIZE + 1; } + { return ulonglong2double(data_file_length) / IO_SIZE + 2; } virtual double read_time(uint index, uint ranges, ha_rows rows) { return rows2double(ranges+rows); } virtual bool fast_key_read() { return 0;} @@ -395,6 +397,8 @@ int ha_commit_complete(THD *thd); int ha_release_temporary_latches(THD *thd); int ha_commit_trans(THD *thd, THD_TRANS *trans); int ha_rollback_trans(THD *thd, THD_TRANS *trans); +int ha_rollback_to_savepoint(THD *thd, char *savepoint_name); +int ha_savepoint(THD *thd, char *savepoint_name); int ha_autocommit_or_rollback(THD *thd, int error); void ha_set_spin_retries(uint retries); bool ha_flush_logs(void); diff --git a/sql/item.h b/sql/item.h index 621dc017d25..457978fced6 100644 --- a/sql/item.h +++ b/sql/item.h @@ -144,7 +144,24 @@ public: virtual double val_result() { return val(); } virtual longlong val_int_result() { return val_int(); } virtual String *str_result(String* tmp) { return val_str(tmp); } + /* bit map of tables used by item */ virtual table_map used_tables() const { return (table_map) 0L; } + /* + Return table map of tables that can't be NULL tables (tables that are + used in a context where if they would contain a NULL row generated + by a LEFT or RIGHT join, the item would not be true). + This expression is used on WHERE item to determinate if a LEFT JOIN can be + converted to a normal join. + Generally this function should return used_tables() if the function + would return null if any of the arguments are null + As this is only used in the beginning of optimization, the value don't + have to be updated in update_used_tables() + */ + virtual table_map not_null_tables() const { return used_tables(); } + /* + Returns true if this is a simple constant item like an integer, not + a constant expression + */ virtual bool basic_const_item() const { return 0; } virtual Item *new_item() { return 0; } /* Only for const items */ virtual cond_result eq_cmp_result() const { return COND_OK; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7d8da16338b..b9e7b1d14ad 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -109,7 +109,7 @@ longlong Item_func_not::val_int() static bool convert_constant_item(Field *field, Item **item) { - if ((*item)->const_item() && (*item)->type() != Item::INT_ITEM) + if ((*item)->const_item()) { if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { @@ -535,6 +535,7 @@ void Item_func_interval::fix_length_and_dec() maybe_null= 0; max_length= 2; used_tables_cache|= row->used_tables(); + not_null_tables_cache&= row->not_null_tables(); with_sum_func= with_sum_func || row->with_sum_func; } @@ -548,25 +549,27 @@ void Item_func_interval::fix_length_and_dec() longlong Item_func_interval::val_int() { - double value=row->el(0)->val(); + double value= row->el(0)->val(); + uint i; + if (row->el(0)->null_value) return -1; // -1 if null if (intervals) { // Use binary search to find interval uint start,end; - start=1; end=row->cols()-2; + start= 1; + end= row->cols()-2; while (start != end) { - uint mid=(start+end+1)/2; + uint mid= (start + end + 1) / 2; if (intervals[mid] <= value) - start=mid; + start= mid; else - end=mid-1; + end= mid - 1; } - return (value < intervals[start]) ? 0 : start+1; + return (value < intervals[start]) ? 0 : start + 1; } - uint i; for (i=1 ; i < row->cols() ; i++) { if (row->el(i)->val() > value) @@ -1454,7 +1457,6 @@ void Item_func_in::fix_length_and_dec() } maybe_null= args[0]->maybe_null; max_length= 1; - const_item_cache&=args[0]->const_item(); } @@ -1535,13 +1537,19 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) #ifndef EMBEDDED_LIBRARY char buff[sizeof(char*)]; // Max local vars in function #endif - used_tables_cache=0; - const_item_cache=0; + not_null_tables_cache= used_tables_cache= 0; + const_item_cache= 0; + /* + and_table_cache is the value that Item_cond_or() returns for + not_null_tables() + */ + and_tables_cache= ~(table_map) 0; if (thd && check_stack_overrun(thd,buff)) return 0; // Fatal error flag is set! while ((item=li++)) { + table_map tmp_table_map; while (item->type() == Item::COND_ITEM && ((Item_cond*) item)->functype() == functype()) { // Identical function @@ -1556,9 +1564,12 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) item->top_level_item(); if (item->fix_fields(thd, tables, li.ref()) || item->check_cols(1)) return 1; /* purecov: inspected */ - used_tables_cache|=item->used_tables(); - with_sum_func= with_sum_func || item->with_sum_func; - const_item_cache&=item->const_item(); + used_tables_cache|= item->used_tables(); + tmp_table_map= item->not_null_tables(); + not_null_tables_cache|= tmp_table_map; + and_tables_cache&= tmp_table_map; + const_item_cache&= item->const_item(); + with_sum_func= with_sum_func || item->with_sum_func; if (item->maybe_null) maybe_null=1; } @@ -1608,17 +1619,19 @@ Item_cond::used_tables() const return used_tables_cache; } + void Item_cond::update_used_tables() { - used_tables_cache=0; - const_item_cache=1; List_iterator_fast<Item> li(list); Item *item; + + used_tables_cache=0; + const_item_cache=1; while ((item=li++)) { item->update_used_tables(); - used_tables_cache|=item->used_tables(); - const_item_cache&= item->const_item(); + used_tables_cache|= item->used_tables(); + const_item_cache&= item->const_item(); } } @@ -1722,12 +1735,16 @@ Item *and_expressions(Item *a, Item *b, Item **org_item) { Item_cond *res; if ((res= new Item_cond_and(a, (Item*) b))) + { res->used_tables_cache= a->used_tables() | b->used_tables(); + res->not_null_tables_cache= a->not_null_tables() | b->not_null_tables(); + } return res; } if (((Item_cond_and*) a)->add((Item*) b)) return 0; ((Item_cond_and*) a)->used_tables_cache|= b->used_tables(); + ((Item_cond_and*) a)->not_null_tables_cache|= b->not_null_tables(); return a; } @@ -1898,6 +1915,8 @@ Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return 1; used_tables_cache=args[0]->used_tables() | args[1]->used_tables(); + not_null_tables_cache= (args[0]->not_null_tables() | + args[1]->not_null_tables()); const_item_cache=args[0]->const_item() && args[1]->const_item(); if (!regex_compiled && args[1]->const_item()) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 25cc97d60bf..2570a86ea80 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -291,6 +291,7 @@ public: enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "ifnull"; } + table_map not_null_tables() const { return 0; } }; @@ -312,6 +313,7 @@ public: } void fix_length_and_dec(); const char *func_name() const { return "if"; } + table_map not_null_tables() const { return 0; } }; @@ -328,6 +330,7 @@ public: enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "nullif"; } + table_map not_null_tables() const { return 0; } }; @@ -344,8 +347,10 @@ public: void fix_length_and_dec(); enum Item_result result_type () const { return cached_result_type; } const char *func_name() const { return "coalesce"; } + table_map not_null_tables() const { return 0; } }; + class Item_func_case :public Item_func { int first_expr_num, else_expr_num; @@ -376,6 +381,7 @@ public: longlong val_int(); String *val_str(String *); void fix_length_and_dec(); + table_map not_null_tables() const { return 0; } enum Item_result result_type () const { return cached_result_type; } const char *func_name() const { return "case"; } void print(String *str); @@ -668,6 +674,7 @@ public: } } } + table_map not_null_tables() const { return 0; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } }; @@ -699,8 +706,10 @@ public: } const char *func_name() const { return "isnotnull"; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } + table_map not_null_tables() const { return 0; } }; + class Item_func_like :public Item_bool_func2 { char escape; @@ -773,6 +782,8 @@ class Item_cond :public Item_bool_func protected: List<Item> list; bool abort_on_null; + table_map and_tables_cache; + public: /* Item_cond() is only used to create top level items */ Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; } @@ -812,15 +823,23 @@ public: enum Functype functype() const { return COND_OR_FUNC; } longlong val_int(); const char *func_name() const { return "or"; } + table_map not_null_tables() const { return and_tables_cache; } }; +/* + XOR is Item_cond, not an Item_int_func bevause we could like to + optimize (a XOR b) later on. It's low prio, though +*/ + class Item_cond_xor :public Item_cond { public: Item_cond_xor() :Item_cond() {} Item_cond_xor(Item *i1,Item *i2) :Item_cond(i1,i2) {} enum Functype functype() const { return COND_XOR_FUNC; } + /* TODO: remove the next line when implementing XOR optimization */ + enum Type type() const { return FUNC_ITEM; } longlong val_int(); const char *func_name() const { return "xor"; } }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 815aeba1e23..c960b6ad54e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -175,7 +175,7 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) Item **arg,**arg_end; char buff[STACK_BUFF_ALLOC]; // Max argument in function - used_tables_cache=0; + used_tables_cache= not_null_tables_cache= 0; const_item_cache=1; if (thd && check_stack_overrun(thd,buff)) @@ -194,8 +194,9 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) maybe_null=1; with_sum_func= with_sum_func || item->with_sum_func; - used_tables_cache|=item->used_tables(); - const_item_cache&= item->const_item(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache|= item->not_null_tables(); + const_item_cache&= item->const_item(); } } fix_length_and_dec(); @@ -252,6 +253,13 @@ table_map Item_func::used_tables() const return used_tables_cache; } + +table_map Item_func::not_null_tables() const +{ + return not_null_tables_cache; +} + + void Item_func::print(String *str) { str->append(func_name()); @@ -2118,6 +2126,7 @@ bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables, if (Item_func::fix_fields(thd, tables, ref) || !(entry= get_variable(&thd->user_vars, name, 1))) return 1; + entry->type= cached_result_type; entry->update_query_id=thd->query_id; return 0; } @@ -2676,6 +2685,9 @@ double Item_func_match::val() if (ft_handler == NULL) DBUG_RETURN(-1.0); + if (table->null_row) /* NULL row from an outer join */ + return 0.0; + if (join_key) { if (table->file->ft_handler) diff --git a/sql/item_func.h b/sql/item_func.h index 56ee0379cd5..3d1de6a7a5f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -35,7 +35,7 @@ protected: uint allowed_arg_cols; public: uint arg_count; - table_map used_tables_cache; + table_map used_tables_cache, not_null_tables_cache; bool const_item_cache; enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, GE_FUNC,GT_FUNC,FT_FUNC, @@ -107,6 +107,7 @@ public: ~Item_func() {} /* Nothing to do; Items are freed automaticly */ bool fix_fields(THD *,struct st_table_list *, Item **ref); table_map used_tables() const; + table_map not_null_tables() const; void update_used_tables(); bool eq(const Item *item, bool binary_cmp) const; virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; } @@ -767,6 +768,7 @@ public: return res; } Item_result result_type () const { return udf.result_type(); } + table_map not_null_tables() const { return 0; } }; @@ -1004,6 +1006,7 @@ public: } enum Functype functype() const { return FT_FUNC; } void update_used_tables() {} + table_map not_null_tables() const { return 0; } bool fix_fields(THD *thd, struct st_table_list *tlist, Item **ref); bool eq(const Item *, bool binary_cmp) const; longlong val_int() { return val()!=0.0; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b92839e6c1e..b0b9cc01bf1 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -637,9 +637,10 @@ void Item_func_concat_ws::fix_length_and_dec() max_length=MAX_BLOB_WIDTH; maybe_null=1; } - used_tables_cache|=separator->used_tables(); - const_item_cache&=separator->const_item(); - with_sum_func= with_sum_func || separator->with_sum_func; + used_tables_cache|= separator->used_tables(); + not_null_tables_cache&= separator->not_null_tables(); + const_item_cache&= separator->const_item(); + with_sum_func= with_sum_func || separator->with_sum_func; } void Item_func_concat_ws::update_used_tables() @@ -1627,6 +1628,10 @@ String *Item_func_format::val_str(String *str) return 0; /* purecov: inspected */ dec= decimals ? decimals+1 : 0; str->set(nr,decimals,default_charset()); +#ifdef HAVE_ISNAN + if (isnan(nr)) + return str; +#endif str_length=str->length(); if (nr < 0) str_length--; // Don't count sign @@ -1639,7 +1644,7 @@ String *Item_func_format::val_str(String *str) str= copy_if_not_alloced(&tmp_str,str,length); str->length(length); tmp= (char*) str->ptr()+length - dec-1; - for (pos= (char*) str->ptr()+length ; pos != tmp; pos--) + for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--) pos[0]= pos[-(int) diff]; while (diff) { @@ -1675,40 +1680,40 @@ void Item_func_elt::fix_length_and_dec() double Item_func_elt::val() { uint tmp; + null_value=1; if ((tmp=(uint) args[0]->val_int()) == 0 || tmp >= arg_count) - { - null_value=1; return 0.0; - } - null_value=0; - return args[tmp]->val(); + double result= args[tmp]->val(); + null_value= args[tmp]->null_value; + return result; } + longlong Item_func_elt::val_int() { uint tmp; + null_value=1; if ((tmp=(uint) args[0]->val_int()) == 0 || tmp >= arg_count) - { - null_value=1; return 0; - } - null_value=0; - return args[tmp]->val_int(); + + longlong result= args[tmp]->val_int(); + null_value= args[tmp]->null_value; + return result; } + String *Item_func_elt::val_str(String *str) { uint tmp; - String *res; + null_value=1; if ((tmp=(uint) args[0]->val_int()) == 0 || tmp >= arg_count) - { - null_value=1; return NULL; - } - null_value=0; - res= args[tmp]->val_str(str); - res->set_charset(collation.collation); - return res; + + String *result= args[tmp]->val_str(str); + if (result) + result->set_charset(collation.collation); + null_value= args[tmp]->null_value; + return result; } @@ -1738,8 +1743,9 @@ void Item_func_make_set::fix_length_and_dec() for (uint i=0 ; i < arg_count ; i++) max_length+=args[i]->max_length; - used_tables_cache|=item->used_tables(); - const_item_cache&=item->const_item(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache&= item->not_null_tables(); + const_item_cache&= item->const_item(); with_sum_func= with_sum_func || item->with_sum_func; } @@ -1953,7 +1959,7 @@ String *Item_func_rpad::val_str(String *str) String *res =args[0]->val_str(str); String *rpad = args[2]->val_str(str); - if (!res || args[1]->null_value || !rpad) + if (!res || args[1]->null_value || !rpad || count < 0) goto err; null_value=0; if (count <= (int32) (res_length=res->length())) diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 62d8afd7ec0..c3e441b33a4 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1289,14 +1289,12 @@ String *Item_date_add_interval::val_str(String *str) longlong Item_date_add_interval::val_int() { TIME ltime; + longlong date; if (Item_date_add_interval::get_date(<ime,0)) return (longlong) 0; - return ((longlong) (((ulong) ltime.year)*10000L+ - (((uint) ltime.month)*100+ - (uint) ltime.day))*(longlong) 1000000L+ - (longlong) ((ulong) ((uint) ltime.hour)*10000L+ - (ulong) (((uint)ltime.minute)*100L+ - (uint) ltime.second))); + date = (ltime.year*100L + ltime.month)*100L + ltime.day; + return ltime.time_type == TIMESTAMP_DATE ? date : + ((date*100L + ltime.hour)*100L+ ltime.minute)*100L + ltime.second; } void Item_extract::fix_length_and_dec() @@ -1388,6 +1386,22 @@ longlong Item_extract::val_int() return 0; // Impossible } +bool Item_extract::eq(const Item *item, bool binary_cmp) const +{ + if (this == item) + return 1; + if (item->type() != FUNC_ITEM || + func_name() != ((Item_func*)item)->func_name()) + return 0; + + Item_extract* ie= (Item_extract*)item; + if (ie->int_type != int_type) + return 0; + + if (!args[0]->eq(ie->args[0], binary_cmp)) + return 0; + return 1; +} void Item_typecast::print(String *str) { diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a6fb9b11de4..4b0ef02f20d 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -516,6 +516,7 @@ class Item_extract :public Item_int_func longlong val_int(); const char *func_name() const { return "extract"; } void fix_length_and_dec(); + bool eq(const Item *item, bool binary_cmp) const; }; diff --git a/sql/lex.h b/sql/lex.h index c2860f4551a..15d33e761ef 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -340,6 +340,7 @@ static SYMBOL symbols[] = { { "ROW", SYM(ROW_SYM),0,0}, { "ROWS", SYM(ROWS_SYM),0,0}, { "RTREE", SYM(RTREE_SYM),0,0}, + { "SAVEPOINT", SYM(SAVEPOINT_SYM),0,0}, { "SECOND", SYM(SECOND_SYM),0,0}, { "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM),0,0}, { "SEPARATOR", SYM(SEPARATOR_SYM),0,0}, diff --git a/sql/log.cc b/sql/log.cc index dd544dcac93..b3f7e753010 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL 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 @@ -85,11 +85,13 @@ static int find_uniq_filename(char *name) MYSQL_LOG::MYSQL_LOG() :bytes_written(0), last_time(0), query_start(0), name(0), file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0), - no_rotate(0), need_start_event(1) + need_start_event(1) { /* - We don't want to intialize LOCK_Log here as the thread system may - not have been initailized yet. We do it instead at 'open'. + We don't want to initialize LOCK_Log here as such initialization depends on + safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is + called only in main(). Doing initialization here would make it happen + before main(). */ index_file_name[0] = 0; bzero((char*) &log_file,sizeof(log_file)); @@ -102,12 +104,14 @@ MYSQL_LOG::~MYSQL_LOG() cleanup(); } +/* this is called only once */ + void MYSQL_LOG::cleanup() { if (inited) { - close(1); inited= 0; + close(LOG_CLOSE_INDEX); (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); (void) pthread_cond_destroy(&update_cond); @@ -135,18 +139,26 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) void MYSQL_LOG::init(enum_log_type log_type_arg, enum cache_type io_cache_type_arg, - bool no_auto_events_arg) + bool no_auto_events_arg, + ulong max_size_arg) { + DBUG_ENTER("MYSQL_LOG::init"); log_type = log_type_arg; io_cache_type = io_cache_type_arg; no_auto_events = no_auto_events_arg; - if (!inited) - { - inited= 1; - (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); - (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); - (void) pthread_cond_init(&update_cond, 0); - } + max_size=max_size_arg; + DBUG_PRINT("info",("log_type: %d max_size: %lu", log_type, max_size)); + DBUG_VOID_RETURN; +} + + +void MYSQL_LOG::init_pthread_objects() +{ + DBUG_ASSERT(inited == 0); + inited= 1; + (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); + (void) pthread_cond_init(&update_cond, 0); } @@ -167,7 +179,8 @@ void MYSQL_LOG::init(enum_log_type log_type_arg, bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, const char *new_name, const char *index_file_name_arg, enum cache_type io_cache_type_arg, - bool no_auto_events_arg) + bool no_auto_events_arg, + ulong max_size) { char buff[512]; File file= -1, index_file_nr= -1; @@ -178,9 +191,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, last_time=query_start=0; write_error=0; - if (!inited && log_type_arg == LOG_BIN && *fn_ext(log_name)) - no_rotate = 1; - init(log_type_arg,io_cache_type_arg,no_auto_events_arg); + init(log_type_arg,io_cache_type_arg,no_auto_events_arg,max_size); if (!(name=my_strdup(log_name,MYF(MY_WME)))) goto err; @@ -311,6 +322,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, break; } case LOG_CLOSED: // Impossible + case LOG_TO_BE_OPENED: DBUG_ASSERT(1); break; } @@ -328,7 +340,7 @@ shutdown the MySQL server and restart it.", log_name, errno); end_io_cache(&log_file); end_io_cache(&index_file); safeFree(name); - log_type=LOG_CLOSED; + log_type= LOG_CLOSED; DBUG_RETURN(1); } @@ -561,7 +573,7 @@ bool MYSQL_LOG::reset_logs(THD* thd) save_name=name; name=0; // Protect against free save_log_type=log_type; - close(0); // Don't close the index file + close(LOG_CLOSE_TO_BE_OPENED); /* First delete all old log files */ @@ -579,12 +591,12 @@ bool MYSQL_LOG::reset_logs(THD* thd) } /* Start logging with a new file */ - close(1); // Close index file + close(LOG_CLOSE_INDEX); my_delete(index_file_name, MYF(MY_WME)); // Reset (open will update) if (!thd->slave_thread) need_start_event=1; open(save_name, save_log_type, 0, index_file_name, - io_cache_type, no_auto_events); + io_cache_type, no_auto_events, max_size); my_free((gptr) save_name, MYF(0)); err: @@ -736,7 +748,6 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) RETURN VALUES 0 ok - LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated LOG_INFO_EOF to_log not found */ @@ -835,9 +846,6 @@ int MYSQL_LOG::purge_logs_before_date(time_t purge_time) DBUG_ENTER("purge_logs_before_date"); - if (no_rotate) - DBUG_RETURN(LOG_INFO_PURGE_NO_ROTATE); - pthread_mutex_lock(&LOCK_index); /* @@ -888,14 +896,11 @@ err: void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) { - if (inited) // QQ When is this not true ? - { - uint dir_len = dirname_length(log_file_name); - if (dir_len > FN_REFLEN) - dir_len=FN_REFLEN-1; - strnmov(buf, log_file_name, dir_len); - strmake(buf+dir_len, log_ident, FN_REFLEN - dir_len); - } + uint dir_len = dirname_length(log_file_name); + if (dir_len > FN_REFLEN) + dir_len=FN_REFLEN-1; + strnmov(buf, log_file_name, dir_len); + strmake(buf+dir_len, log_ident, FN_REFLEN - dir_len); } @@ -905,7 +910,7 @@ void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) bool MYSQL_LOG::is_active(const char *log_file_name_arg) { - return inited && !strcmp(log_file_name, log_file_name_arg); + return !strcmp(log_file_name, log_file_name_arg); } @@ -926,8 +931,12 @@ void MYSQL_LOG::new_file(bool need_lock) char new_name[FN_REFLEN], *new_name_ptr, *old_name; enum_log_type save_log_type; + DBUG_ENTER("MYSQL_LOG::new_file"); if (!is_open()) - return; // Should never happen + { + DBUG_PRINT("info",("log is closed")); + DBUG_VOID_RETURN; + } if (need_lock) { @@ -937,52 +946,50 @@ void MYSQL_LOG::new_file(bool need_lock) safe_mutex_assert_owner(&LOCK_log); safe_mutex_assert_owner(&LOCK_index); - // Reuse old name if not binlog and not update log + /* Reuse old name if not binlog and not update log */ new_name_ptr= name; /* - Only rotate open logs that are marked non-rotatable - (binlog with constant name are non-rotatable) + If user hasn't specified an extension, generate a new log name + We have to do this here and not in open as we want to store the + new file name in the current binary log file. */ - if (!no_rotate) + if (generate_new_name(new_name, name)) + goto end; + new_name_ptr=new_name; + + if (log_type == LOG_BIN) { - /* - If user hasn't specified an extension, generate a new log name - We have to do this here and not in open as we want to store the - new file name in the current binary log file. - */ - if (generate_new_name(new_name, name)) - goto end; - new_name_ptr=new_name; - - if (log_type == LOG_BIN) + if (!no_auto_events) { - if (!no_auto_events) - { - /* - We log the whole file name for log file as the user may decide - to change base names at some point. - */ - THD* thd = current_thd; - Rotate_log_event r(thd,new_name+dirname_length(new_name)); - r.set_log_pos(this); - r.write(&log_file); - bytes_written += r.get_event_len(); - } /* - Update needs to be signalled even if there is no rotate event - log rotation should give the waiting thread a signal to - discover EOF and move on to the next log. + We log the whole file name for log file as the user may decide + to change base names at some point. */ - signal_update(); + THD* thd = current_thd; + Rotate_log_event r(thd,new_name+dirname_length(new_name)); + r.set_log_pos(this); + r.write(&log_file); + bytes_written += r.get_event_len(); } + /* + Update needs to be signalled even if there is no rotate event + log rotation should give the waiting thread a signal to + discover EOF and move on to the next log. + */ + signal_update(); } old_name=name; save_log_type=log_type; name=0; // Don't free name - close(); + close(LOG_CLOSE_TO_BE_OPENED); + + /* + Note that at this point, log_type == LOG_CLOSED (important for is_open()). + */ + open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type, - no_auto_events); + no_auto_events, max_size); my_free(old_name,MYF(0)); end: @@ -991,6 +998,7 @@ end: pthread_mutex_unlock(&LOCK_index); pthread_mutex_unlock(&LOCK_log); } + DBUG_VOID_RETURN; } @@ -998,7 +1006,8 @@ bool MYSQL_LOG::append(Log_event* ev) { bool error = 0; pthread_mutex_lock(&LOCK_log); - + DBUG_ENTER("MYSQL_LOG::append"); + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); /* Log_event::write() is smart enough to use my_b_write() or @@ -1010,7 +1019,8 @@ bool MYSQL_LOG::append(Log_event* ev) goto err; } bytes_written += ev->get_event_len(); - if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + DBUG_PRINT("info",("max_size: %lu",max_size)); + if ((uint) my_b_append_tell(&log_file) > max_size) { pthread_mutex_lock(&LOCK_index); new_file(0); @@ -1020,13 +1030,14 @@ bool MYSQL_LOG::append(Log_event* ev) err: pthread_mutex_unlock(&LOCK_log); signal_update(); // Safe as we don't call close - return error; + DBUG_RETURN(error); } bool MYSQL_LOG::appendv(const char* buf, uint len,...) { bool error= 0; + DBUG_ENTER("MYSQL_LOG::appendv"); va_list(args); va_start(args,len); @@ -1042,8 +1053,8 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...) } bytes_written += len; } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); - - if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + DBUG_PRINT("info",("max_size: %lu",max_size)); + if ((uint) my_b_append_tell(&log_file) > max_size) { pthread_mutex_lock(&LOCK_index); new_file(0); @@ -1054,7 +1065,7 @@ err: pthread_mutex_unlock(&LOCK_log); if (!error) signal_update(); - return error; + DBUG_RETURN(error); } @@ -1158,14 +1169,13 @@ bool MYSQL_LOG::write(Log_event* event_info) bool should_rotate = 0; DBUG_ENTER("MYSQL_LOG::write(event)"); - if (!inited) // Can't use mutex if not init - { - DBUG_PRINT("error",("not initied")); - DBUG_RETURN(0); - } pthread_mutex_lock(&LOCK_log); - /* In most cases this is only called if 'is_open()' is true */ + /* + In most cases this is only called if 'is_open()' is true; in fact this is + mostly called if is_open() *was* true a few instructions before, but it + could have changed since. + */ if (is_open()) { const char *local_db = event_info->get_db(); @@ -1177,6 +1187,11 @@ bool MYSQL_LOG::write(Log_event* event_info) IO_CACHE *file = &log_file; #endif #ifdef HAVE_REPLICATION + /* + In the future we need to add to the following if tests like + "do the involved tables match (to be implemented) + binlog_[wild_]{do|ignore}_table?" (WL#1049)" + */ if ((thd && !(thd->options & OPTION_BIN_LOG) && (thd->master_access & SUPER_ACL)) || (local_db && !db_ok(local_db, binlog_do_db, binlog_ignore_db))) @@ -1328,8 +1343,9 @@ bool MYSQL_LOG::write(Log_event* event_info) called_handler_commit=1; } } - /* we wrote to the real log, check automatic rotation */ - should_rotate= (my_b_tell(file) >= (my_off_t) max_binlog_size); + /* We wrote to the real log, check automatic rotation; */ + DBUG_PRINT("info",("max_size: %lu",max_size)); + should_rotate= (my_b_tell(file) >= (my_off_t) max_size); } error=0; @@ -1467,7 +1483,8 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) log_file.pos_in_file))) goto err; signal_update(); - if (my_b_tell(&log_file) >= (my_off_t) max_binlog_size) + DBUG_PRINT("info",("max_size: %lu",max_size)); + if (my_b_tell(&log_file) >= (my_off_t) max_size) { pthread_mutex_lock(&LOCK_index); new_file(0); // inside mutex @@ -1505,123 +1522,123 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, time_t query_start_arg) { bool error=0; + time_t current_time; + if (!is_open()) + return 0; + VOID(pthread_mutex_lock(&LOCK_log)); if (is_open()) - { - time_t current_time; - VOID(pthread_mutex_lock(&LOCK_log)); - if (is_open()) - { // Safety agains reopen - int tmp_errno=0; - char buff[80],*end; - end=buff; - if (!(thd->options & OPTION_UPDATE_LOG) && - (thd->master_access & SUPER_ACL)) - { - VOID(pthread_mutex_unlock(&LOCK_log)); - return 0; - } - if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start_arg) - { - current_time=time(NULL); - if (current_time != last_time) - { - last_time=current_time; - struct tm tm_tmp; - struct tm *start; - localtime_r(¤t_time,&tm_tmp); - start=&tm_tmp; - /* Note that my_b_write() assumes it knows the length for this */ - sprintf(buff,"# Time: %02d%02d%02d %2d:%02d:%02d\n", - start->tm_year % 100, - start->tm_mon+1, - start->tm_mday, - start->tm_hour, - start->tm_min, - start->tm_sec); - if (my_b_write(&log_file, (byte*) buff,24)) - 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) - tmp_errno=errno; - } - if (query_start_arg) - { - /* For slow query log */ - if (my_b_printf(&log_file, - "# Query_time: %lu Lock_time: %lu Rows_sent: %lu Rows_examined: %lu\n", - (ulong) (current_time - query_start_arg), - (ulong) (thd->time_after_lock - query_start_arg), - (ulong) thd->sent_row_count, - (ulong) thd->examined_row_count) == (uint) -1) - 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) - tmp_errno=errno; - strmov(db,thd->db); - } - if (thd->last_insert_id_used) - { - end=strmov(end,",last_insert_id="); - end=longlong10_to_str((longlong) thd->current_insert_id,end,-10); - } - // Save value if we do an insert. - if (thd->insert_id_used) - { - if (specialflag & SPECIAL_LONG_LOG_FORMAT) - { - end=strmov(end,",insert_id="); - end=longlong10_to_str((longlong) thd->last_insert_id,end,-10); - } - } - if (thd->query_start_used) + { // Safety agains reopen + int tmp_errno=0; + char buff[80],*end; + end=buff; + if (!(thd->options & OPTION_UPDATE_LOG) && + (thd->master_access & SUPER_ACL)) + { + VOID(pthread_mutex_unlock(&LOCK_log)); + return 0; + } + if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start_arg) + { + current_time=time(NULL); + if (current_time != last_time) { - if (query_start_arg != thd->query_start()) - { - query_start_arg=thd->query_start(); - end=strmov(end,",timestamp="); - end=int10_to_str((long) query_start_arg,end,10); - } + last_time=current_time; + struct tm tm_tmp; + struct tm *start; + localtime_r(¤t_time,&tm_tmp); + start=&tm_tmp; + /* Note that my_b_write() assumes it knows the length for this */ + sprintf(buff,"# Time: %02d%02d%02d %2d:%02d:%02d\n", + start->tm_year % 100, + start->tm_mon+1, + start->tm_mday, + start->tm_hour, + start->tm_min, + start->tm_sec); + if (my_b_write(&log_file, (byte*) buff,24)) + tmp_errno=errno; } - if (end != buff) + 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) + tmp_errno=errno; + } + if (query_start_arg) + { + /* For slow query log */ + if (my_b_printf(&log_file, + "# Query_time: %lu Lock_time: %lu Rows_sent: %lu Rows_examined: %lu\n", + (ulong) (current_time - query_start_arg), + (ulong) (thd->time_after_lock - query_start_arg), + (ulong) thd->sent_row_count, + (ulong) thd->examined_row_count) == (uint) -1) + 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) + tmp_errno=errno; + strmov(db,thd->db); + } + if (thd->last_insert_id_used) + { + end=strmov(end,",last_insert_id="); + end=longlong10_to_str((longlong) thd->current_insert_id,end,-10); + } + // Save value if we do an insert. + if (thd->insert_id_used) + { + if (specialflag & SPECIAL_LONG_LOG_FORMAT) { - *end++=';'; - *end='\n'; - if (my_b_write(&log_file, (byte*) "SET ",4) || - my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff))) - tmp_errno=errno; + end=strmov(end,",insert_id="); + end=longlong10_to_str((longlong) thd->last_insert_id,end,-10); } - if (!query) + } + if (thd->query_start_used) + { + if (query_start_arg != thd->query_start()) { - end=strxmov(buff, "# administrator command: ", - command_name[thd->command], NullS); - query_length=(ulong) (end-buff); - query=buff; + query_start_arg=thd->query_start(); + end=strmov(end,",timestamp="); + end=int10_to_str((long) query_start_arg,end,10); } - if (my_b_write(&log_file, (byte*) query,query_length) || - my_b_write(&log_file, (byte*) ";\n",2) || - flush_io_cache(&log_file)) - tmp_errno=errno; - if (tmp_errno) + } + if (end != buff) + { + *end++=';'; + *end='\n'; + if (my_b_write(&log_file, (byte*) "SET ",4) || + my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff))) + tmp_errno=errno; + } + if (!query) + { + end=strxmov(buff, "# administrator command: ", + command_name[thd->command], NullS); + query_length=(ulong) (end-buff); + query=buff; + } + if (my_b_write(&log_file, (byte*) query,query_length) || + my_b_write(&log_file, (byte*) ";\n",2) || + flush_io_cache(&log_file)) + tmp_errno=errno; + if (tmp_errno) + { + error=1; + if (! write_error) { - error=1; - if (! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); - } + write_error=1; + sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); } } - VOID(pthread_mutex_unlock(&LOCK_log)); } + VOID(pthread_mutex_unlock(&LOCK_log)); return error; } + /* Wait until we get a signal that the binary log has been updated @@ -1655,25 +1672,26 @@ void MYSQL_LOG:: wait_for_update(THD* thd) SYNOPSIS close() - exiting Set to 1 if we should also close the index file - This can be set to 0 if we are going to do call open - at once after close, in which case we don't want to - close the index file. - We only write a 'stop' event to the log if exiting is set + exiting Bitmask for one or more of the following bits: + LOG_CLOSE_INDEX if we should close the index file + LOG_CLOSE_TO_BE_OPENED if we intend to call open + at once after close. + LOG_CLOSE_STOP_EVENT write a 'stop' event to the log NOTES One can do an open on the object at once after doing a close. The internal structures are not freed until cleanup() is called */ -void MYSQL_LOG::close(bool exiting) +void MYSQL_LOG::close(uint exiting) { // One can't set log_type here! DBUG_ENTER("MYSQL_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); - if (is_open()) + if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED) { #ifdef HAVE_REPLICATION - if (log_type == LOG_BIN && !no_auto_events && exiting) + if (log_type == LOG_BIN && !no_auto_events && + (exiting & LOG_CLOSE_STOP_EVENT)) { Stop_log_event s; s.set_log_pos(this); @@ -1694,7 +1712,7 @@ void MYSQL_LOG::close(bool exiting) called a not complete close earlier and the index file is still open. */ - if (exiting && my_b_inited(&index_file)) + if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file)) { end_io_cache(&index_file); if (my_close(index_file.file, MYF(0)) < 0 && ! write_error) @@ -1703,12 +1721,30 @@ void MYSQL_LOG::close(bool exiting) sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno); } } - log_type= LOG_CLOSED; + log_type= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; safeFree(name); DBUG_VOID_RETURN; } +void MYSQL_LOG::set_max_size(ulong max_size_arg) +{ + /* + We need to take locks, otherwise this may happen: + new_file() is called, calls open(old_max_size), then before open() starts, + set_max_size() sets max_size to max_size_arg, then open() starts and + uses the old_max_size argument, so max_size_arg has been overwritten and + it's like if the SET command was never run. + */ + DBUG_ENTER("MYSQL_LOG::set_max_size"); + pthread_mutex_lock(&LOCK_log); + if (is_open()) + max_size= max_size_arg; + pthread_mutex_unlock(&LOCK_log); + DBUG_VOID_RETURN; +} + + /* Check if a string is a valid number diff --git a/sql/log_event.cc b/sql/log_event.cc index 749732384c7..9f7d09785ac 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL 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 @@ -72,7 +72,7 @@ static void pretty_print_str(FILE* file, char* str, int len) } fputc('\'', file); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ /* @@ -82,33 +82,35 @@ static void pretty_print_str(FILE* file, char* str, int len) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) inline int ignored_error_code(int err_code) { - return use_slave_mask && bitmap_is_set(&slave_error_mask, err_code); + return ((err_code == ER_SLAVE_IGNORED_TABLE) || + (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code))); } #endif + /* pretty_print_str() */ #ifndef MYSQL_CLIENT -static char* pretty_print_str(char* packet, char* str, int len) +static char *pretty_print_str(char *packet, char *str, int len) { - char* end = str + len; - char* pos= packet; + char *end= str + len; + char *pos= packet; *pos++= '\''; while (str < end) { char c; switch ((c=*str++)) { - case '\n': pos= strmov(pos, "\\n"); break; - case '\r': pos= strmov(pos, "\\r"); break; - case '\\': pos= strmov(pos, "\\\\"); break; - case '\b': pos= strmov(pos, "\\b"); break; - case '\t': pos= strmov(pos, "\\t"); break; - case '\'': pos= strmov(pos, "\\'"); break; - case 0 : pos= strmov(pos, "\\0"); break; + case '\n': *pos++= '\'; *pos++= 'n'; break; + case '\r': *pos++= '\'; *pos++= 'r'; break; + case '\\': *pos++= '\'; *pos++= '\'; break; + case '\b': *pos++= '\'; *pos++= 'b'; break; + case '\t': *pos++= '\'; *pos++= 't'; break; + case '\'': *pos++= '\'; *pos++= '\''; break; + case 0 : *pos++= '\'; *pos++= '0'; break; default: - *pos++= (char)c; + *pos++= c; break; } } @@ -126,7 +128,7 @@ static char* pretty_print_str(char* packet, char* str, int len) static inline char* slave_load_file_stem(char*buf, uint file_id, int event_server_id) { - fn_format(buf,"SQL_LOAD-",slave_load_tmpdir,"",0); /* 4+32); */ + fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", MY_UNPACK_FILENAME); buf = strend(buf); buf = int10_to_str(::server_id, buf, 10); *buf++ = '-'; @@ -136,6 +138,7 @@ static inline char* slave_load_file_stem(char*buf, uint file_id, } #endif + /* cleanup_load_tmpdir() @@ -153,6 +156,8 @@ static void cleanup_load_tmpdir() MY_DIR *dirp; FILEINFO *file; uint i; + char fname[FN_REFLEN]; + if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME)))) return; @@ -160,7 +165,10 @@ static void cleanup_load_tmpdir() { file=dirp->dir_entry+i; if (is_prefix(file->name,"SQL_LOAD-")) - my_delete(file->name, MYF(0)); + { + fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME); + my_delete(fname, MYF(0)); + } } my_dirend(dirp); @@ -220,7 +228,7 @@ const char* Log_event::get_type_str() case EXEC_LOAD_EVENT: return "Exec_load"; case RAND_EVENT: return "RAND"; case USER_VAR_EVENT: return "User var"; - default: /* impossible */ return "Unknown"; + default: return "Unknown"; /* impossible */ } } @@ -390,7 +398,7 @@ void Log_event::init_show_field_list(List<Item>* field_list) field_list->push_back(new Item_empty_string("Info", 20)); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ /* Log_event::write() @@ -485,7 +493,7 @@ end: pthread_mutex_unlock(log_lock); DBUG_RETURN(result); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ #ifndef MYSQL_CLIENT #define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock); @@ -558,8 +566,9 @@ err: UNLOCK_MUTEX; if (error) { - sql_print_error("Error in Log_event::read_log_event(): '%s', \ -data_len=%d,event_type=%d",error,data_len,head[EVENT_TYPE_OFFSET]); + sql_print_error("\ +Error in Log_event::read_log_event(): '%s', data_len: %d, event_type: %d", + error,data_len,head[EVENT_TYPE_OFFSET]); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); /* The SQL slave thread will check if file->error<0 to know @@ -596,8 +605,6 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len, ev = new Query_log_event(buf, event_len, old_format); break; case LOAD_EVENT: - ev = new Load_log_event(buf, event_len, old_format); - break; case NEW_LOAD_EVENT: ev = new Load_log_event(buf, event_len, old_format); break; @@ -700,7 +707,8 @@ void Log_event::print_timestamp(FILE* file, time_t* ts) res->tm_sec); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ + /* Log_event::set_log_pos() @@ -712,7 +720,7 @@ void Log_event::set_log_pos(MYSQL_LOG* log) if (!log_pos) log_pos = my_b_tell(&log->log_file); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ /************************************************************************** @@ -735,8 +743,7 @@ void Query_log_event::pack_info(Protocol *protocol) { pos= strmov(buf, "use `"); memcpy(pos, db, db_len); - pos+= db_len; - pos= strmov(pos, "`; "); + pos= strmov(pos+db_len, "`; "); } if (query && q_len) { @@ -801,6 +808,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, } #endif /* MYSQL_CLIENT */ + /* Query_log_event::Query_log_event() */ @@ -885,8 +893,9 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int Query_log_event::exec_event(struct st_relay_log_info* rli) { - int expected_error,actual_error = 0; - thd->db = rewrite_db((char*)db); + int expected_error,actual_error= 0; + init_sql_alloc(&thd->mem_root, 8192,0); + thd->db= (char*) rewrite_db(db); /* InnoDB internally stores the master log position it has executed so far, @@ -908,11 +917,10 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) thd->query = (char*)query; thd->query_id = query_id++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - thd->query_error= 0; // clear error + thd->query_error= 0; // clear error thd->clear_error(); - thd->variables.pseudo_thread_id= thread_id; // for temp tables - + /* Sanity check to make sure the master did not get a really bad error on the query. @@ -924,6 +932,10 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) DBUG_PRINT("query",("%s",thd->query)); mysql_parse(thd, thd->query, q_len); + /* + If we expected a non-zero error code, and we don't get the same error + code, and none of them should be ignored. + */ DBUG_PRINT("info",("expected_error: %d last_errno: %d", expected_error, thd->net.last_errno)); if ((expected_error != (actual_error= thd->net.last_errno)) && @@ -931,14 +943,22 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) !ignored_error_code(actual_error) && !ignored_error_code(expected_error)) { - const char* errmsg = "Slave: did not get the expected error\ - running query from master - expected: '%s' (%d), got '%s' (%d)"; - sql_print_error(errmsg, ER_SAFE(expected_error), - expected_error, - actual_error ? thd->net.last_error: "no error", - actual_error); - thd->query_error = 1; + slave_print_error(rli, 0, + "\ +Query '%s' caused different errors on master and slave. \ +Error on master: '%s' (%d), Error on slave: '%s' (%d). \ +Default database: '%s'", + query, + ER_SAFE(expected_error), + expected_error, + actual_error ? thd->net.last_error: "no error", + actual_error, + print_slave_db_safe(db)); + thd->query_error= 1; } + /* + If we get the same error code as expected, or they should be ignored. + */ else if (expected_error == actual_error || ignored_error_code(actual_error)) { @@ -947,37 +967,39 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) *rli->last_slave_error = 0; rli->last_slave_errno = 0; } - } - else - { - // master could be inconsistent, abort and tell DBA to check/fix it - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->db = thd->query = 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - //thd->variables.convert_set = 0; - close_thread_tables(thd); - free_root(&thd->mem_root,0); - return 1; - } - } - thd->db= 0; // prevent db from being freed + /* + Other cases: mostly we expected no error and get one. + */ + else if (thd->query_error || thd->is_fatal_error) + { + slave_print_error(rli,actual_error, + "Error '%s' on query '%s'. Default database: '%s'", + (actual_error ? thd->net.last_error : + "unexpected success or fatal error"), + query, + print_slave_db_safe(db)); + thd->query_error= 1; + } + } + /* + End of sanity check. If the test was wrong, the query got a really bad + error on the master, which could be inconsistent, abort and tell DBA to + check/fix it. check_expected_error() already printed the message to + stderr and rli, and set thd->query_error to 1. + */ + } /* End of if (db_ok(... */ + +end: + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->db= 0; // prevent db from being freed thd->query= 0; // just to be sure VOID(pthread_mutex_unlock(&LOCK_thread_count)); // assume no convert for next query unless set explictly //thd->variables.convert_set = 0; - close_thread_tables(thd); - - if (thd->query_error || thd->is_fatal_error) - { - slave_print_error(rli,actual_error, "error '%s' on query '%s'", - actual_error ? thd->net.last_error : - "unexpected success or fatal error", query); - free_root(&thd->mem_root,0); - return 1; - } + close_thread_tables(thd); free_root(&thd->mem_root,0); - return Log_event::exec_event(rli); + return (thd->query_error ? thd->query_error : Log_event::exec_event(rli)); } #endif @@ -997,8 +1019,8 @@ void Start_log_event::pack_info(Protocol *protocol) pos= strmov(buf, "Server ver: "); pos= strmov(pos, server_version); pos= strmov(pos, ", Binlog ver: "); - pos=int10_to_str(binlog_version, pos, 10); - protocol->store(buf, pos-buf, &my_charset_bin); + pos= int10_to_str(binlog_version, pos, 10); + protocol->store(buf, (uint) (pos-buf), &my_charset_bin); } #endif @@ -1016,7 +1038,9 @@ void Start_log_event::print(FILE* file, bool short_form, char* last_db) print_header(file); fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version, server_version); - print_timestamp(file, &created); + print_timestamp(file); + if (created) + fprintf(file," at startup"); fputc('\n', file); fflush(file); } @@ -1057,13 +1081,10 @@ int Start_log_event::write_data(IO_CACHE* file) The master started IMPLEMENTATION - - To handle the case where the master died without having time to write DROP - TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is TODO), - we clean up all temporary tables + locks that we got. - However, we don't clean temporary tables if the master was 3.23 - (this is because a 3.23 master writes a Start_log_event at every - binlog rotation; if we were not careful we would remove temp tables - on the slave when FLUSH LOGS is issued on the master). + - To handle the case where the master died without having time to write + DROP TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is + TODO), we clean up all temporary tables that we got, if we are sure we + can (see below). TODO - Remove all active user locks. @@ -1078,8 +1099,8 @@ int Start_log_event::write_data(IO_CACHE* file) the table changes are committed, rollback has occured on the master; we should rather rollback on the slave and go on. If we don't rollback, and the next query is not BEGIN, then it will be considered as part of the - unfinished transaction, and so will be rolled back at next BEGIN, which is - a bug. + unfinished transaction, and so will be rolled back at next BEGIN, which + is a bug. */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) @@ -1087,23 +1108,46 @@ int Start_log_event::exec_event(struct st_relay_log_info* rli) { DBUG_ENTER("Start_log_event::exec_event"); - if (!rli->mi->old_format) - { + switch (rli->mi->old_format) { + case BINLOG_FORMAT_CURRENT: + /* + This is 4.x, so a Start_log_event is only at master startup, + so we are sure the master has restarted and cleared his temp tables. + */ + /* If the master died before writing the COMMIT to the binlog, rollback; otherwise it does not hurt to rollback. */ ha_rollback(thd); + close_temporary_tables(thd); + cleanup_load_tmpdir(); + break; + /* - If 4.0 master, all temporary tables have been deleted on the master; - if 3.23 master, this is far from sure. + Now the older formats; in that case load_tmpdir is cleaned up by the I/O + thread. */ - close_temporary_tables(thd); + case BINLOG_FORMAT_323_LESS_57: /* - If we have old format, load_tmpdir is cleaned up by the I/O thread + Cannot distinguish a Start_log_event generated at master startup and + one generated by master FLUSH LOGS, so cannot be sure temp tables + have to be dropped. So do nothing. */ - cleanup_load_tmpdir(); + break; + case BINLOG_FORMAT_323_GEQ_57: + /* + Can distinguish, based on the value of 'created', + which was generated at master startup. + */ + if (created) + close_temporary_tables(thd); + break; + default: + /* this case is impossible */ + return 1; } + DBUG_RETURN(Log_event::exec_event(rli)); } #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ @@ -1125,6 +1169,7 @@ void Load_log_event::pack_info(Protocol *protocol) buf_len= 5 + db_len + 3 + // "use DB; " 18 + fname_len + 2 + // "LOAD DATA INFILE 'file''" + 7 + // LOCAL 9 + // " REPLACE or IGNORE " 11 + table_name_len + // "INTO TABLE table" 21 + sql_ex.field_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'" @@ -1135,22 +1180,22 @@ void Load_log_event::pack_info(Protocol *protocol) 15 + 22 + // " IGNORE xxx LINES" 3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)" - buf= my_malloc(buf_len, MYF(MY_WME)); - if (!buf) + if (!(buf= my_malloc(buf_len, MYF(MY_WME)))) return; pos= buf; if (db && db_len) { pos= strmov(pos, "use `"); memcpy(pos, db, db_len); - pos+= db_len; - pos= strmov(pos, "`; "); + pos= strmov(pos+db_len, "`; "); } - pos= strmov(pos, "LOAD DATA INFILE '"); + pos= strmov(pos, "LOAD DATA "); + if (check_fname_outside_temp_buf()) + pos= strmov(pos, "LOCAL "); + pos= strmov(pos, "INFILE '"); memcpy(pos, fname, fname_len); - pos+= fname_len; - pos= strmov(pos, "' "); + pos= strmov(pos+fname_len, "' "); if (sql_ex.opt_flags & REPLACE_FLAG) pos= strmov(pos, " REPLACE "); @@ -1197,31 +1242,34 @@ void Load_log_event::pack_info(Protocol *protocol) pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len); } - if ((int)skip_lines > 0) + if ((long) skip_lines > 0) { pos= strmov(pos, " IGNORE "); - pos= longlong10_to_str((long) skip_lines, pos, 10); + pos= longlong10_to_str((longlong) skip_lines, pos, 10); pos= strmov(pos," LINES "); } if (num_fields) { uint i; - const char* field = fields; + const char *field= fields; pos= strmov(pos, " ("); for (i = 0; i < num_fields; i++) { if (i) - pos= strmov(pos, " ,"); + { + *pos++= ' '; + *pos++= ','; + } memcpy(pos, field, field_lens[i]); - pos+= field_lens[i]; - field += field_lens[i] + 1; + pos+= field_lens[i]; + field+= field_lens[i] + 1; } *pos++= ')'; } protocol->store(buf, pos-buf, &my_charset_bin); - my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); + my_free(buf, MYF(0)); } #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ @@ -1527,7 +1575,8 @@ void Load_log_event::set_fields(List<Item> &field_list) field+= field_lens[i] + 1; } } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ + #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) /* @@ -1559,11 +1608,12 @@ void Load_log_event::set_fields(List<Item> &field_list) int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, bool use_rli_only_for_errors) { - thd->db = rewrite_db((char*)db); + init_sql_alloc(&thd->mem_root, 8192,0); + thd->db= (char*) rewrite_db(db); DBUG_ASSERT(thd->query == 0); thd->query = 0; // Should not be needed thd->query_error = 0; - + if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); @@ -1643,12 +1693,14 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, thd->query_error = 1; if (thd->cuted_fields) { - /* - log_pos is the position of the LOAD event in the master log - */ - sql_print_error("Slave: load data infile at position %s in log \ -'%s' produced %d warning(s)", llstr(log_pos,llbuff), RPL_LOG_NAME, - thd->cuted_fields ); + /* log_pos is the position of the LOAD event in the master log */ + sql_print_error("\ +Slave: load data infile on table '%s' at log position %s in log \ +'%s' produced %ld warning(s). Default database: '%s'", + (char*) table_name, + llstr(log_pos,llbuff), RPL_LOG_NAME, + (ulong) thd->cuted_fields, + print_slave_db_safe(db)); } if (net) net->pkt_nr= thd->net.pkt_nr; @@ -1680,9 +1732,9 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, sql_errno=ER_UNKNOWN_ERROR; err=ER(sql_errno); } - slave_print_error(rli,sql_errno, - "Error '%s' running load data infile", - err); + slave_print_error(rli,sql_errno,"\ +Error '%s' running lOAD DATA INFILE on table '%s'. Default database: '%s'", + err, (char*)table_name, print_slave_db_safe(db)); free_root(&thd->mem_root,0); return 1; } @@ -1690,7 +1742,9 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, if (thd->is_fatal_error) { - sql_print_error("Fatal error running LOAD DATA INFILE"); + slave_print_error(rli,ER_UNKNOWN_ERROR, "\ +Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'", + (char*)table_name, print_slave_db_safe(db)); return 1; } @@ -1713,12 +1767,10 @@ void Rotate_log_event::pack_info(Protocol *protocol) char *buf, *b_pos; if (!(buf= my_malloc(ident_len + 45, MYF(MY_WME)))) return; - b_pos= buf; memcpy(buf, new_log_ident, ident_len); - b_pos+= ident_len; - b_pos= strmov(b_pos, ";pos="); - b_pos=longlong10_to_str(pos, b_pos, 10); - protocol->store(buf, b_pos-buf, &my_charset_bin); + b_pos= strmov(buf + ident_len, ";pos="); + b_pos= longlong10_to_str(pos, b_pos, 10); + protocol->store(buf, (uint) (b_pos-buf), &my_charset_bin); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } #endif @@ -1744,7 +1796,7 @@ void Rotate_log_event::print(FILE* file, bool short_form, char* last_db) fputc('\n', file); fflush(file); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ /* @@ -1790,7 +1842,7 @@ Rotate_log_event::Rotate_log_event(const char* buf, int event_len, int Rotate_log_event::write_data(IO_CACHE* file) { char buf[ROTATE_HEADER_LEN]; - int8store(buf, pos + R_POS_OFFSET); + int8store(buf + R_POS_OFFSET, pos); return (my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) || my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len)); } @@ -1823,7 +1875,8 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli) rli->group_master_log_pos = pos; rli->event_relay_log_pos += get_event_len(); rli->group_relay_log_pos = rli->event_relay_log_pos; - DBUG_PRINT("info", ("group_master_log_pos: %d", (ulong) rli->group_master_log_pos)); + DBUG_PRINT("info", ("group_master_log_pos: %lu", + (ulong) rli->group_master_log_pos)); pthread_mutex_unlock(&rli->data_lock); pthread_cond_broadcast(&rli->data_cond); flush_relay_log_info(rli); @@ -1845,9 +1898,9 @@ void Intvar_log_event::pack_info(Protocol *protocol) { char buf[64], *pos; pos= strmov(buf, get_var_type_name()); - *(pos++)='='; + *pos++= '='; pos= longlong10_to_str(val, pos, -10); - protocol->store(buf, pos-buf, &my_charset_bin); + protocol->store(buf, (uint) (pos-buf), &my_charset_bin); } #endif @@ -1994,7 +2047,7 @@ void Rand_log_event::print(FILE* file, bool short_form, char* last_db) llstr(seed1, llbuff),llstr(seed2, llbuff2)); fflush(file); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) @@ -2005,7 +2058,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli) rli->inc_event_relay_log_pos(get_event_len()); return 0; } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ /************************************************************************** @@ -2063,7 +2116,7 @@ void User_var_log_event::pack_info(Protocol* protocol) protocol->store(buf, event_len, &my_charset_bin); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ User_var_log_event::User_var_log_event(const char* buf, bool old_format) @@ -2236,7 +2289,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) rli->inc_event_relay_log_pos(get_event_len()); return 0; } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ /************************************************************************** @@ -2258,7 +2311,7 @@ void Unknown_log_event::print(FILE* file, bool short_form, char* last_db) #ifndef MYSQL_CLIENT void Slave_log_event::pack_info(Protocol *protocol) { - char buf[256], *pos; + char buf[256+HOSTNAME_LENGTH], *pos; pos= strmov(buf, "host="); pos= strnmov(pos, master_host, HOSTNAME_LENGTH); pos= strmov(pos, ",port="); @@ -2269,7 +2322,7 @@ void Slave_log_event::pack_info(Protocol *protocol) pos= longlong10_to_str(master_pos, pos, 10); protocol->store(buf, pos-buf, &my_charset_bin); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ #ifndef MYSQL_CLIENT @@ -2306,7 +2359,7 @@ Slave_log_event::Slave_log_event(THD* thd_arg, pthread_mutex_unlock(&mi->data_lock); DBUG_VOID_RETURN; } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ Slave_log_event::~Slave_log_event() @@ -2323,11 +2376,11 @@ void Slave_log_event::print(FILE* file, bool short_form, char* last_db) return; print_header(file); fputc('\n', file); - fprintf(file, "Slave: master_host: '%s' master_port: %d \ -master_log: '%s' master_pos: %s\n", + fprintf(file, "\ +Slave: master_host: '%s' master_port: %d master_log: '%s' master_pos: %s\n", master_host, master_port, master_log, llstr(master_pos, llbuff)); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ int Slave_log_event::get_data_size() @@ -2383,7 +2436,7 @@ int Slave_log_event::exec_event(struct st_relay_log_info* rli) mysql_bin_log.write(this); return Log_event::exec_event(rli); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ /************************************************************************** @@ -2404,7 +2457,7 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db) fprintf(file, "\tStop\n"); fflush(file); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ /* @@ -2431,7 +2484,7 @@ int Stop_log_event::exec_event(struct st_relay_log_info* rli) could give false triggers in MASTER_POS_WAIT() that we have reached the target position when in fact we have not. */ - rli->inc_group_relay_log_pos(get_event_len(), 0); + rli->inc_group_relay_log_pos(get_event_len(), 0); flush_relay_log_info(rli); return 0; } @@ -2460,7 +2513,7 @@ Create_file_log_event(THD* thd_arg, sql_exchange* ex, { sql_ex.force_new_format(); } -#endif // !MYSQL_CLIENT +#endif /* !MYSQL_CLIENT */ /* @@ -2563,12 +2616,13 @@ void Create_file_log_event::print(FILE* file, bool short_form, fprintf(file, " file_id: %d block_len: %d\n", file_id, block_len); } + void Create_file_log_event::print(FILE* file, bool short_form, char* last_db) { print(file,short_form,last_db,0); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ /* @@ -2581,15 +2635,13 @@ void Create_file_log_event::pack_info(Protocol *protocol) char buf[NAME_LEN*2 + 30 + 21*2], *pos; pos= strmov(buf, "db="); memcpy(pos, db, db_len); - pos+= db_len; - pos= strmov(pos, ";table="); + pos= strmov(pos + db_len, ";table="); memcpy(pos, table_name, table_name_len); - pos+= table_name_len; - pos= strmov(pos, ";file_id="); + pos= strmov(pos + table_name_len, ";file_id="); pos= int10_to_str((long) file_id, pos, 10); pos= strmov(pos, ";block_len="); pos= int10_to_str((long) block_len, pos, 10); - protocol->store(buf, pos-buf, &my_charset_bin); + protocol->store(buf, (uint) (pos-buf), &my_charset_bin); } #endif @@ -2720,7 +2772,7 @@ void Append_block_log_event::print(FILE* file, bool short_form, fprintf(file, "#Append_block: file_id: %d block_len: %d\n", file_id, block_len); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ /* @@ -2735,7 +2787,7 @@ void Append_block_log_event::pack_info(Protocol *protocol) length= (uint) my_sprintf(buf, (buf, ";file_id=%u;block_len=%u", file_id, block_len)); - protocol->store(buf, (int32) length, &my_charset_bin); + protocol->store(buf, length, &my_charset_bin); } #endif @@ -2829,7 +2881,7 @@ void Delete_file_log_event::print(FILE* file, bool short_form, fputc('\n', file); fprintf(file, "#Delete_file: file_id=%u\n", file_id); } -#endif // MYSQL_CLIENT +#endif /* MYSQL_CLIENT */ /* Delete_file_log_event::pack_info() @@ -2982,8 +3034,24 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) */ if (lev->exec_event(0,rli,1)) { - slave_print_error(rli,my_errno, "Failed executing load from '%s'", fname); - thd->options = save_options; + /* + We want to indicate the name of the file that could not be loaded + (SQL_LOADxxx). + But as we are here we are sure the error is in rli->last_slave_error and + rli->last_slave_errno (example of error: duplicate entry for key), so we + don't want to overwrite it with the filename. + What we want instead is add the filename to the current error message. + */ + char *tmp= my_strdup(rli->last_slave_error,MYF(MY_WME)); + if (tmp) + { + slave_print_error(rli, + rli->last_slave_errno, /* ok to re-use error code */ + "%s. Failed executing load from '%s'", + tmp, fname); + my_free(tmp,MYF(0)); + } + thd->options= save_options; goto err; } thd->options = save_options; @@ -3089,5 +3157,3 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) } return buf; } - - diff --git a/sql/log_event.h b/sql/log_event.h index bd5e1a82be4..a58479e2589 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -559,10 +559,15 @@ public: /* fname doesn't point to memory inside Log_event::temp_buf */ void set_fname_outside_temp_buf(const char *afname, uint alen) - {fname=afname;fname_len=alen;} + { + fname= afname; + fname_len= alen; + } /* fname doesn't point to memory inside Log_event::temp_buf */ int check_fname_outside_temp_buf() - {return fname<temp_buf || fname>temp_buf+cached_event_len;} + { + return fname < temp_buf || fname > temp_buf+ cached_event_len; + } #ifndef MYSQL_CLIENT String field_lens_buf; @@ -619,6 +624,26 @@ extern char server_version[SERVER_VERSION_LENGTH]; class Start_log_event: public Log_event { public: + /* + If this event is at the start of the first binary log since server startup + 'created' should be the timestamp when the event (and the binary log) was + created. + In the other case (i.e. this event is at the start of a binary log created + by FLUSH LOGS or automatic rotation), 'created' should be 0. + This "trick" is used by MySQL >=4.0.14 slaves to know if they must drop the + stale temporary tables or not. + Note that when 'created'!=0, it is always equal to the event's timestamp; + indeed Start_log_event is written only in log.cc where the first + constructor below is called, in which 'created' is set to 'when'. + So in fact 'created' is a useless variable. When it is 0 + we can read the actual value from timestamp ('when') and when it is + non-zero we can read the same value from timestamp ('when'). Conclusion: + - we use timestamp to print when the binlog was created. + - we use 'created' only to know if this is a first binlog or not. + In 3.23.57 we did not pay attention to this identity, so mysqlbinlog in + 3.23.57 does not print 'created the_date' if created was zero. This is now + fixed. + */ time_t created; uint16 binlog_version; char server_version[ST_SERVER_VER_LEN]; @@ -947,6 +972,7 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, bool short_form = 0, char* last_db = 0); + void print(FILE* file, bool short_form, char* last_db, bool enable_local); #endif Delete_file_log_event(const char* buf, int event_len); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2c41d973f2e..2bca2c3eef2 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -77,6 +77,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; /* Password lengh for 4.1 version previous versions had 16 bytes password hash */ #define HASH_PASSWORD_LENGTH 45 #define HASH_OLD_PASSWORD_LENGTH 16 +#define MAX_PASSWORD_LENGTH 32 #define HOST_CACHE_SIZE 128 #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 @@ -158,6 +159,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define TEST_NO_EXTRA 128 #define TEST_CORE_ON_SIGNAL 256 /* Give core if signal */ #define TEST_NO_STACKTRACE 512 +#define TEST_SIGINT 1024 /* Allow sigint on threads */ /* options for select set by the yacc parser (stored in lex->options) */ #define SELECT_DISTINCT 1 @@ -285,6 +287,17 @@ typedef struct st_sql_list { next= next_ptr; *next=0; } + inline void save_and_clear(struct st_sql_list *save) + { + *save= *this; + empty(); + } + inline void push_front(struct st_sql_list *save) + { + *save->next= first; /* link current list last */ + first= save->first; + elements+= save->elements; + } } SQL_LIST; @@ -689,8 +702,8 @@ bool fn_format_relative_to_data_home(my_string to, const char *name, bool open_log(MYSQL_LOG *log, const char *hostname, const char *opt_name, const char *extension, const char *index_file_name, - enum_log_type type, bool read_append = 0, - bool no_auto_events = 0); + enum_log_type type, bool read_append, + bool no_auto_events, ulong max_size); /* External variables @@ -737,7 +750,8 @@ extern ulong max_insert_delayed_threads, max_user_connections; extern ulong long_query_count, what_to_log,flush_time; extern ulong query_buff_size, thread_stack,thread_stack_min; extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; -extern ulong max_binlog_size, rpl_recovery_rank, thread_cache_size; +extern ulong max_binlog_size, max_relay_log_size; +extern ulong rpl_recovery_rank, thread_cache_size; extern ulong com_stat[(uint) SQLCOM_END], com_other, back_log; extern ulong specialflag, current_pid; extern ulong expire_logs_days; @@ -916,6 +930,10 @@ Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, /* log.cc */ bool flush_error_log(void); +/* sql_list.cc */ +void free_list(I_List <i_string_pair> *list); +void free_list(I_List <i_string> *list); + /* Some inline functions for more speed */ inline bool add_item_to_list(THD *thd, Item *item) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8ddcbdc572f..b5afbb422d5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -252,7 +252,7 @@ bool volatile ready_to_exit, shutdown_in_progress, grant_option; my_bool opt_skip_slave_start = 0; // If set, slave is not autostarted my_bool opt_reckless_slave = 0; -my_bool opt_enable_named_pipe= 0; +my_bool opt_enable_named_pipe= 0, opt_debugging= 0; my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; @@ -270,14 +270,15 @@ ulong back_log, connect_timeout, concurrency; ulong server_id, thd_startup_options; ulong table_cache_size, thread_stack, thread_stack_min, what_to_log; ulong query_buff_size, slow_launch_time, slave_open_temp_tables; -ulong open_files_limit, max_binlog_size; +ulong open_files_limit, max_binlog_size, max_relay_log_size; ulong slave_net_timeout; ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; ulong query_cache_size=0; ulong com_stat[(uint) SQLCOM_END], com_other; ulong bytes_sent, bytes_received, net_big_packet_count; -ulong refresh_version=1L,flush_version=1L; /* Increments on each reload */ -ulong query_id, long_query_count, aborted_threads, aborted_connects; +ulong refresh_version, flush_version; /* Increments on each reload */ +ulong query_id, long_query_count; +ulong aborted_threads, killed_threads, aborted_connects; ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size; ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use; ulong delayed_insert_errors,flush_time, thread_created; @@ -847,7 +848,6 @@ extern "C" void unireg_abort(int exit_code) sql_print_error("Aborting\n"); clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */ DBUG_PRINT("quit",("done with cleanup in unireg_abort")); - my_thread_end(); clean_up_mutexes(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(exit_code); /* purecov: inspected */ @@ -900,6 +900,11 @@ void clean_up(bool print_message) free_max_user_conn(); #ifdef HAVE_REPLICATION end_slave_list(); + free_list(&replicate_do_db); + free_list(&replicate_ignore_db); + free_list(&binlog_do_db); + free_list(&binlog_ignore_db); + free_list(&replicate_rewrite_db); #endif #ifdef HAVE_OPENSSL if (ssl_acceptor_fd) @@ -1263,7 +1268,10 @@ extern "C" sig_handler end_thread_signal(int sig __attribute__((unused))) THD *thd=current_thd; DBUG_ENTER("end_thread_signal"); if (thd && ! thd->bootstrap) + { + statistic_increment(killed_threads, &LOCK_status); end_thread(thd,0); + } DBUG_VOID_RETURN; /* purecov: deadcode */ } @@ -1582,7 +1590,8 @@ static void init_signals(void) struct sigaction sa; DBUG_ENTER("init_signals"); - sigset(THR_KILL_SIGNAL,end_thread_signal); + if (test_flags & TEST_SIGINT) + sigset(THR_KILL_SIGNAL,end_thread_signal); sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called! if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL)) @@ -1641,7 +1650,8 @@ static void init_signals(void) sigaddset(&set,SIGTSTP); #endif sigaddset(&set,THR_SERVER_ALARM); - sigdelset(&set,THR_KILL_SIGNAL); // May be SIGINT + if (test_flags & TEST_SIGINT) + sigdelset(&set,THR_KILL_SIGNAL); // May be SIGINT sigdelset(&set,THR_CLIENT_ALARM); // For alarms sigprocmask(SIG_SETMASK,&set,NULL); pthread_sigmask(SIG_SETMASK,&set,NULL); @@ -1697,9 +1707,12 @@ extern "C" void *signal_hand(void *arg __attribute__((unused))) */ init_thr_alarm(max_connections+max_insert_delayed_threads+10); #if SIGINT != THR_KILL_SIGNAL - (void) sigemptyset(&set); // Setup up SIGINT for debug - (void) sigaddset(&set,SIGINT); // For debugging - (void) pthread_sigmask(SIG_UNBLOCK,&set,NULL); + if (test_flags & TEST_SIGINT) + { + (void) sigemptyset(&set); // Setup up SIGINT for debug + (void) sigaddset(&set,SIGINT); // For debugging + (void) pthread_sigmask(SIG_UNBLOCK,&set,NULL); + } #endif (void) sigemptyset(&set); // Setup up SIGINT for debug #ifdef USE_ONE_SIGNAL_HAND @@ -1931,7 +1944,7 @@ bool open_log(MYSQL_LOG *log, const char *hostname, const char *opt_name, const char *extension, const char *index_file_name, enum_log_type type, bool read_append, - bool no_auto_events) + bool no_auto_events, ulong max_size) { char tmp[FN_REFLEN]; if (!opt_name || !opt_name[0]) @@ -1948,16 +1961,13 @@ bool open_log(MYSQL_LOG *log, const char *hostname, if (type == LOG_BIN) { char *p = fn_ext(opt_name); - if (p) - { - uint length=(uint) (p-opt_name); - strmake(tmp,opt_name,min(length,FN_REFLEN)); - opt_name=tmp; - } + uint length=(uint) (p-opt_name); + strmake(tmp,opt_name,min(length,FN_REFLEN)); + opt_name=tmp; } return log->open(opt_name, type, 0, index_file_name, (read_append) ? SEQ_READ_APPEND : WRITE_CACHE, - no_auto_events); + no_auto_events, max_size); } @@ -1989,6 +1999,17 @@ static int init_common_variables(const char *conf_file_name, int argc, } #endif + /* + Init mutexes for the global MYSQL_LOG objects. + As safe_mutex depends on what MY_INIT() does, we can't init the mutexes of + global MYSQL_LOGs in their constructors, because then they would be inited + before MY_INIT(). So we do it here. + */ + mysql_log.init_pthread_objects(); + mysql_update_log.init_pthread_objects(); + mysql_slow_log.init_pthread_objects(); + mysql_bin_log.init_pthread_objects(); + if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0) strmov(glob_hostname,"mysql"); strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); @@ -2151,21 +2172,21 @@ static int init_server_components() /* Setup log files */ if (opt_log) open_log(&mysql_log, glob_hostname, opt_logname, ".log", NullS, - LOG_NORMAL); + LOG_NORMAL, 0, 0, 0); if (opt_update_log) { open_log(&mysql_update_log, glob_hostname, opt_update_logname, "", - NullS, LOG_NEW); +! NullS, LOG_NEW, 0, 0, 0); using_update_log=1; } if (opt_slow_log) open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", - NullS, LOG_NORMAL); + NullS, LOG_NORMAL, 0, 0, 0); if (opt_bin_log) { open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", - opt_binlog_index_name,LOG_BIN); + opt_binlog_index_name, LOG_BIN, 0, 0, max_binlog_size); using_update_log=1; #ifdef HAVE_REPLICATION if (expire_logs_days) @@ -2176,6 +2197,12 @@ static int init_server_components() } #endif } + else if (opt_log_slave_updates) + { + sql_print_error("\ +Warning: you need to use --log-bin to make --log-slave-updates work. \ +Now disabling --log-slave-updates."); + } if (opt_error_log) { @@ -2459,6 +2486,8 @@ The server will not act as a slave."); if (!opt_noacl) udf_init(); #endif + if (opt_bootstrap) /* If running with bootstrap, do not start replication. */ + opt_skip_slave_start= 1; /* init_slave() must be called after the thread keys are created */ init_slave(); @@ -2564,6 +2593,19 @@ int mysql_service(void *p) return 0; } + +/* Quote string if it contains space, else copy */ + +static char *add_quoted_string(char *to, const char *from, char *to_end) +{ + uint length= (uint) (to_end-to); + + if (!strchr(from, ' ')) + return strnmov(to, from, length); + return strxnmov(to, length, "\"", from, "\"", NullS); +} + + /* Handle basic handling of services, like installation and removal @@ -2573,25 +2615,41 @@ int mysql_service(void *p) servicename Internal name of service displayname Display name of service (in taskbar ?) file_path Path to this program + startup_option Startup option to mysqld RETURN VALUES 0 option handled 1 Could not handle option */ -bool default_service_handling(char **argv, - const char *servicename, - const char *displayname, - const char *file_path) +static bool +default_service_handling(char **argv, + const char *servicename, + const char *displayname, + const char *file_path, + const char *extra_opt) { + char path_and_service[FN_REFLEN+FN_REFLEN+32], *pos, *end; + end= path_and_service + sizeof(path_and_service)-1; + + /* We have to quote filename if it contains spaces */ + pos= add_quoted_string(path_and_service, file_path, end); + if (*extra_opt) + { + /* Add (possible quoted) option after file_path */ + *pos++= ' '; + pos= add_quoted_string(pos, extra_opt, end); + } + *pos= 0; // Ensure end null + if (Service.got_service_option(argv, "install")) { - Service.Install(1, servicename, displayname, file_path); + Service.Install(1, servicename, displayname, path_and_service); return 0; } if (Service.got_service_option(argv, "install-manual")) { - Service.Install(0, servicename, displayname, file_path); + Service.Install(0, servicename, displayname, path_and_service); return 0; } if (Service.got_service_option(argv, "remove")) @@ -2617,12 +2675,13 @@ int main(int argc, char **argv) { char file_path[FN_REFLEN]; my_path(file_path, argv[0], ""); /* Find name in path */ - fn_format(file_path,argv[0],file_path,"",1+4+16); /* Force full path */ + fn_format(file_path,argv[0],file_path,"", + MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS); if (argc == 2) { - if (!default_service_handling(argv,MYSQL_SERVICENAME, MYSQL_SERVICENAME, - file_path)) + if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME, + file_path, "")) return 0; if (Service.IsService(argv[1])) { @@ -2635,12 +2694,8 @@ int main(int argc, char **argv) } else if (argc == 3) /* install or remove any optional service */ { - /* Add service name after filename */ - uint length=strlen(file_path); - *strxnmov(file_path + length, sizeof(file_path)-length-2, " ", - argv[2], NullS)= '\0'; - - if (!default_service_handling(argv, argv[2], argv[2], file_path)) + if (!default_service_handling(argv, argv[2], argv[2], file_path, + argv[2])) return 0; if (Service.IsService(argv[2])) { @@ -2662,10 +2717,8 @@ int main(int argc, char **argv) Install an optional service with optional config file mysqld --install-manual mysqldopt --defaults-file=c:\miguel\my.ini */ - uint length=strlen(file_path); - *strxnmov(file_path + length, sizeof(file_path)-length-2, " ", - argv[3], " ", argv[2], NullS)= '\0'; - if (!default_service_handling(argv, argv[2], argv[2], file_path)) + if (!default_service_handling(argv, argv[2], argv[2], file_path, + argv[3])) return 0; } else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME)) @@ -2809,6 +2862,7 @@ static void create_new_thread(THD *thd) thread_count--; thd->killed=1; // Safety (void) pthread_mutex_unlock(&LOCK_thread_count); + statistic_increment(aborted_connects,&LOCK_status); net_printf(thd,ER_CANT_CREATE_THREAD,error); (void) pthread_mutex_lock(&LOCK_thread_count); close_connection(thd,0,0); @@ -3424,7 +3478,6 @@ enum options OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE, OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE, OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE, OPT_LOCAL_INFILE, - OPT_RECKLESS_SLAVE, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_BACK_LOG, OPT_BINLOG_CACHE_SIZE, @@ -3438,8 +3491,9 @@ enum options OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, - OPT_MAX_JOIN_SIZE, OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_SORT_LENGTH, - OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, + OPT_MAX_JOIN_SIZE, OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH, + OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, + OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, OPT_MAX_ERROR_COUNT, OPT_MAX_PREP_STMT, OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, @@ -3452,7 +3506,7 @@ enum options OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER, OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_RELAY_LOG_PURGE, OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME, - OPT_READONLY, + OPT_READONLY, OPT_DEBUGGING, OPT_SORT_BUFFER, OPT_TABLE_CACHE, OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK, @@ -3607,6 +3661,10 @@ Disable with --skip-bdb (will save memory).", REQUIRED_ARG, 1024, 4, (long) ~0, 0, 1, 0}, /* We must always support the next option to make scripts like mysqltest easier to do */ + {"gdb", OPT_DEBUGGING, + "Set up signals usable for debugging", + (gptr*) &opt_debugging, (gptr*) &opt_debugging, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"init-rpl-role", OPT_INIT_RPL_ROLE, "Set the replication role.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH, @@ -3638,7 +3696,7 @@ Disable with --skip-bdb (will save memory).", 0, 0, 0}, {"innodb_fast_shutdown", OPT_INNODB_FAST_SHUTDOWN, "Speeds up server shutdown process.", (gptr*) &innobase_fast_shutdown, - (gptr*) &innobase_fast_shutdown, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + (gptr*) &innobase_fast_shutdown, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT, "Percentage of dirty pages allowed in bufferpool.", (gptr*) &srv_max_buf_pool_modified_pct, (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0}, @@ -3804,8 +3862,6 @@ Does nothing yet.", OPT_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "Port number to use for connection.", (gptr*) &mysqld_port, (gptr*) &mysqld_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"reckless-slave", OPT_RECKLESS_SLAVE, "Used for debugging.", 0, 0, 0, GET_NO_ARG, - NO_ARG, 0, 0, 0, 0, 0, 0}, {"replicate-do-db", OPT_REPLICATE_DO_DB, "Tells the slave thread to restrict replication to the specified database. To specify more than one database, use the directive multiple times, once for each database. Note that this will only work if you do not use cross-database queries such as UPDATE some_db.some_table SET foo='bar' while having selected a different or no database. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-do-table=db_name.%.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -3920,7 +3976,7 @@ Disable with --skip-isam.", {"skip-stack-trace", OPT_SKIP_STACK_TRACE, "Don't print a stack trace on failure.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables. Depricated option. Use --skip-symbolic-links instead.", + {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables. Deprecated option. Use --skip-symbolic-links instead.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"skip-thread-priority", OPT_SKIP_PRIOR, "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, @@ -3974,10 +4030,10 @@ replicating a LOAD DATA INFILE command.", {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.", (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"use-symbolic-links", 's', "Enable symbolic link support. Depricated option; Use --symbolic-links instead.", + {"use-symbolic-links", 's', "Enable symbolic link support. Deprecated option; Use --symbolic-links instead.", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, IF_PURIFY(0,1), 0, 0, 0, 0, 0}, - {"--symbolic-links", 's', "Enable symbolic link support.", + {"symbolic-links", 's', "Enable symbolic link support.", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, IF_PURIFY(0,1), 0, 0, 0, 0, 0}, {"user", 'u', "Run mysqld daemon as user.", 0, 0, 0, GET_STR, REQUIRED_ARG, @@ -4144,9 +4200,11 @@ replicating a LOAD DATA INFILE command.", (gptr*) &max_binlog_cache_size, (gptr*) &max_binlog_cache_size, 0, GET_ULONG, REQUIRED_ARG, ~0L, IO_SIZE, ~0L, 0, IO_SIZE, 0}, {"max_binlog_size", OPT_MAX_BINLOG_SIZE, - "Binary log will be rotated automatically when the size crosses the limit.", + "Binary log will be rotated automatically when the size exceeds this \ +value. Will also apply to relay logs if max_relay_log_size is 0. \ +The minimum value for this variable is 4096.", (gptr*) &max_binlog_size, (gptr*) &max_binlog_size, 0, GET_ULONG, - REQUIRED_ARG, 1024*1024L*1024L, 1024, 1024*1024L*1024L, 0, 1, 0}, + REQUIRED_ARG, 1024*1024L*1024L, IO_SIZE, 1024*1024L*1024L, 0, IO_SIZE, 0}, {"max_connections", OPT_MAX_CONNECTIONS, "The number of simultaneous clients allowed.", (gptr*) &max_connections, (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1, @@ -4184,6 +4242,15 @@ replicating a LOAD DATA INFILE command.", (gptr*) &global_system_variables.max_prep_stmt_count, (gptr*) &max_system_variables.max_prep_stmt_count, 0, GET_ULONG, REQUIRED_ARG, DEFAULT_PREP_STMT_COUNT, 0, ~0L, 0, 1, 0}, + {"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE, + "If non-zero: relay log will be rotated automatically when the size exceeds this value; if zero (the default): when the size exceeds max_binlog_size. 0 expected, the minimum value for this variable is 4096.", + (gptr*) &max_relay_log_size, (gptr*) &max_relay_log_size, 0, GET_ULONG, + REQUIRED_ARG, 0L, 0L, 1024*1024L*1024L, 0, IO_SIZE, 0}, + { "max_seeks_for_key", OPT_MAX_SEEKS_FOR_KEY, + "Limit assumed max number of seeks when looking up rows based on a key", + (gptr*) &global_system_variables.max_seeks_for_key, + (gptr*) &max_system_variables.max_seeks_for_key, 0, GET_ULONG, + REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0 }, {"max_sort_length", OPT_MAX_SORT_LENGTH, "The number of bytes to use when sorting BLOB or TEXT values (only the first max_sort_length bytes of each value are used; the rest are ignored).", (gptr*) &global_system_variables.max_sort_length, @@ -4427,6 +4494,7 @@ struct show_var_st status_vars[]= { {"Com_restore_table", (char*) (com_stat+(uint) SQLCOM_RESTORE_TABLE),SHOW_LONG}, {"Com_revoke", (char*) (com_stat+(uint) SQLCOM_REVOKE),SHOW_LONG}, {"Com_rollback", (char*) (com_stat+(uint) SQLCOM_ROLLBACK),SHOW_LONG}, + {"Com_savepoint", (char*) (com_stat+(uint) SQLCOM_SAVEPOINT),SHOW_LONG}, {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, @@ -4578,8 +4646,8 @@ static void usage(void) puts("\ Copyright (C) 2000 MySQL AB, by Monty and others\n\ This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ -and you are welcome to modify and redistribute it under the GPL license\n\ -Starts the MySQL server\n"); +and you are welcome to modify and redistribute it under the GPL license\n\n\ +Starts the MySQL database server\n"); printf("Usage: %s [OPTIONS]\n", my_progname); if (!opt_verbose) @@ -4689,7 +4757,7 @@ static void mysql_init_variables(void) protocol_version= PROTOCOL_VERSION; what_to_log= ~ (1L << (uint) COM_TIME); refresh_version= flush_version= 1L; /* Increments on each reload */ - thread_id= 1L; + query_id= thread_id= 1L; strmov(server_version, MYSQL_SERVER_VERSION); myisam_recover_options_str= sql_mode_str= "OFF"; my_bind_addr = htonl(INADDR_ANY); @@ -4872,7 +4940,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), #endif case OPT_SAFEMALLOC_MEM_LIMIT: #if !defined(DBUG_OFF) && defined(SAFEMALLOC) - safemalloc_mem_limit = atoi(argument); + sf_malloc_mem_limit = atoi(argument); #endif break; #ifdef EMBEDDED_LIBRARY @@ -5028,10 +5096,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), table_rules_on = 1; break; } - case (int)OPT_RECKLESS_SLAVE: - opt_reckless_slave = 1; - init_slave_skip_errors("all"); - break; #endif /* HAVE_REPLICATION */ case (int) OPT_SLOW_QUERY_LOG: opt_slow_log=1; @@ -5376,6 +5440,12 @@ static void get_options(int argc,char **argv) have_symlink=SHOW_OPTION_DISABLED; } #endif + if (opt_debugging) + { + /* Allow break with SIGINT, no core or stack trace */ + test_flags|= TEST_SIGINT | TEST_NO_STACKTRACE; + test_flags&= ~TEST_CORE_ON_SIGNAL; + } /* Set global MyISAM variables from delay_key_write_options */ fix_delay_key_write((THD*) 0, OPT_GLOBAL); diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 9e9eaf9eea1..f12705572d6 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -28,6 +28,9 @@ Read packets are reallocated dynamicly when reading big packets. Each logical packet has the following pre-info: 3 byte length & 1 byte package-number. + + This file needs to be written in C as it's used by the libmysql client as a + C file. */ /* diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 935abac5146..839804b20d5 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2390,9 +2390,17 @@ QUICK_SELECT *get_quick_select_for_ref(TABLE *table, TABLE_REF *ref) if (!quick) return 0; + if (cp_buffer_from_ref(ref)) + { + if (current_thd->fatal_error) + return 0; // End of memory + return quick; // empty range + } + QUICK_RANGE *range= new QUICK_RANGE(); - if (!range || cp_buffer_from_ref(ref)) + if (!range) goto err; + range->min_key=range->max_key=(char*) ref->key_buff; range->min_length=range->max_length=ref->key_length; range->flag= ((ref->key_length == key_info->key_length && diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 60af9a92c76..e9c3b1ed0b0 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -72,6 +72,7 @@ static int init_failsafe_rpl_thread(THD* thd) if (init_thr_lock() || thd->store_globals()) { close_connection(thd, ER_OUT_OF_RESOURCES, 1); // is this needed? + statistic_increment(aborted_connects,&LOCK_status); end_thread(thd,0); DBUG_RETURN(-1); } @@ -867,7 +868,7 @@ int load_master_data(THD* thd) of LOAD DATA FROM MASTER - no reason to forbid it, really, although it does not make much sense for the user to do it */ - if (row[0] && row[1]) + if (row && row[0] && row[1]) { strmake(active_mi->master_log_name, row[0], sizeof(active_mi->master_log_name)); diff --git a/sql/set_var.cc b/sql/set_var.cc index 6a6e010578f..eebefc8b79c 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -88,6 +88,8 @@ static void fix_query_cache_size(THD *thd, enum_var_type type); static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type); static void fix_myisam_max_extra_sort_file_size(THD *thd, enum_var_type type); static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type); +static void fix_max_binlog_size(THD *thd, enum_var_type type); +static void fix_max_relay_log_size(THD *thd, enum_var_type type); static KEY_CACHE *create_key_cache(const char *name, uint length); void fix_sql_mode_var(THD *thd, enum_var_type type); static byte *get_error_count(THD *thd); @@ -155,7 +157,8 @@ sys_var_thd_ulong sys_max_allowed_packet("max_allowed_packet", sys_var_long_ptr sys_max_binlog_cache_size("max_binlog_cache_size", &max_binlog_cache_size); sys_var_long_ptr sys_max_binlog_size("max_binlog_size", - &max_binlog_size); + &max_binlog_size, + fix_max_binlog_size); sys_var_long_ptr sys_max_connections("max_connections", &max_connections); sys_var_long_ptr sys_max_connect_errors("max_connect_errors", @@ -175,6 +178,8 @@ sys_var_pseudo_thread_id sys_pseudo_thread_id("pseudo_thread_id", sys_var_thd_ha_rows sys_max_join_size("max_join_size", &SV::max_join_size, fix_max_join_size); +sys_var_thd_ulong sys_max_seeks_for_key("max_seeks_for_key", + &SV::max_seeks_for_key); sys_var_thd_ulong sys_max_length_for_sort_data("max_length_for_sort_data", &SV::max_length_for_sort_data); #ifndef TO_BE_DELETED /* Alias for max_join_size */ @@ -184,6 +189,9 @@ sys_var_thd_ha_rows sys_sql_max_join_size("sql_max_join_size", #endif sys_var_thd_ulong sys_max_prep_stmt_count("max_prepared_statements", &SV::max_prep_stmt_count); +sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size", + &max_relay_log_size, + fix_max_relay_log_size); sys_var_thd_ulong sys_max_sort_length("max_sort_length", &SV::max_sort_length); sys_var_long_ptr sys_max_user_connections("max_user_connections", @@ -318,7 +326,6 @@ static sys_var_thd_bit sys_unique_checks("unique_checks", OPTION_RELAXED_UNIQUE_CHECKS, 1); - /* Local state variables */ static sys_var_thd_ha_rows sys_select_limit("sql_select_limit", @@ -409,6 +416,8 @@ sys_var *sys_variables[]= &sys_max_join_size, &sys_max_length_for_sort_data, &sys_max_prep_stmt_count, + &sys_max_relay_log_size, + &sys_max_seeks_for_key, &sys_max_sort_length, &sys_max_tmp_tables, &sys_max_user_connections, @@ -573,8 +582,9 @@ struct show_var_st init_vars[]= { {sys_max_delayed_threads.name,(char*) &sys_max_delayed_threads, SHOW_SYS}, {sys_max_heap_table_size.name,(char*) &sys_max_heap_table_size, SHOW_SYS}, {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS}, - {sys_max_length_for_sort_data.name, - (char*) &sys_max_length_for_sort_data, + {sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size, SHOW_SYS}, + {sys_max_seeks_for_key.name, (char*) &sys_max_seeks_for_key, SHOW_SYS}, + {sys_max_length_for_sort_data.name, (char*) &sys_max_length_for_sort_data, SHOW_SYS}, {sys_max_prep_stmt_count.name,(char*) &sys_max_prep_stmt_count, SHOW_SYS}, {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, @@ -807,6 +817,26 @@ void fix_delay_key_write(THD *thd, enum_var_type type) } } +void fix_max_binlog_size(THD *thd, enum_var_type type) +{ + DBUG_ENTER("fix_max_binlog_size"); + DBUG_PRINT("info",("max_binlog_size=%lu max_relay_log_size=%lu", + max_binlog_size, max_relay_log_size)); + mysql_bin_log.set_max_size(max_binlog_size); + if (!max_relay_log_size) + active_mi->rli.relay_log.set_max_size(max_binlog_size); + DBUG_VOID_RETURN; +} + +void fix_max_relay_log_size(THD *thd, enum_var_type type) +{ + DBUG_ENTER("fix_max_relay_log_size"); + DBUG_PRINT("info",("max_binlog_size=%lu max_relay_log_size=%lu", + max_binlog_size, max_relay_log_size)); + active_mi->rli.relay_log.set_max_size(max_relay_log_size ? + max_relay_log_size: max_binlog_size); + DBUG_VOID_RETURN; +} bool sys_var_long_ptr::update(THD *thd, set_var *var) { @@ -1060,6 +1090,11 @@ err: } +void sys_var_thd_conv_charset::set_default(THD *thd, enum_var_type type) +{ + thd->variables.convert_set= global_system_variables.convert_set; +} + bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) { diff --git a/sql/set_var.h b/sql/set_var.h index 968687ad382..f9c5d8f7822 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -167,6 +167,7 @@ public: return type != STRING_RESULT; /* Only accept strings */ } bool check_default(enum_var_type type) { return 0; } + void set_default(THD *thd, enum_var_type type); }; diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index b43c4b43b50..1beffa81671 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -247,6 +247,7 @@ v/* "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 2eb9e6d2219..c13141fc669 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -241,6 +241,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 2a663a176f8..b454566f3ba 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -249,6 +249,7 @@ "Foutieve toepassing/plaatsing van '%s'", "Deze versie van MySQL ondersteunt nog geen '%s'", "Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index f4019d63055..5655ad32d3d 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -119,7 +119,7 @@ "Unknown character set: '%-.64s'", "Too many tables. MySQL can only use %d tables in a join", "Too many columns", -"Too big row size. The maximum row size, not counting BLOBs, is %d. You have to change some fields to TEXTs or BLOBs", +"Too big row size. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some fields to TEXT or BLOBs", "Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed", "Cross dependency found in OUTER JOIN. Examine your ON conditions", "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL", @@ -238,6 +238,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index d3a38ede5bc..8e7a7121dfa 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -243,6 +243,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index ccff24c5759..8a6d786bee8 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -238,6 +238,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 52f3eb78c11..db20cc5dad9 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -247,6 +247,7 @@ "Falsche Verwendung oder Platzierung von '%s'", "Diese MySQL-Version unterstützt momentan nicht '%s'.", "Schwerer Fehler %d: '%-.128s vom Master beim Lesen des Binary Logs.", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Falsche Foreign-Key Definition für '%-64s': %s", "Schlüssel- und Tabellenreferenz passen nicht zueinander.", "Kardinalitäts-Fehler (mehr/oder weniger als %d Spalten).", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 1ce052bdf22..f844ee59dab 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -238,6 +238,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 6143ea2a1c4..7e60515aace 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -240,6 +240,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 8164757d823..a624d90661d 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -238,6 +238,7 @@ "Uso/posizione di '%s' sbagliato", "Questa versione di MySQL non supporta ancora '%s'", "Errore fatale %d: '%-.128s' dal master leggendo i dati dal log binario", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 747d3611cc9..b5693fe6e41 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -240,6 +240,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 93d86d32937..bc2d6eb9caa 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -238,6 +238,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index e9319246fc6..9bf0d4845c7 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -240,6 +240,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index edb5854db7e..4a025e7218f 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -240,6 +240,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 27b4d0d661f..1a6634fb96c 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -242,6 +242,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 60ace09ab33..99a940e62ab 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -238,6 +238,7 @@ "Uso/localização errada de '%s'", "Esta versão de MySQL não suporta ainda '%s'", "Obteve fatal error %d: '%-.128s' a partir do master quando lendo dados do binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Definição errada da chave estrangeira para '%-.64s': %s", "Referência da chave e referência da tabela não coincidem", "Error de cardinalidade (mais/menos que %d colunas)", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 8824d64876a..61591d54d40 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -242,6 +242,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index ddfc0a8f7de..b1876f83008 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -240,6 +240,7 @@ "îÅ×ÅÒÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÉÌÉ × ÎÅ×ÅÒÎÏÍ ÍÅÓÔÅ ÕËÁÚÁÎ '%s'", "üÔÁ ×ÅÒÓÉÑ MySQL ÐÏËÁ ÅÝÅ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ '%s'", "ðÏÌÕÞÅÎÁ ÎÅÉÓÐÒÁ×ÉÍÁÑ ÏÛÉÂËÁ %d: '%-.128s' ÏÔ ÇÏÌÏ×ÎÏÇÏ ÓÅÒ×ÅÒÁ × ÐÒÏÃÅÓÓÅ ×ÙÂÏÒËÉ ÄÁÎÎÙÈ ÉÚ Ä×ÏÉÞÎÏÇÏ ÖÕÒÎÁÌÁ", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "ïÛÉÂËÁ ÍÏÝØÎÏÓÔÉ ÍÎÏÖÅÓÔ×Á (ÂÏÌØÛÅ/ÍÅÎØÛÅ %d ËÏÌÏÎÏË)", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index ed1d8cadb80..a96d512cb72 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -246,6 +246,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 5f3a2f38109..df18b8bf7f0 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -212,14 +212,14 @@ "Bloqueos de actualización no pueden ser adqueridos durante una transición READ UNCOMMITTED", "DROP DATABASE no permitido mientras un thread está ejerciendo un bloqueo de lectura global", "CREATE DATABASE no permitido mientras un thread está ejerciendo un bloqueo de lectura global", -"Wrong arguments to %s", -"'%-.32s'@'%-.64s' is not allowed to create new users", -"Incorrect table definition; all MERGE tables must be in the same database", -"Deadlock found when trying to get lock; Try restarting transaction", -"The used table type doesn't support FULLTEXT indexes", -"Cannot add foreign key constraint", -"Cannot add a child row: a foreign key constraint fails", -"Cannot delete a parent row: a foreign key constraint fails", +"Argumentos errados para %s", +"'%-.32s`@`%-.64s` no es permitido para crear nuevos usuarios", +"Incorrecta definición de la tabla; Todas las tablas MERGE deben estar en el mismo banco de datos", +"Encontrado deadlock cuando tentando obtener el bloqueo; Tente recomenzar la transición", +"El tipo de tabla usada no soporta índices FULLTEXT", +"No puede adicionar clave extranjera constraint", +"No puede adicionar una línea hijo: falla de clave extranjera constraint", +"No puede deletar una línea padre: falla de clave extranjera constraint", "Error de coneccion a master: %-128s", "Error executando el query en master: %-128%", "Error de %s: %-128%", @@ -239,6 +239,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index d108618834e..07187f7ca34 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -237,7 +237,8 @@ "Variabeln '%-.64s' kan endast sättas, inte läsas", "Fel använding/placering av '%s'", "Denna version av MySQL kan ännu inte utföra '%s'", -"Fick fatalt fel %d: '%-.128s' från master vid läsning av binärloggen" +"Fick fatalt fel %d: '%-.128s' från master vid läsning av binärloggen", +"Slav SQL tråden ignorerade frågan pga en replicate-*-table regel", "Felaktig FOREIGN KEY-definition för '%-.64s': %s", "Nyckelreferensen och tabellreferensen stämmer inte överens", "Kardinalitetsfel (fler/färre än %d kolumner)", diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 96b9f40feac..a0604ba862c 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -243,6 +243,7 @@ "Wrong usage/placement of '%s'", "This version of MySQL doesn't yet support '%s'", "Got fatal error %d: '%-.128s' from master when reading data from binary log", +"Slave SQL thread ignored the query because of replicate-*-table rules", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (¦ÌØÛÅ/ÍÅÎØÛÅ Î¦Ö %d ÓÔÏ×Âæ×)", diff --git a/sql/slave.cc b/sql/slave.cc index 37979576b73..a6c86b08010 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -74,7 +74,6 @@ static int request_table_dump(MYSQL* mysql, const char* db, const char* table); static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, const char* table_name); static int check_master_version(MYSQL* mysql, MASTER_INFO* mi); -char* rewrite_db(char* db); /* @@ -139,6 +138,8 @@ int init_slave() { DBUG_ENTER("init_slave"); + /* This is called when mysqld starts */ + /* TODO: re-write this to interate through the list of files for multi-master @@ -150,11 +151,16 @@ int init_slave() If master_host is specified, create the master_info file if it doesn't exists. */ - if (!active_mi || - init_master_info(active_mi,master_info_file,relay_log_info_file, + if (!active_mi) + { + sql_print_error("Failed to allocate memory for the master info structure"); + goto err; + } + + if (init_master_info(active_mi,master_info_file,relay_log_info_file, !master_host)) { - sql_print_error("Note: Failed to initialized master info"); + sql_print_error("Failed to initialize the master info structure"); goto err; } @@ -174,7 +180,7 @@ int init_slave() relay_log_info_file, SLAVE_IO | SLAVE_SQL)) { - sql_print_error("Warning: Can't create threads to handle slave"); + sql_print_error("Failed to create slave threads"); goto err; } } @@ -300,6 +306,7 @@ err: pthread_cond_broadcast(&rli->data_cond); if (need_data_lock) pthread_mutex_unlock(&rli->data_lock); + pthread_mutex_unlock(log_lock); DBUG_RETURN ((*errmsg) ? 1 : 0); } @@ -380,7 +387,10 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, rli->group_master_log_pos= 0; if (!rli->inited) + { + DBUG_PRINT("info", ("rli->inited == 0")); DBUG_RETURN(0); + } DBUG_ASSERT(rli->slave_running == 0); DBUG_ASSERT(rli->mi->slave_running == 0); @@ -872,15 +882,38 @@ static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) } +/* + Writes an error message to rli->last_slave_error and rli->last_slave_errno + (which will be displayed by SHOW SLAVE STATUS), and prints it to stderr. + + SYNOPSIS + slave_print_error() + rli + err_code The error code + msg The error message (usually related to the error code, but can + contain more information). + ... (this is printf-like format, with % symbols in msg) + + RETURN VALUES + void +*/ + void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...) { va_list args; va_start(args,msg); my_vsnprintf(rli->last_slave_error, sizeof(rli->last_slave_error), msg, args); - sql_print_error("Slave: %s, error_code=%d", rli->last_slave_error, - err_code); rli->last_slave_errno = err_code; + /* If the error string ends with '.', do not add a ',' it would be ugly */ + if (rli->last_slave_error[0] && + (*(strend(rli->last_slave_error)-1) == '.')) + sql_print_error("Slave: %s Error_code: %d", rli->last_slave_error, + err_code); + else + sql_print_error("Slave: %s, Error_code: %d", rli->last_slave_error, + err_code); + } /* @@ -905,7 +938,7 @@ bool net_request_file(NET* net, const char* fname) } -char* rewrite_db(char* db) +const char *rewrite_db(const char* db) { if (replicate_rewrite_db.is_empty() || !db) return db; @@ -920,6 +953,17 @@ char* rewrite_db(char* db) return db; } +/* + From other comments and tests in code, it looks like + sometimes Query_log_event and Load_log_event can have db == 0 + (see rewrite_db() above for example) + (cases where this happens are unclear; it may be when the master is 3.23). +*/ + +const char *print_slave_db_safe(const char* db) +{ + return (db ? rewrite_db(db) : ""); +} /* Checks whether a db matches some do_db and ignore_db rules @@ -1027,13 +1071,19 @@ static int check_master_version(MYSQL* mysql, MASTER_INFO* mi) { const char* errmsg= 0; + /* + Note the following switch will bug when we have MySQL branch 30 ;) + */ switch (*mysql->server_version) { case '3': - mi->old_format = 1; + mi->old_format = + (strncmp(mysql->server_version, "3.23.57", 7) < 0) /* < .57 */ ? + BINLOG_FORMAT_323_LESS_57 : + BINLOG_FORMAT_323_GEQ_57 ; break; case '4': case '5': - mi->old_format = 0; + mi->old_format = BINLOG_FORMAT_CURRENT; break; default: errmsg = "Master reported unrecognized MySQL version"; @@ -1246,11 +1296,55 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) strmov(strcend(tmp,'.'),"-relay-bin"); opt_relay_logname=my_strdup(tmp,MYF(MY_WME)); } + + /* + The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE. It is + notable that the last kilobytes of it (8 kB for example) may live in + memory, not on disk (depending on what the thread using it does). While + this is efficient, it has a side-effect one must know: + The size of the relay log on disk (displayed by 'ls -l' on Unix) can be a + few kilobytes less than one would expect by doing SHOW SLAVE STATUS; this + happens when only the IO thread is started (not the SQL thread). The + "missing" kilobytes are in memory, are preserved during 'STOP SLAVE; START + SLAVE IO_THREAD', and are flushed to disk when the slave's mysqld stops. So + this does not cause any bug. Example of how disk size grows by leaps: + + Read_Master_Log_Pos: 7811 -rw-rw---- 1 guilhem qq 4 Jun 5 16:19 gbichot2-relay-bin.002 + ...later... + Read_Master_Log_Pos: 9744 -rw-rw---- 1 guilhem qq 8192 Jun 5 16:27 gbichot2-relay-bin.002 + + See how 4 is less than 7811 and 8192 is less than 9744. + + WARNING: this is risky because the slave can stay like this for a long + time; then if it has a power failure, master.info says the I/O thread has + read until 9744 while the relay-log contains only until 8192 (the + in-memory part from 8192 to 9744 has been lost), so the SQL slave thread + will miss some events, silently breaking replication. + Ideally we would like to flush master.info only when we know that the relay + log has no in-memory tail. + Note that the above problem may arise only when only the IO thread is + started, which is unlikely. + */ + + /* + For the maximum log size, we choose max_relay_log_size if it is + non-zero, max_binlog_size otherwise. If later the user does SET + GLOBAL on one of these variables, fix_max_binlog_size and + fix_max_relay_log_size will reconsider the choice (for example + if the user changes max_relay_log_size to zero, we have to + switch to using max_binlog_size for the relay log) and update + rli->relay_log.max_size (and mysql_bin_log.max_size). + */ + if (open_log(&rli->relay_log, glob_hostname, opt_relay_logname, "-relay-bin", opt_relaylog_index_name, LOG_BIN, 1 /* read_append cache */, - 1 /* no auto events */)) + 1 /* no auto events */, + max_relay_log_size ? max_relay_log_size : max_binlog_size)) + { + sql_print_error("Failed in open_log() called from init_relay_log_info()"); DBUG_RETURN(1); + } /* if file does not exist */ if (access(fname,F_OK)) @@ -1261,10 +1355,18 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) */ if (info_fd >= 0) my_close(info_fd, MYF(MY_WME)); - if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || - init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, - MYF(MY_WME))) + if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0) { + sql_print_error("Failed to create a new relay log info file (\ +file '%s', errno %d)", fname, my_errno); + msg= current_thd->net.last_error; + goto err; + } + if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, + MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on relay log info file '%s'", + fname); msg= current_thd->net.last_error; goto err; } @@ -1272,7 +1374,10 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) /* Init relay log with first entry in the relay index file */ if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, &msg)) + { + sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4"); goto err; + } rli->group_master_log_name[0]= 0; rli->group_master_log_pos= 0; rli->info_fd= info_fd; @@ -1281,18 +1386,34 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) { if (info_fd >= 0) reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); - else if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || - init_io_cache(&rli->info_file, info_fd, - IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) + else { - if (info_fd >= 0) - my_close(info_fd, MYF(0)); - rli->info_fd= -1; - rli->relay_log.close(1); - pthread_mutex_unlock(&rli->data_lock); - DBUG_RETURN(1); + int error=0; + if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0) + { + sql_print_error("\ +Failed to open the existing relay log info file '%s' (errno %d)", + fname, my_errno); + error= 1; + } + else if (init_io_cache(&rli->info_file, info_fd, + IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on relay log info file '%s'", + fname); + error= 1; + } + if (error) + { + if (info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd= -1; + rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); + } } - + rli->info_fd = info_fd; int relay_log_pos, master_log_pos; if (init_strvar_from_file(rli->group_relay_log_name, @@ -1318,7 +1439,12 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) rli->group_relay_log_pos, 0 /* no data lock*/, &msg)) + { + char llbuf[22]; + sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)", + rli->relay_log_name, llstr(rli->relay_log_pos, llbuf)); goto err; + } } DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); @@ -1327,7 +1453,8 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) before flush_relay_log_info */ reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); - error= flush_relay_log_info(rli); + if ((error= flush_relay_log_info(rli))) + sql_print_error("Failed to flush relay log info file"); if (count_relay_log_space(rli)) { msg="Error counting relay log space"; @@ -1343,7 +1470,7 @@ err: if (info_fd >= 0) my_close(info_fd, MYF(0)); rli->info_fd= -1; - rli->relay_log.close(1); + rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); pthread_mutex_unlock(&rli->data_lock); DBUG_RETURN(1); } @@ -1372,7 +1499,7 @@ static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) { bool slave_killed=0; MASTER_INFO* mi = rli->mi; - const char* save_proc_info; + const char *save_proc_info; THD* thd = mi->io_thd; DBUG_ENTER("wait_for_relay_log_space"); @@ -1380,12 +1507,14 @@ static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) pthread_mutex_lock(&rli->log_space_lock); save_proc_info = thd->proc_info; thd->proc_info = "Waiting for relay log space to free"; - + save_proc_info= thd->enter_cond(&rli->log_space_cond, + &rli->log_space_lock, + "Waiting for relay log space to free"); while (rli->log_space_limit < rli->log_space_total && !(slave_killed=io_slave_killed(thd,mi)) && !rli->ignore_log_space_limit) pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock); - thd->proc_info = save_proc_info; + thd->exit_cond(save_proc_info); pthread_mutex_unlock(&rli->log_space_lock); DBUG_RETURN(slave_killed); } @@ -1406,9 +1535,36 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) if (add_relay_log(rli,&linfo)) DBUG_RETURN(1); } while (!rli->relay_log.find_next_log(&linfo, 1)); + /* + As we have counted everything, including what may have written in a + preceding write, we must reset bytes_written, or we may count some space + twice. + */ + rli->relay_log.reset_bytes_written(); DBUG_RETURN(0); } +void init_master_info_with_options(MASTER_INFO* mi) +{ + mi->master_log_name[0] = 0; + mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number + + if (master_host) + strmake(mi->host, master_host, sizeof(mi->host) - 1); + if (master_user) + strmake(mi->user, master_user, sizeof(mi->user) - 1); + if (master_password) + strmake(mi->password, master_password, HASH_PASSWORD_LENGTH); + mi->port = master_port; + mi->connect_retry = master_connect_retry; +} + +void clear_last_slave_error(RELAY_LOG_INFO* rli) +{ + //Clear the errors displayed by SHOW SLAVE STATUS + rli->last_slave_error[0]=0; + rli->last_slave_errno=0; +} int init_master_info(MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname, @@ -1431,6 +1587,8 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname, pthread_mutex_lock(&mi->data_lock); fd = mi->fd; + + /* does master.info exist ? */ if (access(fname,F_OK)) { @@ -1445,32 +1603,44 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname, */ if (fd >= 0) my_close(fd, MYF(MY_WME)); - if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || - init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, + if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) + { + sql_print_error("Failed to create a new master info file (\ +file '%s', errno %d)", fname, my_errno); + goto err; + } + if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on master info file (\ +file '%s')", fname); goto err; + } - mi->master_log_name[0] = 0; - mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number mi->fd = fd; - - if (master_host) - strmake(mi->host, master_host, sizeof(mi->host) - 1); - if (master_user) - strmake(mi->user, master_user, sizeof(mi->user) - 1); - if (master_password) - strmake(mi->password, master_password, HASH_PASSWORD_LENGTH); - mi->port = master_port; - mi->connect_retry = master_connect_retry; + init_master_info_with_options(mi); + } else // file exists { if (fd >= 0) reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0); - else if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || - init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L, - 0, MYF(MY_WME))) - goto err; + else + { + if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) + { + sql_print_error("Failed to open the existing master info file (\ +file '%s', errno %d)", fname, my_errno); + goto err; + } + if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L, + 0, MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on master info file (\ +file '%s')", fname); + goto err; + } + } mi->fd = fd; int port, connect_retry, master_log_pos; @@ -1511,7 +1681,8 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname, mi->inited = 1; // now change cache READ -> WRITE - must do this before flush_master_info reinit_io_cache(&mi->file, WRITE_CACHE,0L,0,1); - error=test(flush_master_info(mi)); + if ((error=test(flush_master_info(mi)))) + sql_print_error("Failed to flush master info file"); pthread_mutex_unlock(&mi->data_lock); DBUG_RETURN(error); @@ -1653,6 +1824,7 @@ int show_master_info(THD* thd, MASTER_INFO* mi) if (mi->host[0]) { + DBUG_PRINT("info",("host is set: '%s'", mi->host)); String *packet= &thd->packet; protocol->prepare_for_resend(); @@ -1732,8 +1904,8 @@ st_relay_log_info::st_relay_log_info() group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; last_slave_error[0]=0; - bzero(&info_file,sizeof(info_file)); - bzero(&cache_buf, sizeof(cache_buf)); + bzero((char*) &info_file, sizeof(info_file)); + bzero((char*) &cache_buf, sizeof(cache_buf)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); @@ -1741,6 +1913,7 @@ st_relay_log_info::st_relay_log_info() pthread_cond_init(&start_cond, NULL); pthread_cond_init(&stop_cond, NULL); pthread_cond_init(&log_space_cond, NULL); + relay_log.init_pthread_objects(); } @@ -1799,11 +1972,17 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, pthread_mutex_lock(&data_lock); /* - This function will abort when it notices that - some CHANGE MASTER or RESET MASTER has changed - the master info. To catch this, these commands - modify abort_pos_wait ; we just monitor abort_pos_wait - and see if it has changed. + This function will abort when it notices that some CHANGE MASTER or + RESET MASTER has changed the master info. + To catch this, these commands modify abort_pos_wait ; We just monitor + abort_pos_wait and see if it has changed. + Why do we have this mechanism instead of simply monitoring slave_running + in the loop (we do this too), as CHANGE MASTER/RESET SLAVE require that + the SQL thread be stopped? + This is becasue if someones does: + STOP SLAVE;CHANGE MASTER/RESET SLAVE; START SLAVE; + the change may happen very quickly and we may not notice that + slave_running briefly switches between 1/0/1. */ init_abort_pos_wait= abort_pos_wait; @@ -1824,7 +2003,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, error= -2; //means improper arguments goto err; } - //p points to '.' + /* p points to '.' */ log_name_extension= strtoul(++p, &p_end, 10); /* p_end points to the first invalid character. @@ -1837,10 +2016,10 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, goto err; } - //"compare and wait" main loop + /* The "compare and wait" main loop */ while (!thd->killed && init_abort_pos_wait == abort_pos_wait && - mi->slave_running) + slave_running) { bool pos_reached; int cmp_result= 0; @@ -1878,6 +2057,10 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, DBUG_PRINT("info",("Waiting for master update")); const char* msg = thd->enter_cond(&data_cond, &data_lock, "Waiting for master update"); + /* + We are going to pthread_cond_(timed)wait(); if the SQL thread stops it + will wake us up. + */ if (timeout > 0) { /* @@ -1895,6 +2078,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, } else pthread_cond_wait(&data_cond, &data_lock); + DBUG_PRINT("info",("Got signal of master update")); thd->exit_cond(msg); if (error == ETIMEDOUT || error == ETIME) { @@ -1903,6 +2087,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, } error=0; event_count++; + DBUG_PRINT("info",("Testing if killed or SQL thread not running")); } err: @@ -1911,11 +2096,11 @@ err: improper_arguments: %d timed_out: %d", (int) thd->killed, (int) (init_abort_pos_wait != abort_pos_wait), - (int) mi->slave_running, + (int) slave_running, (int) (error == -2), (int) (error == -1))); if (thd->killed || init_abort_pos_wait != abort_pos_wait || - !mi->slave_running) + !slave_running) { error= -2; } @@ -2137,14 +2322,13 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) case ER_NET_ERROR_ON_WRITE: case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: - my_snprintf(rli->last_slave_error, sizeof(rli->last_slave_error), - "Slave: query '%s' partially completed on the master \ + slave_print_error(rli,expected_error, + "query '%s' partially completed on the master \ and was aborted. There is a chance that your master is inconsistent at this \ point. If you are sure that your master is ok, run this query manually on the\ slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;\ - SLAVE START;", thd->query); - rli->last_slave_errno = expected_error; - sql_print_error("%s",rli->last_slave_error); + SLAVE START; .", thd->query); + thd->query_error= 1; return 1; default: return 0; @@ -2158,8 +2342,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) DBUG_ASSERT(rli->sql_thd==thd); if (sql_slave_killed(thd,rli)) { - /* do not forget to free ev ! */ - if (ev) delete ev; + delete ev; return 1; } if (ev) @@ -2453,6 +2636,17 @@ reconnect done to recover from failed read"); for no reason, but this function will do a clean read, notice the clean value and exit immediately. */ +#ifndef DBUG_OFF + { + char llbuf1[22], llbuf2[22]; + DBUG_PRINT("info", ("log_space_limit=%s log_space_total=%s \ +ignore_log_space_limit=%d", + llstr(mi->rli.log_space_limit,llbuf1), + llstr(mi->rli.log_space_total,llbuf2), + (int) mi->rli.ignore_log_space_limit)); + } +#endif + if (mi->rli.log_space_limit && mi->rli.log_space_limit < mi->rli.log_space_total && !mi->rli.ignore_log_space_limit) @@ -2565,6 +2759,13 @@ slave_begin: pthread_mutex_unlock(&rli->run_lock); pthread_cond_broadcast(&rli->start_cond); + /* + Reset errors for a clean start (otherwise, if the master is idle, the SQL + thread may execute no Query_log_event, so the error will remain even + though there's no problem anymore). + */ + clear_last_slave_error(rli); + //tell the I/O thread to take relay_log_space_limit into account from now on pthread_mutex_lock(&rli->log_space_lock); rli->ignore_log_space_limit= 0; @@ -2624,8 +2825,16 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&rli->run_lock); + /* We need data_lock, at least to wake up any waiting master_pos_wait() */ + pthread_mutex_lock(&rli->data_lock); DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun - rli->slave_running = 0; + /* When master_pos_wait() wakes up it will check this and terminate */ + rli->slave_running= 0; + /* Wake up master_pos_wait() */ + pthread_mutex_unlock(&rli->data_lock); + DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions")); + pthread_cond_broadcast(&rli->data_cond); + rli->ignore_log_space_limit= 0; /* don't need any lock */ rli->save_temporary_tables = thd->temporary_tables; /* @@ -3010,7 +3219,7 @@ void end_relay_log_info(RELAY_LOG_INFO* rli) rli->cur_log_fd = -1; } rli->inited = 0; - rli->relay_log.close(1); + rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); DBUG_VOID_RETURN; } @@ -3273,8 +3482,22 @@ Log_event* next_event(RELAY_LOG_INFO* rli) hot_log=0; // Using old binary log } } - DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos); +#ifndef DBUG_OFF + { + char llbuf1[22], llbuf2[22]; + DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE); + /* + The next assertion sometimes (very rarely) fails, let's try to track + it + */ + DBUG_PRINT("info", ("\ +Before assert, my_b_tell(cur_log)=%s rli->relay_log_pos=%s rli->pending=%lu", + llstr(my_b_tell(cur_log),llbuf1), + llstr(rli->relay_log_pos,llbuf2), + rli->pending)); + DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos); + } +#endif /* Relay log is always in new format - if the master is 3.23, the I/O thread will convert the format for us @@ -3334,8 +3557,8 @@ Log_event* next_event(RELAY_LOG_INFO* rli) log), and also when the SQL thread starts. We should also reset ignore_log_space_limit to 0 when the user does RESET SLAVE, but in fact, no need as RESET SLAVE requires that the slave - be stopped, and when the SQL thread is later restarted - ignore_log_space_limit will be reset to 0. + be stopped, and the SQL thread sets ignore_log_space_limit to 0 when + it stops. */ pthread_mutex_lock(&rli->log_space_lock); // prevent the I/O thread from blocking next times @@ -3466,6 +3689,53 @@ err: DBUG_RETURN(0); } +/* + Rotate a relay log (this is used only by FLUSH LOGS; the automatic rotation + because of size is simpler because when we do it we already have all relevant + locks; here we don't, so this function is mainly taking locks). + Returns nothing as we cannot catch any error (MYSQL_LOG::new_file() is void). +*/ + +void rotate_relay_log(MASTER_INFO* mi) +{ + DBUG_ENTER("rotate_relay_log"); + RELAY_LOG_INFO* rli= &mi->rli; + + lock_slave_threads(mi); + pthread_mutex_lock(&rli->data_lock); + /* + We need to test inited because otherwise, new_file() will attempt to lock + LOCK_log, which may not be inited (if we're not a slave). + */ + if (!rli->inited) + { + DBUG_PRINT("info", ("rli->inited == 0")); + goto end; + } + + /* If the relay log is closed, new_file() will do nothing. */ + rli->relay_log.new_file(1); + + /* + We harvest now, because otherwise BIN_LOG_HEADER_SIZE will not immediately + be counted, so imagine a succession of FLUSH LOGS and assume the slave + threads are started: + relay_log_space decreases by the size of the deleted relay log, but does + not increase, so flush-after-flush we may become negative, which is wrong. + Even if this will be corrected as soon as a query is replicated on the + slave (because the I/O thread will then call harvest_bytes_written() which + will harvest all these BIN_LOG_HEADER_SIZE we forgot), it may give strange + output in SHOW SLAVE STATUS meanwhile. So we harvest now. + If the log is closed, then this will just harvest the last writes, probably + 0 as they probably have been harvested. + */ + rli->relay_log.harvest_bytes_written(&rli->log_space_total); +end: + pthread_mutex_unlock(&rli->data_lock); + unlock_slave_threads(mi); + DBUG_VOID_RETURN; +} + #ifdef __GNUC__ template class I_List_iterator<i_string>; diff --git a/sql/slave.h b/sql/slave.h index 668fff52d08..0cd291a50f8 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -47,6 +47,11 @@ extern "C" { extern ulong slave_net_timeout; }; +enum enum_binlog_formats { + BINLOG_FORMAT_CURRENT=0, /* 0 is important for easy 'if (mi->old_format)' */ + BINLOG_FORMAT_323_LESS_57, + BINLOG_FORMAT_323_GEQ_57 }; + /* TODO: this needs to be redone, but for now it does not matter since we do not have multi-master yet. @@ -284,20 +289,20 @@ Log_event* next_event(RELAY_LOG_INFO* rli); typedef struct st_master_info { char master_log_name[FN_REFLEN]; + char host[HOSTNAME_LENGTH+1]; + char user[USERNAME_LENGTH+1]; + char password[MAX_PASSWORD_LENGTH+1]; my_off_t master_log_pos; File fd; // we keep the file open, so we need to remember the file pointer IO_CACHE file; /* the variables below are needed because we can change masters on the fly */ - char host[HOSTNAME_LENGTH+1]; - char user[USERNAME_LENGTH+1]; - char password[HASH_PASSWORD_LENGTH+1]; pthread_mutex_t data_lock,run_lock; pthread_cond_t data_cond,start_cond,stop_cond; THD *io_thd; MYSQL* mysql; - uint32 file_id; /* for 3.23 load data infile */ + uint32 file_id; /* for 3.23 load data infile */ RELAY_LOG_INFO rli; uint port; uint connect_retry; @@ -305,16 +310,16 @@ typedef struct st_master_info int events_till_abort; #endif bool inited; - bool old_format; /* master binlog is in 3.23 format */ + enum enum_binlog_formats old_format; volatile bool abort_slave, slave_running; volatile ulong slave_run_id; st_master_info() - :fd(-1), io_thd(0), inited(0), old_format(0),abort_slave(0), - slave_running(0), slave_run_id(0) + :fd(-1), io_thd(0), inited(0), old_format(BINLOG_FORMAT_CURRENT), + abort_slave(0),slave_running(0), slave_run_id(0) { host[0] = 0; user[0] = 0; password[0] = 0; - bzero(&file, sizeof(file)); + bzero((char*) &file, sizeof(file)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); pthread_cond_init(&data_cond, NULL); @@ -418,12 +423,15 @@ int add_table_rule(HASH* h, const char* table_spec); int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec); void init_table_rule_hash(HASH* h, bool* h_inited); void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited); -char* rewrite_db(char* db); +const char *rewrite_db(const char* db); +const char *print_slave_db_safe(const char* db); int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int error_code); void skip_load_data_infile(NET* net); -void slave_print_error(RELAY_LOG_INFO* rli,int err_code, const char* msg, ...); +void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...); void end_slave(); /* clean up */ +void init_master_info_with_options(MASTER_INFO* mi); +void clear_last_slave_error(RELAY_LOG_INFO* rli); int init_master_info(MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file); @@ -438,6 +446,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos, int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, const char** errmsg); +void rotate_relay_log(MASTER_INFO* mi); extern "C" pthread_handler_decl(handle_slave_io,arg); extern "C" pthread_handler_decl(handle_slave_sql,arg); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 54a0ee05db2..08212bbf65b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (C) 2000-2003 MySQL 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 @@ -155,7 +155,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) host.db= get_field(&mem, table->field[1]); host.access= get_access(table,2); host.access= fix_rights_for_db(host.access); - host.sort= get_sort(2,host.host.hostname,host.db); + host.sort= get_sort(2,host.host.hostname,host.db); #ifndef TO_BE_REMOVED if (table->fields == 8) { // Without grant @@ -175,8 +175,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) if (table->field[2]->field_length == 8 && protocol_version == PROTOCOL_VERSION) { - sql_print_error( - "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */ + sql_print_error("Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */ protocol_version=9; /* purecov: tested */ } @@ -207,10 +206,11 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) { user.pversion=get_password_version(user.password); /* Only passwords of specific lengths depending on version are allowed */ - if ( (!user.pversion && length % 8) || (user.pversion && length!=45 )) + if ((!user.pversion && (length % 8 || length > 16)) || + (user.pversion && length != 45)) { sql_print_error( - "Found invalid password for user: '%s@%s'; Ignoring user", + "Found invalid password for user: '%s'@'%s'; Ignoring user", user.user ? user.user : "", user.host.hostname ? user.host.hostname : ""); /* purecov: tested */ continue; /* purecov: tested */ @@ -221,7 +221,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) user.sort=get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); - if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ + if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ { char *ssl_type=get_field(&mem, table->field[24]); if (!ssl_type) @@ -250,7 +250,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) else { user.ssl_type=SSL_TYPE_NONE; - bzero(&(user.user_resource),sizeof(user.user_resource)); + bzero((char *)&(user.user_resource),sizeof(user.user_resource)); #ifndef TO_BE_REMOVED if (table->fields <= 13) { // Without grant @@ -342,7 +342,14 @@ void acl_free(bool end) } } - /* Reload acl list if possible */ + +/* + Forget current privileges and read new privileges from the privilege tables + + SYNOPSIS + acl_reload() + thd Thread handle +*/ void acl_reload(THD *thd) { @@ -507,7 +514,7 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ACL_USER *acl_user= NULL; DBUG_ENTER("acl_getroot"); - bzero(mqh,sizeof(USER_RESOURCES)); + bzero((char*) mqh, sizeof(USER_RESOURCES)); if (!initialized) { // If no data allow anything @@ -616,7 +623,9 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, We need to check for absence of SSL because without SSL we should reject connection. */ - if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_peer_certificate(vio->ssl_)) + if (vio_type(vio) == VIO_TYPE_SSL && + SSL_get_verify_result(vio->ssl_) == X509_V_OK && + SSL_get_peer_certificate(vio->ssl_)) user_access=acl_user->access; break; case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ @@ -626,7 +635,8 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, If cipher name is specified, we compare it to actual cipher in use. */ - if (vio_type(vio) != VIO_TYPE_SSL) + if (vio_type(vio) != VIO_TYPE_SSL || + SSL_get_verify_result(vio->ssl_) != X509_V_OK) break; if (acl_user->ssl_cipher) { @@ -687,9 +697,9 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, } break; #else /* HAVE_OPENSSL */ - default: + default: /* - If we don't have SSL but SSL is required for this user the + If we don't have SSL but SSL is required for this user the authentication should fail. */ break; @@ -787,7 +797,7 @@ static void acl_insert_user(const char *user, const char *host, ulong privileges) { ACL_USER acl_user; - acl_user.user=strdup_root(&mem,user); + acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host,strdup_root(&mem,host)); acl_user.password=0; acl_user.access=privileges; @@ -878,9 +888,10 @@ static void acl_insert_db(const char *user, const char *host, const char *db, } -/***************************************************************************** -** Get privilege for a host, user and db combination -*****************************************************************************/ + +/* + Get privilege for a host, user and db combination +*/ ulong acl_get(const char *host, const char *ip, const char *bin_ip, const char *user, const char *db, my_bool db_is_pattern) @@ -1003,11 +1014,14 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr) DBUG_RETURN (*str != '\0'); } -/***************************************************************************** -** check if there are any possible matching entries for this host -** All host names without wild cards are stored in a hash table, -** entries with wildcards are stored in a dynamic array -*****************************************************************************/ + +/* + Check if there are any possible matching entries for this host + + NOTES + All host names without wild cards are stored in a hash table, + entries with wildcards are stored in a dynamic array +*/ static void init_check_host(void) { @@ -1081,10 +1095,6 @@ bool acl_check_host(const char *host, const char *ip) return 1; // Host is not allowed } -/***************************************************************************** - Change password for the user if it's not an anonymous user - Note: This should write the error directly to the client! -*****************************************************************************/ /* Check if the user is allowed to change password @@ -1096,8 +1106,8 @@ bool acl_check_host(const char *host, const char *ip) user user name RETURN VALUE - 0 OK - 1 ERROR ; In this case the error is sent to the client. + 0 OK + 1 ERROR ; In this case the error is sent to the client. */ bool check_change_password(THD *thd, const char *host, const char *user) @@ -1212,7 +1222,11 @@ find_acl_user(const char *host, const char *user) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", - user,acl_user->user,(host),(acl_user->host))); + user, + acl_user->user ? acl_user->user : "", + host, + acl_user->host.hostname ? acl_user->host.hostname : + "")); if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { @@ -1225,15 +1239,18 @@ find_acl_user(const char *host, const char *user) DBUG_RETURN(0); } -/***************************************************************************** - Handle comparing of hostname - A hostname may be of type: - hostname (May include wildcards); monty.pp.sci.fi - ip (May include wildcards); 192.168.0.0 - ip/netmask 192.168.0.0/255.255.255.0 - A net mask of 0.0.0.0 is not allowed. -*****************************************************************************/ +/* + Comparing of hostnames + + NOTES + A hostname may be of type: + hostname (May include wildcards); monty.pp.sci.fi + ip (May include wildcards); 192.168.0.0 + ip/netmask 192.168.0.0/255.255.255.0 + + A net mask of 0.0.0.0 is not allowed. +*/ static const char *calc_ip(const char *ip, long *val, char end) { @@ -1281,9 +1298,9 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, } -/**************************************************************************** - Code to update grants in the user and database privilege tables -****************************************************************************/ +/* + Update grants in the user and database privilege tables +*/ static bool update_user_table(THD *thd, const char *host, const char *user, const char *new_password) @@ -1297,6 +1314,24 @@ static bool update_user_table(THD *thd, const char *host, const char *user, bzero((char*) &tables,sizeof(tables)); tables.alias=tables.real_name=(char*) "user"; tables.db=(char*) "mysql"; +#ifdef HAVE_REPLICATION + /* + GRANT and REVOKE are applied the slave in/exclusion rules as they are + some kind of updates to the mysql.% tables. + */ + if (thd->slave_thread && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. It's ok to leave 'updating' set after tables_ok. + */ + tables.updating= 1; + /* Thanks to bzero, tables.next==0 */ + if (!tables_ok(0, &tables)) + DBUG_RETURN(0); + } +#endif + if (!(table=open_ltable(thd,&tables,TL_WRITE))) DBUG_RETURN(1); /* purecov: deadcode */ table->field[0]->store(host,(uint) strlen(host), &my_charset_latin1); @@ -1349,7 +1384,7 @@ static bool test_if_create_new_users(THD *thd) /**************************************************************************** -** Handle GRANT commands + Handle GRANT commands ****************************************************************************/ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, @@ -1372,8 +1407,8 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, && combo.password.length != HASH_OLD_PASSWORD_LENGTH) { my_printf_error(ER_PASSWORD_NO_MATCH, - "Password hash should be a %d-digit hexadecimal number", - MYF(0),HASH_PASSWORD_LENGTH); + "Password hash should be a %d-digit hexadecimal number", + MYF(0),HASH_PASSWORD_LENGTH); DBUG_RETURN(-1); } password=combo.password.str; @@ -1546,7 +1581,7 @@ static int replace_db_table(TABLE *table, const char *db, char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_db_table"); - // is there such a user in user table in memory ???? + /* Check if there is such a user in user table in memory? */ if (!initialized || !find_acl_user(combo.host.str,combo.user.str)) { my_error(ER_PASSWORD_NO_MATCH,MYF(0)); @@ -1558,7 +1593,7 @@ static int replace_db_table(TABLE *table, const char *db, table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->file->index_init(0); if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,0, - HA_READ_KEY_EXACT)) + HA_READ_KEY_EXACT)) { if (what == 'N') { // no row, no revoke @@ -1589,7 +1624,7 @@ static int replace_db_table(TABLE *table, const char *db, if (old_row_exists) { - // update old existing row + /* update old existing row */ if (rights) { if ((error=table->file->update_row(table->record[1],table->record[0]))) @@ -1616,11 +1651,11 @@ static int replace_db_table(TABLE *table, const char *db, DBUG_RETURN(0); /* This could only happen if the grant tables got corrupted */ - table_error: +table_error: table->file->print_error(error,MYF(0)); /* purecov: deadcode */ table->file->index_end(); - abort: +abort: DBUG_RETURN(-1); } @@ -1736,7 +1771,7 @@ public: if (!(mem_check = new GRANT_COLUMN(*res, fix_rights_for_column(priv)))) { - // Don't use this entry + /* Don't use this entry */ privs = cols = 0; /* purecov: deadcode */ return; /* purecov: deadcode */ } @@ -1918,7 +1953,7 @@ static int replace_column_table(GRANT_TABLE *g_t, key_length, HA_READ_KEY_EXACT)) goto end; - // Scan trough all rows with the same host,db,user and table + /* Scan through all rows with the same host,db,user and table */ do { ulong privileges = (ulong) table->field[6]->val_int(); @@ -1968,7 +2003,7 @@ static int replace_column_table(GRANT_TABLE *g_t, !key_cmp(table,key,0,key_length)); } - end: +end: table->file->index_end(); DBUG_RETURN(result); } @@ -2037,7 +2072,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, if (revoke_grant) { - // column rights are already fixed in mysql_table_grant ! + /* column rights are already fixed in mysql_table_grant */ store_table_rights=j & ~store_table_rights; } else @@ -2073,7 +2108,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, if (rights | col_rights) { grant_table->privs= rights; - grant_table->cols= col_rights; + grant_table->cols= col_rights; } else { @@ -2081,19 +2116,36 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, } DBUG_RETURN(0); - /* This should never happen */ - table_error: + /* This should never happen */ +table_error: table->file->print_error(error,MYF(0)); /* purecov: deadcode */ DBUG_RETURN(-1); /* purecov: deadcode */ } +/* + Store table level and column level grants in the privilege tables + + SYNOPSIS + mysql_table_grant() + thd Thread handle + table_list List of tables to give grant + user_list List of users to give grant + columns List of columns to give grant + rights Table level grant + revoke_grant Set to 1 if this is a REVOKE command + + RETURN + 0 ok + 1 error +*/ + int mysql_table_grant(THD *thd, TABLE_LIST *table_list, List <LEX_USER> &user_list, List <LEX_COLUMN> &columns, ulong rights, bool revoke_grant) { - ulong column_priv = 0; + ulong column_priv= 0; List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str; TABLE_LIST tables[3]; @@ -2103,7 +2155,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (!initialized) { send_error(thd, ER_UNKNOWN_COM_ERROR); /* purecov: inspected */ - return 1; /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ } if (rights & ~TABLE_ACLS) { @@ -2114,21 +2166,21 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (columns.elements && !revoke_grant) { TABLE *table; - class LEX_COLUMN *check; - List_iterator <LEX_COLUMN> iter(columns); + class LEX_COLUMN *column; + List_iterator <LEX_COLUMN> column_iter(columns); if (!(table=open_ltable(thd,table_list,TL_READ))) DBUG_RETURN(-1); - while ((check = iter++)) + while ((column = column_iter++)) { - if (!find_field_in_table(thd,table,check->column.ptr(), - check->column.length(),0,0)) + if (!find_field_in_table(thd,table,column->column.ptr(), + column->column.length(),0,0)) { my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), - check->column.c_ptr(), table_list->alias); + column->column.c_ptr(), table_list->alias); DBUG_RETURN(-1); } - column_priv |= check->rights | (rights & COL_ACLS); + column_priv|= column->rights; } close_thread_tables(thd); } @@ -2164,8 +2216,16 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on && !tables_ok(0, tables)) - DBUG_RETURN(0); + if (thd->slave_thread && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. + */ + tables[0].updating= tables[1].updating= tables[2].updating= 1; + if (!tables_ok(0, tables)) + DBUG_RETURN(0); + } #endif if (open_and_lock_tables(thd,tables)) @@ -2233,21 +2293,21 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, /* If revoke_grant, calculate the new column privilege for tables_priv */ if (revoke_grant) { - class LEX_COLUMN *check; - List_iterator <LEX_COLUMN> iter(columns); + class LEX_COLUMN *column; + List_iterator <LEX_COLUMN> column_iter(columns); GRANT_COLUMN *grant_column; /* Fix old grants */ - while ((check = iter++)) + while ((column = column_iter++)) { grant_column = column_hash_search(grant_table, - check->column.ptr(), - check->column.length()); + column->column.ptr(), + column->column.length()); if (grant_column) - grant_column->rights&= ~(check->rights | rights); + grant_column->rights&= ~(column->rights | rights); } /* scan trough all columns to get new column grant */ - column_priv=0; + column_priv= 0; for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++) { grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns, @@ -2330,8 +2390,16 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on && !tables_ok(0, tables)) - DBUG_RETURN(0); + if (thd->slave_thread && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. + */ + tables[0].updating= tables[1].updating= 1; + if (!tables_ok(0, tables)) + DBUG_RETURN(0); + } #endif if (open_and_lock_tables(thd,tables)) @@ -2343,7 +2411,7 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, if (!revoke_grant) create_new_users= test_if_create_new_users(thd); - // go through users in user_list + /* go through users in user_list */ rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); grant_version++; @@ -2359,16 +2427,25 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, continue; } if ((replace_user_table(thd, - tables[0].table, + tables[0].table, *Str, (!db ? rights : 0), revoke_grant, create_new_users))) result= -1; - else + else if (db) { - if (db && replace_db_table(tables[1].table, db, *Str, rights & DB_ACLS, - revoke_grant)) + ulong db_rights= rights & DB_ACLS; + if (db_rights == rights) + { + if (replace_db_table(tables[1].table, db, *Str, db_rights, + revoke_grant)) + result= -1; + } + else + { + net_printf(&thd->net,ER_WRONG_USAGE,"DB GRANT","GLOBAL PRIVILEGES"); result= -1; + } } } VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -2481,7 +2558,16 @@ end: } -/* Reload grant array (table and column privileges) if possible */ +/* + Reload grant array (table and column privileges) if possible + + SYNOPSIS + grant_reload() + thd Thread handler + + NOTES + Locked tables are checked by acl_init and doesn't have to be checked here +*/ void grant_reload(THD *thd) { @@ -2490,8 +2576,6 @@ void grant_reload(THD *thd) MEM_ROOT old_mem; DBUG_ENTER("grant_reload"); - // Locked tables are checked by acl_init and doesn't have to be checked here - rw_wrlock(&LOCK_grant); grant_version++; old_column_priv_hash= column_priv_hash; @@ -2568,29 +2652,29 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, rw_unlock(&LOCK_grant); return 0; - err: +err: rw_unlock(&LOCK_grant); if (!no_errors) // Not a silent skip of table { const char *command=""; if (want_access & SELECT_ACL) - command ="select"; + command= "select"; else if (want_access & INSERT_ACL) - command = "insert"; + command= "insert"; else if (want_access & UPDATE_ACL) - command = "update"; + command= "update"; else if (want_access & DELETE_ACL) - command = "delete"; + command= "delete"; else if (want_access & DROP_ACL) - command = "drop"; + command= "drop"; else if (want_access & CREATE_ACL) - command = "create"; + command= "create"; else if (want_access & ALTER_ACL) - command = "alter"; + command= "alter"; else if (want_access & INDEX_ACL) - command = "index"; + command= "index"; else if (want_access & GRANT_ACL) - command = "grant"; + command= "grant"; net_printf(thd,ER_TABLEACCESS_DENIED_ERROR, command, thd->priv_user, @@ -2613,7 +2697,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name, rw_rdlock(&LOCK_grant); - // reload table if someone has modified any grants + /* reload table if someone has modified any grants */ if (table->grant.version != grant_version) { @@ -2641,7 +2725,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name, #endif /* We must use my_printf_error() here! */ - err: +err: rw_unlock(&LOCK_grant); if (!show_tables) { @@ -2677,7 +2761,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) rw_rdlock(&LOCK_grant); - // reload table if someone has modified any grants + /* reload table if someone has modified any grants */ if (table->grant.version != grant_version) { @@ -2687,7 +2771,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) table->real_name,0); /* purecov: inspected */ table->grant.version=grant_version; /* purecov: inspected */ } - // The following should always be true + /* The following should always be true */ if (!(grant_table=table->grant.grant_table)) goto err; /* purecov: inspected */ @@ -2705,11 +2789,11 @@ bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) err: rw_unlock(&LOCK_grant); err2: - const char *command=""; + const char *command= ""; if (want_access & SELECT_ACL) - command ="select"; + command= "select"; else if (want_access & INSERT_ACL) - command = "insert"; + command= "insert"; my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, ER(ER_COLUMNACCESS_DENIED_ERROR), MYF(0), @@ -2722,11 +2806,11 @@ err2: } -/**************************************************************************** +/* Check if a user has the right to access a database Access is accepted if he has a grant for any table in the database Return 1 if access is denied -****************************************************************************/ +*/ bool check_grant_db(THD *thd,const char *db) { @@ -2771,8 +2855,8 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) #ifdef EMBEDDED_LIBRARY grant_table= NULL; #else - grant_table = table_hash_search(thd->host,thd->ip,db,user, - table->real_name,0); + grant_table= table_hash_search(thd->host, thd->ip, db, user, + table->real_name, 0); #endif table->grant.grant_table=grant_table; // Remember for column test table->grant.version=grant_version; @@ -2791,7 +2875,7 @@ ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) ulong priv; rw_rdlock(&LOCK_grant); - // reload table if someone has modified any grants + /* reload table if someone has modified any grants */ if (table->grant.version != grant_version) { table->grant.grant_table= @@ -2816,11 +2900,20 @@ ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) return priv; } +/* Help function for mysql_show_grants */ -/***************************************************************************** - SHOW GRANTS : send to client grant-like strings depicting user@host - privileges -*****************************************************************************/ +static void add_user_option(String *grant, ulong value, const char *name) +{ + if (value) + { + char buff[22], *p; // just as in int2str + grant->append(' '); + grant->append(name, strlen(name)); + grant->append(' '); + p=int10_to_str(value, buff, 10); + grant->append(buff,p-buff); + } +} static const char *command_array[]= { @@ -2829,12 +2922,20 @@ static const char *command_array[]= "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", }; + static uint command_lengths[]= { 6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18 }; +/* + SHOW GRANTS; Send grants for a user to the client + + IMPLEMENTATION + Send to client grant-like strings depicting user@host privileges +*/ + int mysql_show_grants(THD *thd,LEX_USER *lex_user) { ulong want_access; @@ -2940,25 +3041,25 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append(" REQUIRE ",9); if (acl_user->x509_issuer) { - ssl_options++; - global.append("ISSUER \'",8); - global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); + ssl_options++; + global.append("ISSUER \'",8); + global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); global.append('\''); } if (acl_user->x509_subject) { - if (ssl_options++) - global.append(' '); - global.append("SUBJECT \'",9); - global.append(acl_user->x509_subject,strlen(acl_user->x509_subject)); + if (ssl_options++) + global.append(' '); + global.append("SUBJECT \'",9); + global.append(acl_user->x509_subject,strlen(acl_user->x509_subject)); global.append('\''); } if (acl_user->ssl_cipher) { - if (ssl_options++) - global.append(' '); - global.append("CIPHER '",8); - global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher)); + if (ssl_options++) + global.append(' '); + global.append("CIPHER '",8); + global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher)); global.append('\''); } } @@ -2969,27 +3070,12 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append(" WITH",5); if (want_access & GRANT_ACL) global.append(" GRANT OPTION",13); - if (acl_user->user_resource.questions) - { - char buff[22], *p; // just as in int2str - global.append(" MAX_QUERIES_PER_HOUR ",22); - p=int10_to_str(acl_user->user_resource.questions,buff,10); - global.append(buff,p-buff); - } - if (acl_user->user_resource.updates) - { - char buff[22], *p; // just as in int2str - global.append(" MAX_UPDATES_PER_HOUR ",22); - p=int10_to_str(acl_user->user_resource.updates,buff,10); - global.append(buff,p-buff); - } - if (acl_user->user_resource.connections) - { - char buff[22], *p; // just as in int2str - global.append(" MAX_CONNECTIONS_PER_HOUR ",26); - p=int10_to_str(acl_user->user_resource.connections,buff,10); - global.append(buff,p-buff); - } + add_user_option(&global, acl_user->user_resource.questions, + "MAX_QUERIES_PER_HOUR"); + add_user_option(&global, acl_user->user_resource.updates, + "MAX_UPDATES_PER_HOUR"); + add_user_option(&global, acl_user->user_resource.connections, + "MAX_CONNECTIONS_PER_HOUR"); } protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); @@ -3024,7 +3110,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL))) db.append("ALL PRIVILEGES",14); else if (!(want_access & ~GRANT_ACL)) - db.append("USAGE",5); + db.append("USAGE",5); else { int found=0, cnt; @@ -3075,32 +3161,32 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if (!strcmp(lex_user->user.str,user) && !my_strcasecmp(&my_charset_latin1, lex_user->host.str, host)) { - want_access=grant_table->privs; - if ((want_access | grant_table->cols) != 0) + ulong table_access= grant_table->privs; + if ((table_access | grant_table->cols) != 0) { String global(buff,sizeof(buff),&my_charset_latin1); global.length(0); global.append("GRANT ",6); - if (test_all_bits(grant_table->privs,(TABLE_ACLS & ~GRANT_ACL))) + if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL))) global.append("ALL PRIVILEGES",14); else { - int found=0; - ulong j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL; + int found= 0; + ulong j,test_access= (table_access | grant_table->cols) & ~GRANT_ACL; - for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1) + for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1) { if (test_access & j) { if (found) global.append(", ",2); - found = 1; + found= 1; global.append(command_array[counter],command_lengths[counter]); if (grant_table->cols) { - uint found_col=0; + uint found_col= 0; for (uint col_index=0 ; col_index < grant_table->hash_columns.records ; col_index++) @@ -3111,8 +3197,18 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) { if (!found_col) { + found_col= 1; + /* + If we have a duplicated table level privilege, we + must write the access privilege name again. + */ + if (table_access & j) + { + global.append(", ", 2); + global.append(command_array[counter], + command_lengths[counter]); + } global.append(" (",2); - found_col=1; } else global.append(", ",2); @@ -3135,7 +3231,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append("'@'",3); global.append(lex_user->host.str,lex_user->host.length); global.append('\''); - if (want_access & GRANT_ACL) + if (table_access & GRANT_ACL) global.append(" WITH GRANT OPTION",18); protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); @@ -3147,7 +3243,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - end: +end: VOID(pthread_mutex_unlock(&acl_cache->lock)); rw_unlock(&LOCK_grant); @@ -3223,9 +3319,9 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) */ if (thd->slave_thread && table_rules_on) { - /* - The tables must be marked "updating" so that tables_ok() takes them into - account in tests. + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. */ tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=1; if (!tables_ok(0, tables)) @@ -3265,9 +3361,10 @@ ACL_USER *check_acl_user(LEX_USER *user_name, return 0; *acl_user_idx= counter; - return acl_user; + return acl_user; } + int mysql_drop_user(THD *thd, List <LEX_USER> &list) { uint counter, user_id; @@ -3351,10 +3448,10 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) continue; } - tables[0].table->field[0]->store(user_name->host.str,(uint) + tables[0].table->field[0]->store(user_name->host.str,(uint) user_name->host.length, system_charset_info); - tables[0].table->field[1]->store(user_name->user.str,(uint) + tables[0].table->field[1]->store(user_name->user.str,(uint) user_name->user.length, system_charset_info); if (!tables[0].table->file->index_read_idx(tables[0].table->record[0],0, @@ -3409,7 +3506,7 @@ int mysql_revoke_all(THD *thd, List <LEX_USER> &list) result= -1; continue; } - + if (replace_user_table(thd, tables[0].table, *lex_user, ~0, 1, 0)) { diff --git a/sql/sql_acl.h b/sql/sql_acl.h index ca976f43999..0dc8c058b3d 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -76,8 +76,8 @@ #define get_rights_for_db(A) (((A) & 63) | (((A) & DB_CHUNK1) >> 4) | (((A) & DB_CHUNK2) >> 6)) #define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4)) #define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4)) -#define fix_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) << 7)) -#define get_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) >> 7)) +#define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) +#define get_rights_for_column(A) (((A) & 7) | ((A) >> 8)) /* Classes */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a5807914abd..fd0768d6ae7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2024,11 +2024,11 @@ bool setup_tables(TABLE_LIST *tables) table->used_fields=0; table->const_table=0; - table->outer_join=table->null_row=0; + table->null_row=0; table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= table->keys_in_use; table->used_keys= table->keys_for_keyread; - table->maybe_null=test(table->outer_join=table_list->outer_join); + table->maybe_null=test(table->outer_join= table_list->outer_join); table->tablenr=tablenr; table->map= (table_map) 1 << tablenr; table->force_index= table_list->force_index; @@ -2150,6 +2150,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { + table_map not_null_tables= 0; DBUG_ENTER("setup_conds"); thd->set_query_id=1; @@ -2159,6 +2160,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) thd->where="where clause"; if ((*conds)->fix_fields(thd, tables, conds) || (*conds)->check_cols(1)) DBUG_RETURN(1); + not_null_tables= (*conds)->not_null_tables(); } /* Check if we are using outer joins */ @@ -2173,9 +2175,15 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) DBUG_RETURN(1); thd->lex.current_select->cond_count++; - /* If it's a normal join, add the ON/USING expression to the WHERE */ - if (!table->outer_join) + /* + If it's a normal join or a LEFT JOIN which can be optimized away + add the ON/USING expression to the WHERE + */ + if (!table->outer_join || + ((table->table->map & not_null_tables) && + !(specialflag & SPECIAL_NO_NEW_FUNC))) { + table->outer_join= 0; if (!(*conds=and_conds(*conds, table->on_expr))) DBUG_RETURN(1); table->on_expr=0; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 767a1a46dcc..64e8be8e224 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -760,9 +760,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_VOID_RETURN; uint8 tables_type= 0; - if ((local_tables = is_cacheable(thd, thd->query_length, - thd->query, &thd->lex, tables_used, - &tables_type))) + if ((local_tables= is_cacheable(thd, thd->query_length, + thd->query, &thd->lex, tables_used, + &tables_type))) { NET *net= &thd->net; byte flags= (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index eea542e9d06..f6eb7c7a0fb 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -61,7 +61,7 @@ #define QUERY_CACHE_PACK_ITERATION 2 #define QUERY_CACHE_PACK_LIMIT (512*1024L) -#define TABLE_COUNTER_TYPE uint8 +#define TABLE_COUNTER_TYPE uint struct Query_cache_block; struct Query_cache_block_table; diff --git a/sql/sql_class.h b/sql/sql_class.h index f6336cb7dd9..414b4eac6a6 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -30,22 +30,26 @@ class Slave_log_event; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_IGNORE, DUP_UPDATE }; -enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN }; +enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN}; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, DELAY_KEY_WRITE_ALL }; extern char internal_table_name[2]; -// log info errors +/* log info errors */ #define LOG_INFO_EOF -1 #define LOG_INFO_IO -2 #define LOG_INFO_INVALID -3 #define LOG_INFO_SEEK -4 -#define LOG_INFO_PURGE_NO_ROTATE -5 #define LOG_INFO_MEM -6 #define LOG_INFO_FATAL -7 #define LOG_INFO_IN_USE -8 +/* bitmap to SQL_LOG::close() */ +#define LOG_CLOSE_INDEX 1 +#define LOG_CLOSE_TO_BE_OPENED 2 +#define LOG_CLOSE_STOP_EVENT 4 + struct st_relay_log_info; typedef struct st_log_info @@ -70,8 +74,10 @@ typedef struct st_user_var_events class Log_event; -class MYSQL_LOG { +class MYSQL_LOG + { private: + /* LOCK_log and LOCK_index are inited by init_pthread_objects() */ pthread_mutex_t LOCK_log, LOCK_index; pthread_cond_t update_cond; ulonglong bytes_written; @@ -86,15 +92,20 @@ class MYSQL_LOG { uint open_count; // For replication volatile enum_log_type log_type; enum cache_type io_cache_type; - bool write_error,inited; - /* - For binlog - if log name can never change we should not try to rotate it - or write any rotation events. The user should use FLUSH MASTER instead - of FLUSH LOGS for purging. - */ - bool no_rotate; + bool write_error, inited; bool need_start_event; - bool no_auto_events; // for relay binlog + bool no_auto_events; // For relay binlog + /* + The max size before rotation (usable only if log_type == LOG_BIN: binary + logs and relay logs). + For a binlog, max_size should be max_binlog_size. + For a relay log, it should be max_relay_log_size if this is non-zero, + max_binlog_size otherwise. + max_size is set in init(), and dynamically changed (when one does SET + GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and + fix_max_relay_log_size). + */ + ulong max_size; friend class Log_event; public: @@ -116,17 +127,19 @@ public: bytes_written=0; DBUG_VOID_RETURN; } + void set_max_size(ulong max_size_arg); void signal_update() { pthread_cond_broadcast(&update_cond);} void wait_for_update(THD* thd); void set_need_start_event() { need_start_event = 1; } void init(enum_log_type log_type_arg, - enum cache_type io_cache_type_arg = WRITE_CACHE, - bool no_auto_events_arg = 0); + enum cache_type io_cache_type_arg, + bool no_auto_events_arg, ulong max_size); + void init_pthread_objects(); void cleanup(); bool open(const char *log_name,enum_log_type log_type, const char *new_name, const char *index_file_name_arg, enum cache_type io_cache_type_arg, - bool no_auto_events_arg); + bool no_auto_events_arg, ulong max_size); void new_file(bool need_lock= 1); bool write(THD *thd, enum enum_server_command command, const char *format,...); @@ -152,8 +165,7 @@ public: int purge_logs_before_date(time_t purge_time); int purge_first_log(struct st_relay_log_info* rli, bool included); bool reset_logs(THD* thd); - // if we are exiting, we also want to close the index file - void close(bool exiting = 0); + void close(uint exiting); // iterating through the log index file int find_log_pos(LOG_INFO* linfo, const char* log_name, @@ -161,7 +173,6 @@ public: int find_next_log(LOG_INFO* linfo, bool need_mutex); int get_current_log(LOG_INFO* linfo); uint next_file_id(); - inline bool is_open() { return log_type != LOG_CLOSED; } inline char* get_index_fname() { return index_file_name;} inline char* get_log_fname() { return log_file_name; } @@ -376,6 +387,7 @@ struct system_variables ulong tx_isolation; ulong sql_mode; ulong default_week_format; + ulong max_seeks_for_key; ulong group_concat_max_len; /* In slave thread we need to know in behalf of which @@ -1003,11 +1015,12 @@ class Unique :public Sql_alloc TREE tree; byte *record_pointers; bool flush(); + uint size; public: ulong elements; Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, - uint size, ulong max_in_memory_size_arg); + uint size_arg, ulong max_in_memory_size_arg); ~Unique(); inline bool unique_add(gptr ptr) { @@ -1022,10 +1035,11 @@ public: friend int unique_write_to_ptrs(gptr key, element_count count, Unique *unique); }; -class multi_delete : public select_result + +class multi_delete :public select_result { TABLE_LIST *delete_tables, *table_being_deleted; - Unique **tempfiles; + Unique **tempfiles; THD *thd; ha_rows deleted; uint num_of_tables; @@ -1045,7 +1059,7 @@ public: }; -class multi_update : public select_result +class multi_update :public select_result { TABLE_LIST *all_tables, *update_tables, *table_being_updated; THD *thd; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index ebb09b99df7..13038435986 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -164,7 +164,15 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, else { table->file->print_error(error,MYF(0)); - error=0; + /* + In < 4.0.14 we set the error number to 0 here, but that + was not sensible, because then MySQL would not roll back the + failed DELETE, and also wrote it to the binlog. For MyISAM + tables a DELETE probably never should fail (?), but for + InnoDB it can fail in a FOREIGN KEY error or an + out-of-tablespace error. + */ + error= 1; break; } } diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 79d13039784..0387f02e83f 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -203,6 +203,13 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, Item *item; for (key_len=0 ; (item=it_ke++) ; key_part++) { + if (item->fix_fields(thd, tables)) + goto err; + if (item->used_tables() & ~RAND_TABLE_BIT) + { + my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ"); + goto err; + } (void) item->save_in_field(key_part->field, 1); key_len+=key_part->store_length; } @@ -237,7 +244,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, } if (cond && !cond->val_int()) continue; - if (!err && num_rows >= offset_limit) + if (num_rows >= offset_limit) { String *packet = &thd->packet; Item *item; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6b3da620fc1..03a0f7beda9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -61,7 +61,9 @@ enum enum_sql_command { SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, SQLCOM_PRELOAD_KEYS, SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, - SQLCOM_ROLLBACK, SQLCOM_COMMIT, SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP, + SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT, + SQLCOM_COMMIT, SQLCOM_SAVEPOINT, + SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP, SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER, SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE, SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS, @@ -487,9 +489,10 @@ typedef struct st_lex List<List_item> many_values; List<set_var_base> var_list; List<Item> param_list; - SQL_LIST proc_list, auxilliary_table_list; + SQL_LIST proc_list, auxilliary_table_list, save_list; TYPELIB *interval; create_field *last_field; + char *savepoint_name; // Transaction savepoint id Item *default_value, *comment; uint uint_geom_type; LEX_USER *grant_user; diff --git a/sql/sql_list.cc b/sql/sql_list.cc index 1124605ca24..c99cfb8c918 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -22,3 +22,18 @@ #include "mysql_priv.h" list_node end_of_list; + +void free_list(I_List <i_string_pair> *list) +{ + i_string_pair *tmp; + while ((tmp= list->get())) + delete tmp; +} + + +void free_list(I_List <i_string> *list) +{ + i_string *tmp; + while ((tmp= list->get())) + delete tmp; +} diff --git a/sql/sql_list.h b/sql/sql_list.h index 2450b2051f2..7200046e6c5 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -15,12 +15,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* mysql standard open memoryallocator */ - #ifdef __GNUC__ #pragma interface /* gcc class implementation */ #endif +/* mysql standard class memoryallocator */ class Sql_alloc { @@ -48,14 +47,15 @@ public: }; + /* -** basic single linked list -** Used for item and item_buffs. -** All list ends with a pointer to the 'end_of_list' element, which -** data pointer is a null pointer and the next pointer points to itself. -** This makes it very fast to traverse lists as we don't have to -** test for a specialend condition for list that can't contain a null -** pointer. + Basic single linked list + Used for item and item_buffs. + All list ends with a pointer to the 'end_of_list' element, which + data pointer is a null pointer and the next pointer points to itself. + This makes it very fast to traverse lists as we don't have to + test for a specialend condition for list that can't contain a null + pointer. */ class list_node :public Sql_alloc @@ -75,9 +75,11 @@ public: friend class base_list_iterator; }; + extern list_node end_of_list; -class base_list :public Sql_alloc { +class base_list :public Sql_alloc +{ protected: list_node *first,**last; @@ -267,6 +269,7 @@ public: inline T** ref(void) { return (T**) base_list_iterator::ref(); } }; + template <class T> class List_iterator_fast :public base_list_iterator { protected: @@ -288,11 +291,12 @@ public: /* -** A simple intrusive list which automaticly removes element from list -** on delete (for THD element) + A simple intrusive list which automaticly removes element from list + on delete (for THD element) */ -struct ilink { +struct ilink +{ struct ilink **prev,*next; static void *operator new(size_t size) { @@ -317,9 +321,11 @@ struct ilink { virtual ~ilink() { unlink(); } /*lint -e1740 */ }; + template <class T> class I_List_iterator; -class base_ilist { +class base_ilist +{ public: struct ilink *first,last; inline void empty() { first= &last; last.prev= &first; } @@ -368,7 +374,8 @@ public: template <class T> -class I_List :private base_ilist { +class I_List :private base_ilist +{ public: I_List() :base_ilist() {} inline void empty() { base_ilist::empty(); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5880a9d4c8a..d9f6b93d2bb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -216,7 +216,7 @@ static int check_user(THD *thd,enum_server_command command, const char *user, thd->db=0; thd->db_length=0; USER_RESOURCES ur; - char tmp_passwd[SCRAMBLE41_LENGTH]; + char tmp_passwd[SCRAMBLE41_LENGTH+1]; DBUG_ENTER("check_user"); /* @@ -304,10 +304,11 @@ static int check_user(THD *thd,enum_server_command command, const char *user, db ? db : (char*) ""); thd->db_access=0; /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || ur.connections) && + if ((ur.questions || ur.updates || ur.connections || max_user_connections) && get_or_create_user_conn(thd,user,thd->host_or_ip,&ur)) DBUG_RETURN(1); - if (thd->user_connect && thd->user_connect->user_resources.connections && + if (thd->user_connect && ((thd->user_connect->user_resources.connections) || + max_user_connections) && check_for_max_user_connections(thd, thd->user_connect)) DBUG_RETURN(1); @@ -356,7 +357,7 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc) DBUG_ENTER("check_for_max_user_connections"); if (max_user_connections && - (max_user_connections <= (uint) uc->connections)) + (max_user_connections < (uint) uc->connections)) { net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user); error=1; @@ -567,7 +568,10 @@ check_connections(THD *thd) #if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) /* Fast local hostname resolve for Win32 */ if (!strcmp(thd->ip,"127.0.0.1")) - thd->host=(char*) localhost; + { + thd->host= (char*) localhost; + thd->host_or_ip= localhost; + } else #endif { @@ -694,6 +698,11 @@ check_connections(THD *thd) if (thd->client_capabilities & CLIENT_SSL) { /* Do the SSL layering. */ + if (!ssl_acceptor_fd) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } DBUG_PRINT("info", ("IO layer change in progress...")); if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout)) { @@ -1444,7 +1453,8 @@ restore_user: pos = uint4korr(packet); flags = uint2korr(packet + 4); thd->server_id=0; /* avoid suicide */ - kill_zombie_dump_threads(slave_server_id = uint4korr(packet+6)); + if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0 + kill_zombie_dump_threads(slave_server_id); thd->server_id = slave_server_id; mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); unregister_slave(thd,1,1); @@ -1503,9 +1513,10 @@ restore_user: opened_tables,refresh_version, cached_tables(), uptime ? (float)thd->query_id/(float)uptime : 0); #ifdef SAFEMALLOC - if (lCurMemory) // Using SAFEMALLOC + if (sf_malloc_cur_memory) // Using SAFEMALLOC sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK", - (lCurMemory+1023L)/1024L,(lMaxMemory+1023L)/1024L); + (sf_malloc_cur_memory+1023L)/1024L, + (sf_malloc_max_memory+1023L)/1024L); #endif VOID(my_net_write(net, buff,(uint) strlen(buff))); VOID(net_flush(net)); @@ -1670,7 +1681,11 @@ mysql_execute_command(THD *thd) given and the table list says the query should not be replicated */ if (table_rules_on && tables && !tables_ok(thd,tables)) + { + /* we warn the slave SQL thread */ + my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); DBUG_VOID_RETURN; + } #ifndef TO_BE_DELETED /* This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 @@ -1710,14 +1725,8 @@ mysql_execute_command(THD *thd) } } } - if ((&lex->select_lex != lex->all_selects_list && - lex->unit.create_total_list(thd, lex, &tables, 0)) -#ifdef HAVE_REPLICATION - || - (table_rules_on && tables && thd->slave_thread && - !tables_ok(thd,tables)) -#endif - ) + if (&lex->select_lex != lex->all_selects_list && + lex->unit.create_total_list(thd, lex, &tables, 0)) DBUG_VOID_RETURN; /* @@ -2445,13 +2454,15 @@ mysql_execute_command(THD *thd) if (find_real_table_in_list(tables->next, tables->db, tables->real_name)) { - net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name); - DBUG_VOID_RETURN; + /* Using same table for INSERT and SELECT */ + select_lex->options |= OPTION_BUFFER_RESULT; } /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); + lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE; + if (!(res=open_and_lock_tables(thd, tables))) { if ((result=new select_insert(tables->table,&lex->field_list, @@ -2684,6 +2695,14 @@ mysql_execute_command(THD *thd) } if (check_access(thd,SELECT_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ + if (!thd->col_access && check_grant_db(thd,db)) + { + net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, + thd->priv_user, + thd->priv_host, + db); + goto error; + } /* grant is checked in mysqld_show_tables */ if (select_lex->options & SELECT_DESCRIBE) res= mysqld_extend_show_tables(thd,db, @@ -2842,7 +2861,10 @@ mysql_execute_command(THD *thd) if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) + { + my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); break; + } #endif if (check_access(thd,CREATE_ACL,lex->name,0,1)) break; @@ -2867,7 +2889,10 @@ mysql_execute_command(THD *thd) if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) + { + my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); break; + } #endif if (check_access(thd,DROP_ACL,lex->name,0,1)) break; @@ -3105,8 +3130,12 @@ mysql_execute_command(THD *thd) res = mysql_ha_close(thd, tables); break; case SQLCOM_HA_READ: - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables)) + /* + There is no need to check for table permissions here, because + if a user has no permissions to read a table, he won't be + able to open it (with SQLCOM_HA_OPEN) in the first place. + */ + if (check_db_used(thd,tables)) goto error; res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir, lex->insert_list, lex->ha_rkey_mode, select_lex->where, @@ -3162,6 +3191,23 @@ mysql_execute_command(THD *thd) res= -1; thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); break; + case SQLCOM_ROLLBACK_TO_SAVEPOINT: + if (!ha_rollback_to_savepoint(thd, lex->savepoint_name)) + { + if (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) + send_warning(&thd->net,ER_WARNING_NOT_COMPLETE_ROLLBACK,0); + else + send_ok(&thd->net); + } + else + res= -1; + break; + case SQLCOM_SAVEPOINT: + if (!ha_savepoint(thd, lex->savepoint_name)) + send_ok(&thd->net); + else + res= -1; + break; default: /* Impossible */ send_ok(thd); break; @@ -3612,8 +3658,7 @@ void mysql_init_multi_delete(LEX *lex) mysql_init_select(lex); lex->select_lex.select_limit= lex->unit.select_limit_cnt= HA_POS_ERROR; - lex->auxilliary_table_list= lex->select_lex.table_list; - lex->select_lex.table_list.empty(); + lex->select->table_list.save_and_clear(&lex->auxilliary_table_list); } @@ -4273,6 +4318,11 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } if (options & REFRESH_LOG) { + /* + Flush the normal query log, the update log, the binary log, + the slow query log, and the relay log (if it exists). + */ + /* Writing this command to the binlog may result in infinite loops when doing mysqlbinlog|mysql, and anyway it does not really make sense to log it @@ -4282,6 +4332,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, mysql_log.new_file(1); mysql_update_log.new_file(1); mysql_bin_log.new_file(1); + mysql_slow_log.new_file(1); #ifdef HAVE_REPLICATION if (expire_logs_days) { @@ -4289,8 +4340,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (purge_time >= 0) mysql_bin_log.purge_logs_before_date(purge_time); } + LOCK_ACTIVE_MI; + rotate_relay_log(active_mi); + UNLOCK_ACTIVE_MI; #endif - mysql_slow_log.new_file(1); if (ha_flush_logs()) result=1; if (flush_error_log()) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 121411379f8..faf128d5e02 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -51,7 +51,7 @@ int check_binlog_magic(IO_CACHE* log, const char** errmsg) } static int fake_rotate_event(NET* net, String* packet, char* log_file_name, - const char**errmsg) + ulonglong position, const char**errmsg) { char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN]; memset(header, 0, 4); // when does not matter @@ -68,9 +68,7 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name, int4store(header + LOG_POS_OFFSET, 0); packet->append(header, sizeof(header)); - /* We need to split the next statement because of problem with cxx */ - int4store(buf,4); // tell slave to skip magic number - int4store(buf+4,0); + int8store(buf+R_POS_OFFSET,position); packet->append(buf, ROTATE_HEADER_LEN); packet->append(p,ident_len); if (my_net_write(net, (char*)packet->ptr(), packet->length())) @@ -159,10 +157,18 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, File file; DBUG_ENTER("open_binlog"); - if ((file = my_open(log_file_name, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0 || - init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0, + if ((file = my_open(log_file_name, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) + { + sql_print_error("Failed to open log (\ +file '%s', errno %d)", log_file_name, my_errno); + *errmsg = "Could not open log file"; // This will not be sent + goto err; + } + if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0, MYF(MY_WME | MY_DONT_CHECK_FILESIZE))) { + sql_print_error("Failed to create a cache on log (\ +file '%s')", log_file_name); *errmsg = "Could not open log file"; // This will not be sent goto err; } @@ -258,22 +264,20 @@ bool log_in_use(const char* log_name) int purge_error_message(THD* thd, int res) { - const char* errmsg = 0; + const char *errmsg= 0; - switch(res) { + switch (res) { case 0: break; - case LOG_INFO_EOF: errmsg = "Target log not found in binlog index"; break; - case LOG_INFO_IO: errmsg = "I/O error reading log index file"; break; - case LOG_INFO_INVALID: errmsg = "Server configuration does not permit \ -binlog purge"; break; - case LOG_INFO_SEEK: errmsg = "Failed on fseek()"; break; - case LOG_INFO_PURGE_NO_ROTATE: errmsg = "Cannot purge unrotatable log"; - break; - case LOG_INFO_MEM: errmsg = "Out of memory"; break; - case LOG_INFO_FATAL: errmsg = "Fatal error during purge"; break; - case LOG_INFO_IN_USE: errmsg = "A purgeable log is in use, will not purge"; + case LOG_INFO_EOF: errmsg= "Target log not found in binlog index"; break; + case LOG_INFO_IO: errmsg= "I/O error reading log index file"; break; + case LOG_INFO_INVALID: + errmsg= "Server configuration does not permit binlog purge"; break; + case LOG_INFO_SEEK: errmsg= "Failed on fseek()"; break; + case LOG_INFO_MEM: errmsg= "Out of memory"; break; + case LOG_INFO_FATAL: errmsg= "Fatal error during purge"; break; + case LOG_INFO_IN_USE: errmsg= "A purgeable log is in use, will not purge"; break; - default: errmsg = "Unknown error during purge"; break; + default: errmsg= "Unknown error during purge"; break; } if (errmsg) @@ -281,19 +285,24 @@ binlog purge"; break; send_error(thd, 0, errmsg); return 1; } - else - send_ok(thd); + send_ok(thd); return 0; } + int purge_master_logs(THD* thd, const char* to_log) { char search_file_name[FN_REFLEN]; + if (!mysql_bin_log.is_open()) + { + send_ok(); + return 0; + } mysql_bin_log.make_log_name(search_file_name, to_log); - int res = mysql_bin_log.purge_logs(search_file_name, 0, 1, 1, NULL); - - return purge_error_message(thd, res); + return purge_error_message(thd, + mysql_bin_log.purge_logs(search_file_name, 0, 1, + 1, NULL); } @@ -385,17 +394,31 @@ impossible position"; */ packet->set("\0", 1, &my_charset_bin); - // if we are at the start of the log - if (pos == BIN_LOG_HEADER_SIZE) + /* + Before 4.0.14 we called fake_rotate_event below only if + (pos == BIN_LOG_HEADER_SIZE), because if this is false then the slave + already knows the binlog's name. + Now we always call fake_rotate_event; if the slave already knew the log's + name (ex: CHANGE MASTER TO MASTER_LOG_FILE=...) this is useless but does + not harm much. It is nice for 3.23 (>=.58) slaves which test Rotate events + to see if the master is 4.0 (then they choose to stop because they can't + replicate 4.0); by always calling fake_rotate_event we are sure that + 3.23.58 and newer will detect the problem as soon as replication starts + (BUG#198). + Always calling fake_rotate_event makes sending of normal + (=from-binlog) Rotate events a priori unneeded, but it is not so simple: + the 2 Rotate events are not equivalent, the normal one is before the Stop + event, the fake one is after. If we don't send the normal one, then the + Stop event will be interpreted (by existing 4.0 slaves) as "the master + stopped", which is wrong. So for safety, given that we want minimum + modification of 4.0, we send the normal and fake Rotates. + */ + if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg)) { - // tell the client log name with a fake rotate_event - if (fake_rotate_event(net, packet, log_file_name, &errmsg)) - { - my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; - goto err; - } - packet->set("\0", 1, &my_charset_bin); + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; + goto err; } + packet->set("\0", 1, &my_charset_bin); while (!net->error && net->vio != 0 && !thd->killed) { @@ -514,6 +537,11 @@ Increase max_allowed_packet on master"; case LOG_READ_EOF: DBUG_PRINT("wait",("waiting for data in binary log")); + if (thd->server_id==0) // for mysqlbinlog (mysqlbinlog.server_id==0) + { + pthread_mutex_unlock(log_lock); + goto end; + } if (!thd->killed) { /* Note that the following call unlocks lock_log */ @@ -588,10 +616,12 @@ Increase max_allowed_packet on master"; end_io_cache(&log); (void) my_close(file, MYF(MY_WME)); - // fake Rotate_log event just in case it did not make it to the log - // otherwise the slave make get confused about the offset + /* + Even if the previous log contained a Rotate_log_event, we still fake + one. + */ if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 || - fake_rotate_event(net, packet, log_file_name, &errmsg)) + fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE, &errmsg)) { my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; @@ -601,6 +631,7 @@ Increase max_allowed_packet on master"; } } +end: end_io_cache(&log); (void)my_close(file, MYF(MY_WME)); @@ -611,7 +642,7 @@ Increase max_allowed_packet on master"; pthread_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; - err: +err: thd->proc_info = "waiting to finalize termination"; end_io_cache(&log); /* @@ -774,9 +805,15 @@ int reset_slave(THD *thd, MASTER_INFO* mi) &errmsg))) goto err; - // Clear master's log coordinates (only for good display of SHOW SLAVE STATUS) - mi->master_log_name[0]= 0; - mi->master_log_pos= BIN_LOG_HEADER_SIZE; + /* + Clear master's log coordinates and reset host/user/etc to the values + specified in mysqld's options (only for good display of SHOW SLAVE STATUS; + next init_master_info() (in start_slave() for example) would have set them + the same way; but here this is for the case where the user does SHOW SLAVE + STATUS; before doing START SLAVE; + */ + init_master_info_with_options(mi); + clear_last_slave_error(&mi->rli); // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 end_master_info(mi); // and delete these two files @@ -801,6 +838,25 @@ err: DBUG_RETURN(error); } +/* + + Kill all Binlog_dump threads which previously talked to the same slave + ("same" means with the same server id). Indeed, if the slave stops, if the + Binlog_dump thread is waiting (pthread_cond_wait) for binlog update, then it + will keep existing until a query is written to the binlog. If the master is + idle, then this could last long, and if the slave reconnects, we could have 2 + Binlog_dump threads in SHOW PROCESSLIST, until a query is written to the + binlog. To avoid this, when the slave reconnects and sends COM_BINLOG_DUMP, + the master kills any existing thread with the slave's server id (if this id is + not zero; it will be true for real slaves, but false for mysqlbinlog when it + sends COM_BINLOG_DUMP to get a remote binlog dump). + + SYNOPSIS + kill_zombie_dump_threads() + slave_server_id the slave's server id + +*/ + void kill_zombie_dump_threads(uint32 slave_server_id) { @@ -852,7 +908,7 @@ int change_master(THD* thd, MASTER_INFO* mi) // TODO: see if needs re-write if (init_master_info(mi, master_info_file, relay_log_info_file, 0)) { - send_error(thd, 0, "Could not initialize master info"); + send_error(thd, ER_MASTER_INFO); unlock_slave_threads(mi); DBUG_RETURN(1); } @@ -862,16 +918,21 @@ int change_master(THD* thd, MASTER_INFO* mi) and we have the hold on the run locks which will keep all threads that could possibly modify the data structures from running */ + + /* + If the user specified host or port without binlog or position, + reset binlog's name to FIRST and position to 4. + */ + if ((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) { - // if we change host or port, we must reset the postion mi->master_log_name[0] = 0; mi->master_log_pos= BIN_LOG_HEADER_SIZE; } if (lex_mi->log_file_name) strmake(mi->master_log_name, lex_mi->log_file_name, - sizeof(mi->master_log_name)); + sizeof(mi->master_log_name)-1); if (lex_mi->pos) { mi->master_log_pos= lex_mi->pos; @@ -879,11 +940,11 @@ int change_master(THD* thd, MASTER_INFO* mi) DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); if (lex_mi->host) - strmake(mi->host, lex_mi->host, sizeof(mi->host)); + strmake(mi->host, lex_mi->host, sizeof(mi->host)-1); if (lex_mi->user) - strmake(mi->user, lex_mi->user, sizeof(mi->user)); + strmake(mi->user, lex_mi->user, sizeof(mi->user)-1); if (lex_mi->password) - strmake(mi->password, lex_mi->password, sizeof(mi->password)); + strmake(mi->password, lex_mi->password, sizeof(mi->password)-1); if (lex_mi->port) mi->port = lex_mi->port; if (lex_mi->connect_retry) @@ -936,13 +997,25 @@ int change_master(THD* thd, MASTER_INFO* mi) } mi->rli.group_master_log_pos = mi->master_log_pos; DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + /* If changing RELAY_LOG_FILE or RELAY_LOG_POS, this will be nonsense: */ + mi->rli.master_log_pos = mi->master_log_pos; strmake(mi->rli.group_master_log_name,mi->master_log_name, sizeof(mi->rli.group_master_log_name)-1); if (!mi->rli.group_master_log_name[0]) // uninitialized case mi->rli.group_master_log_pos=0; pthread_mutex_lock(&mi->rli.data_lock); - mi->rli.abort_pos_wait++; + mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */ + /* Clear the error, for a clean start. */ + clear_last_slave_error(&mi->rli); + /* + If we don't write new coordinates to disk now, then old will remain in + relay-log.info until START SLAVE is issued; but if mysqld is shutdown + before START SLAVE, then old will remain in relay-log.info, and will be the + in-memory value at restart (thus causing errors, as the old relay log does + not exist anymore). + */ + flush_relay_log_info(&mi->rli); pthread_cond_broadcast(&mi->data_cond); pthread_mutex_unlock(&mi->rli.data_lock); @@ -1112,7 +1185,7 @@ int show_binlog_info(THD* thd) /* - Send a lost of all binary logs to client + Send a list of all binary logs to client SYNOPSIS show_binlogs() @@ -1125,7 +1198,6 @@ int show_binlog_info(THD* thd) int show_binlogs(THD* thd) { - const char *errmsg; IO_CACHE *index_file; char fname[FN_REFLEN]; NET* net = &thd->net; @@ -1138,8 +1210,8 @@ int show_binlogs(THD* thd) if (!mysql_bin_log.is_open()) { //TODO: Replace with ER() error message - errmsg= "You are not using binary logging"; - goto err_with_msg; + send_error(net, 0, "You are not using binary logging"); + return 1; } field_list.push_back(new Item_empty_string("Log_name", 255)); @@ -1164,8 +1236,6 @@ int show_binlogs(THD* thd) send_eof(thd); DBUG_RETURN(0); -err_with_msg: - send_error(thd, ER_UNKNOWN_ERROR, errmsg); err: mysql_bin_log.unlock_index(); DBUG_RETURN(1); diff --git a/sql/sql_repl.h b/sql/sql_repl.h index e3d600b9798..fe1b7167d4a 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -7,7 +7,7 @@ typedef struct st_slave_info uint32 rpl_recovery_rank, master_id; char host[HOSTNAME_LENGTH+1]; char user[USERNAME_LENGTH+1]; - char password[HASH_PASSWORD_LENGTH+1]; + char password[MAX_PASSWORD_LENGTH+1]; uint16 port; THD* thd; } SLAVE_INFO; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 71c0d0bdddc..a75f9ddf3b6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -525,6 +525,8 @@ JOIN::optimize() DBUG_RETURN(1); } + /* Remove distinct if only const tables */ + select_distinct= select_distinct && (const_tables != tables); thd->proc_info= "preparing"; if (result->initialize_tables(this)) { @@ -1723,9 +1725,11 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, /* Set a max range of how many seeks we can expect when using keys - This was (s->read_time*5), but this was too low with small rows + This is can't be to high as otherwise we are likely to use + table scan. */ - s->worst_seeks= (double) s->found_records / 5; + s->worst_seeks= min((double) s->found_records / 10, + (double) s->read_time*3); if (s->worst_seeks < 2.0) // Fix for small tables s->worst_seeks=2.0; @@ -2148,6 +2152,9 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) key_field->field->table->reginfo.not_exists_optimize=1; } + +#define FT_KEYPART (MAX_REF_PARTS+10) + static void add_ft_keys(DYNAMIC_ARRAY *keyuse_array, JOIN_TAB *stat,COND *cond,table_map usable_tables) @@ -2167,17 +2174,17 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, { Item_func *arg0=(Item_func *)(func->arguments()[0]), *arg1=(Item_func *)(func->arguments()[1]); - if ((functype == Item_func::GE_FUNC || - functype == Item_func::GT_FUNC) && - arg0->type() == Item::FUNC_ITEM && - arg0->functype() == Item_func::FT_FUNC && - arg1->const_item() && arg1->val()>0) + if (arg1->const_item() && + ((functype == Item_func::GE_FUNC && arg1->val()> 0) || + (functype == Item_func::GT_FUNC && arg1->val()>=0)) && + arg0->type() == Item::FUNC_ITEM && + arg0->functype() == Item_func::FT_FUNC) cond_func=(Item_func_match *) arg0; - else if ((functype == Item_func::LE_FUNC || - functype == Item_func::LT_FUNC) && + else if (arg0->const_item() && + ((functype == Item_func::LE_FUNC && arg0->val()> 0) || + (functype == Item_func::LT_FUNC && arg0->val()>=0)) && arg1->type() == Item::FUNC_ITEM && - arg1->functype() == Item_func::FT_FUNC && - arg0->const_item() && arg0->val()>0) + arg1->functype() == Item_func::FT_FUNC) cond_func=(Item_func_match *) arg1; } } @@ -2188,34 +2195,20 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) { Item *item; - /* - I'm (Sergei) too lazy to implement proper recursive descent here, - and anyway, nobody will use such a stupid queries - that will require it :-) - May be later... - */ while ((item=li++)) - { - if (item->type() == Item::FUNC_ITEM && - ((Item_func *)item)->functype() == Item_func::FT_FUNC) - { - cond_func=(Item_func_match *)item; - break; - } - } + add_ft_keys(keyuse_array,stat,item,usable_tables); } } - if (!cond_func || cond_func->key == NO_SUCH_KEY) + if (!cond_func || cond_func->key == NO_SUCH_KEY || + !(usable_tables & cond_func->table->map)) return; KEYUSE keyuse; - keyuse.table= cond_func->table; keyuse.val = cond_func; keyuse.key = cond_func->key; -#define FT_KEYPART (MAX_REF_PARTS+10) - keyuse.keypart=FT_KEYPART; + keyuse.keypart= FT_KEYPART; keyuse.used_tables=cond_func->key_item()->used_tables(); VOID(insert_dynamic(keyuse_array,(gptr) &keyuse)); } @@ -2443,6 +2436,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, best=best_time=records=DBL_MAX; KEYUSE *best_key=0; uint best_max_key_part=0; + my_bool found_constrain= 0; if (s->keyuse) { /* Use key if possible */ @@ -2507,6 +2501,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, } else { + found_constrain= 1; /* Check if we found full key */ @@ -2544,16 +2539,18 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, records=2.0; // Can't be as good as a unique } } + /* Limit the number of matched rows */ + tmp= records; + set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); if (table->used_keys & ((key_map) 1 << key)) { /* we can use only index tree */ uint keys_per_block= table->file->block_size/2/ (keyinfo->key_length+table->file->ref_length)+1; - tmp=(record_count*(records+keys_per_block-1)/ - keys_per_block); + tmp=record_count*(tmp+keys_per_block-1)/keys_per_block; } else - tmp=record_count*min(records,s->worst_seeks); + tmp=record_count*min(tmp,s->worst_seeks); } } else @@ -2584,7 +2581,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, { /* Assume that the first key part matches 1% of the file - and that the hole key matches 10 (dupplicates) or 1 + and that the hole key matches 10 (duplicates) or 1 (unique) records. Assume also that more key matches proportionally more records @@ -2622,6 +2619,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, records*= 2.0; } } + /* Limit the number of matched rows */ + set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); if (table->used_keys & ((key_map) 1 << key)) { /* we can use only index tree */ @@ -2664,20 +2663,31 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, s->table->used_keys && best_key) && !(s->table->force_index && best_key)) { // Check full join + ha_rows rnd_records= s->found_records; if (s->on_expr) { - tmp=rows2double(s->found_records); // Can't use read cache + tmp=rows2double(rnd_records); // Can't use read cache } else { tmp=(double) s->read_time; - /* Calculate time to read through cache */ + /* Calculate time to read previous rows through cache */ tmp*=(1.0+floor((double) cache_record_length(join,idx)* record_count / (double) thd->variables.join_buff_size)); } + + /* + If there is a restriction on the table, assume that 25% of the + rows can be skipped on next part. + This is to force tables that this table depends on before this + table + */ + if (found_constrain) + rnd_records-= rnd_records/4; + if (best == DBL_MAX || - (tmp + record_count/(double) TIME_FOR_COMPARE*s->found_records < + (tmp + record_count/(double) TIME_FOR_COMPARE*rnd_records < best + record_count/(double) TIME_FOR_COMPARE*records)) { /* @@ -2685,7 +2695,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, will ensure that this will be used */ best=tmp; - records= rows2double(s->found_records); + records= rows2double(rnd_records); best_key=0; } } @@ -3194,9 +3204,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) join->best_positions[i].records_read && !(join->select_options & OPTION_FOUND_ROWS))) { - /* Join with outer join condition */ - COND *orig_cond=sel->cond; - sel->cond=and_conds(sel->cond,tab->on_expr); if (sel->test_quick_select(tab->keys, used_tables & ~ current_map, (join->select_options & @@ -3204,7 +3211,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) HA_POS_ERROR : join->unit->select_limit_cnt)) < 0) DBUG_RETURN(1); // Impossible range - sel->cond=orig_cond; /* Fix for EXPLAIN */ if (sel->quick) join->best_positions[i].records_read= sel->quick->records; @@ -3222,7 +3228,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) (select->quick && (select->quick->records >= 100L)))) ? 2 : 1; - sel->read_tables= used_tables; + sel->read_tables= used_tables & ~current_map; } if (i != join->const_tables && tab->use_quick != 2) { /* Read with cache */ @@ -4271,6 +4277,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::STRING_ITEM: case Item::REF_ITEM: case Item::NULL_ITEM: + case Item::VARBIN_ITEM: { bool maybe_null=item->maybe_null; Field *new_field; @@ -6534,6 +6541,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, */ if (!select->quick->reverse_sorted()) { + if (table->file->index_flags(ref_key) & HA_NOT_READ_PREFIX_LAST) + DBUG_RETURN(0); // Use filesort // ORDER BY range_key DESC QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, used_key_parts); @@ -6675,6 +6684,8 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, /* We have a ref on a const; Change this to a range that filesort can use. + For impossible ranges (like when doing a lookup on NULL on a NOT NULL + field, quick will contain an empty record set. */ if (!(select->quick=get_ft_or_quick_select_for_ref(table, tab))) goto err; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e16d7a0067d..b99dec8f250 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -761,6 +761,8 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, sql_field->flags|= NOT_NULL_FLAG; sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL; } + else + key_info->flags|= HA_NULL_PART_KEY; if (!(file->table_flags() & HA_NULL_KEY)) { my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX), @@ -772,7 +774,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, my_error(ER_SPATIAL_CANT_HAVE_NULL, MYF(0)); DBUG_RETURN(-1); } - key_info->flags|= HA_NULL_PART_KEY; } if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) { @@ -1088,7 +1089,8 @@ mysql_rename_table(enum db_type base, wait_while_table_is_used() thd Thread handler table Table to remove from cache - + function HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted + HA_EXTRA_FORCE_REOPEN if table is not be used NOTES When returning, the table will be unusable for other threads until the table is closed. @@ -1098,13 +1100,14 @@ mysql_rename_table(enum db_type base, Win32 clients must also have a WRITE LOCK on the table ! */ -static void wait_while_table_is_used(THD *thd,TABLE *table) +static void wait_while_table_is_used(THD *thd,TABLE *table, + enum ha_extra_function function) { DBUG_PRINT("enter",("table: %s", table->real_name)); DBUG_ENTER("wait_while_table_is_used"); safe_mutex_assert_owner(&LOCK_open); - VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Close all data files + VOID(table->file->extra(function)); /* Mark all tables that are in use as 'old' */ mysql_lock_abort(thd, table); // end threads waiting on lock @@ -1140,7 +1143,7 @@ static bool close_cached_table(THD *thd, TABLE *table) { DBUG_ENTER("close_cached_table"); - wait_while_table_is_used(thd,table); + wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE); /* Close lock if this is not got with LOCK TABLES */ if (thd->lock) { @@ -1366,6 +1369,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, if (protocol->send_fields(&field_list, 1)) DBUG_RETURN(-1); + mysql_ha_closeall(thd, tables); for (table = tables; table; table = table->next) { char table_name[NAME_LEN*2+2]; @@ -1852,7 +1856,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, break; case ENABLE: VOID(pthread_mutex_lock(&LOCK_open)); - wait_while_table_is_used(thd, table); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); VOID(pthread_mutex_unlock(&LOCK_open)); error= table->file->activate_all_index(thd); /* COND_refresh will be signaled in close_thread_tables() */ @@ -1861,7 +1865,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (table->db_type == DB_TYPE_MYISAM) { VOID(pthread_mutex_lock(&LOCK_open)); - wait_while_table_is_used(thd, table); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); VOID(pthread_mutex_unlock(&LOCK_open)); table->file->deactivate_non_unique_index(HA_POS_ERROR); } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 57044a8370a..4a61639b3ee 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -83,7 +83,7 @@ bool select_union::send_data(List<Item> &values) { thd->clear_error(); // do not report user about table overflow if (create_myisam_from_heap(thd, table, &tmp_table_param, - info.last_errno, 0)) + info.last_errno, 1)) return 1; } else @@ -211,7 +211,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, select_limit_cnt= sl->select_limit+sl->offset_limit; if (select_limit_cnt < sl->select_limit) select_limit_cnt= HA_POS_ERROR; // no limit - if (select_limit_cnt == HA_POS_ERROR) + if (select_limit_cnt == HA_POS_ERROR && !sl->braces) sl->options&= ~OPTION_FOUND_ROWS; res= join->prepare(&sl->ref_pointer_array, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index e1c28dd0e4d..199693df7d6 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -91,6 +91,7 @@ int mysql_update(THD *thd, bzero((char*) &tables,sizeof(tables)); // For ORDER BY tables.table= table; + tables.alias= table_list->alias; if (setup_tables(update_table_list) || setup_conds(thd,update_table_list,&conds) || diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aef58e6cb94..f3631263735 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -154,6 +154,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token RESET_SYM %token ROLLBACK_SYM %token ROLLUP_SYM +%token SAVEPOINT_SYM %token SELECT_SYM %token SHOW %token SLAVE @@ -616,7 +617,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); key_alg opt_btree_or_rtree %type <string_list> - key_usage_list + key_usage_list %type <key_part> key_part @@ -665,7 +666,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); query verb_clause create change select do drop insert replace insert2 insert_values update delete truncate rename show describe load alter optimize preload flush - reset purge begin commit rollback slave master_def master_defs + reset purge begin commit rollback savepoint + slave master_def master_defs repair restore backup analyze check start field_list field_list_item field_spec kill column_def key_def preload_list preload_keys @@ -746,6 +748,7 @@ verb_clause: | restore | revoke | rollback + | savepoint | select | set | slave @@ -814,10 +817,10 @@ master_def: MASTER_LOG_POS_SYM EQ ulonglong_num { Lex->mi.pos = $3; - /* + /* If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it - instead of causing subsequent errors. - We need to do it in this file, because only there we know that + instead of causing subsequent errors. + We need to do it in this file, because only there we know that MASTER_LOG_POS has been explicitely specified. On the contrary in change_master() (sql_repl.cc) we cannot distinguish between 0 (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified), @@ -876,7 +879,7 @@ create: lex->name=0; } create2 - {} + { Lex->select= &Lex->select_lex; } | CREATE opt_unique_or_fulltext INDEX ident key_alg ON table_ident { LEX *lex=Lex; @@ -896,7 +899,7 @@ create: lex->key_list.push_back(new Key($2,$4.str, $5, lex->col_list)); lex->col_list.empty(); } - | CREATE DATABASE opt_if_not_exists ident + | CREATE DATABASE opt_if_not_exists ident { Lex->create_info.table_charset=NULL; } opt_create_database_options { @@ -921,7 +924,7 @@ create: ; create2: - '(' field_list ')' opt_create_table_options create3 {} + '(' create2a {} | opt_create_table_options create3 {} | LIKE table_ident { @@ -935,14 +938,31 @@ create2: if (!(lex->name= (char *)$3)) YYABORT; } - ; + ; + +create2a: + field_list ')' opt_create_table_options create3 {} + | create_select ')' { Select->braces= 1;} union_opt {} + ; create3: /* empty */ {} - | opt_duplicate opt_as SELECT_SYM + | opt_duplicate opt_as create_select + { Select->braces= 0;} opt_union {} + | opt_duplicate opt_as '(' create_select ')' + { Select->braces= 1;} union_opt {} + ; + +create_select: + SELECT_SYM { LEX *lex=Lex; lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; + if (lex->sql_command == SQLCOM_INSERT) + lex->sql_command= SQLCOM_INSERT_SELECT; + else if (lex->sql_command == SQLCOM_REPLACE) + lex->sql_command= SQLCOM_REPLACE_SELECT; + lex->select->table_list.save_and_clear(&lex->save_list); mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LEX_NODE::SELECT_LIST; } @@ -950,8 +970,9 @@ create3: { Select->parsing_place= SELECT_LEX_NODE::NO_MATTER; } - opt_select_from union_clause {} - ; + opt_select_from + { Lex->select->table_list.push_front(&Lex->save_list); } + ; opt_as: /* empty */ {} @@ -1169,10 +1190,10 @@ type: | char opt_binary { Lex->length=(char*) "1"; $$=FIELD_TYPE_STRING; } | nchar '(' NUM ')' { Lex->length=$3.str; - $$=FIELD_TYPE_STRING; + $$=FIELD_TYPE_STRING; Lex->charset=national_charset_info; } | nchar { Lex->length=(char*) "1"; - $$=FIELD_TYPE_STRING; + $$=FIELD_TYPE_STRING; Lex->charset=national_charset_info; } | BINARY '(' NUM ')' { Lex->length=$3.str; Lex->charset=&my_charset_bin; @@ -1351,8 +1372,8 @@ attribute: | UNIQUE_SYM { Lex->type|= UNIQUE_FLAG; } | UNIQUE_SYM KEY_SYM { Lex->type|= UNIQUE_KEY_FLAG; } | COMMENT_SYM text_literal { Lex->comment= $2; } - | COLLATE_SYM collation_name - { + | COLLATE_SYM collation_name + { if (Lex->charset && !my_charset_same(Lex->charset,$2)) { net_printf(YYTHD,ER_COLLATION_CHARSET_MISMATCH, @@ -1817,7 +1838,7 @@ optimize: { LEX *lex=Lex; lex->sql_command = SQLCOM_OPTIMIZE; - lex->no_write_to_binlog= $2; + lex->no_write_to_binlog= $2; lex->check_opt.init(); } table_list opt_mi_check_type @@ -1846,7 +1867,7 @@ table_to_table_list: table_to_table: table_ident TO_SYM table_ident { - LEX *lex=Lex; + LEX *lex=Lex; SELECT_LEX_NODE *sl= lex->current_select; if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING, TL_IGNORE) || @@ -1874,8 +1895,8 @@ preload_keys: { LEX *lex=Lex; SELECT_LEX *sel= &lex->select_lex; - if (!sel->add_table_to_list(lex->thd, $1, NULL, $3, - TL_READ, + if (!sel->add_table_to_list(lex->thd, $1, NULL, $3, + TL_READ, sel->get_use_index(), (List<String> *)0)) YYABORT; @@ -1884,13 +1905,13 @@ preload_keys: preload_keys_spec: keys_or_index { Select->select_lex()->interval_list.empty(); } - preload_key_list_or_empty + preload_key_list_or_empty { LEX *lex=Lex; SELECT_LEX *sel= &lex->select_lex; sel->use_index= sel->interval_list; sel->use_index_ptr= &sel->use_index; - } + } ; preload_key_list_or_empty: @@ -2274,7 +2295,7 @@ interval_expr: simple_expr: simple_ident | simple_expr COLLATE_SYM ident_or_text %prec NEG - { + { $$= new Item_func_set_collation($1, new Item_string($3.str, $3.length, @@ -2324,7 +2345,7 @@ simple_expr: { Select->add_ftfunc_to_list((Item_func_match *) ($$=new Item_func_match_bool(*$2,$5))); } | ASCII_SYM '(' expr ')' { $$= new Item_func_ascii($3); } - | BINARY expr %prec NEG + | BINARY expr %prec NEG { $$= new Item_func_set_collation($2,new Item_string(binary_keyword, 6, &my_charset_latin1)); @@ -2500,14 +2521,14 @@ simple_expr: | LINEFROMTEXT '(' expr ',' expr ')' { $$= new Item_func_geometry_from_text($3, $5); } | MASTER_POS_WAIT '(' expr ',' expr ')' - { + { $$= new Item_master_pos_wait($3, $5); - Lex->safe_to_cache_query=0; + Lex->safe_to_cache_query=0; } | MASTER_POS_WAIT '(' expr ',' expr ',' expr ')' - { + { $$= new Item_master_pos_wait($3, $5, $7); - Lex->safe_to_cache_query=0; + Lex->safe_to_cache_query=0; } | MICROSECOND_SYM '(' expr ')' { $$= new Item_func_microsecond($3); } @@ -2670,9 +2691,9 @@ simple_expr: | USER '(' ')' { $$= new Item_func_user(); Lex->safe_to_cache_query=0; } | WEEK_SYM '(' expr ')' - { + { $$= new Item_func_week($3,new Item_int((char*) "0", - YYTHD->variables.default_week_format,1)); + YYTHD->variables.default_week_format,1)); } | WEEK_SYM '(' expr ',' expr ')' { $$= new Item_func_week($3,$5); } @@ -2723,9 +2744,10 @@ sum_expr: { $$=new Item_sum_variance($3); } | SUM_SYM '(' in_sum_expr ')' { $$=new Item_sum_sum($3); } - | GROUP_CONCAT_SYM '(' opt_distinct expr_list opt_gorder_clause opt_gconcat_separator ')' - { - $$=new Item_func_group_concat($3,$4,Lex->gorder_list,$6); + | GROUP_CONCAT_SYM '(' opt_distinct expr_list opt_gorder_clause + opt_gconcat_separator ')' + { + $$=new Item_func_group_concat($3,$4,Lex->gorder_list,$6); $4->empty(); }; @@ -2736,10 +2758,10 @@ opt_distinct: opt_gconcat_separator: /* empty */ { $$ = new String(",",1,default_charset_info); } |SEPARATOR_SYM text_string { $$ = $2; }; - + opt_gorder_clause: - /* empty */ + /* empty */ { LEX *lex=Lex; lex->gorder_list = NULL; @@ -2750,7 +2772,7 @@ opt_gorder_clause: lex->gorder_list= (SQL_LIST*) sql_memdup((char*) &lex->current_select->order_list,sizeof(st_sql_list)); lex->current_select->order_list.empty(); }; - + in_sum_expr: opt_all @@ -2839,7 +2861,7 @@ join_table_list: | join_table_list normal_join join_table_list ON expr { add_join_on($3,$5); $$=$3; } | join_table_list normal_join join_table_list - USING + USING { SELECT_LEX *sel= Select->select_lex(); sel->db1=$1->db; sel->table1=$1->alias; @@ -2925,8 +2947,9 @@ select_derived: { LEX *lex= Lex; lex->derived_tables= 1; - if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && - lex->sql_command <= (int)SQLCOM_HA_READ) || lex->sql_command == (int)SQLCOM_KILL) + if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && + lex->sql_command <= (int)SQLCOM_HA_READ) || + lex->sql_command == (int)SQLCOM_KILL) { send_error(lex->thd, ER_SYNTAX_ERROR); YYABORT; @@ -3368,7 +3391,7 @@ drop: LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_USER; lex->users_list.empty(); - } + } user_list {} ; @@ -3411,6 +3434,7 @@ insert: opt_ignore insert2 { Select->set_lock_for_tables($3); + Lex->select= &Lex->select_lex; } insert_field_spec opt_insert_update {} @@ -3427,6 +3451,7 @@ replace: replace_lock_option insert2 { Select->set_lock_for_tables($3); + Lex->select= &Lex->select_lex; } insert_field_spec {} @@ -3458,7 +3483,9 @@ insert_table: }; insert_field_spec: - opt_field_spec insert_values {} + insert_values {} + | '(' ')' insert_values {} + | '(' fields ')' insert_values {} | SET { LEX *lex=Lex; @@ -3480,27 +3507,9 @@ fields: insert_values: VALUES values_list {} | VALUE_SYM values_list {} - | SELECT_SYM - { - LEX *lex=Lex; - lex->sql_command = (lex->sql_command == SQLCOM_INSERT ? - SQLCOM_INSERT_SELECT : SQLCOM_REPLACE_SELECT); - lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; - mysql_init_select(lex); - /* - it is not simple select => table list will be - preprocessed before passing to handle_select - */ - lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE; - lex->current_select->parsing_place= SELECT_LEX_NODE::SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= SELECT_LEX_NODE::NO_MATTER; - } - opt_select_from select_lock_type - union_clause {} - ; + | create_select { Select->braces= 0;} opt_union {} + | '(' create_select ')' { Select->braces= 1;} union_opt {} + ; values_list: values_list ',' no_braces @@ -3905,7 +3914,7 @@ flush: { LEX *lex=Lex; lex->sql_command= SQLCOM_FLUSH; lex->type=0; - lex->no_write_to_binlog= $2; + lex->no_write_to_binlog= $2; } flush_options {} @@ -3974,7 +3983,7 @@ purge_option: if ($2->check_cols(1) || $2->fix_fields(Lex->thd, 0, &$2)) { net_printf(Lex->thd, ER_WRONG_ARGUMENTS, "PURGE LOGS BEFORE"); - YYABORT; + YYABORT; } Item *tmp= new Item_func_unix_timestamp($2); Lex->sql_command = SQLCOM_PURGE_BEFORE; @@ -4146,8 +4155,8 @@ literal: { Item *tmp= new Item_varbinary($2.str,$2.length); String *str= tmp ? tmp->val_str((String*) 0) : (String*) 0; - $$ = new Item_string(str ? str->ptr() : "", str ? str->length() : 0, - Lex->charset); + $$ = new Item_string(str ? str->ptr() : "", str ? str->length() : + 0, Lex->charset); } | DATE_SYM text_literal { $$ = $2; } | TIME_SYM text_literal { $$ = $2; } @@ -4162,7 +4171,7 @@ insert_ident: | table_wild { $$=$1; }; table_wild: - ident '.' '*' + ident '.' '*' { $$ = new Item_field(NullS,$1.str,"*"); Lex->current_select->select_lex()->with_wild++; @@ -4195,7 +4204,7 @@ simple_ident: SELECT_LEX_NODE *sel= lex->current_select; if (sel->no_table_names_allowed) { - my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, + my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, ER(ER_TABLENAME_NOT_ALLOWED_HERE), MYF(0), $1.str, thd->where); } @@ -4211,7 +4220,7 @@ simple_ident: SELECT_LEX_NODE *sel= lex->current_select; if (sel->no_table_names_allowed) { - my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, + my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, ER(ER_TABLENAME_NOT_ALLOWED_HERE), MYF(0), $2.str, thd->where); } @@ -4227,7 +4236,7 @@ simple_ident: SELECT_LEX_NODE *sel= lex->current_select; if (sel->no_table_names_allowed) { - my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, + my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, ER(ER_TABLENAME_NOT_ALLOWED_HERE), MYF(0), $3.str, thd->where); } @@ -4489,6 +4498,7 @@ keyword: | ROWS_SYM {} | ROW_FORMAT_SYM {} | ROW_SYM {} + | SAVEPOINT_SYM {} | SECOND_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} @@ -4838,7 +4848,7 @@ grant: lex->select_lex.db= 0; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; - bzero(&(lex->mqh),sizeof(lex->mqh)); + bzero((char *)&(lex->mqh),sizeof(lex->mqh)); } grant_privileges ON opt_table TO_SYM user_list require_clause grant_options @@ -5099,8 +5109,21 @@ commit: COMMIT_SYM { Lex->sql_command = SQLCOM_COMMIT;}; rollback: - ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;}; - + ROLLBACK_SYM + { + Lex->sql_command = SQLCOM_ROLLBACK; + } + | ROLLBACK_SYM TO_SYM SAVEPOINT_SYM ident + { + Lex->sql_command = SQLCOM_ROLLBACK_TO_SAVEPOINT; + Lex->savepoint_name = $4.str; + }; +savepoint: + SAVEPOINT_SYM ident + { + Lex->sql_command = SQLCOM_SAVEPOINT; + Lex->savepoint_name = $2.str; + }; /* UNIONS : glue selects together diff --git a/sql/table.cc b/sql/table.cc index 1cee0587f17..a76bdc84690 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -484,7 +484,10 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, /* This has to be done after the above fulltext correction */ index_flags=outparam->file->index_flags(key); if (!(index_flags & HA_KEY_READ_ONLY)) + { + outparam->read_only_keys|= ((key_map) 1 << key); outparam->keys_for_keyread&= ~((key_map) 1 << key); + } if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME)) { diff --git a/sql/table.h b/sql/table.h index 2fab0087da4..4b8a5a78e1f 100644 --- a/sql/table.h +++ b/sql/table.h @@ -76,7 +76,7 @@ struct st_table { uint uniques; uint null_fields; /* number of null fields */ uint blob_fields; /* number of blob fields */ - key_map keys_in_use, keys_for_keyread; + key_map keys_in_use, keys_for_keyread, read_only_keys; key_map quick_keys, used_keys, keys_in_use_for_query; KEY *key_info; /* data of keys in database */ TYPELIB keynames; /* Pointers to keynames */ diff --git a/sql/uniques.cc b/sql/uniques.cc index c6fb0f25643..4514de834a8 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -49,8 +49,8 @@ int unique_write_to_ptrs(gptr key, element_count count, Unique *unique) } Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, - uint size, ulong max_in_memory_size_arg) - :max_in_memory_size(max_in_memory_size_arg),elements(0) + uint size_arg, ulong max_in_memory_size_arg) + :max_in_memory_size(max_in_memory_size_arg), size(size_arg), elements(0) { my_b_clear(&file); init_tree(&tree, max_in_memory_size / 16, 0, size, comp_func, 0, NULL, @@ -101,7 +101,7 @@ bool Unique::get(TABLE *table) { /* Whole tree is in memory; Don't use disk if you don't need to */ if ((record_pointers=table->sort.record_pointers= (byte*) - my_malloc(tree.size_of_element * tree.elements_in_tree, MYF(0)))) + my_malloc(size * tree.elements_in_tree, MYF(0)))) { (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs, this, left_root_right); |