summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <monty@narttu.mysql.fi>2008-04-28 19:24:05 +0300
committerunknown <monty@narttu.mysql.fi>2008-04-28 19:24:05 +0300
commit50ceea65cf18a64db017f06d2be5197807927588 (patch)
treeff5ab0c71ce5a2792c4f169a880e0174f8e194ad /sql
parented5fe6d3eb5d702e2fe27de3c65c550ea0dbf17f (diff)
parentc481f6b3cf80f471321d2ef4e7c68fe9e6e5d0ca (diff)
downloadmariadb-git-50ceea65cf18a64db017f06d2be5197807927588.tar.gz
Merge mysql.com:/home/my/mysql-5.1
into mysql.com:/home/my/mysql-new BitKeeper/etc/ignore: auto-union BUILD/SETUP.sh: Auto merged CMakeLists.txt: Auto merged client/get_password.c: Auto merged client/mysqldump.c: Auto merged client/mysqltest.c: Auto merged cmd-line-utils/readline/bind.c: Auto merged cmd-line-utils/readline/display.c: Auto merged cmd-line-utils/readline/histexpand.c: Auto merged cmd-line-utils/readline/history.c: Auto merged cmd-line-utils/readline/readline.c: Auto merged cmd-line-utils/readline/text.c: Auto merged dbug/user.r: Auto merged extra/yassl/src/handshake.cpp: Auto merged include/config-win.h: Auto merged include/m_string.h: Auto merged include/my_global.h: Auto merged include/my_pthread.h: Auto merged include/mysql/plugin.h: Auto merged include/mysql_com.h: Auto merged include/thr_alarm.h: Auto merged libmysql/CMakeLists.txt: Auto merged libmysql/Makefile.shared: Auto merged libmysql/dll.c: Auto merged libmysql/get_password.c: Auto merged libmysql/libmysql.c: Auto merged libmysqld/Makefile.am: Auto merged mysql-test/lib/mtr_cases.pl: Auto merged mysql-test/mysql-test-run.pl: Auto merged mysql-test/r/alter_table.result: Auto merged mysql-test/r/change_user.result: Auto merged mysql-test/r/create.result: Auto merged mysql-test/r/innodb.result: Auto merged mysql-test/r/merge.result: Auto merged mysql-test/r/mix2_myisam.result: Auto merged mysql-test/r/mysqldump.result: Auto merged mysql-test/r/query_cache.result: Auto merged mysql-test/r/subselect.result: Auto merged mysql-test/valgrind.supp: Auto merged mysql-test/r/view.result: Auto merged mysql-test/suite/rpl/r/rpl_events.result: Auto merged mysql-test/suite/rpl/t/rpl_switch_stm_row_mixed.test: Auto merged mysql-test/t/create.test: Auto merged mysql-test/t/mysqldump.test: Auto merged mysql-test/t/query_cache.test: Auto merged mysql-test/t/subselect.test: Auto merged mysql-test/t/variables.test: Auto merged mysql-test/t/view.test: Auto merged mysys/mf_iocache.c: Auto merged mysys/mf_tempfile.c: Auto merged mysys/my_atomic.c: Auto merged mysys/my_bit.c: Auto merged mysys/my_bitmap.c: Auto merged mysys/my_compress.c: Auto merged mysys/my_create.c: Auto merged mysys/my_delete.c: Auto merged mysys/my_error.c: Auto merged mysys/my_init.c: Auto merged mysys/my_open.c: Auto merged mysys/my_realloc.c: Auto merged mysys/my_rename.c: Auto merged mysys/my_symlink.c: Auto merged mysys/my_sync.c: Auto merged mysys/my_thr_init.c: Auto merged mysys/thr_alarm.c: Auto merged mysys/thr_lock.c: Auto merged scripts/make_binary_distribution.sh: Auto merged server-tools/instance-manager/mysql_connection.cc: Auto merged sql/CMakeLists.txt: Auto merged sql/Makefile.am: Auto merged sql/events.cc: Auto merged sql/field.cc: Auto merged sql/field.h: Auto merged sql/filesort.cc: Auto merged sql/gen_lex_hash.cc: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/ha_partition.h: Auto merged sql/handler.h: Auto merged sql/item.cc: Auto merged sql/item.h: Auto merged sql/item_cmpfunc.cc: Auto merged sql/item_func.cc: Auto merged sql/item_func.h: Auto merged sql/item_strfunc.cc: Auto merged sql/item_strfunc.h: Auto merged sql/item_subselect.cc: Auto merged sql/lock.cc: Auto merged sql/log.cc: Auto merged sql/log_event.cc: Auto merged sql/net_serv.cc: Auto merged sql/opt_range.cc: Auto merged sql/partition_info.cc: Auto merged sql/rpl_injector.cc: Auto merged sql/set_var.cc: Auto merged sql/slave.cc: Auto merged sql/slave.h: Auto merged sql/sp_head.cc: Auto merged sql/sql_acl.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_cache.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_delete.cc: Auto merged sql/sql_load.cc: Auto merged sql/sql_plugin.cc: Auto merged sql/sql_prepare.cc: Auto merged sql/sql_repl.cc: Auto merged sql/sql_test.cc: Auto merged sql/sql_union.cc: Auto merged sql/sql_update.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/table.cc: Auto merged sql/table.h: Auto merged sql/unireg.cc: Auto merged sql/share/errmsg.txt: Auto merged storage/csv/ha_tina.cc: Auto merged storage/csv/ha_tina.h: Auto merged storage/myisam/CMakeLists.txt: Auto merged storage/myisam/ft_boolean_search.c: Auto merged storage/myisam/ft_eval.c: Auto merged storage/myisam/ft_nlq_search.c: Auto merged storage/myisam/ft_parser.c: Auto merged storage/myisam/ft_static.c: Auto merged storage/myisam/ft_stopwords.c: Auto merged storage/myisam/ft_test1.c: Auto merged storage/myisam/ft_update.c: Auto merged storage/myisam/ha_myisam.cc: Auto merged storage/myisam/mi_check.c: Auto merged storage/myisam/mi_create.c: Auto merged storage/myisam/mi_delete.c: Auto merged storage/myisam/mi_delete_all.c: Auto merged storage/myisam/mi_dynrec.c: Auto merged storage/myisam/mi_key.c: Auto merged storage/myisam/mi_packrec.c: Auto merged storage/myisam/mi_range.c: Auto merged storage/myisam/mi_search.c: Auto merged storage/myisam/mi_test1.c: Auto merged storage/myisam/mi_test2.c: Auto merged storage/myisam/mi_test3.c: Auto merged storage/myisam/mi_unique.c: Auto merged storage/myisam/mi_write.c: Auto merged storage/myisam/myisamchk.c: Auto merged storage/myisam/myisamdef.h: Auto merged storage/myisam/myisampack.c: Auto merged storage/myisam/sort.c: Auto merged storage/myisam/sp_test.c: Auto merged support-files/mysql.spec.sh: Auto merged tests/mysql_client_test.c: Auto merged configure.in: Manual merge dbug/dbug.c: Restore to original state in Maria tree The big diff comes from a wrong pull from 5.0 -> 5.1 after backporting dbug to 5.0 from 5.1 include/Makefile.am: Manual merge include/my_atomic.h: Ignore changes include/my_base.h: Manual merge include/my_dbug.h: Use orginal my_dbug.h from maria tree include/my_handler.h: Manual merge include/my_sys.h: Manual merge include/myisam.h: Manual merge mysql-test/lib/mtr_report.pl: Manual merge mysql-test/r/myisam.result: Manual merge mysql-test/suite/binlog/r/binlog_unsafe.result: Manual merge mysql-test/suite/binlog/t/binlog_unsafe.test: Manual merge mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result: Manual merge mysql-test/suite/rpl/r/rpl_switch_stm_row_mixed.result: No changes mysql-test/suite/rpl/t/rpl_row_flsh_tbls.test: Manual merge mysql-test/t/change_user.test: Manual merge mysql-test/t/disabled.def: Manual merge mysql-test/t/merge.test: No changes mysql-test/t/myisam.test: Manual merge mysys/Makefile.am: Manual merge mysys/array.c: Manual merge mysys/mf_keycache.c: Manual merge mysys/my_getsystime.c: Manual merge mysys/my_handler.c: Manual merge mysys/my_pread.c: Manual merge mysys/safemalloc.c: Manual merge sql/ha_partition.cc: Manual merge sql/handler.cc: Manual merge sql/lex.h: Manual merge sql/mysql_priv.h: Manual merge sql/mysqld.cc: Manual merge sql/set_var.h: Manual merge sql/sql_class.cc: Manual merge sql/sql_insert.cc: Manual merge sql/sql_parse.cc: Manual merge sql/sql_select.cc: Manual merge sql/sql_show.cc: Manual merge sql/sql_table.cc: Manual merge storage/myisam/mi_checksum.c: No changes storage/myisam/mi_extra.c: Manual merge storage/myisam/mi_open.c: Manual merge storage/myisammrg/ha_myisammrg.cc: Manual merge strings/strmake.c: No changes
Diffstat (limited to 'sql')
-rwxr-xr-xsql/CMakeLists.txt4
-rw-r--r--sql/Makefile.am3
-rw-r--r--sql/event_db_repository.cc69
-rw-r--r--sql/event_queue.cc38
-rw-r--r--sql/events.cc9
-rw-r--r--sql/field.cc112
-rw-r--r--sql/field.h5
-rw-r--r--sql/filesort.cc10
-rw-r--r--sql/ha_ndbcluster.cc96
-rw-r--r--sql/ha_ndbcluster.h8
-rw-r--r--sql/ha_ndbcluster_binlog.cc16
-rw-r--r--sql/ha_ndbcluster_cond.cc9
-rw-r--r--sql/ha_ndbcluster_cond.h1
-rw-r--r--sql/ha_partition.cc26
-rw-r--r--sql/handler.cc840
-rw-r--r--sql/handler.h125
-rw-r--r--sql/item.cc286
-rw-r--r--sql/item.h226
-rw-r--r--sql/item_cmpfunc.cc61
-rw-r--r--sql/item_cmpfunc.h51
-rw-r--r--sql/item_create.cc4
-rw-r--r--sql/item_func.cc129
-rw-r--r--sql/item_func.h91
-rw-r--r--sql/item_geofunc.h9
-rw-r--r--sql/item_row.cc4
-rw-r--r--sql/item_row.h2
-rw-r--r--sql/item_strfunc.cc32
-rw-r--r--sql/item_strfunc.h12
-rw-r--r--sql/item_subselect.cc75
-rw-r--r--sql/item_subselect.h20
-rw-r--r--sql/item_sum.cc91
-rw-r--r--sql/item_sum.h13
-rw-r--r--sql/item_timefunc.cc32
-rw-r--r--sql/item_timefunc.h14
-rw-r--r--sql/key.cc7
-rw-r--r--sql/log.cc480
-rw-r--r--sql/log_event.cc141
-rw-r--r--sql/log_event.h100
-rw-r--r--sql/log_event_old.cc22
-rw-r--r--sql/matherr.c42
-rw-r--r--sql/mysql_priv.h64
-rw-r--r--sql/mysqld.cc318
-rw-r--r--sql/net_serv.cc24
-rw-r--r--sql/opt_range.cc90
-rw-r--r--sql/partition_info.cc352
-rw-r--r--sql/partition_info.h3
-rw-r--r--sql/procedure.h2
-rw-r--r--sql/protocol.cc8
-rw-r--r--sql/repl_failsafe.cc23
-rw-r--r--sql/rpl_injector.cc20
-rw-r--r--sql/rpl_mi.cc4
-rw-r--r--sql/rpl_rli.cc17
-rw-r--r--sql/rpl_rli.h2
-rw-r--r--sql/set_var.cc196
-rw-r--r--sql/set_var.h154
-rw-r--r--sql/share/errmsg.txt15
-rw-r--r--sql/slave.cc177
-rw-r--r--sql/slave.h3
-rw-r--r--sql/sp.cc71
-rw-r--r--sql/sp_head.cc21
-rw-r--r--sql/sp_pcontext.cc2
-rw-r--r--sql/sql_acl.cc151
-rw-r--r--sql/sql_acl.h6
-rw-r--r--sql/sql_analyse.cc2
-rw-r--r--sql/sql_analyse.h2
-rw-r--r--sql/sql_base.cc71
-rw-r--r--sql/sql_binlog.cc3
-rw-r--r--sql/sql_bitmap.h2
-rw-r--r--sql/sql_cache.cc3
-rw-r--r--sql/sql_class.cc108
-rw-r--r--sql/sql_class.h46
-rw-r--r--sql/sql_connect.cc59
-rw-r--r--sql/sql_cursor.cc103
-rw-r--r--sql/sql_db.cc21
-rw-r--r--sql/sql_delete.cc85
-rw-r--r--sql/sql_do.cc14
-rw-r--r--sql/sql_error.cc2
-rw-r--r--sql/sql_handler.cc6
-rw-r--r--sql/sql_help.cc2
-rw-r--r--sql/sql_insert.cc98
-rw-r--r--sql/sql_lex.cc30
-rw-r--r--sql/sql_lex.h28
-rw-r--r--sql/sql_load.cc70
-rw-r--r--sql/sql_parse.cc354
-rw-r--r--sql/sql_partition.cc325
-rw-r--r--sql/sql_plugin.cc106
-rw-r--r--sql/sql_prepare.cc69
-rw-r--r--sql/sql_profile.cc2
-rw-r--r--sql/sql_rename.cc2
-rw-r--r--sql/sql_repl.cc20
-rw-r--r--sql/sql_repl.h6
-rw-r--r--sql/sql_select.cc193
-rw-r--r--sql/sql_servers.cc76
-rw-r--r--sql/sql_show.cc59
-rw-r--r--sql/sql_string.cc62
-rw-r--r--sql/sql_string.h3
-rw-r--r--sql/sql_table.cc125
-rw-r--r--sql/sql_test.cc6
-rw-r--r--sql/sql_trigger.cc4
-rw-r--r--sql/sql_udf.cc9
-rw-r--r--sql/sql_update.cc88
-rw-r--r--sql/sql_view.cc309
-rw-r--r--sql/sql_view.h3
-rw-r--r--sql/sql_yacc.yy122
-rw-r--r--sql/stacktrace.c339
-rw-r--r--sql/stacktrace.h30
-rw-r--r--sql/structs.h21
-rw-r--r--sql/table.cc41
-rw-r--r--sql/table.h26
-rw-r--r--sql/unireg.cc4
-rw-r--r--sql/unireg.h5
111 files changed, 5702 insertions, 2500 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index d7c712c8545..25223f41b21 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -43,7 +43,7 @@ ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN)
ADD_EXECUTABLE(mysqld
../sql-common/client.c derror.cc des_key_file.cc
- discover.cc ../libmysql/errmsg.c field.cc field_conv.cc
+ discover.cc ../libmysql/errmsg.c field.cc stacktrace.c stacktrace.h field_conv.cc
filesort.cc gstream.cc
ha_partition.cc
handler.cc hash_filo.cc hash_filo.h
@@ -171,5 +171,5 @@ SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
"lex_hash.h;message.rc;message.h;sql_yacc.h;sql_yacc.cc")
ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def)
-ADD_DEPENDENCIES(udf_example strings)
+ADD_DEPENDENCIES(udf_example strings GenError)
TARGET_LINK_LIBRARIES(udf_example strings wsock32)
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 194b6ab29ef..3a6f4bcb7a2 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -19,6 +19,7 @@ MYSQLDATAdir = $(localstatedir)
MYSQLSHAREdir = $(pkgdatadir)
MYSQLBASEdir= $(prefix)
MYSQLLIBdir= $(pkglibdir)
+pkgplugindir = $(pkglibdir)/plugin
INCLUDES = @ZLIB_INCLUDES@ \
-I$(top_builddir)/include -I$(top_srcdir)/include \
-I$(top_srcdir)/regex -I$(srcdir) $(openssl_includes)
@@ -138,7 +139,7 @@ DEFS = -DMYSQL_SERVER \
-DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
-DDATADIR="\"$(MYSQLDATAdir)\"" \
-DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
- -DLIBDIR="\"$(MYSQLLIBdir)\"" \
+ -DPLUGINDIR="\"$(pkgplugindir)\"" \
@DEFS@
BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.h
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 9a33b33d8c9..401f76f5d26 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -111,7 +111,7 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
- "'HIGH_NOT_PRECEDENCE')") },
+ "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
{NULL, 0}
},
{
@@ -172,11 +172,13 @@ mysql_event_fill_row(THD *thd,
TABLE *table,
Event_parse_data *et,
sp_head *sp,
+ ulong sql_mode,
my_bool is_update)
{
CHARSET_INFO *scs= system_charset_info;
enum enum_events_table_field f_num;
Field **fields= table->field;
+ int rs= FALSE;
DBUG_ENTER("mysql_event_fill_row");
@@ -205,12 +207,9 @@ mysql_event_fill_row(THD *thd,
goto err_truncate;
/* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
- fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
-
- fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
-
- fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
-
+ rs|= fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
+ rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
+ rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
/*
Change the SQL_MODE only if body was present in an ALTER EVENT and of course
@@ -220,7 +219,7 @@ mysql_event_fill_row(THD *thd,
{
DBUG_ASSERT(sp->m_body.str);
- fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE);
+ rs|= fields[ET_FIELD_SQL_MODE]->store((longlong)sql_mode, TRUE);
if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
sp->m_body.length,
@@ -236,16 +235,16 @@ mysql_event_fill_row(THD *thd,
if (!is_update || !et->starts_null)
{
fields[ET_FIELD_TIME_ZONE]->set_notnull();
- fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
- tz_name->charset());
+ rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
+ tz_name->charset());
}
fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
- fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
+ rs|= fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
- fields[ET_FIELD_TRANSIENT_INTERVAL]->
+ rs|= fields[ET_FIELD_TRANSIENT_INTERVAL]->
store(interval_type_to_name[et->interval].str,
interval_type_to_name[et->interval].length,
scs);
@@ -274,8 +273,8 @@ mysql_event_fill_row(THD *thd,
{
const String *tz_name= thd->variables.time_zone->get_name();
fields[ET_FIELD_TIME_ZONE]->set_notnull();
- fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
- tz_name->charset());
+ rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
+ tz_name->charset());
fields[ET_FIELD_INTERVAL_EXPR]->set_null();
fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
@@ -308,13 +307,13 @@ mysql_event_fill_row(THD *thd,
}
fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
- fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
+ rs|= fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
thd->variables.character_set_client->csname,
strlen(thd->variables.character_set_client->csname),
system_charset_info);
fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull();
- fields[ET_FIELD_COLLATION_CONNECTION]->store(
+ rs|= fields[ET_FIELD_COLLATION_CONNECTION]->store(
thd->variables.collation_connection->name,
strlen(thd->variables.collation_connection->name),
system_charset_info);
@@ -323,15 +322,23 @@ mysql_event_fill_row(THD *thd,
CHARSET_INFO *db_cl= get_default_db_collation(thd, et->dbname.str);
fields[ET_FIELD_DB_COLLATION]->set_notnull();
- fields[ET_FIELD_DB_COLLATION]->store(
- db_cl->name, strlen(db_cl->name), system_charset_info);
+ rs|= fields[ET_FIELD_DB_COLLATION]->store(db_cl->name,
+ strlen(db_cl->name),
+ system_charset_info);
}
if (et->body_changed)
{
fields[ET_FIELD_BODY_UTF8]->set_notnull();
- fields[ET_FIELD_BODY_UTF8]->store(
- sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info);
+ rs|= fields[ET_FIELD_BODY_UTF8]->store(sp->m_body_utf8.str,
+ sp->m_body_utf8.length,
+ system_charset_info);
+ }
+
+ if (rs)
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name, rs);
+ DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
@@ -585,12 +592,16 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
int ret= 1;
TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
+ ulong saved_mode= thd->variables.sql_mode;
DBUG_ENTER("Event_db_repository::create_event");
DBUG_PRINT("info", ("open mysql.event for update"));
DBUG_ASSERT(sp);
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
if (open_event_table(thd, TL_WRITE, &table))
goto end;
@@ -646,7 +657,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here
*/
- if (mysql_event_fill_row(thd, table, parse_data, sp, FALSE))
+ if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
goto end;
table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE);
@@ -661,6 +672,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
end:
if (table)
close_thread_tables(thd);
+ thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
}
@@ -691,6 +703,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
CHARSET_INFO *scs= system_charset_info;
TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
+ ulong saved_mode= thd->variables.sql_mode;
int ret= 1;
DBUG_ENTER("Event_db_repository::update_event");
@@ -698,6 +711,9 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
/* None or both must be set */
DBUG_ASSERT(new_dbname && new_name || new_dbname == new_name);
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
if (open_event_table(thd, TL_WRITE, &table))
goto end;
@@ -736,7 +752,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here
*/
- if (mysql_event_fill_row(thd, table, parse_data, sp, TRUE))
+ if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
goto end;
if (new_dbname)
@@ -755,6 +771,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
end:
if (table)
close_thread_tables(thd);
+ thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
}
@@ -950,13 +967,17 @@ bool
Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
LEX_STRING name, Event_basic *etn)
{
- TABLE *table= NULL;
bool ret;
+ TABLE *table= NULL;
+ ulong saved_mode= thd->variables.sql_mode;
DBUG_ENTER("Event_db_repository::load_named_event");
DBUG_PRINT("enter",("thd: 0x%lx name: %*s", (long) thd,
(int) name.length, name.str));
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
if (!(ret= open_event_table(thd, TL_READ, &table)))
{
if ((ret= find_named_event(dbname, name, table)))
@@ -967,7 +988,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
close_thread_tables(thd);
}
-
+ thd->variables.sql_mode= saved_mode;
DBUG_RETURN(ret);
}
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 898fc206c34..719a837cbfb 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -60,8 +60,16 @@ extern "C" int event_queue_element_compare_q(void *, uchar *, uchar *);
int event_queue_element_compare_q(void *vptr, uchar* a, uchar *b)
{
- my_time_t lhs = ((Event_queue_element *)a)->execute_at;
- my_time_t rhs = ((Event_queue_element *)b)->execute_at;
+ Event_queue_element *left = (Event_queue_element *)a;
+ Event_queue_element *right = (Event_queue_element *)b;
+ my_time_t lhs = left->execute_at;
+ my_time_t rhs = right->execute_at;
+
+ if (left->status == Event_queue_element::DISABLED)
+ return right->status != Event_queue_element::DISABLED;
+
+ if (right->status == Event_queue_element::DISABLED)
+ return 1;
return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0));
}
@@ -434,8 +442,34 @@ Event_queue::recalculate_activation_times(THD *thd)
((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
}
queue_fix(&queue);
+ /*
+ The disabled elements are moved to the end during the `fix`.
+ Start from the end and remove all of the elements which are
+ disabled. When we find the first non-disabled one we break, as we
+ have removed all. The queue has been ordered in a way the disabled
+ events are at the end.
+ */
+ for (i= queue.elements; i > 0; i--)
+ {
+ Event_queue_element *element = (Event_queue_element*)queue_element(&queue, i - 1);
+ if (element->status != Event_queue_element::DISABLED)
+ break;
+ /*
+ This won't cause queue re-order, because we remove
+ always the last element.
+ */
+ queue_remove(&queue, i - 1);
+ delete element;
+ }
UNLOCK_QUEUE_DATA();
+ /*
+ XXX: The events are dropped only from memory and not from disk
+ even if `drop_list[j]->dropped` is TRUE. There will be still on the
+ disk till next server restart.
+ Please add code here to do it.
+ */
+
DBUG_VOID_RETURN;
}
diff --git a/sql/events.cc b/sql/events.cc
index f32b23a23eb..309b6b1ac71 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -736,7 +736,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -1184,7 +1184,12 @@ Events::load_events_from_db(THD *thd)
{
/*
If not created, a stale event - drop if immediately if
- ON COMPLETION NOT PRESERVE
+ ON COMPLETION NOT PRESERVE.
+ XXX: This won't be replicated, thus the drop won't appear in
+ in the slave. When the slave is restarted it will drop events.
+ However, as the slave will be "out of sync", it might happen that
+ an event created on the master, after master restart, won't be
+ replicated to the slave correctly, as the create will fail there.
*/
int rc= table->file->ha_delete_row(table->record[0]);
if (rc)
diff --git a/sql/field.cc b/sql/field.cc
index 6e521c05778..f955cf15eb9 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2261,13 +2261,11 @@ int Field_decimal::store(double nr)
return 1;
}
-#ifdef HAVE_FINITE
- if (!finite(nr)) // Handle infinity as special case
+ if (!isfinite(nr)) // Handle infinity as special case
{
overflow(nr < 0.0);
return 1;
}
-#endif
reg4 uint i;
size_t length;
@@ -6321,26 +6319,41 @@ check_string_copy_error(Field_str *field,
}
-
/*
- Send a truncation warning or a truncation error
- after storing a too long character string info a field.
+ Check if we lost any important data and send a truncation error/warning
SYNOPSIS
- report_data_too_long()
- field - Field
+ Field_longstr::report_if_important_data()
+ ptr - Truncated rest of string
+ end - End of truncated string
- RETURN
- N/A
+ RETURN VALUES
+ 0 - None was truncated (or we don't count cut fields)
+ 2 - Some bytes was truncated
+
+ NOTE
+ Check if we lost any important data (anything in a binary string,
+ or any non-space in others). If only trailing spaces was lost,
+ send a truncation note, otherwise send a truncation error.
*/
-inline void
-report_data_too_long(Field_str *field)
+int
+Field_longstr::report_if_important_data(const char *ptr, const char *end)
{
- if (field->table->in_use->abort_on_warning)
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
- else
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ if ((ptr < end) && table->in_use->count_cuted_fields)
+ {
+ if (test_if_important_data(field_charset, ptr, end))
+ {
+ if (table->in_use->abort_on_warning)
+ set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
+ else
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ }
+ else /* If we lost only spaces then produce a NOTE, not a WARNING */
+ set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ return 2;
+ }
+ return 0;
}
@@ -6375,19 +6388,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
cannot_convert_error_pos, from + length, cs))
return 2;
- /*
- Check if we lost any important data (anything in a binary string,
- or any non-space in others).
- */
- if ((from_end_pos < from + length) && table->in_use->count_cuted_fields)
- {
- if (test_if_important_data(field_charset, from_end_pos, from + length))
- {
- report_data_too_long(this);
- return 2;
- }
- }
- return 0;
+ return report_if_important_data(from_end_pos, from + length);
}
@@ -6962,16 +6963,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
cannot_convert_error_pos, from + length, cs))
return 2;
- // Check if we lost something other than just trailing spaces
- if ((from_end_pos < from + length) && table->in_use->count_cuted_fields)
- {
- if (test_if_important_data(field_charset, from_end_pos, from + length))
- report_data_too_long(this);
- else /* If we lost only spaces then produce a NOTE, not a WARNING */
- set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
- return 2;
- }
- return 0;
+ return report_if_important_data(from_end_pos, from + length);
}
@@ -7642,6 +7634,17 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
if (value.alloc(new_length))
goto oom_error;
+
+ if (f_is_hex_escape(flags))
+ {
+ copy_length= my_copy_with_hex_escaping(field_charset,
+ (char*) value.ptr(), new_length,
+ from, length);
+ Field_blob::store_length(copy_length);
+ tmp= value.ptr();
+ bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
+ return 0;
+ }
/*
"length" is OK as "nchars" argument to well_formed_copy_nchars as this
is never used to limit the length of the data. The cut of long data
@@ -7663,13 +7666,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
cannot_convert_error_pos, from + length, cs))
return 2;
- if (from_end_pos < from + length)
- {
- report_data_too_long(this);
- return 2;
- }
-
- return 0;
+ return report_if_important_data(from_end_pos, from + length);
oom_error:
/* Fatal OOM error */
@@ -8617,10 +8614,10 @@ int Field_set::store(longlong nr, bool unsigned_val)
{
ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
- if ((ulonglong) nr > (ulonglong) (((longlong) 1 << typelib->count) -
- (longlong) 1))
+ ulonglong max_nr= set_bits(ulonglong, typelib->count);
+ if ((ulonglong) nr > max_nr)
{
- nr&= (longlong) (((longlong) 1 << typelib->count) - (longlong) 1);
+ nr&= max_nr;
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
error=1;
}
@@ -8791,6 +8788,23 @@ Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
}
+void Field_bit::hash(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ CHARSET_INFO *cs= &my_charset_bin;
+ longlong value= Field_bit::val_int();
+ uchar tmp[8];
+ mi_int8store(tmp,value);
+ cs->coll->hash_sort(cs, tmp, 8, nr, nr2);
+ }
+}
+
+
size_t
Field_bit::do_last_null_byte() const
{
diff --git a/sql/field.h b/sql/field.h
index e03131a10e3..61ad2c39235 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -593,6 +593,8 @@ public:
class Field_longstr :public Field_str
{
+protected:
+ int report_if_important_data(const char *ptr, const char *end);
public:
Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -1904,6 +1906,7 @@ public:
Field::move_field_offset(ptr_diff);
bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
}
+ void hash(ulong *nr, ulong *nr2);
private:
virtual size_t do_last_null_byte() const;
@@ -2068,6 +2071,7 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions);
#define FIELDFLAG_NO_DEFAULT 16384 /* sql */
#define FIELDFLAG_SUM ((uint) 32768)// predit: +#fieldflag
#define FIELDFLAG_MAYBE_NULL ((uint) 32768)// sql
+#define FIELDFLAG_HEX_ESCAPE ((uint) 0x10000)
#define FIELDFLAG_PACK_SHIFT 3
#define FIELDFLAG_DEC_SHIFT 8
#define FIELDFLAG_MAX_DEC 31
@@ -2093,3 +2097,4 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions);
#define f_maybe_null(x) (x & FIELDFLAG_MAYBE_NULL)
#define f_no_default(x) (x & FIELDFLAG_NO_DEFAULT)
#define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR)
+#define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE)
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 12cc76b5852..66a1bf87d25 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -120,6 +120,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
FILESORT_INFO table_sort;
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
+
+ /*
+ Release InnoDB's adaptive hash index latch (if holding) before
+ running a sort.
+ */
+ ha_release_temporary_latches(thd);
+
/*
Don't use table->sort in filesort as it is also used by
QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
@@ -223,8 +230,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
sort_keys= table_sort.sort_keys;
if (memavl < min_sort_memory)
{
- my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),
- thd->variables.sortbuff_size);
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG));
goto err;
}
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 441da21bf1c..1cfe403407e 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -316,6 +316,10 @@ int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans,
/*
Place holder for ha_ndbcluster thread specific data
*/
+typedef struct st_thd_ndb_share {
+ const void *key;
+ struct Ndb_local_table_statistics stat;
+} THD_NDB_SHARE;
static
uchar *thd_ndb_share_get_key(THD_NDB_SHARE *thd_ndb_share, size_t *length,
my_bool not_used __attribute__((unused)))
@@ -372,41 +376,6 @@ Thd_ndb::init_open_tables()
my_hash_reset(&open_tables);
}
-THD_NDB_SHARE *
-Thd_ndb::get_open_table(THD *thd, const void *key)
-{
- DBUG_ENTER("Thd_ndb::get_open_table");
- HASH_SEARCH_STATE state;
- THD_NDB_SHARE *thd_ndb_share=
- (THD_NDB_SHARE*)hash_first(&open_tables, (uchar *)&key, sizeof(key), &state);
- while (thd_ndb_share && thd_ndb_share->key != key)
- thd_ndb_share= (THD_NDB_SHARE*)hash_next(&open_tables, (uchar *)&key, sizeof(key), &state);
- if (thd_ndb_share == 0)
- {
- thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
- sizeof(THD_NDB_SHARE));
- if (!thd_ndb_share)
- {
- mem_alloc_error(sizeof(THD_NDB_SHARE));
- DBUG_RETURN(NULL);
- }
- thd_ndb_share->key= key;
- thd_ndb_share->stat.last_count= count;
- thd_ndb_share->stat.no_uncommitted_rows_count= 0;
- thd_ndb_share->stat.records= ~(ha_rows)0;
- my_hash_insert(&open_tables, (uchar *)thd_ndb_share);
- }
- else if (thd_ndb_share->stat.last_count != count)
- {
- thd_ndb_share->stat.last_count= count;
- thd_ndb_share->stat.no_uncommitted_rows_count= 0;
- thd_ndb_share->stat.records= ~(ha_rows)0;
- }
- DBUG_PRINT("exit", ("thd_ndb_share: 0x%lx key: 0x%lx",
- (long) thd_ndb_share, (long) key));
- DBUG_RETURN(thd_ndb_share);
-}
-
inline
Ndb *ha_ndbcluster::get_ndb()
{
@@ -2742,10 +2711,13 @@ ha_ndbcluster::set_auto_inc(Field *field)
("Trying to set next auto increment value to %s",
llstr(next_val, buff)));
#endif
- Ndb_tuple_id_range_guard g(m_share);
- if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE)
- == -1)
- ERR_RETURN(ndb->getNdbError());
+ if (ndb->checkUpdateAutoIncrementValue(m_share->tuple_id_range, next_val))
+ {
+ Ndb_tuple_id_range_guard g(m_share);
+ if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE)
+ == -1)
+ ERR_RETURN(ndb->getNdbError());
+ }
DBUG_RETURN(0);
}
@@ -4554,12 +4526,48 @@ int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb)
thd_ndb->trans_options|= TNTO_INJECTED_APPLY_STATUS;
}
#endif
- // TODO remove double pointers...
- if (!(m_thd_ndb_share= thd_ndb->get_open_table(thd, m_table)))
+
+ if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
{
- DBUG_RETURN(1);
+ const void *key= m_table;
+ HASH_SEARCH_STATE state;
+ THD_NDB_SHARE *thd_ndb_share=
+ (THD_NDB_SHARE*)hash_first(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
+ while (thd_ndb_share && thd_ndb_share->key != key)
+ thd_ndb_share= (THD_NDB_SHARE*)hash_next(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
+ if (thd_ndb_share == 0)
+ {
+ thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
+ sizeof(THD_NDB_SHARE));
+ if (!thd_ndb_share)
+ {
+ mem_alloc_error(sizeof(THD_NDB_SHARE));
+ DBUG_RETURN(1);
+ }
+ thd_ndb_share->key= key;
+ thd_ndb_share->stat.last_count= thd_ndb->count;
+ thd_ndb_share->stat.no_uncommitted_rows_count= 0;
+ thd_ndb_share->stat.records= ~(ha_rows)0;
+ my_hash_insert(&thd_ndb->open_tables, (uchar *)thd_ndb_share);
+ }
+ else if (thd_ndb_share->stat.last_count != thd_ndb->count)
+ {
+ thd_ndb_share->stat.last_count= thd_ndb->count;
+ thd_ndb_share->stat.no_uncommitted_rows_count= 0;
+ thd_ndb_share->stat.records= ~(ha_rows)0;
+ }
+ DBUG_PRINT("exit", ("thd_ndb_share: 0x%lx key: 0x%lx",
+ (long) thd_ndb_share, (long) key));
+ m_table_info= &thd_ndb_share->stat;
+ }
+ else
+ {
+ struct Ndb_local_table_statistics &stat= m_table_info_instance;
+ stat.last_count= thd_ndb->count;
+ stat.no_uncommitted_rows_count= 0;
+ stat.records= ~(ha_rows)0;
+ m_table_info= &stat;
}
- m_table_info= &m_thd_ndb_share->stat;
DBUG_RETURN(0);
}
@@ -9446,7 +9454,7 @@ ha_ndbcluster::cond_push(const COND *cond)
my_errno= HA_ERR_OUT_OF_MEM;
DBUG_RETURN(NULL);
}
- DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
+ DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname, QT_ORDINARY););
DBUG_RETURN(m_cond->cond_push(cond, table, (NDBTAB *)m_table));
}
diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h
index cc79402fe92..a17323d3fd6 100644
--- a/sql/ha_ndbcluster.h
+++ b/sql/ha_ndbcluster.h
@@ -195,11 +195,6 @@ struct Ndb_local_table_statistics {
ha_rows records;
};
-typedef struct st_thd_ndb_share {
- const void *key;
- struct Ndb_local_table_statistics stat;
-} THD_NDB_SHARE;
-
class Thd_ndb
{
public:
@@ -207,7 +202,6 @@ class Thd_ndb
~Thd_ndb();
void init_open_tables();
- THD_NDB_SHARE *get_open_table(THD *thd, const void *key);
Ndb *ndb;
ulong count;
@@ -514,6 +508,7 @@ private:
NdbScanOperation *m_active_cursor;
const NdbDictionary::Table *m_table;
struct Ndb_local_table_statistics *m_table_info;
+ struct Ndb_local_table_statistics m_table_info_instance;
char m_dbname[FN_HEADLEN];
//char m_schemaname[FN_HEADLEN];
char m_tabname[FN_HEADLEN];
@@ -522,7 +517,6 @@ private:
bool m_lock_tuple;
NDB_SHARE *m_share;
NDB_INDEX_DATA m_index[MAX_KEY];
- THD_NDB_SHARE *m_thd_ndb_share;
// NdbRecAttr has no reference to blob
NdbValue m_value[NDB_MAX_ATTRIBUTES_IN_TABLE];
uchar m_ref[NDB_HIDDEN_PRIMARY_KEY_LENGTH];
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 841dce2d832..4fd5ee1b402 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -283,6 +283,7 @@ static void run_query(THD *thd, char *buf, char *end,
thd_ndb->m_error_code,
(int) thd->is_error(), thd->is_slave_error);
}
+ close_thread_tables(thd);
/*
XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command()
can not be called from within a statement, and
@@ -683,6 +684,18 @@ static void ndbcluster_reset_slave(THD *thd)
/*
Initialize the binlog part of the ndb handlerton
*/
+
+/**
+ Upon the sql command flush logs, we need to ensure that all outstanding
+ ndb data to be logged has made it to the binary log to get a deterministic
+ behavior on the rotation of the log.
+ */
+static bool ndbcluster_flush_logs(handlerton *hton)
+{
+ ndbcluster_binlog_wait(current_thd);
+ return FALSE;
+}
+
static int ndbcluster_binlog_func(handlerton *hton, THD *thd,
enum_binlog_func fn,
void *arg)
@@ -711,6 +724,7 @@ static int ndbcluster_binlog_func(handlerton *hton, THD *thd,
void ndbcluster_binlog_init_handlerton()
{
handlerton *h= ndbcluster_hton;
+ h->flush_logs= ndbcluster_flush_logs;
h->binlog_func= ndbcluster_binlog_func;
h->binlog_log_query= ndbcluster_binlog_log_query;
}
@@ -1002,7 +1016,7 @@ static void ndbcluster_get_schema(NDB_SHARE *share,
uint blob_len= field_blob->get_length((*field)->ptr);
uchar *blob_ptr= 0;
field_blob->get_ptr(&blob_ptr);
- assert(blob_len == 0 || blob_ptr != 0);
+ DBUG_ASSERT(blob_len == 0 || blob_ptr != 0);
s->query_length= blob_len;
s->query= sql_strmake((char*) blob_ptr, blob_len);
}
diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc
index d8dff3c7e2f..bb35211944b 100644
--- a/sql/ha_ndbcluster_cond.cc
+++ b/sql/ha_ndbcluster_cond.cc
@@ -117,7 +117,8 @@ void ndb_serialize_cond(const Item *item, void *arg)
if (item->type() == Item::FUNC_ITEM)
{
Item_func *func_item= (Item_func *) item;
- if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
+ if ((func_item->functype() == Item_func::UNKNOWN_FUNC ||
+ func_item->functype() == Item_func::NEG_FUNC) &&
func_item->const_item())
{
// Skip any arguments since we will evaluate function instead
@@ -369,8 +370,9 @@ void ndb_serialize_cond(const Item *item, void *arg)
{
Item_func *func_item= (Item_func *) item;
// Check that we expect a function or functional expression here
- if (context->expecting(Item::FUNC_ITEM) ||
- func_item->functype() == Item_func::UNKNOWN_FUNC)
+ if (context->expecting(Item::FUNC_ITEM) ||
+ func_item->functype() == Item_func::UNKNOWN_FUNC ||
+ func_item->functype() == Item_func::NEG_FUNC)
context->expect_nothing();
else
{
@@ -584,6 +586,7 @@ void ndb_serialize_cond(const Item *item, void *arg)
context->expect(Item::FUNC_ITEM);
break;
}
+ case Item_func::NEG_FUNC:
case Item_func::UNKNOWN_FUNC:
{
DBUG_PRINT("info", ("UNKNOWN_FUNC %s",
diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h
index 4d1d2ef9f92..4401a93c9e1 100644
--- a/sql/ha_ndbcluster_cond.h
+++ b/sql/ha_ndbcluster_cond.h
@@ -233,6 +233,7 @@ public:
case (Item_func::ISNOTNULL_FUNC): { return NDB_ISNOTNULL_FUNC; }
case (Item_func::LIKE_FUNC): { return NDB_LIKE_FUNC; }
case (Item_func::NOT_FUNC): { return NDB_NOT_FUNC; }
+ case (Item_func::NEG_FUNC): { return NDB_UNKNOWN_FUNC; }
case (Item_func::UNKNOWN_FUNC): { return NDB_UNKNOWN_FUNC; }
case (Item_func::COND_AND_FUNC): { return NDB_COND_AND_FUNC; }
case (Item_func::COND_OR_FUNC): { return NDB_COND_OR_FUNC; }
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 8c6ed76ddff..3045d5d73a9 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1028,6 +1028,7 @@ int ha_partition::repair_partitions(THD *thd)
0 Success
*/
+#ifdef WL4176_IS_DONE
static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
handler *file, uint flag)
{
@@ -1035,6 +1036,12 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
DBUG_ENTER("handle_opt_part");
DBUG_PRINT("enter", ("flag = %u", flag));
+ /*
+ TODO:
+ Rewrite the code for ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION WL4176
+ */
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+
if (flag == OPTIMIZE_PARTS)
error= file->ha_optimize(thd, check_opt);
else if (flag == ANALYZE_PARTS)
@@ -1052,6 +1059,7 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
error= 0;
DBUG_RETURN(error);
}
+#endif
/*
@@ -1072,14 +1080,22 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
uint flag, bool all_parts)
{
+#ifdef WL4176_IS_DONE
List_iterator<partition_element> part_it(m_part_info->partitions);
uint no_parts= m_part_info->no_parts;
uint no_subparts= m_part_info->no_subparts;
uint i= 0;
int error;
+#endif
DBUG_ENTER("ha_partition::handle_opt_partitions");
DBUG_PRINT("enter", ("all_parts %u, flag= %u", all_parts, flag));
+ /*
+ TODO:
+ Rewrite the code for ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION WL4176
+ */
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+#ifdef WL4176_IS_DONE
do
{
partition_element *part_elem= part_it++;
@@ -1110,6 +1126,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
}
} while (++i < no_parts);
DBUG_RETURN(FALSE);
+#endif
}
/*
@@ -1629,6 +1646,15 @@ void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
{
(*file_array)->change_table_ptr(table_arg, share);
} while (*(++file_array));
+ if (m_added_file && m_added_file[0])
+ {
+ /* if in middle of a drop/rename etc */
+ file_array= m_added_file;
+ do
+ {
+ (*file_array)->change_table_ptr(table_arg, share);
+ } while (*(++file_array));
+ }
}
/*
diff --git a/sql/handler.cc b/sql/handler.cc
index 0685069f6e0..5722a9ea23a 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -573,6 +573,295 @@ void ha_close_connection(THD* thd)
======================= TRANSACTIONS ===================================*/
/**
+ Transaction handling in the server
+ ==================================
+
+ In each client connection, MySQL maintains two transactional
+ states:
+ - a statement transaction,
+ - a standard, also called normal transaction.
+
+ Historical note
+ ---------------
+ "Statement transaction" is a non-standard term that comes
+ from the times when MySQL supported BerkeleyDB storage engine.
+
+ First of all, it should be said that in BerkeleyDB auto-commit
+ mode auto-commits operations that are atomic to the storage
+ engine itself, such as a write of a record, and are too
+ high-granular to be atomic from the application perspective
+ (MySQL). One SQL statement could involve many BerkeleyDB
+ auto-committed operations and thus BerkeleyDB auto-commit was of
+ little use to MySQL.
+
+ Secondly, instead of SQL standard savepoints, BerkeleyDB
+ provided the concept of "nested transactions". In a nutshell,
+ transactions could be arbitrarily nested, but when the parent
+ transaction was committed or aborted, all its child (nested)
+ transactions were handled committed or aborted as well.
+ Commit of a nested transaction, in turn, made its changes
+ visible, but not durable: it destroyed the nested transaction,
+ all its changes would become available to the parent and
+ currently active nested transactions of this parent.
+
+ So the mechanism of nested transactions was employed to
+ provide "all or nothing" guarantee of SQL statements
+ required by the standard.
+ A nested transaction would be created at start of each SQL
+ statement, and destroyed (committed or aborted) at statement
+ end. Such nested transaction was internally referred to as
+ a "statement transaction" and gave birth to the term.
+
+ <Historical note ends>
+
+ Since then a statement transaction is started for each statement
+ that accesses transactional tables or uses the binary log. If
+ the statement succeeds, the statement transaction is committed.
+ If the statement fails, the transaction is rolled back. Commits
+ of statement transactions are not durable -- each such
+ transaction is nested in the normal transaction, and if the
+ normal transaction is rolled back, the effects of all enclosed
+ statement transactions are undone as well. Technically,
+ a statement transaction can be viewed as a savepoint which is
+ maintained automatically in order to make effects of one
+ statement atomic.
+
+ The normal transaction is started by the user and is ended
+ usually upon a user request as well. The normal transaction
+ encloses transactions of all statements issued between
+ its beginning and its end.
+ In autocommit mode, the normal transaction is equivalent
+ to the statement transaction.
+
+ Since MySQL supports PSEA (pluggable storage engine
+ architecture), more than one transactional engine can be
+ active at a time. Hence transactions, from the server
+ point of view, are always distributed. In particular,
+ transactional state is maintained independently for each
+ engine. In order to commit a transaction the two phase
+ commit protocol is employed.
+
+ Not all statements are executed in context of a transaction.
+ Administrative and status information statements do not modify
+ engine data, and thus do not start a statement transaction and
+ also have no effect on the normal transaction. Examples of such
+ statements are SHOW STATUS and RESET SLAVE.
+
+ Similarly DDL statements are not transactional,
+ and therefore a transaction is [almost] never started for a DDL
+ statement. The difference between a DDL statement and a purely
+ administrative statement though is that a DDL statement always
+ commits the current transaction before proceeding, if there is
+ any.
+
+ At last, SQL statements that work with non-transactional
+ engines also have no effect on the transaction state of the
+ connection. Even though they are written to the binary log,
+ and the binary log is, overall, transactional, the writes
+ are done in "write-through" mode, directly to the binlog
+ file, followed with a OS cache sync, in other words,
+ bypassing the binlog undo log (translog).
+ They do not commit the current normal transaction.
+ A failure of a statement that uses non-transactional tables
+ would cause a rollback of the statement transaction, but
+ in case there no non-transactional tables are used,
+ no statement transaction is started.
+
+ Data layout
+ -----------
+
+ The server stores its transaction-related data in
+ thd->transaction. This structure has two members of type
+ THD_TRANS. These members correspond to the statement and
+ normal transactions respectively:
+
+ - thd->transaction.stmt contains a list of engines
+ that are participating in the given statement
+ - thd->transaction.all contains a list of engines that
+ have participated in any of the statement transactions started
+ within the context of the normal transaction.
+ Each element of the list contains a pointer to the storage
+ engine, engine-specific transactional data, and engine-specific
+ transaction flags.
+
+ In autocommit mode thd->transaction.all is empty.
+ Instead, data of thd->transaction.stmt is
+ used to commit/rollback the normal transaction.
+
+ The list of registered engines has a few important properties:
+ - no engine is registered in the list twice
+ - engines are present in the list a reverse temporal order --
+ new participants are always added to the beginning of the list.
+
+ Transaction life cycle
+ ----------------------
+
+ When a new connection is established, thd->transaction
+ members are initialized to an empty state.
+ If a statement uses any tables, all affected engines
+ are registered in the statement engine list. In
+ non-autocommit mode, the same engines are registered in
+ the normal transaction list.
+ At the end of the statement, the server issues a commit
+ or a roll back for all engines in the statement list.
+ At this point transaction flags of an engine, if any, are
+ propagated from the statement list to the list of the normal
+ transaction.
+ When commit/rollback is finished, the statement list is
+ cleared. It will be filled in again by the next statement,
+ and emptied again at the next statement's end.
+
+ The normal transaction is committed in a similar way
+ (by going over all engines in thd->transaction.all list)
+ but at different times:
+ - upon COMMIT SQL statement is issued by the user
+ - implicitly, by the server, at the beginning of a DDL statement
+ or SET AUTOCOMMIT={0|1} statement.
+
+ The normal transaction can be rolled back as well:
+ - if the user has requested so, by issuing ROLLBACK SQL
+ statement
+ - if one of the storage engines requested a rollback
+ by setting thd->transaction_rollback_request. This may
+ happen in case, e.g., when the transaction in the engine was
+ chosen a victim of the internal deadlock resolution algorithm
+ and rolled back internally. When such a situation happens, there
+ is little the server can do and the only option is to rollback
+ transactions in all other participating engines. In this case
+ the rollback is accompanied by an error sent to the user.
+
+ As follows from the use cases above, the normal transaction
+ is never committed when there is an outstanding statement
+ transaction. In most cases there is no conflict, since
+ commits of the normal transaction are issued by a stand-alone
+ administrative or DDL statement, thus no outstanding statement
+ transaction of the previous statement exists. Besides,
+ all statements that manipulate with the normal transaction
+ are prohibited in stored functions and triggers, therefore
+ no conflicting situation can occur in a sub-statement either.
+ The remaining rare cases when the server explicitly has
+ to commit the statement transaction prior to committing the normal
+ one cover error-handling scenarios (see for example
+ SQLCOM_LOCK_TABLES).
+
+ When committing a statement or a normal transaction, the server
+ either uses the two-phase commit protocol, or issues a commit
+ in each engine independently. The two-phase commit protocol
+ is used only if:
+ - all participating engines support two-phase commit (provide
+ handlerton::prepare PSEA API call) and
+ - transactions in at least two engines modify data (i.e. are
+ not read-only).
+
+ Note that the two phase commit is used for
+ statement transactions, even though they are not durable anyway.
+ This is done to ensure logical consistency of data in a multiple-
+ engine transaction.
+ For example, imagine that some day MySQL supports unique
+ constraint checks deferred till the end of statement. In such
+ case a commit in one of the engines may yield ER_DUP_KEY,
+ and MySQL should be able to gracefully abort statement
+ transactions of other participants.
+
+ After the normal transaction has been committed,
+ thd->transaction.all list is cleared.
+
+ When a connection is closed, the current normal transaction, if
+ any, is rolled back.
+
+ Roles and responsibilities
+ --------------------------
+
+ The server has no way to know that an engine participates in
+ the statement and a transaction has been started
+ in it unless the engine says so. Thus, in order to be
+ a part of a transaction, the engine must "register" itself.
+ This is done by invoking trans_register_ha() server call.
+ Normally the engine registers itself whenever handler::external_lock()
+ is called. trans_register_ha() can be invoked many times: if
+ an engine is already registered, the call does nothing.
+ In case autocommit is not set, the engine must register itself
+ twice -- both in the statement list and in the normal transaction
+ list.
+ In which list to register is a parameter of trans_register_ha().
+
+ Note, that although the registration interface in itself is
+ fairly clear, the current usage practice often leads to undesired
+ effects. E.g. since a call to trans_register_ha() in most engines
+ is embedded into implementation of handler::external_lock(), some
+ DDL statements start a transaction (at least from the server
+ point of view) even though they are not expected to. E.g.
+ CREATE TABLE does not start a transaction, since
+ handler::external_lock() is never called during CREATE TABLE. But
+ CREATE TABLE ... SELECT does, since handler::external_lock() is
+ called for the table that is being selected from. This has no
+ practical effects currently, but must be kept in mind
+ nevertheless.
+
+ Once an engine is registered, the server will do the rest
+ of the work.
+
+ During statement execution, whenever any of data-modifying
+ PSEA API methods is used, e.g. handler::write_row() or
+ handler::update_row(), the read-write flag is raised in the
+ statement transaction for the involved engine.
+ Currently All PSEA calls are "traced", and the data can not be
+ changed in a way other than issuing a PSEA call. Important:
+ unless this invariant is preserved the server will not know that
+ a transaction in a given engine is read-write and will not
+ involve the two-phase commit protocol!
+
+ At the end of a statement, server call
+ ha_autocommit_or_rollback() is invoked. This call in turn
+ invokes handlerton::prepare() for every involved engine.
+ Prepare is followed by a call to handlerton::commit_one_phase()
+ If a one-phase commit will suffice, handlerton::prepare() is not
+ invoked and the server only calls handlerton::commit_one_phase().
+ At statement commit, the statement-related read-write engine
+ flag is propagated to the corresponding flag in the normal
+ transaction. When the commit is complete, the list of registered
+ engines is cleared.
+
+ Rollback is handled in a similar fashion.
+
+ Additional notes on DDL and the normal transaction.
+ ---------------------------------------------------
+
+ DDLs and operations with non-transactional engines
+ do not "register" in thd->transaction lists, and thus do not
+ modify the transaction state. Besides, each DDL in
+ MySQL is prefixed with an implicit normal transaction commit
+ (a call to end_active_trans()), and thus leaves nothing
+ to modify.
+ However, as it has been pointed out with CREATE TABLE .. SELECT,
+ some DDL statements can start a *new* transaction.
+
+ Behaviour of the server in this case is currently badly
+ defined.
+ DDL statements use a form of "semantic" logging
+ to maintain atomicity: if CREATE TABLE .. SELECT failed,
+ the newly created table is deleted.
+ In addition, some DDL statements issue interim transaction
+ commits: e.g. ALTER TABLE issues a commit after data is copied
+ from the original table to the internal temporary table. Other
+ statements, e.g. CREATE TABLE ... SELECT do not always commit
+ after itself.
+ And finally there is a group of DDL statements such as
+ RENAME/DROP TABLE that doesn't start a new transaction
+ and doesn't commit.
+
+ This diversity makes it hard to say what will happen if
+ by chance a stored function is invoked during a DDL --
+ whether any modifications it makes will be committed or not
+ is not clear. Fortunately, SQL grammar of few DDLs allows
+ invocation of a stored function.
+
+ A consistent behaviour is perhaps to always commit the normal
+ transaction after all DDLs, just like the statement transaction
+ is always committed at the end of all statements.
+*/
+
+/**
Register a storage engine for a transaction.
Every storage engine MUST call this function when it starts
@@ -589,7 +878,7 @@ void ha_close_connection(THD* thd)
void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
{
THD_TRANS *trans;
- handlerton **ht;
+ Ha_trx_info *ha_info;
DBUG_ENTER("trans_register_ha");
DBUG_PRINT("enter",("%s", all ? "all" : "stmt"));
@@ -601,12 +890,13 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
else
trans= &thd->transaction.stmt;
- for (ht=trans->ht; *ht; ht++)
- if (*ht == ht_arg)
- DBUG_VOID_RETURN; /* already registered, return */
+ ha_info= thd->ha_data[ht_arg->slot].ha_info + static_cast<unsigned>(all);
+
+ if (ha_info->is_started())
+ DBUG_VOID_RETURN; /* already registered, return */
+
+ ha_info->register_ha(trans, ht_arg);
- trans->ht[trans->nht++]=ht_arg;
- DBUG_ASSERT(*ht == ht_arg);
trans->no_2pc|=(ht_arg->prepare==0);
if (thd->transaction.xid_state.xid.is_null())
thd->transaction.xid_state.xid.set(thd->query_id);
@@ -623,18 +913,19 @@ int ha_prepare(THD *thd)
{
int error=0, all=1;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- handlerton **ht=trans->ht;
+ Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_prepare");
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
- for (; *ht; ht++)
+ for (; ha_info; ha_info= ha_info->next())
{
int err;
+ handlerton *ht= ha_info->ht();
status_var_increment(thd->status_var.ha_prepare_count);
- if ((*ht)->prepare)
+ if (ht->prepare)
{
- if ((err= (*(*ht)->prepare)(*ht, thd, all)))
+ if ((err= ht->prepare(ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
ha_rollback_trans(thd, all);
@@ -655,6 +946,62 @@ int ha_prepare(THD *thd)
}
/**
+ Check if we can skip the two-phase commit.
+
+ A helper function to evaluate if two-phase commit is mandatory.
+ As a side effect, propagates the read-only/read-write flags
+ of the statement transaction to its enclosing normal transaction.
+
+ @retval TRUE we must run a two-phase commit. Returned
+ if we have at least two engines with read-write changes.
+ @retval FALSE Don't need two-phase commit. Even if we have two
+ transactional engines, we can run two independent
+ commits if changes in one of the engines are read-only.
+*/
+
+static
+bool
+ha_check_and_coalesce_trx_read_only(THD *thd, Ha_trx_info *ha_list,
+ bool all)
+{
+ /* The number of storage engines that have actual changes. */
+ unsigned rw_ha_count= 0;
+ Ha_trx_info *ha_info;
+
+ for (ha_info= ha_list; ha_info; ha_info= ha_info->next())
+ {
+ if (ha_info->is_trx_read_write())
+ ++rw_ha_count;
+
+ if (! all)
+ {
+ Ha_trx_info *ha_info_all= &thd->ha_data[ha_info->ht()->slot].ha_info[1];
+ DBUG_ASSERT(ha_info != ha_info_all);
+ /*
+ Merge read-only/read-write information about statement
+ transaction to its enclosing normal transaction. Do this
+ only if in a real transaction -- that is, if we know
+ that ha_info_all is registered in thd->transaction.all.
+ Since otherwise we only clutter the normal transaction flags.
+ */
+ if (ha_info_all->is_started()) /* FALSE if autocommit. */
+ ha_info_all->coalesce_trx_with(ha_info);
+ }
+ else if (rw_ha_count > 1)
+ {
+ /*
+ It is a normal transaction, so we don't need to merge read/write
+ information up, and the need for two-phase commit has been
+ already established. Break the loop prematurely.
+ */
+ break;
+ }
+ }
+ return rw_ha_count > 1;
+}
+
+
+/**
@retval
0 ok
@retval
@@ -671,12 +1018,25 @@ int ha_prepare(THD *thd)
int ha_commit_trans(THD *thd, bool all)
{
int error= 0, cookie= 0;
+ /*
+ 'all' means that this is either an explicit commit issued by
+ user, or an implicit commit issued by a DDL.
+ */
THD_TRANS *trans= all ? &thd->transaction.all : &thd->transaction.stmt;
- bool is_real_trans= all || thd->transaction.all.nht == 0;
- handlerton **ht= trans->ht;
+ bool is_real_trans= all || thd->transaction.all.ha_list == 0;
+ Ha_trx_info *ha_info= trans->ha_list;
my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
DBUG_ENTER("ha_commit_trans");
+ /*
+ We must not commit the normal transaction if a statement
+ transaction is pending. Otherwise statement transaction
+ flags will not get propagated to its normal transaction's
+ counterpart.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
+ trans == &thd->transaction.stmt);
+
if (thd->in_sub_stmt)
{
/*
@@ -698,8 +1058,10 @@ int ha_commit_trans(THD *thd, bool all)
DBUG_RETURN(2);
}
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
+ bool must_2pc;
+
if (is_real_trans && wait_if_global_read_lock(thd, 0, 0))
{
ha_rollback_trans(thd, all);
@@ -724,12 +1086,26 @@ int ha_commit_trans(THD *thd, bool all)
if (is_real_trans) /* not a statement commit */
thd->stmt_map.close_transient_cursors();
- if (!trans->no_2pc && trans->nht > 1)
+ must_2pc= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
+
+ if (!trans->no_2pc && must_2pc)
{
- for (; *ht && !error; ht++)
+ for (; ha_info && !error; ha_info= ha_info->next())
{
int err;
- if ((err= (*(*ht)->prepare)(*ht, thd, all)))
+ handlerton *ht= ha_info->ht();
+ /*
+ Do not call two-phase commit if this particular
+ transaction is read-only. This allows for simpler
+ implementation in engines that are always read-only.
+ */
+ if (! ha_info->is_trx_read_write())
+ continue;
+ /*
+ Sic: we know that prepare() is not NULL since otherwise
+ trans->no_2pc would have been set.
+ */
+ if ((err= ht->prepare(ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error= 1;
@@ -767,24 +1143,26 @@ int ha_commit_one_phase(THD *thd, bool all)
{
int error=0;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- bool is_real_trans=all || thd->transaction.all.nht == 0;
- handlerton **ht=trans->ht;
+ bool is_real_trans=all || thd->transaction.all.ha_list == 0;
+ Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
DBUG_ENTER("ha_commit_one_phase");
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
- for (ht=trans->ht; *ht; ht++)
+ for (; ha_info; ha_info= ha_info_next)
{
int err;
- if ((err= (*(*ht)->commit)(*ht, thd, all)))
+ handlerton *ht= ha_info->ht();
+ if ((err= ht->commit(ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error=1;
}
status_var_increment(thd->status_var.ha_commit_count);
- *ht= 0;
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
}
- trans->nht=0;
+ trans->ha_list= 0;
trans->no_2pc=0;
if (is_real_trans)
thd->transaction.xid_state.xid.null();
@@ -807,8 +1185,17 @@ int ha_rollback_trans(THD *thd, bool all)
{
int error=0;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- bool is_real_trans=all || thd->transaction.all.nht == 0;
+ Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
+ bool is_real_trans=all || thd->transaction.all.ha_list == 0;
DBUG_ENTER("ha_rollback_trans");
+
+ /*
+ We must not rollback the normal transaction if a statement
+ transaction is pending.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
+ trans == &thd->transaction.stmt);
+
if (thd->in_sub_stmt)
{
/*
@@ -823,24 +1210,26 @@ int ha_rollback_trans(THD *thd, bool all)
DBUG_RETURN(1);
}
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
/* Close all cursors that can not survive ROLLBACK */
if (is_real_trans) /* not a statement commit */
thd->stmt_map.close_transient_cursors();
- for (handlerton **ht=trans->ht; *ht; ht++)
+ for (; ha_info; ha_info= ha_info_next)
{
int err;
- if ((err= (*(*ht)->rollback)(*ht, thd, all)))
+ handlerton *ht= ha_info->ht();
+ if ((err= ht->rollback(ht, thd, all)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
status_var_increment(thd->status_var.ha_rollback_count);
- *ht= 0;
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
}
- trans->nht=0;
+ trans->ha_list= 0;
trans->no_2pc=0;
if (is_real_trans)
thd->transaction.xid_state.xid.null();
@@ -886,17 +1275,19 @@ int ha_autocommit_or_rollback(THD *thd, int error)
{
DBUG_ENTER("ha_autocommit_or_rollback");
#ifdef USING_TRANSACTIONS
- if (thd->transaction.stmt.nht)
+ if (thd->transaction.stmt.ha_list)
{
if (!error)
{
- if (ha_commit_stmt(thd))
+ if (ha_commit_trans(thd, 0))
error=1;
}
- else if (thd->transaction_rollback_request && !thd->in_sub_stmt)
- (void) ha_rollback(thd);
- else
- (void) ha_rollback_stmt(thd);
+ else
+ {
+ (void) ha_rollback_trans(thd, 0);
+ if (thd->transaction_rollback_request && !thd->in_sub_stmt)
+ (void) ha_rollback(thd);
+ }
thd->variables.tx_isolation=thd->session_tx_isolation;
}
@@ -1196,7 +1587,7 @@ bool mysql_xa_recover(THD *thd)
}
pthread_mutex_unlock(&LOCK_xid_cache);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(0);
}
@@ -1243,43 +1634,49 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
int error=0;
THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
&thd->transaction.all);
- handlerton **ht=trans->ht, **end_ht;
+ Ha_trx_info *ha_info, *ha_info_next;
+
DBUG_ENTER("ha_rollback_to_savepoint");
- trans->nht=sv->nht;
trans->no_2pc=0;
- end_ht=ht+sv->nht;
/*
rolling back to savepoint in all storage engines that were part of the
transaction when the savepoint was set
*/
- for (; ht < end_ht; ht++)
+ for (ha_info= sv->ha_list; ha_info; ha_info= ha_info->next())
{
int err;
- DBUG_ASSERT((*ht)->savepoint_set != 0);
- if ((err= (*(*ht)->savepoint_rollback)(*ht, thd, (uchar *)(sv+1)+(*ht)->savepoint_offset)))
+ handlerton *ht= ha_info->ht();
+ DBUG_ASSERT(ht);
+ DBUG_ASSERT(ht->savepoint_set != 0);
+ if ((err= ht->savepoint_rollback(ht, thd,
+ (uchar *)(sv+1)+ht->savepoint_offset)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
status_var_increment(thd->status_var.ha_savepoint_rollback_count);
- trans->no_2pc|=(*ht)->prepare == 0;
+ trans->no_2pc|= ht->prepare == 0;
}
/*
rolling back the transaction in all storage engines that were not part of
the transaction when the savepoint was set
*/
- for (; *ht ; ht++)
+ for (ha_info= trans->ha_list; ha_info != sv->ha_list;
+ ha_info= ha_info_next)
{
int err;
- if ((err= (*(*ht)->rollback)(*ht, thd, !thd->in_sub_stmt)))
+ handlerton *ht= ha_info->ht();
+ if ((err= ht->rollback(ht, thd, !thd->in_sub_stmt)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
status_var_increment(thd->status_var.ha_rollback_count);
- *ht=0; // keep it conveniently zero-filled
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
}
+ trans->ha_list= sv->ha_list;
DBUG_RETURN(error);
}
@@ -1294,26 +1691,32 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
int error=0;
THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
&thd->transaction.all);
- handlerton **ht=trans->ht;
+ Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_savepoint");
#ifdef USING_TRANSACTIONS
- for (; *ht; ht++)
+ for (; ha_info; ha_info= ha_info->next())
{
int err;
- if (! (*ht)->savepoint_set)
+ handlerton *ht= ha_info->ht();
+ DBUG_ASSERT(ht);
+ if (! ht->savepoint_set)
{
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "SAVEPOINT");
error=1;
break;
}
- if ((err= (*(*ht)->savepoint_set)(*ht, thd, (uchar *)(sv+1)+(*ht)->savepoint_offset)))
+ if ((err= ht->savepoint_set(ht, thd, (uchar *)(sv+1)+ht->savepoint_offset)))
{ // cannot happen
my_error(ER_GET_ERRNO, MYF(0), err);
error=1;
}
status_var_increment(thd->status_var.ha_savepoint_count);
}
- sv->nht=trans->nht;
+ /*
+ Remember the list of registered storage engines. All new
+ engines are prepended to the beginning of the list.
+ */
+ sv->ha_list= trans->ha_list;
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
}
@@ -1321,20 +1724,19 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
{
int error=0;
- THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
- &thd->transaction.all);
- handlerton **ht=trans->ht, **end_ht;
+ Ha_trx_info *ha_info= sv->ha_list;
DBUG_ENTER("ha_release_savepoint");
- end_ht=ht+sv->nht;
- for (; ht < end_ht; ht++)
+ for (; ha_info; ha_info= ha_info->next())
{
int err;
- if (!(*ht)->savepoint_release)
+ handlerton *ht= ha_info->ht();
+ /* Savepoint life time is enclosed into transaction life time. */
+ DBUG_ASSERT(ht);
+ if (!ht->savepoint_release)
continue;
- if ((err= (*(*ht)->savepoint_release)(*ht, thd,
- (uchar *)(sv+1)+
- (*ht)->savepoint_offset)))
+ if ((err= ht->savepoint_release(ht, thd,
+ (uchar *)(sv+1) + ht->savepoint_offset)))
{ // cannot happen
my_error(ER_GET_ERRNO, MYF(0), err);
error=1;
@@ -1519,6 +1921,13 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
handler *handler::clone(MEM_ROOT *mem_root)
{
handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type());
+ /*
+ Allocate handler->ref here because otherwise ha_open will allocate it
+ on this->table->mem_root and we will not be able to reclaim that memory
+ when the clone handler object is destroyed.
+ */
+ if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
+ return NULL;
if (new_handler && !new_handler->ha_open(table,
table->s->normalized_path.str,
table->db_stat,
@@ -1587,7 +1996,9 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
- if (!(ref= (uchar*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2)))
+ /* ref is already allocated for us if we're called from handler::clone() */
+ if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root,
+ ALIGN_SIZE(ref_length)*2)))
{
close();
error=HA_ERR_OUT_OF_MEM;
@@ -2503,6 +2914,36 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
return update_frm_version(table);
}
+/**
+ A helper function to mark a transaction read-write,
+ if it is started.
+*/
+
+inline
+void
+handler::mark_trx_read_write()
+{
+ Ha_trx_info *ha_info= &ha_thd()->ha_data[ht->slot].ha_info[0];
+ /*
+ When a storage engine method is called, the transaction must
+ have been started, unless it's a DDL call, for which the
+ storage engine starts the transaction internally, and commits
+ it internally, without registering in the ha_list.
+ Unfortunately here we can't know know for sure if the engine
+ has registered the transaction or not, so we must check.
+ */
+ if (ha_info->is_started())
+ {
+ DBUG_ASSERT(has_transactions());
+ /*
+ table_share can be NULL in ha_delete_table(). See implementation
+ of standalone function ha_delete_table() in sql_base.cc.
+ */
+ if (table_share == NULL || table_share->tmp_table == NO_TMP_TABLE)
+ ha_info->set_trx_read_write();
+ }
+}
+
/**
Repair table: public interface.
@@ -2513,6 +2954,9 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt)
{
int result;
+
+ mark_trx_read_write();
+
if ((result= repair(thd, check_opt)))
return result;
return update_frm_version(table);
@@ -2529,6 +2973,8 @@ int
handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
uint *dup_key_found)
{
+ mark_trx_read_write();
+
return bulk_update_row(old_data, new_data, dup_key_found);
}
@@ -2542,6 +2988,8 @@ handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
int
handler::ha_delete_all_rows()
{
+ mark_trx_read_write();
+
return delete_all_rows();
}
@@ -2555,6 +3003,8 @@ handler::ha_delete_all_rows()
int
handler::ha_reset_auto_increment(ulonglong value)
{
+ mark_trx_read_write();
+
return reset_auto_increment(value);
}
@@ -2568,6 +3018,8 @@ handler::ha_reset_auto_increment(ulonglong value)
int
handler::ha_backup(THD* thd, HA_CHECK_OPT* check_opt)
{
+ mark_trx_read_write();
+
return backup(thd, check_opt);
}
@@ -2581,6 +3033,8 @@ handler::ha_backup(THD* thd, HA_CHECK_OPT* check_opt)
int
handler::ha_restore(THD* thd, HA_CHECK_OPT* check_opt)
{
+ mark_trx_read_write();
+
return restore(thd, check_opt);
}
@@ -2594,6 +3048,8 @@ handler::ha_restore(THD* thd, HA_CHECK_OPT* check_opt)
int
handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
{
+ mark_trx_read_write();
+
return optimize(thd, check_opt);
}
@@ -2607,6 +3063,8 @@ handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
int
handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
{
+ mark_trx_read_write();
+
return analyze(thd, check_opt);
}
@@ -2620,6 +3078,8 @@ handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
bool
handler::ha_check_and_repair(THD *thd)
{
+ mark_trx_read_write();
+
return check_and_repair(thd);
}
@@ -2633,6 +3093,8 @@ handler::ha_check_and_repair(THD *thd)
int
handler::ha_disable_indexes(uint mode)
{
+ mark_trx_read_write();
+
return disable_indexes(mode);
}
@@ -2646,6 +3108,8 @@ handler::ha_disable_indexes(uint mode)
int
handler::ha_enable_indexes(uint mode)
{
+ mark_trx_read_write();
+
return enable_indexes(mode);
}
@@ -2659,6 +3123,8 @@ handler::ha_enable_indexes(uint mode)
int
handler::ha_discard_or_import_tablespace(my_bool discard)
{
+ mark_trx_read_write();
+
return discard_or_import_tablespace(discard);
}
@@ -2674,6 +3140,8 @@ handler::ha_discard_or_import_tablespace(my_bool discard)
void
handler::ha_prepare_for_alter()
{
+ mark_trx_read_write();
+
prepare_for_alter();
}
@@ -2687,6 +3155,8 @@ handler::ha_prepare_for_alter()
int
handler::ha_rename_table(const char *from, const char *to)
{
+ mark_trx_read_write();
+
return rename_table(from, to);
}
@@ -2700,6 +3170,8 @@ handler::ha_rename_table(const char *from, const char *to)
int
handler::ha_delete_table(const char *name)
{
+ mark_trx_read_write();
+
return delete_table(name);
}
@@ -2713,6 +3185,8 @@ handler::ha_delete_table(const char *name)
void
handler::ha_drop_table(const char *name)
{
+ mark_trx_read_write();
+
return drop_table(name);
}
@@ -2726,6 +3200,8 @@ handler::ha_drop_table(const char *name)
int
handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
{
+ mark_trx_read_write();
+
return create(name, form, info);
}
@@ -2740,6 +3216,8 @@ int
handler::ha_create_handler_files(const char *name, const char *old_name,
int action_flag, HA_CREATE_INFO *info)
{
+ mark_trx_read_write();
+
return create_handler_files(name, old_name, action_flag, info);
}
@@ -2758,6 +3236,8 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info,
const uchar *pack_frm_data,
size_t pack_frm_len)
{
+ mark_trx_read_write();
+
return change_partitions(create_info, path, copied, deleted,
pack_frm_data, pack_frm_len);
}
@@ -2772,6 +3252,8 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info,
int
handler::ha_drop_partitions(const char *path)
{
+ mark_trx_read_write();
+
return drop_partitions(path);
}
@@ -2785,6 +3267,8 @@ handler::ha_drop_partitions(const char *path)
int
handler::ha_rename_partitions(const char *path)
{
+ mark_trx_read_write();
+
return rename_partitions(path);
}
@@ -2798,6 +3282,8 @@ handler::ha_rename_partitions(const char *path)
int
handler::ha_optimize_partitions(THD *thd)
{
+ mark_trx_read_write();
+
return optimize_partitions(thd);
}
@@ -2811,6 +3297,8 @@ handler::ha_optimize_partitions(THD *thd)
int
handler::ha_analyze_partitions(THD *thd)
{
+ mark_trx_read_write();
+
return analyze_partitions(thd);
}
@@ -2824,6 +3312,8 @@ handler::ha_analyze_partitions(THD *thd)
int
handler::ha_check_partitions(THD *thd)
{
+ mark_trx_read_write();
+
return check_partitions(thd);
}
@@ -2837,6 +3327,8 @@ handler::ha_check_partitions(THD *thd)
int
handler::ha_repair_partitions(THD *thd)
{
+ mark_trx_read_write();
+
return repair_partitions(thd);
}
@@ -2853,7 +3345,7 @@ int ha_enable_transaction(THD *thd, bool on)
{
int error=0;
DBUG_ENTER("ha_enable_transaction");
- DBUG_PRINT("info", ("on: %d", (int) on));
+ DBUG_PRINT("enter", ("on: %d", (int) on));
if ((thd->transaction.on= on))
{
@@ -2863,7 +3355,7 @@ int ha_enable_transaction(THD *thd, bool on)
is an optimization hint that storage engine is free to ignore.
So, let's commit an open transaction (if any) now.
*/
- if (!(error= ha_commit_stmt(thd)))
+ if (!(error= ha_commit_trans(thd, 0)))
error= end_trans(thd, COMMIT);
}
DBUG_RETURN(error);
@@ -3157,7 +3649,6 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
}
-
/**
Try to discover one table from handler(s).
@@ -3823,7 +4314,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
}
if (!result)
- send_eof(thd);
+ my_eof(thd);
return result;
}
@@ -3840,29 +4331,25 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
- table is not mysql.event
*/
-/* The Sun compiler cannot instantiate the template below if this is
- declared static, but it works by putting it into an anonymous
- namespace. */
-namespace {
- bool check_table_binlog_row_based(THD *thd, TABLE *table)
+static bool check_table_binlog_row_based(THD *thd, TABLE *table)
+{
+ if (table->s->cached_row_logging_check == -1)
{
- if (table->s->cached_row_logging_check == -1)
- {
- int const check(table->s->tmp_table == NO_TMP_TABLE &&
- binlog_filter->db_ok(table->s->db.str));
- table->s->cached_row_logging_check= check;
- }
+ int const check(table->s->tmp_table == NO_TMP_TABLE &&
+ binlog_filter->db_ok(table->s->db.str));
+ table->s->cached_row_logging_check= check;
+ }
- DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
- table->s->cached_row_logging_check == 1);
+ DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
+ table->s->cached_row_logging_check == 1);
- return (thd->current_stmt_binlog_row_based &&
- table->s->cached_row_logging_check &&
- (thd->options & OPTION_BIN_LOG) &&
- mysql_bin_log.is_open());
- }
+ return (thd->current_stmt_binlog_row_based &&
+ table->s->cached_row_logging_check &&
+ (thd->options & OPTION_BIN_LOG) &&
+ mysql_bin_log.is_open());
}
+
/** @brief
Write table maps for all (manually or automatically) locked tables
to the binary log.
@@ -3876,7 +4363,7 @@ namespace {
that are locked by the thread 'thd'. Either manually locked
(stored in THD::locked_tables) and automatically locked (stored
in THD::lock) are considered.
-
+
RETURN VALUE
0 All OK
1 Failed to write all table maps
@@ -3885,115 +4372,97 @@ namespace {
THD::lock
THD::locked_tables
*/
-namespace
+
+static int write_locked_table_maps(THD *thd)
{
- int write_locked_table_maps(THD *thd)
- {
- DBUG_ENTER("write_locked_table_maps");
- DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx "
- "thd->extra_lock: 0x%lx",
- (long) thd, (long) thd->lock,
- (long) thd->locked_tables, (long) thd->extra_lock));
+ DBUG_ENTER("write_locked_table_maps");
+ DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx "
+ "thd->extra_lock: 0x%lx",
+ (long) thd, (long) thd->lock,
+ (long) thd->locked_tables, (long) thd->extra_lock));
- if (thd->get_binlog_table_maps() == 0)
+ if (thd->get_binlog_table_maps() == 0)
+ {
+ MYSQL_LOCK *locks[3];
+ locks[0]= thd->extra_lock;
+ locks[1]= thd->lock;
+ locks[2]= thd->locked_tables;
+ for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
{
- MYSQL_LOCK *locks[3];
- locks[0]= thd->extra_lock;
- locks[1]= thd->lock;
- locks[2]= thd->locked_tables;
- for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
+ MYSQL_LOCK const *const lock= locks[i];
+ if (lock == NULL)
+ continue;
+
+ TABLE **const end_ptr= lock->table + lock->table_count;
+ for (TABLE **table_ptr= lock->table ;
+ table_ptr != end_ptr ;
+ ++table_ptr)
{
- MYSQL_LOCK const *const lock= locks[i];
- if (lock == NULL)
- continue;
-
- TABLE **const end_ptr= lock->table + lock->table_count;
- for (TABLE **table_ptr= lock->table ;
- table_ptr != end_ptr ;
- ++table_ptr)
+ TABLE *const table= *table_ptr;
+ DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
+ if (table->current_lock == F_WRLCK &&
+ check_table_binlog_row_based(thd, table))
{
- TABLE *const table= *table_ptr;
- DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
- if (table->current_lock == F_WRLCK &&
- check_table_binlog_row_based(thd, table))
- {
- int const has_trans= table->file->has_transactions();
- int const error= thd->binlog_write_table_map(table, has_trans);
- /*
- If an error occurs, it is the responsibility of the caller to
- roll back the transaction.
- */
- if (unlikely(error))
- DBUG_RETURN(1);
- }
+ int const has_trans= table->file->has_transactions();
+ int const error= thd->binlog_write_table_map(table, has_trans);
+ /*
+ If an error occurs, it is the responsibility of the caller to
+ roll back the transaction.
+ */
+ if (unlikely(error))
+ DBUG_RETURN(1);
}
}
}
- DBUG_RETURN(0);
}
+ DBUG_RETURN(0);
+}
+
- template<class RowsEventT> int
- binlog_log_row(TABLE* table,
- const uchar *before_record,
- const uchar *after_record)
+typedef bool Log_func(THD*, TABLE*, bool, MY_BITMAP*,
+ uint, const uchar*, const uchar*);
+
+static int binlog_log_row(TABLE* table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func)
+{
+ if (table->no_replicate)
+ return 0;
+ bool error= 0;
+ THD *const thd= table->in_use;
+
+ if (check_table_binlog_row_based(thd, table))
{
- if (table->no_replicate)
- return 0;
- bool error= 0;
- THD *const thd= table->in_use;
+ MY_BITMAP cols;
+ /* Potential buffer on the stack for the bitmap */
+ uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)];
+ uint n_fields= table->s->fields;
+ my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;
- if (check_table_binlog_row_based(thd, table))
+ /*
+ If there are no table maps written to the binary log, this is
+ the first row handled in this statement. In that case, we need
+ to write table maps for all locked tables to the binary log.
+ */
+ if (likely(!(error= bitmap_init(&cols,
+ use_bitbuf ? bitbuf : NULL,
+ (n_fields + 7) & ~7UL,
+ FALSE))))
{
- MY_BITMAP cols;
- /* Potential buffer on the stack for the bitmap */
- uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)];
- uint n_fields= table->s->fields;
- my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;
-
- /*
- If there are no table maps written to the binary log, this is
- the first row handled in this statement. In that case, we need
- to write table maps for all locked tables to the binary log.
- */
- if (likely(!(error= bitmap_init(&cols,
- use_bitbuf ? bitbuf : NULL,
- (n_fields + 7) & ~7UL,
- FALSE))))
- {
- bitmap_set_all(&cols);
- if (likely(!(error= write_locked_table_maps(thd))))
- {
- error=
- RowsEventT::binlog_row_logging_function(thd, table,
- table->file->
- has_transactions(),
- &cols, table->s->fields,
- before_record,
- after_record);
- }
- if (!use_bitbuf)
- bitmap_free(&cols);
- }
+ bitmap_set_all(&cols);
+ if (likely(!(error= write_locked_table_maps(thd))))
+ error= (*log_func)(thd, table, table->file->has_transactions(),
+ &cols, table->s->fields,
+ before_record, after_record);
+
+ if (!use_bitbuf)
+ bitmap_free(&cols);
}
- return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
}
-
- /*
- Instantiate the versions we need for the above template function,
- because we have -fno-implicit-template as compiling option.
- */
-
- template int
- binlog_log_row<Write_rows_log_event>(TABLE *, const uchar *, const uchar *);
-
- template int
- binlog_log_row<Delete_rows_log_event>(TABLE *, const uchar *, const uchar *);
-
- template int
- binlog_log_row<Update_rows_log_event>(TABLE *, const uchar *, const uchar *);
+ return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
}
-
int handler::ha_external_lock(THD *thd, int lock_type)
{
DBUG_ENTER("handler::ha_external_lock");
@@ -4040,10 +4509,14 @@ int handler::ha_reset()
int handler::ha_write_row(uchar *buf)
{
int error;
+ Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
DBUG_ENTER("handler::ha_write_row");
+
+ mark_trx_read_write();
+
if (unlikely(error= write_row(buf)))
DBUG_RETURN(error);
- if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf)))
+ if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
DBUG_RETURN(error); /* purecov: inspected */
DBUG_RETURN(0);
}
@@ -4052,6 +4525,7 @@ int handler::ha_write_row(uchar *buf)
int handler::ha_update_row(const uchar *old_data, uchar *new_data)
{
int error;
+ Log_func *log_func= Update_rows_log_event::binlog_row_logging_function;
/*
Some storage engines require that the new record is in record[0]
@@ -4059,9 +4533,11 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
*/
DBUG_ASSERT(new_data == table->record[0]);
+ mark_trx_read_write();
+
if (unlikely(error= update_row(old_data, new_data)))
return error;
- if (unlikely(error= binlog_log_row<Update_rows_log_event>(table, old_data, new_data)))
+ if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func)))
return error;
return 0;
}
@@ -4069,9 +4545,13 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
int handler::ha_delete_row(const uchar *buf)
{
int error;
+ Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+
+ mark_trx_read_write();
+
if (unlikely(error= delete_row(buf)))
return error;
- if (unlikely(error= binlog_log_row<Delete_rows_log_event>(table, buf, 0)))
+ if (unlikely(error= binlog_log_row(table, buf, 0, log_func)))
return error;
return 0;
}
diff --git a/sql/handler.h b/sql/handler.h
index a4ef34d5f2d..222337fd01b 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -271,6 +271,7 @@ enum legacy_db_type
DB_TYPE_TABLE_FUNCTION,
DB_TYPE_MEMCACHE,
DB_TYPE_FALCON,
+ DB_TYPE_MARIA,
DB_TYPE_FIRST_DYNAMIC=42,
DB_TYPE_DEFAULT=127 // Must be last
};
@@ -727,14 +728,14 @@ inline LEX_STRING *hton_name(const handlerton *hton)
#define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables
#define HTON_NO_PARTITION (1 << 8) //You can not partition these tables
-typedef struct st_thd_trans
+class Ha_trx_info;
+
+struct THD_TRANS
{
- /* number of entries in the ht[] */
- uint nht;
/* true is not all entries in the ht[] support 2pc */
bool no_2pc;
- /* storage engines that registered themselves for this transaction */
- handlerton *ht[MAX_HA];
+ /* storage engines that registered in this transaction */
+ Ha_trx_info *ha_list;
/*
The purpose of this flag is to keep track of non-transactional
tables that were modified in scope of:
@@ -764,7 +765,106 @@ typedef struct st_thd_trans
saved value.
*/
bool modified_non_trans_table;
-} THD_TRANS;
+
+ void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; }
+};
+
+
+/**
+ Either statement transaction or normal transaction - related
+ thread-specific storage engine data.
+
+ If a storage engine participates in a statement/transaction,
+ an instance of this class is present in
+ thd->transaction.{stmt|all}.ha_list. The addition to
+ {stmt|all}.ha_list is made by trans_register_ha().
+
+ When it's time to commit or rollback, each element of ha_list
+ is used to access storage engine's prepare()/commit()/rollback()
+ methods, and also to evaluate if a full two phase commit is
+ necessary.
+
+ @sa General description of transaction handling in handler.cc.
+*/
+
+class Ha_trx_info
+{
+public:
+ /** Register this storage engine in the given transaction context. */
+ void register_ha(THD_TRANS *trans, handlerton *ht_arg)
+ {
+ DBUG_ASSERT(m_flags == 0);
+ DBUG_ASSERT(m_ht == NULL);
+ DBUG_ASSERT(m_next == NULL);
+
+ m_ht= ht_arg;
+ m_flags= (int) TRX_READ_ONLY; /* Assume read-only at start. */
+
+ m_next= trans->ha_list;
+ trans->ha_list= this;
+ }
+
+ /** Clear, prepare for reuse. */
+ void reset()
+ {
+ m_next= NULL;
+ m_ht= NULL;
+ m_flags= 0;
+ }
+
+ Ha_trx_info() { reset(); }
+
+ void set_trx_read_write()
+ {
+ DBUG_ASSERT(is_started());
+ m_flags|= (int) TRX_READ_WRITE;
+ }
+ bool is_trx_read_write() const
+ {
+ DBUG_ASSERT(is_started());
+ return m_flags & (int) TRX_READ_WRITE;
+ }
+ bool is_started() const { return m_ht != NULL; }
+ /** Mark this transaction read-write if the argument is read-write. */
+ void coalesce_trx_with(const Ha_trx_info *stmt_trx)
+ {
+ /*
+ Must be called only after the transaction has been started.
+ Can be called many times, e.g. when we have many
+ read-write statements in a transaction.
+ */
+ DBUG_ASSERT(is_started());
+ if (stmt_trx->is_trx_read_write())
+ set_trx_read_write();
+ }
+ Ha_trx_info *next() const
+ {
+ DBUG_ASSERT(is_started());
+ return m_next;
+ }
+ handlerton *ht() const
+ {
+ DBUG_ASSERT(is_started());
+ return m_ht;
+ }
+private:
+ enum { TRX_READ_ONLY= 0, TRX_READ_WRITE= 1 };
+ /** Auxiliary, used for ha_list management */
+ Ha_trx_info *m_next;
+ /**
+ Although a given Ha_trx_info instance is currently always used
+ for the same storage engine, 'ht' is not-NULL only when the
+ corresponding storage is a part of a transaction.
+ */
+ handlerton *m_ht;
+ /**
+ Transaction flags related to this engine.
+ Not-null only if this instance is a part of transaction.
+ May assume a combination of enum values above.
+ */
+ uchar m_flags;
+};
+
enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
ISO_REPEATABLE_READ, ISO_SERIALIZABLE};
@@ -1651,9 +1751,16 @@ protected:
provide useful functionality.
*/
virtual int rename_table(const char *from, const char *to);
+ /**
+ Delete a table in the engine. Called for base as well as temporary
+ tables.
+ */
virtual int delete_table(const char *name);
private:
+ /* Private helpers */
+ inline void mark_trx_read_write();
+private:
/*
Low-level primitives for storage engines. These should be
overridden by the storage engine class. To call these methods, use
@@ -1789,7 +1896,7 @@ private:
{ return HA_ADMIN_NOT_IMPLEMENTED; }
virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
- virtual bool check_and_repair(THD *thd) { return HA_ERR_WRONG_COMMAND; }
+ virtual bool check_and_repair(THD *thd) { return TRUE; }
virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int discard_or_import_tablespace(my_bool discard)
@@ -1833,9 +1940,7 @@ extern TYPELIB tx_isolation_typelib;
extern TYPELIB myisam_stats_method_typelib;
extern ulong total_ha, total_ha_2pc;
- /* Wrapper functions */
-#define ha_commit_stmt(thd) (ha_commit_trans((thd), FALSE))
-#define ha_rollback_stmt(thd) (ha_rollback_trans((thd), FALSE))
+ /* Wrapper functions */
#define ha_commit(thd) (ha_commit_trans((thd), TRUE))
#define ha_rollback(thd) (ha_rollback_trans((thd), TRUE))
diff --git a/sql/item.cc b/sql/item.cc
index 021decd9e75..f2dc1238ff0 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -18,6 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
+#include <mysql.h>
#include <m_ctype.h>
#include "my_dir.h"
#include "sp_rcontext.h"
@@ -440,9 +441,10 @@ uint Item::decimal_precision() const
}
-void Item::print_item_w_name(String *str)
+void Item::print_item_w_name(String *str, enum_query_type query_type)
{
- print(str);
+ print(str, query_type);
+
if (name)
{
THD *thd= current_thd;
@@ -1126,7 +1128,7 @@ Item_splocal::this_item_addr(THD *thd, Item **)
}
-void Item_splocal::print(String *str)
+void Item_splocal::print(String *str, enum_query_type)
{
str->reserve(m_name.length+8);
str->append(m_name.str, m_name.length);
@@ -1180,7 +1182,7 @@ Item_case_expr::this_item_addr(THD *thd, Item **)
}
-void Item_case_expr::print(String *str)
+void Item_case_expr::print(String *str, enum_query_type)
{
if (str->reserve(MAX_INT_WIDTH + sizeof("case_expr@")))
return; /* purecov: inspected */
@@ -1234,6 +1236,22 @@ bool Item_name_const::is_null()
return value_item->is_null();
}
+
+Item_name_const::Item_name_const(Item *name_arg, Item *val):
+ value_item(val), name_item(name_arg)
+{
+ if (!(valid_args= name_item->basic_const_item() &&
+ (value_item->basic_const_item() ||
+ ((value_item->type() == FUNC_ITEM) &&
+ (((Item_func *) value_item)->functype() ==
+ Item_func::NEG_FUNC) &&
+ (((Item_func *) value_item)->key_item()->type() !=
+ FUNC_ITEM)))))
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST");
+ Item::maybe_null= TRUE;
+}
+
+
Item::Type Item_name_const::type() const
{
/*
@@ -1245,8 +1263,17 @@ Item::Type Item_name_const::type() const
if (item->type() == FIELD_ITEM)
((Item_field *) item)->...
we return NULL_ITEM in the case to avoid wrong casting.
+
+ valid_args guarantees value_item->basic_const_item(); if type is
+ FUNC_ITEM, then we have a fudged item_func_neg() on our hands
+ and return the underlying type.
*/
- return valid_args ? value_item->type() : NULL_ITEM;
+ return valid_args ?
+ (((value_item->type() == FUNC_ITEM) &&
+ (((Item_func *) value_item)->functype() == Item_func::NEG_FUNC)) ?
+ ((Item_func *) value_item)->key_item()->type() :
+ value_item->type()) :
+ NULL_ITEM;
}
@@ -1267,6 +1294,7 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
return TRUE;
}
set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info);
+ collation.set(value_item->collation.collation, DERIVATION_IMPLICIT);
max_length= value_item->max_length;
decimals= value_item->decimals;
fixed= 1;
@@ -1274,12 +1302,12 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
}
-void Item_name_const::print(String *str)
+void Item_name_const::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("NAME_CONST("));
- name_item->print(str);
+ name_item->print(str, query_type);
str->append(',');
- value_item->print(str);
+ value_item->print(str, query_type);
str->append(')');
}
@@ -1296,12 +1324,12 @@ public:
const char *table_name_arg, const char *field_name_arg)
:Item_ref(context_arg, item, table_name_arg, field_name_arg) {}
- void print (String *str)
+ virtual inline void print (String *str, enum_query_type query_type)
{
if (ref)
- (*ref)->print(str);
+ (*ref)->print(str, query_type);
else
- Item_ident::print(str);
+ Item_ident::print(str, query_type);
}
};
@@ -1453,7 +1481,9 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags)
set(dt);
}
else
- ; // Do nothing
+ {
+ // Do nothing
+ }
}
else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
left_is_superset(this, &dt))
@@ -1870,7 +1900,7 @@ const char *Item_ident::full_name() const
return tmp;
}
-void Item_ident::print(String *str)
+void Item_ident::print(String *str, enum_query_type query_type)
{
THD *thd= current_thd;
char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
@@ -2132,7 +2162,7 @@ String *Item_int::val_str(String *str)
return str;
}
-void Item_int::print(String *str)
+void Item_int::print(String *str, enum_query_type query_type)
{
// my_charset_bin is good enough for numbers
str_value.set(value, &my_charset_bin);
@@ -2163,7 +2193,7 @@ String *Item_uint::val_str(String *str)
}
-void Item_uint::print(String *str)
+void Item_uint::print(String *str, enum_query_type query_type)
{
// latin1 is good enough for numbers
str_value.set((ulonglong) value, default_charset());
@@ -2255,7 +2285,7 @@ String *Item_decimal::val_str(String *result)
return result;
}
-void Item_decimal::print(String *str)
+void Item_decimal::print(String *str, enum_query_type query_type)
{
my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value);
str->append(str_value);
@@ -2308,12 +2338,39 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value)
}
-void Item_string::print(String *str)
+void Item_string::print(String *str, enum_query_type query_type)
{
- str->append('_');
- str->append(collation.collation->csname);
+ if (query_type == QT_ORDINARY && is_cs_specified())
+ {
+ str->append('_');
+ str->append(collation.collation->csname);
+ }
+
str->append('\'');
- str_value.print(str);
+
+ if (query_type == QT_ORDINARY ||
+ my_charset_same(str_value.charset(), system_charset_info))
+ {
+ str_value.print(str);
+ }
+ else
+ {
+ THD *thd= current_thd;
+ LEX_STRING utf8_lex_str;
+
+ thd->convert_string(&utf8_lex_str,
+ system_charset_info,
+ str_value.c_ptr_safe(),
+ str_value.length(),
+ str_value.charset());
+
+ String utf8_str(utf8_lex_str.str,
+ utf8_lex_str.length,
+ system_charset_info);
+
+ utf8_str.print(str);
+ }
+
str->append('\'');
}
@@ -2436,14 +2493,14 @@ default_set_param_func(Item_param *param,
Item_param::Item_param(uint pos_in_query_arg) :
- strict_type(FALSE),
state(NO_VALUE),
item_result_type(STRING_RESULT),
/* Don't pretend to be a literal unless value for this item is set. */
item_type(PARAM_ITEM),
param_type(MYSQL_TYPE_VARCHAR),
pos_in_query(pos_in_query_arg),
- set_param_func(default_set_param_func)
+ set_param_func(default_set_param_func),
+ limit_clause_param(FALSE)
{
name= (char*) "?";
/*
@@ -2626,8 +2683,14 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
if (entry && entry->value)
{
item_result_type= entry->type;
- if (strict_type && required_result_type != item_result_type)
- DBUG_RETURN(1);
+ unsigned_flag= entry->unsigned_flag;
+ if (limit_clause_param)
+ {
+ my_bool unused;
+ set_int(entry->val_int(&unused), MY_INT64_NUM_DECIMAL_DIGITS);
+ item_type= Item::INT_ITEM;
+ DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0);
+ }
switch (item_result_type) {
case REAL_RESULT:
set_double(*(double*)entry->value);
@@ -2923,7 +2986,7 @@ const String *Item_param::query_val_str(String* str) const
{
switch (state) {
case INT_VALUE:
- str->set(value.integer, &my_charset_bin);
+ str->set_int(value.integer, unsigned_flag, &my_charset_bin);
break;
case REAL_VALUE:
str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
@@ -3079,7 +3142,7 @@ Item_param::eq(const Item *arg, bool binary_cmp) const
/* End of Item_param related */
-void Item_param::print(String *str)
+void Item_param::print(String *str, enum_query_type query_type)
{
if (state == NO_VALUE)
{
@@ -3938,13 +4001,25 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
}
if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
goto error;
- else if (!ret)
- return FALSE;
outer_fixed= TRUE;
+ if (!ret)
+ goto mark_non_agg_field;
}
else if (!from_field)
goto error;
+ if (!outer_fixed && cached_table && cached_table->select_lex &&
+ context->select_lex &&
+ cached_table->select_lex != context->select_lex)
+ {
+ int ret;
+ if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
+ goto error;
+ outer_fixed= 1;
+ if (!ret)
+ goto mark_non_agg_field;
+ }
+
/*
if it is not expression from merged VIEW we will set this field.
@@ -3960,18 +4035,6 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if (from_field == view_ref_found)
return FALSE;
- if (!outer_fixed && cached_table && cached_table->select_lex &&
- context->select_lex &&
- cached_table->select_lex != context->select_lex)
- {
- int ret;
- if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
- goto error;
- if (!ret)
- return FALSE;
- outer_fixed= 1;
- }
-
set_field(from_field);
if (thd->lex->in_sum_func &&
thd->lex->in_sum_func->nest_level ==
@@ -4038,6 +4101,26 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
thd->lex->current_select->non_agg_fields.push_back(this);
marker= thd->lex->current_select->cur_pos_in_select_list;
}
+mark_non_agg_field:
+ if (fixed && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
+ {
+ /*
+ Mark selects according to presence of non aggregated fields.
+ Fields from outer selects added to the aggregate function
+ outer_fields list as its unknown at the moment whether it's
+ aggregated or not.
+ */
+ if (!thd->lex->in_sum_func)
+ cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED;
+ else
+ {
+ if (outer_fixed)
+ thd->lex->in_sum_func->outer_fields.push_back(this);
+ else if (thd->lex->in_sum_func->nest_level !=
+ thd->lex->current_select->nest_level)
+ cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED;
+ }
+ }
return FALSE;
error:
@@ -4143,6 +4226,30 @@ bool Item_field::subst_argument_checker(uchar **arg)
/**
+ Convert a numeric value to a zero-filled string
+
+ @param[in,out] item the item to operate on
+ @param field The field that this value is equated to
+
+ This function converts a numeric value to a string. In this conversion
+ the zero-fill flag of the field is taken into account.
+ This is required so the resulting string value can be used instead of
+ the field reference when propagating equalities.
+*/
+
+static void convert_zerofill_number_to_string(Item **item, Field_num *field)
+{
+ char buff[MAX_FIELD_WIDTH],*pos;
+ String tmp(buff,sizeof(buff), field->charset()), *res;
+
+ res= (*item)->val_str(&tmp);
+ field->prepend_zeros(res);
+ pos= (char *) sql_strmake (res->ptr(), res->length());
+ *item= new Item_string(pos, res->length(), field->charset());
+}
+
+
+/**
Set a pointer to the multiple equality the field reference belongs to
(if any).
@@ -4188,6 +4295,13 @@ Item *Item_field::equal_fields_propagator(uchar *arg)
if (!item ||
(cmp_context != IMPOSSIBLE_RESULT && item->cmp_context != cmp_context))
item= this;
+ else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type()))
+ {
+ if (item && cmp_context != INT_RESULT)
+ convert_zerofill_number_to_string(&item, (Field_num *)field);
+ else
+ item= this;
+ }
return item;
}
@@ -4364,6 +4478,49 @@ String *Item::check_well_formed_result(String *str, bool send_error)
return str;
}
+/*
+ Compare two items using a given collation
+
+ SYNOPSIS
+ eq_by_collation()
+ item item to compare with
+ binary_cmp TRUE <-> compare as binaries
+ cs collation to use when comparing strings
+
+ DESCRIPTION
+ This method works exactly as Item::eq if the collation cs coincides with
+ the collation of the compared objects. Otherwise, first the collations that
+ differ from cs are replaced for cs and then the items are compared by
+ Item::eq. After the comparison the original collations of items are
+ restored.
+
+ RETURN
+ 1 compared items has been detected as equal
+ 0 otherwise
+*/
+
+bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs)
+{
+ CHARSET_INFO *save_cs= 0;
+ CHARSET_INFO *save_item_cs= 0;
+ if (collation.collation != cs)
+ {
+ save_cs= collation.collation;
+ collation.collation= cs;
+ }
+ if (item->collation.collation != cs)
+ {
+ save_item_cs= item->collation.collation;
+ item->collation.collation= cs;
+ }
+ bool res= eq(item, binary_cmp);
+ if (save_cs)
+ collation.collation= save_cs;
+ if (save_item_cs)
+ item->collation.collation= save_item_cs;
+ return res;
+}
+
/**
Create a field to hold a string value from an item.
@@ -4792,7 +4949,7 @@ int Item_float::save_in_field(Field *field, bool no_conversions)
}
-void Item_float::print(String *str)
+void Item_float::print(String *str, enum_query_type query_type)
{
if (presentation)
{
@@ -4910,7 +5067,7 @@ warn:
}
-void Item_hex_string::print(String *str)
+void Item_hex_string::print(String *str, enum_query_type query_type)
{
char *end= (char*) str_value.ptr() + str_value.length(),
*ptr= end - min(str_value.length(), sizeof(longlong));
@@ -5172,7 +5329,7 @@ Item *Item_field::update_value_transformer(uchar *select_arg)
}
-void Item_field::print(String *str)
+void Item_field::print(String *str, enum_query_type query_type)
{
if (field && field->table->const_table)
{
@@ -5184,7 +5341,7 @@ void Item_field::print(String *str)
str->append('\'');
return;
}
- Item_ident::print(str);
+ Item_ident::print(str, query_type);
}
@@ -5466,13 +5623,16 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
DBUG_ASSERT(*ref);
/*
Check if this is an incorrect reference in a group function or forward
- reference. Do not issue an error if this is an unnamed reference inside an
- aggregate function.
+ reference. Do not issue an error if this is:
+ 1. outer reference (will be fixed later by the fix_inner_refs function);
+ 2. an unnamed reference inside an aggregate function.
*/
- if (((*ref)->with_sum_func && name &&
- !(current_sel->linkage != GLOBAL_OPTIONS_TYPE &&
- current_sel->having_fix_field)) ||
- !(*ref)->fixed)
+ if (!((*ref)->type() == REF_ITEM &&
+ ((Item_ref *)(*ref))->ref_type() == OUTER_REF) &&
+ (((*ref)->with_sum_func && name &&
+ !(current_sel->linkage != GLOBAL_OPTIONS_TYPE &&
+ current_sel->having_fix_field)) ||
+ !(*ref)->fixed))
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0),
name, ((*ref)->with_sum_func?
@@ -5524,7 +5684,7 @@ void Item_ref::cleanup()
}
-void Item_ref::print(String *str)
+void Item_ref::print(String *str, enum_query_type query_type)
{
if (ref)
{
@@ -5535,10 +5695,10 @@ void Item_ref::print(String *str)
append_identifier(thd, str, name, (uint) strlen(name));
}
else
- (*ref)->print(str);
+ (*ref)->print(str, query_type);
}
else
- Item_ident::print(str);
+ Item_ident::print(str, query_type);
}
@@ -5728,11 +5888,11 @@ Item *Item_ref::get_tmp_table_item(THD *thd)
}
-void Item_ref_null_helper::print(String *str)
+void Item_ref_null_helper::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<ref_null_helper>("));
if (ref)
- (*ref)->print(str);
+ (*ref)->print(str, query_type);
else
str->append('?');
str->append(')');
@@ -5924,7 +6084,7 @@ error:
}
-void Item_default_value::print(String *str)
+void Item_default_value::print(String *str, enum_query_type query_type)
{
if (!arg)
{
@@ -5932,7 +6092,7 @@ void Item_default_value::print(String *str)
return;
}
str->append(STRING_WITH_LEN("default("));
- arg->print(str);
+ arg->print(str, query_type);
str->append(')');
}
@@ -6068,10 +6228,10 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
return FALSE;
}
-void Item_insert_value::print(String *str)
+void Item_insert_value::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("values("));
- arg->print(str);
+ arg->print(str, query_type);
str->append(')');
}
@@ -6192,7 +6352,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
}
-void Item_trigger_field::print(String *str)
+void Item_trigger_field::print(String *str, enum_query_type query_type)
{
str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3);
str->append('.');
@@ -6382,13 +6542,13 @@ Item_cache* Item_cache::get_cache(const Item *item)
}
-void Item_cache::print(String *str)
+void Item_cache::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<cache>("));
if (example)
- example->print(str);
+ example->print(str, query_type);
else
- Item::print(str);
+ Item::print(str, query_type);
str->append(')');
}
diff --git a/sql/item.h b/sql/item.h
index 4ac3b2959b3..f4d7da55496 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -113,7 +113,6 @@ public:
}
};
-
/*************************************************************************/
/*
A framework to easily handle different return types for hybrid items
@@ -769,20 +768,24 @@ public:
*/
virtual bool const_during_execution() const
{ return (used_tables() & ~PARAM_TABLE_BIT) == 0; }
- /*
- This is an essential method for correct functioning of VIEWS.
- To save a view in an .frm file we need its unequivocal
- definition in SQL that takes into account sql_mode and
- environmental settings. Currently such definition is restored
- by traversing through the parsed tree of a view and
- print()'ing SQL syntax of every node to a String buffer. This
- method is used to print the SQL definition of an item. The
- second use of this method is for EXPLAIN EXTENDED, to print
- the SQL of a query after all optimizations of the parsed tree
- have been done.
- */
- virtual void print(String *str_arg) { str_arg->append(full_name()); }
- void print_item_w_name(String *);
+
+ /**
+ This method is used for to:
+ - to generate a view definition query (SELECT-statement);
+ - to generate a SQL-query for EXPLAIN EXTENDED;
+ - to generate a SQL-query to be shown in INFORMATION_SCHEMA;
+ - debug.
+
+ For more information about view definition query, INFORMATION_SCHEMA
+ query and why they should be generated from the Item-tree, @see
+ mysql_register_view().
+ */
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(full_name());
+ }
+
+ void print_item_w_name(String *, enum_query_type query_type);
virtual void update_used_tables() {}
virtual void split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields) {}
@@ -1004,12 +1007,30 @@ public:
virtual Field::geometry_type get_geometry_type() const
{ return Field::GEOM_GEOMETRY; };
String *check_well_formed_result(String *str, bool send_error= 0);
+ bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
};
class sp_head;
+class Item_basic_constant :public Item
+{
+public:
+ /* to prevent drop fixed flag (no need parent cleanup call) */
+ void cleanup()
+ {
+ /*
+ Restore the original field name as it might not have been allocated
+ in the statement memory. If the name is auto generated, it must be
+ done again between subsequent executions of a prepared statement.
+ */
+ if (orig_name)
+ name= orig_name;
+ }
+};
+
+
/*****************************************************************************
The class is a base class for representation of stored routine variables in
the Item-hierarchy. There are the following kinds of SP-vars:
@@ -1134,7 +1155,7 @@ public:
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
public:
inline const LEX_STRING *my_name() const;
@@ -1203,7 +1224,7 @@ public:
Item_case_expr can not occur in views, so here it is only for debug
purposes.
*/
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
private:
uint m_case_expr_id;
@@ -1244,14 +1265,7 @@ class Item_name_const : public Item
Item *name_item;
bool valid_args;
public:
- Item_name_const(Item *name_arg, Item *val):
- value_item(val), name_item(name_arg)
- {
- if (!(valid_args= name_item->basic_const_item() &
- value_item->basic_const_item()))
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST");
- Item::maybe_null= TRUE;
- }
+ Item_name_const(Item *name_arg, Item *val);
bool fix_fields(THD *, Item **);
@@ -1261,7 +1275,7 @@ public:
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *);
bool is_null();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_result result_type() const
{
@@ -1292,7 +1306,7 @@ bool agg_item_charsets(DTCollation &c, const char *name,
Item **items, uint nitems, uint flags, int item_sep);
-class Item_num: public Item
+class Item_num: public Item_basic_constant
{
public:
Item_num() {} /* Remove gcc warning */
@@ -1343,7 +1357,7 @@ public:
const char *full_name() const;
void cleanup();
bool remove_dependence_processor(uchar * arg);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
friend bool insert_fields(THD *thd, Name_resolution_context *context,
@@ -1473,7 +1487,7 @@ public:
Item *safe_charset_converter(CHARSET_INFO *tocs);
int fix_outer_field(THD *thd, Field **field, Item **reference);
virtual Item *update_value_transformer(uchar *select_arg);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Field::geometry_type get_geometry_type() const
{
DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
@@ -1484,7 +1498,7 @@ public:
friend class st_select_lex_unit;
};
-class Item_null :public Item
+class Item_null :public Item_basic_constant
{
public:
Item_null(char *name_par=0)
@@ -1506,12 +1520,15 @@ public:
bool send(Protocol *protocol, String *str);
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
- /* to prevent drop fixed flag (no need parent cleanup call) */
- void cleanup() {}
bool basic_const_item() const { return 1; }
Item *clone_item() { return new Item_null(name); }
bool is_null() { return 1; }
- void print(String *str) { str->append(STRING_WITH_LEN("NULL")); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(STRING_WITH_LEN("NULL"));
+ }
+
Item *safe_charset_converter(CHARSET_INFO *tocs);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -1536,8 +1553,6 @@ class Item_param :public Item
char cnvbuf[MAX_FIELD_WIDTH];
String cnvstr;
Item *cnvitem;
- bool strict_type;
- enum Item_result required_result_type;
public:
enum enum_item_param_state
@@ -1645,7 +1660,7 @@ public:
*/
virtual table_map used_tables() const
{ return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool is_null()
{ DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; }
bool basic_const_item() const;
@@ -1667,11 +1682,8 @@ public:
Otherwise return FALSE.
*/
bool eq(const Item *item, bool binary_cmp) const;
- void set_strict_type(enum Item_result result_type_arg)
- {
- strict_type= TRUE;
- required_result_type= result_type_arg;
- }
+ /** Item is a argument to a limit clause. */
+ bool limit_clause_param;
};
@@ -1701,9 +1713,7 @@ public:
int save_in_field(Field *field, bool no_conversions);
bool basic_const_item() const { return 1; }
Item *clone_item() { return new Item_int(name,value,max_length); }
- // to prevent drop fixed flag (no need parent cleanup call)
- void cleanup() {}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_num *neg() { value= -value; return this; }
uint decimal_precision() const
{ return (uint)(max_length - test(value < 0)); }
@@ -1723,7 +1733,7 @@ public:
String *val_str(String*);
Item *clone_item() { return new Item_uint(name, value, max_length); }
int save_in_field(Field *field, bool no_conversions);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_num *neg ();
uint decimal_precision() const { return max_length; }
bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
@@ -1757,9 +1767,7 @@ public:
{
return new Item_decimal(name, &decimal_value, decimals, max_length);
}
- // to prevent drop fixed flag (no need parent cleanup call)
- void cleanup() {}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_num *neg()
{
my_decimal_neg(&decimal_value);
@@ -1813,12 +1821,10 @@ public:
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
bool basic_const_item() const { return 1; }
- // to prevent drop fixed flag (no need parent cleanup call)
- void cleanup() {}
Item *clone_item()
{ return new Item_float(name, value, decimals, max_length); }
Item_num *neg() { value= -value; return this; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool eq(const Item *, bool binary_cmp) const;
};
@@ -1831,17 +1837,23 @@ public:
uint length)
:Item_float(NullS, val_arg, decimal_par, length), func_name(str)
{}
- void print(String *str) { str->append(func_name); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name);
+ }
+
Item *safe_charset_converter(CHARSET_INFO *tocs);
};
-class Item_string :public Item
+class Item_string :public Item_basic_constant
{
public:
Item_string(const char *str,uint length,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
uint repertoire= MY_REPERTOIRE_UNICODE30)
+ : m_cs_specified(FALSE)
{
str_value.set_or_copy_aligned(str, length, cs);
collation.set(cs, dv, repertoire);
@@ -1860,6 +1872,7 @@ public:
}
/* Just create an item and do not fill string representation */
Item_string(CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ : m_cs_specified(FALSE)
{
collation.set(cs, dv);
max_length= 0;
@@ -1870,6 +1883,7 @@ public:
Item_string(const char *name_par, const char *str, uint length,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
uint repertoire= MY_REPERTOIRE_UNICODE30)
+ : m_cs_specified(FALSE)
{
str_value.set_or_copy_aligned(str, length, cs);
collation.set(cs, dv, repertoire);
@@ -1919,10 +1933,50 @@ public:
str_value.append(str, length);
max_length= str_value.numchars() * collation.collation->mbmaxlen;
}
- void print(String *str);
- // to prevent drop fixed flag (no need parent cleanup call)
- void cleanup() {}
+ virtual void print(String *str, enum_query_type query_type);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+
+ /**
+ Return TRUE if character-set-introducer was explicitly specified in the
+ original query for this item (text literal).
+
+ This operation is to be called from Item_string::print(). The idea is
+ that when a query is generated (re-constructed) from the Item-tree,
+ character-set-introducers should appear only for those literals, where
+ they were explicitly specified by the user. Otherwise, that may lead to
+ loss collation information (character set introducers implies default
+ collation for the literal).
+
+ Basically, that makes sense only for views and hopefully will be gone
+ one day when we start using original query as a view definition.
+
+ @return This operation returns the value of m_cs_specified attribute.
+ @retval TRUE if character set introducer was explicitly specified in
+ the original query.
+ @retval FALSE otherwise.
+ */
+ inline bool is_cs_specified() const
+ {
+ return m_cs_specified;
+ }
+
+ /**
+ Set the value of m_cs_specified attribute.
+
+ m_cs_specified attribute shows whether character-set-introducer was
+ explicitly specified in the original query for this text literal or
+ not. The attribute makes sense (is used) only for views.
+
+ This operation is to be called from the parser during parsing an input
+ query.
+ */
+ inline void set_cs_specified(bool cs_specified)
+ {
+ m_cs_specified= cs_specified;
+ }
+
+private:
+ bool m_cs_specified;
};
@@ -1936,7 +1990,12 @@ public:
:Item_string(NullS, str, length, cs, dv), func_name(name_par)
{}
Item *safe_charset_converter(CHARSET_INFO *tocs);
- void print(String *str) { str->append(func_name); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name);
+ }
+
bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -2005,10 +2064,10 @@ public:
};
-class Item_hex_string: public Item
+class Item_hex_string: public Item_basic_constant
{
public:
- Item_hex_string(): Item() {}
+ Item_hex_string() {}
Item_hex_string(const char *str,uint str_length);
enum Type type() const { return VARBIN_ITEM; }
double val_real()
@@ -2024,9 +2083,7 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
enum Item_result cast_to_int_type() const { return INT_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- // to prevent drop fixed flag (no need parent cleanup call)
- void cleanup() {}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool eq(const Item *item, bool binary_cmp) const;
virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
@@ -2147,7 +2204,7 @@ public:
}
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{ return (*ref)->walk(processor, walk_subquery, arg); }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool result_as_longlong()
{
return (*ref)->result_as_longlong();
@@ -2156,6 +2213,35 @@ public:
Item_field *filed_for_view_update()
{ return (*ref)->filed_for_view_update(); }
virtual Ref_Type ref_type() { return REF; }
+
+ // Row emulation: forwarding of ROW-related calls to ref
+ uint cols()
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->cols() : 1;
+ }
+ Item* element_index(uint i)
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->element_index(i) : this;
+ }
+ Item** addr(uint i)
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->addr(i) : 0;
+ }
+ bool check_cols(uint c)
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->check_cols(c)
+ : Item::check_cols(c);
+ }
+ bool null_inside()
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->null_inside() : 0;
+ }
+ void bring_value()
+ {
+ if (ref && result_type() == ROW_RESULT)
+ (*ref)->bring_value();
+ }
+
};
@@ -2294,7 +2380,7 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
/*
we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE
*/
@@ -2469,7 +2555,7 @@ public:
enum Type type() const { return DEFAULT_VALUE_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
int save_in_field(Field *field_arg, bool no_conversions);
table_map used_tables() const { return (table_map)0L; }
@@ -2502,7 +2588,7 @@ public:
arg(a) {}
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
int save_in_field(Field *field_arg, bool no_conversions)
{
return Item_field::save_in_field(field_arg, no_conversions);
@@ -2573,7 +2659,7 @@ public:
enum Type type() const { return TRIGGER_FIELD_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const { return (table_map)0L; }
Field *get_tmp_table_field() { return 0; }
Item *copy_or_same(THD *thd) { return this; }
@@ -2617,7 +2703,7 @@ private:
};
-class Item_cache: public Item
+class Item_cache: public Item_basic_constant
{
protected:
Item *example;
@@ -2664,9 +2750,7 @@ public:
static Item_cache* get_cache(const Item *item);
table_map used_tables() const { return used_table_map; }
virtual void keep_array() {}
- // to prevent drop fixed flag (no need parent cleanup call)
- void cleanup() {}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool eq_def(Field *field)
{
return cached_field ? cached_field->eq_def (field) : FALSE;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index d28b4e172f3..f34cd35b4a9 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -286,10 +286,10 @@ longlong Item_func_not::val_int()
higher than the precedence of NOT.
*/
-void Item_func_not::print(String *str)
+void Item_func_not::print(String *str, enum_query_type query_type)
{
str->append('(');
- Item_func::print(str);
+ Item_func::print(str, query_type);
str->append(')');
}
@@ -321,12 +321,12 @@ bool Item_func_not_all::empty_underlying_subquery()
(test_sub_item && !test_sub_item->any_value()));
}
-void Item_func_not_all::print(String *str)
+void Item_func_not_all::print(String *str, enum_query_type query_type)
{
if (show)
- Item_func::print(str);
+ Item_func::print(str, query_type);
else
- args[0]->print(str);
+ args[0]->print(str, query_type);
}
@@ -1421,10 +1421,10 @@ void Item_func_truth::fix_length_and_dec()
}
-void Item_func_truth::print(String *str)
+void Item_func_truth::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" is "));
if (! affirmative)
str->append(STRING_WITH_LEN("not "));
@@ -2108,16 +2108,16 @@ longlong Item_func_between::val_int()
}
-void Item_func_between::print(String *str)
+void Item_func_between::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (negated)
str->append(STRING_WITH_LEN(" not"));
str->append(STRING_WITH_LEN(" between "));
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(STRING_WITH_LEN(" and "));
- args[2]->print(str);
+ args[2]->print(str, query_type);
str->append(')');
}
@@ -2762,26 +2762,26 @@ uint Item_func_case::decimal_precision() const
Fix this so that it prints the whole CASE expression
*/
-void Item_func_case::print(String *str)
+void Item_func_case::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(case "));
if (first_expr_num != -1)
{
- args[first_expr_num]->print(str);
+ args[first_expr_num]->print(str, query_type);
str->append(' ');
}
for (uint i=0 ; i < ncases ; i+=2)
{
str->append(STRING_WITH_LEN("when "));
- args[i]->print(str);
+ args[i]->print(str, query_type);
str->append(STRING_WITH_LEN(" then "));
- args[i+1]->print(str);
+ args[i+1]->print(str, query_type);
str->append(' ');
}
if (else_expr_num != -1)
{
str->append(STRING_WITH_LEN("else "));
- args[else_expr_num]->print(str);
+ args[else_expr_num]->print(str, query_type);
str->append(' ');
}
str->append(STRING_WITH_LEN("end)"));
@@ -3058,7 +3058,10 @@ void in_string::set(uint pos,Item *item)
{
if (res->uses_buffer_owned_by(str))
res->copy();
- *str= *res;
+ if (item->type() == Item::FUNC_ITEM)
+ str->copy(*res);
+ else
+ *str= *res;
}
if (!str->charset())
{
@@ -3705,14 +3708,14 @@ void Item_func_in::fix_length_and_dec()
}
-void Item_func_in::print(String *str)
+void Item_func_in::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (negated)
str->append(STRING_WITH_LEN(" not"));
str->append(STRING_WITH_LEN(" in ("));
- print_args(str, 1);
+ print_args(str, 1, query_type);
str->append(STRING_WITH_LEN("))"));
}
@@ -4083,19 +4086,19 @@ void Item_cond::update_used_tables()
}
-void Item_cond::print(String *str)
+void Item_cond::print(String *str, enum_query_type query_type)
{
str->append('(');
List_iterator_fast<Item> li(list);
Item *item;
if ((item=li++))
- item->print(str);
+ item->print(str, query_type);
while ((item=li++))
{
str->append(' ');
str->append(func_name());
str->append(' ');
- item->print(str);
+ item->print(str, query_type);
}
str->append(')');
}
@@ -4278,10 +4281,10 @@ longlong Item_func_isnotnull::val_int()
}
-void Item_func_isnotnull::print(String *str)
+void Item_func_isnotnull::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" is not null)"));
}
@@ -5275,24 +5278,24 @@ Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
return Item_func::transform(transformer, arg);
}
-void Item_equal::print(String *str)
+void Item_equal::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
List_iterator_fast<Item_field> it(fields);
Item *item;
if (const_item)
- const_item->print(str);
+ const_item->print(str, query_type);
else
{
item= it++;
- item->print(str);
+ item->print(str, query_type);
}
while ((item= it++))
{
str->append(',');
str->append(' ');
- item->print(str);
+ item->print(str, query_type);
}
str->append(')');
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 188d87a69ca..0166a18029d 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -123,7 +123,7 @@ public:
virtual bool val_bool();
virtual longlong val_int();
virtual void fix_length_and_dec();
- virtual void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
protected:
Item_func_truth(Item *a, bool a_value, bool a_affirmative)
@@ -338,7 +338,12 @@ public:
optimize_type select_optimize() const { return OPTIMIZE_OP; }
virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }
bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; }
- void print(String *str) { Item_func::print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print_op(str, query_type);
+ }
+
bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); }
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
@@ -368,7 +373,7 @@ public:
enum Functype functype() const { return NOT_FUNC; }
const char *func_name() const { return "not"; }
Item *neg_transformer(THD *thd);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
class Item_maxmin_subselect;
@@ -433,7 +438,7 @@ public:
longlong val_int();
enum Functype functype() const { return NOT_ALL_FUNC; }
const char *func_name() const { return "<not>"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; };
void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; };
bool empty_underlying_subquery();
@@ -594,7 +599,7 @@ public:
const char *func_name() const { return "between"; }
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
uint decimal_precision() const { return 1; }
@@ -608,7 +613,11 @@ public:
longlong val_int();
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "strcmp"; }
- void print(String *str) { Item_func::print(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
};
@@ -711,7 +720,12 @@ public:
void fix_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
const char *func_name() const { return "nullif"; }
- void print(String *str) { Item_func::print(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
+
table_map not_null_tables() const { return 0; }
bool is_null();
};
@@ -1141,7 +1155,7 @@ public:
enum Item_result result_type () const { return cached_result_type; }
enum_field_types field_type() const { return cached_field_type; }
const char *func_name() const { return "case"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item *find_item(String *str);
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
void cleanup();
@@ -1208,7 +1222,7 @@ public:
}
optimize_type select_optimize() const
{ return OPTIMIZE_KEY; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
enum Functype functype() const { return IN_FUNC; }
const char *func_name() const { return " IN "; }
bool nulls_in_row();
@@ -1330,7 +1344,7 @@ public:
table_map not_null_tables() const
{ return abort_on_null ? not_null_tables_cache : 0; }
Item *neg_transformer(THD *thd);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
void top_level_item() { abort_on_null=1; }
};
@@ -1395,7 +1409,12 @@ public:
longlong val_int();
bool fix_fields(THD *thd, Item **ref);
const char *func_name() const { return "regexp"; }
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
+
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
};
@@ -1407,7 +1426,11 @@ public:
Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b) {}
longlong val_int() { return 0;}
const char *func_name() const { return "regex"; }
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
};
#endif /* USE_REGEX */
@@ -1444,7 +1467,7 @@ public:
List<Item>* argument_list() { return &list; }
table_map used_tables() const;
void update_used_tables();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
COND **conds);
@@ -1568,7 +1591,7 @@ public:
void update_used_tables();
bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
Item *transform(Item_transformer transformer, uchar *arg);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
CHARSET_INFO *compare_collation()
{ return fields.head()->collation.collation; }
};
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 40578bef5f8..49cc33b95a7 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -4354,12 +4354,12 @@ Create_func_space::create(THD *thd, Item *arg1)
if (cs->mbminlen > 1)
{
uint dummy_errors;
- sp= new (thd->mem_root) Item_string("", 0, cs);
+ sp= new (thd->mem_root) Item_string("", 0, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors);
}
else
{
- sp= new (thd->mem_root) Item_string(" ", 1, cs);
+ sp= new (thd->mem_root) Item_string(" ", 1, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
}
return new (thd->mem_root) Item_func_repeat(sp, arg1);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 47db346023a..202c4bee9c7 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -375,37 +375,37 @@ table_map Item_func::not_null_tables() const
}
-void Item_func::print(String *str)
+void Item_func::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
- print_args(str, 0);
+ print_args(str, 0, query_type);
str->append(')');
}
-void Item_func::print_args(String *str, uint from)
+void Item_func::print_args(String *str, uint from, enum_query_type query_type)
{
for (uint i=from ; i < arg_count ; i++)
{
if (i != from)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
}
-void Item_func::print_op(String *str)
+void Item_func::print_op(String *str, enum_query_type query_type)
{
str->append('(');
for (uint i=0 ; i < arg_count-1 ; i++)
{
- args[i]->print(str);
+ args[i]->print(str, query_type);
str->append(' ');
str->append(func_name());
str->append(' ');
}
- args[arg_count-1]->print(str);
+ args[arg_count-1]->print(str, query_type);
str->append(')');
}
@@ -884,10 +884,10 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value)
}
-void Item_func_signed::print(String *str)
+void Item_func_signed::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as signed)"));
}
@@ -955,10 +955,10 @@ longlong Item_func_signed::val_int()
}
-void Item_func_unsigned::print(String *str)
+void Item_func_unsigned::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as unsigned)"));
}
@@ -1064,7 +1064,7 @@ err:
}
-void Item_decimal_typecast::print(String *str)
+void Item_decimal_typecast::print(String *str, enum_query_type query_type)
{
char len_buf[20*3 + 1];
char *end;
@@ -1072,7 +1072,7 @@ void Item_decimal_typecast::print(String *str)
uint precision= my_decimal_length_to_precision(max_length, decimals,
unsigned_flag);
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as decimal("));
end=int10_to_str(precision, len_buf,10);
@@ -1093,7 +1093,7 @@ double Item_func_plus::real_op()
double value= args[0]->val_real() + args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return value;
+ return fix_result(value);
}
@@ -1171,7 +1171,7 @@ double Item_func_minus::real_op()
double value= args[0]->val_real() - args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return value;
+ return fix_result(value);
}
@@ -1211,7 +1211,7 @@ double Item_func_mul::real_op()
double value= args[0]->val_real() * args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return value;
+ return fix_result(value);
}
@@ -1269,7 +1269,7 @@ double Item_func_div::real_op()
signal_divide_by_null();
return 0.0;
}
- return value/val2;
+ return fix_result(value/val2);
}
@@ -1643,7 +1643,7 @@ double Item_func_exp::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0; /* purecov: inspected */
- return exp(value);
+ return fix_result(exp(value));
}
double Item_func_sqrt::val_real()
@@ -1662,7 +1662,7 @@ double Item_func_pow::val_real()
double val2= args[1]->val_real();
if ((null_value=(args[0]->null_value || args[1]->null_value)))
return 0.0; /* purecov: inspected */
- return pow(value,val2);
+ return fix_result(pow(value,val2));
}
// Trigonometric functions
@@ -1674,7 +1674,7 @@ double Item_func_acos::val_real()
volatile double value= args[0]->val_real();
if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
return 0.0;
- return fix_result(acos(value));
+ return acos(value);
}
double Item_func_asin::val_real()
@@ -1684,7 +1684,7 @@ double Item_func_asin::val_real()
volatile double value= args[0]->val_real();
if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
return 0.0;
- return fix_result(asin(value));
+ return asin(value);
}
double Item_func_atan::val_real()
@@ -1700,7 +1700,7 @@ double Item_func_atan::val_real()
return 0.0;
return fix_result(atan2(value,val2));
}
- return fix_result(atan(value));
+ return atan(value);
}
double Item_func_cos::val_real()
@@ -1709,7 +1709,7 @@ double Item_func_cos::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0;
- return fix_result(cos(value));
+ return cos(value);
}
double Item_func_sin::val_real()
@@ -1718,7 +1718,7 @@ double Item_func_sin::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0;
- return fix_result(sin(value));
+ return sin(value);
}
double Item_func_tan::val_real()
@@ -2537,16 +2537,16 @@ longlong Item_func_locate::val_int()
}
-void Item_func_locate::print(String *str)
+void Item_func_locate::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("locate("));
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(',');
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (arg_count == 3)
{
str->append(',');
- args[2]->print(str);
+ args[2]->print(str, query_type);
}
str->append(')');
}
@@ -3094,7 +3094,7 @@ void Item_udf_func::cleanup()
}
-void Item_udf_func::print(String *str)
+void Item_udf_func::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -3102,7 +3102,7 @@ void Item_udf_func::print(String *str)
{
if (i != 0)
str->append(',');
- args[i]->print_item_w_name(str);
+ args[i]->print_item_w_name(str, query_type);
}
str->append(')');
}
@@ -3645,18 +3645,28 @@ longlong Item_func_benchmark::val_int()
String tmp(buff,sizeof(buff), &my_charset_bin);
my_decimal tmp_decimal;
THD *thd=current_thd;
- ulong loop_count;
+ ulonglong loop_count;
- loop_count= (ulong) args[0]->val_int();
+ loop_count= (ulonglong) args[0]->val_int();
- if (args[0]->null_value)
+ if (args[0]->null_value ||
+ (!args[0]->unsigned_flag && (((longlong) loop_count) < 0)))
{
+ if (!args[0]->null_value)
+ {
+ char buff[22];
+ llstr(((longlong) loop_count), buff);
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
+ "count", buff, "benchmark");
+ }
+
null_value= 1;
return 0;
}
null_value=0;
- for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++)
+ for (ulonglong loop=0 ; loop < loop_count && !thd->killed; loop++)
{
switch (args[1]->result_type()) {
case REAL_RESULT:
@@ -3682,12 +3692,12 @@ longlong Item_func_benchmark::val_int()
}
-void Item_func_benchmark::print(String *str)
+void Item_func_benchmark::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("benchmark("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(',');
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(')');
}
@@ -3704,11 +3714,24 @@ longlong Item_func_sleep::val_int()
DBUG_ASSERT(fixed == 1);
double time= args[0]->val_real();
+ /*
+ On 64-bit OSX pthread_cond_timedwait() waits forever
+ if passed abstime time has already been exceeded by
+ the system time.
+ When given a very short timeout (< 10 mcs) just return
+ immediately.
+ We assume that the lines between this test and the call
+ to pthread_cond_timedwait() will be executed in less than 0.00001 sec.
+ */
+ if (time < 0.00001)
+ return 0;
+
set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000)));
pthread_cond_init(&cond, NULL);
pthread_mutex_lock(&LOCK_user_locks);
+ thd_proc_info(thd, "User sleep");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &cond;
@@ -3720,6 +3743,7 @@ longlong Item_func_sleep::val_int()
break;
error= 0;
}
+ thd_proc_info(thd, 0);
pthread_mutex_unlock(&LOCK_user_locks);
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
@@ -3981,7 +4005,7 @@ double user_var_entry::val_real(my_bool *null_value)
/** Get the value of a variable as an integer. */
-longlong user_var_entry::val_int(my_bool *null_value)
+longlong user_var_entry::val_int(my_bool *null_value) const
{
if ((*null_value= (value == 0)))
return LL(0);
@@ -4268,22 +4292,23 @@ my_decimal *Item_func_set_user_var::val_decimal_result(my_decimal *val)
}
-void Item_func_set_user_var::print(String *str)
+void Item_func_set_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(@"));
str->append(name.str, name.length);
str->append(STRING_WITH_LEN(":="));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
-void Item_func_set_user_var::print_as_stmt(String *str)
+void Item_func_set_user_var::print_as_stmt(String *str,
+ enum_query_type query_type)
{
str->append(STRING_WITH_LEN("set @"));
str->append(name.str, name.length);
str->append(STRING_WITH_LEN(":="));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
@@ -4656,7 +4681,7 @@ enum Item_result Item_func_get_user_var::result_type() const
}
-void Item_func_get_user_var::print(String *str)
+void Item_func_get_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(@"));
str->append(name.str,name.length);
@@ -4754,7 +4779,7 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
}
-void Item_user_var_as_out_param::print(String *str)
+void Item_user_var_as_out_param::print(String *str, enum_query_type query_type)
{
str->append('@');
str->append(name.str,name.length);
@@ -4792,6 +4817,12 @@ Item_func_get_system_var::fix_fields(THD *thd, Item **ref)
}
+bool Item_func_get_system_var::is_written_to_binlog()
+{
+ return var->is_written_to_binlog(var_type);
+}
+
+
longlong Item_func_inet_aton::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -5093,12 +5124,12 @@ double Item_func_match::val_real()
table->record[0], 0));
}
-void Item_func_match::print(String *str)
+void Item_func_match::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(match "));
- print_args(str, 1);
+ print_args(str, 1, query_type);
str->append(STRING_WITH_LEN(" against ("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (flags & FT_BOOL)
str->append(STRING_WITH_LEN(" in boolean mode"));
else if (flags & FT_EXPAND)
@@ -5493,6 +5524,8 @@ Item_func_sp::make_field(Send_field *tmp_field)
DBUG_ENTER("Item_func_sp::make_field");
DBUG_ASSERT(sp_result_field);
sp_result_field->make_field(tmp_field);
+ if (name)
+ tmp_field->col_name= name;
DBUG_VOID_RETURN;
}
@@ -5509,7 +5542,7 @@ Item_result
Item_func_sp::result_type() const
{
DBUG_ENTER("Item_func_sp::result_type");
- DBUG_PRINT("info", ("m_sp = %p", m_sp));
+ DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp));
DBUG_ASSERT(sp_result_field);
DBUG_RETURN(sp_result_field->result_type());
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 4981d1694e0..80d4f58ad8a 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -54,7 +54,8 @@ public:
NOT_FUNC, NOT_ALL_FUNC,
NOW_FUNC, TRIG_COND_FUNC,
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
- EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC };
+ EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
+ NEG_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL };
enum Type type() const { return FUNC_ITEM; }
@@ -140,9 +141,9 @@ public:
inline uint argument_count() const { return arg_count; }
inline void remove_arguments() { arg_count=0; }
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
- void print(String *str);
- void print_op(String *str);
- void print_args(String *str, uint from);
+ virtual void print(String *str, enum_query_type query_type);
+ void print_op(String *str, enum_query_type query_type);
+ void print_args(String *str, uint from, enum_query_type query_type);
virtual void fix_num_length_and_dec();
void count_only_length();
void count_real_length();
@@ -192,6 +193,13 @@ public:
void * arg, traverse_order order);
bool is_expensive_processor(uchar *arg);
virtual bool is_expensive() { return 0; }
+ inline double fix_result(double value)
+ {
+ if (isfinite(value))
+ return value;
+ null_value=1;
+ return 0.0;
+ }
};
@@ -293,10 +301,14 @@ class Item_num_op :public Item_func_numhybrid
public:
Item_num_op(Item *a,Item *b) :Item_func_numhybrid(a, b) {}
virtual void result_precision()= 0;
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
+
void find_num_type();
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -339,7 +351,7 @@ public:
longlong val_int_from_str(int *error);
void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=0; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
};
@@ -352,7 +364,7 @@ public:
void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=1; }
longlong val_int();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -373,7 +385,7 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
void fix_length_and_dec() {};
const char *func_name() const { return "decimal_typecast"; }
- void print(String *);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -382,6 +394,7 @@ class Item_func_additive_op :public Item_num_op
public:
Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {}
void result_precision();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -416,6 +429,7 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
void result_precision();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -441,7 +455,12 @@ public:
longlong val_int();
const char *func_name() const { return "DIV"; }
void fix_length_and_dec();
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
+
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -456,6 +475,7 @@ public:
const char *func_name() const { return "%"; }
void result_precision();
void fix_length_and_dec();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -467,7 +487,7 @@ public:
longlong int_op();
my_decimal *decimal_op(my_decimal *);
const char *func_name() const { return "-"; }
- virtual bool basic_const_item() const { return args[0]->basic_const_item(); }
+ enum Functype functype() const { return NEG_FUNC; }
void fix_length_and_dec();
void fix_num_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
@@ -499,18 +519,6 @@ class Item_dec_func :public Item_real_func
decimals=NOT_FIXED_DEC; max_length=float_length(decimals);
maybe_null=1;
}
- inline double fix_result(double value)
- {
-#ifndef HAVE_FINITE
- return value;
-#else
- /* The following should be safe, even if we compare doubles */
- if (finite(value) && value != POSTFIX_ERROR)
- return value;
- null_value=1;
- return 0.0;
-#endif
- }
};
class Item_func_exp :public Item_dec_func
@@ -843,7 +851,7 @@ public:
const char *func_name() const { return "locate"; }
longlong val_int();
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -900,7 +908,11 @@ public:
Item_func_bit(Item *a, Item *b) :Item_int_func(a, b) {}
Item_func_bit(Item *a) :Item_int_func(a) {}
void fix_length_and_dec() { unsigned_flag= 1; }
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
};
class Item_func_bit_or :public Item_func_bit
@@ -950,7 +962,11 @@ public:
Item_func_bit_neg(Item *a) :Item_func_bit(a) {}
longlong val_int();
const char *func_name() const { return "~"; }
- void print(String *str) { Item_func::print(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
};
@@ -979,7 +995,7 @@ public:
longlong val_int();
const char *func_name() const { return "benchmark"; }
void fix_length_and_dec() { max_length=1; maybe_null=0; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1076,7 +1092,7 @@ public:
Item_result result_type () const { return udf.result_type(); }
table_map not_null_tables() const { return 0; }
bool is_expensive() { return 1; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1313,8 +1329,8 @@ public:
enum Item_result result_type () const { return cached_result_type; }
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
- void print(String *str);
- void print_as_stmt(String *str);
+ virtual void print(String *str, enum_query_type query_type);
+ void print_as_stmt(String *str, enum_query_type query_type);
const char *func_name() const { return "set_user_var"; }
int save_in_field(Field *field, bool no_conversions,
bool can_use_result_field);
@@ -1344,7 +1360,7 @@ public:
my_decimal *val_decimal(my_decimal*);
String *val_str(String* str);
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
enum Item_result result_type() const;
/*
We must always return variables as strings to guard against selects of type
@@ -1389,7 +1405,7 @@ public:
my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs);
};
@@ -1417,6 +1433,15 @@ public:
void fix_length_and_dec() { DBUG_ASSERT(0); }
/* TODO: fix to support views */
const char *func_name() const { return "get_system_var"; }
+ /**
+ Indicates whether this system variable is written to the binlog or not.
+
+ Variables are written to the binlog as part of "status_vars" in
+ Query_log_event, as an Intvar_log_event, or a Rand_log_event.
+
+ @return true if the variable is written to the binlog, false otherwise.
+ */
+ bool is_written_to_binlog();
};
@@ -1466,7 +1491,7 @@ public:
/* The following should be safe, even if we compare doubles */
longlong val_int() { DBUG_ASSERT(fixed == 1); return val_real() != 0.0; }
double val_real();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool fix_index();
void init_search(bool no_order);
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 62be78eee9e..edbe104e307 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -224,8 +224,13 @@ public:
DBUG_ASSERT(0); // Should never happened
return "sp_unknown";
}
- }
- void print(String *str) { Item_func::print(str); }
+ }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
+
void fix_length_and_dec() { maybe_null= 1; }
bool is_null() { (void) val_int(); return null_value; }
};
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 369aa04930e..28de03bf049 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -134,14 +134,14 @@ bool Item_row::check_cols(uint c)
return 0;
}
-void Item_row::print(String *str)
+void Item_row::print(String *str, enum_query_type query_type)
{
str->append('(');
for (uint i= 0; i < arg_count; i++)
{
if (i)
str->append(',');
- items[i]->print(str);
+ items[i]->print(str, query_type);
}
str->append(')');
}
diff --git a/sql/item_row.h b/sql/item_row.h
index dd7436888f0..67441f49603 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -65,7 +65,7 @@ public:
bool const_item() const { return const_item_cache; };
enum Item_result result_type() const { return ROW_RESULT; }
void update_used_tables();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
Item *transform(Item_transformer transformer, uchar *arg);
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 9e16f388be0..925d6addb47 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1594,20 +1594,20 @@ void Item_func_trim::fix_length_and_dec()
}
}
-void Item_func_trim::print(String *str)
+void Item_func_trim::print(String *str, enum_query_type query_type)
{
if (arg_count == 1)
{
- Item_func::print(str);
+ Item_func::print(str, query_type);
return;
}
str->append(Item_func_trim::func_name());
str->append('(');
str->append(mode_name());
str->append(' ');
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(STRING_WITH_LEN(" from "));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
@@ -2116,12 +2116,12 @@ String *Item_func_format::val_str(String *str)
}
-void Item_func_format::print(String *str)
+void Item_func_format::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("format("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(',');
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(')');
}
@@ -2292,14 +2292,14 @@ Item *Item_func_make_set::transform(Item_transformer transformer, uchar *arg)
}
-void Item_func_make_set::print(String *str)
+void Item_func_make_set::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("make_set("));
- item->print(str);
+ item->print(str, query_type);
if (arg_count)
{
str->append(',');
- print_args(str, 0);
+ print_args(str, 0, query_type);
}
str->append(')');
}
@@ -2710,10 +2710,10 @@ void Item_func_conv_charset::fix_length_and_dec()
max_length = args[0]->max_length*conv_charset->mbmaxlen;
}
-void Item_func_conv_charset::print(String *str)
+void Item_func_conv_charset::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("convert("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" using "));
str->append(conv_charset->csname);
str->append(')');
@@ -2781,10 +2781,10 @@ bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const
}
-void Item_func_set_collation::print(String *str)
+void Item_func_set_collation::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" collate "));
DBUG_ASSERT(args[1]->basic_const_item() &&
args[1]->type() == Item::STRING_ITEM);
@@ -2903,10 +2903,10 @@ String *Item_func_unhex::val_str(String *str)
}
-void Item_func_binary::print(String *str)
+void Item_func_binary::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as binary)"));
}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index b1c519be1ad..daf384236a5 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -225,7 +225,7 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "trim"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
virtual const char *mode_name() const { return "both"; }
};
@@ -482,7 +482,7 @@ public:
Item_str_func::walk(processor, walk_subquery, arg);
}
Item *transform(Item_transformer transformer, uchar *arg);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -494,7 +494,7 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "format"; }
- void print(String *);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -617,7 +617,7 @@ public:
collation.set(&my_charset_bin);
max_length=args[0]->max_length;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
const char *func_name() const { return "cast_as_binary"; }
};
@@ -719,7 +719,7 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "convert"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
class Item_func_set_collation :public Item_str_func
@@ -731,7 +731,7 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
const char *func_name() const { return "collate"; }
enum Functype functype() const { return COLLATE_FUNC; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_field *filed_for_view_update()
{
/* this function is transparent for view updating */
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 9dd41ff3770..d126e18d444 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -306,10 +306,10 @@ void Item_subselect::update_used_tables()
}
-void Item_subselect::print(String *str)
+void Item_subselect::print(String *str, enum_query_type query_type)
{
str->append('(');
- engine->print(str);
+ engine->print(str, query_type);
str->append(')');
}
@@ -391,10 +391,10 @@ void Item_maxmin_subselect::cleanup()
}
-void Item_maxmin_subselect::print(String *str)
+void Item_maxmin_subselect::print(String *str, enum_query_type query_type)
{
str->append(max?"<max>":"<min>", 5);
- Item_singlerow_subselect::print(str);
+ Item_singlerow_subselect::print(str, query_type);
}
@@ -630,10 +630,10 @@ Item_exists_subselect::Item_exists_subselect(st_select_lex *select_lex):
}
-void Item_exists_subselect::print(String *str)
+void Item_exists_subselect::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("exists"));
- Item_subselect::print(str);
+ Item_subselect::print(str, query_type);
}
@@ -1288,7 +1288,11 @@ Item_in_subselect::row_value_transformer(JOIN *join)
Item *item_having_part2= 0;
for (uint i= 0; i < cols_num; i++)
{
- DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed);
+ DBUG_ASSERT(left_expr->fixed &&
+ select_lex->ref_pointer_array[i]->fixed ||
+ (select_lex->ref_pointer_array[i]->type() == REF_ITEM &&
+ ((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() ==
+ Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(RES_ERROR);
@@ -1362,7 +1366,11 @@ Item_in_subselect::row_value_transformer(JOIN *join)
for (uint i= 0; i < cols_num; i++)
{
Item *item, *item_isnull;
- DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed);
+ DBUG_ASSERT(left_expr->fixed &&
+ select_lex->ref_pointer_array[i]->fixed ||
+ (select_lex->ref_pointer_array[i]->type() == REF_ITEM &&
+ ((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() ==
+ Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(RES_ERROR);
@@ -1490,6 +1498,19 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func)
DBUG_ENTER("Item_in_subselect::select_in_like_transformer");
+ {
+ /*
+ IN/SOME/ALL/ANY subqueries aren't support LIMIT clause. Without it
+ ORDER BY clause becomes meaningless thus we drop it here.
+ */
+ SELECT_LEX *sl= current->master_unit()->first_select();
+ for (; sl; sl= sl->next_select())
+ {
+ if (sl->join)
+ sl->join->order= 0;
+ }
+ }
+
if (changed)
{
DBUG_RETURN(RES_OK);
@@ -1524,6 +1545,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func)
transformed= 1;
arena= thd->activate_stmt_arena_if_needed(&backup);
+
/*
Both transformers call fix_fields() only for Items created inside them,
and all that items do not make permanent changes in current item arena
@@ -1553,16 +1575,16 @@ err:
}
-void Item_in_subselect::print(String *str)
+void Item_in_subselect::print(String *str, enum_query_type query_type)
{
if (transformed)
str->append(STRING_WITH_LEN("<exists>"));
else
{
- left_expr->print(str);
+ left_expr->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
}
- Item_subselect::print(str);
+ Item_subselect::print(str, query_type);
}
@@ -1587,18 +1609,18 @@ Item_allany_subselect::select_transformer(JOIN *join)
}
-void Item_allany_subselect::print(String *str)
+void Item_allany_subselect::print(String *str, enum_query_type query_type)
{
if (transformed)
str->append(STRING_WITH_LEN("<exists>"));
else
{
- left_expr->print(str);
+ left_expr->print(str, query_type);
str->append(' ');
str->append(func->symbol(all));
str->append(all ? " all " : " any ", 5);
}
- Item_subselect::print(str);
+ Item_subselect::print(str, query_type);
}
@@ -2384,22 +2406,24 @@ table_map subselect_union_engine::upper_select_const_tables()
}
-void subselect_single_select_engine::print(String *str)
+void subselect_single_select_engine::print(String *str,
+ enum_query_type query_type)
{
- select_lex->print(thd, str);
+ select_lex->print(thd, str, query_type);
}
-void subselect_union_engine::print(String *str)
+void subselect_union_engine::print(String *str, enum_query_type query_type)
{
- unit->print(str);
+ unit->print(str, query_type);
}
-void subselect_uniquesubquery_engine::print(String *str)
+void subselect_uniquesubquery_engine::print(String *str,
+ enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<primary_index_lookup>("));
- tab->ref.items[0]->print(str);
+ tab->ref.items[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
KEY *key_info= tab->table->key_info+ tab->ref.key;
@@ -2408,16 +2432,17 @@ void subselect_uniquesubquery_engine::print(String *str)
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
- cond->print(str);
+ cond->print(str, query_type);
}
str->append(')');
}
-void subselect_indexsubquery_engine::print(String *str)
+void subselect_indexsubquery_engine::print(String *str,
+ enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<index_lookup>("));
- tab->ref.items[0]->print(str);
+ tab->ref.items[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
KEY *key_info= tab->table->key_info+ tab->ref.key;
@@ -2428,12 +2453,12 @@ void subselect_indexsubquery_engine::print(String *str)
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
- cond->print(str);
+ cond->print(str, query_type);
}
if (having)
{
str->append(STRING_WITH_LEN(" having "));
- having->print(str);
+ having->print(str, query_type);
}
str->append(')');
}
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index de4b1cbdc06..d4aa621c083 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -103,7 +103,7 @@ public:
inline bool get_const_item_cache() { return const_item_cache; }
Item *get_tmp_table_item(THD *thd);
void update_used_tables();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
virtual bool have_guarded_conds() { return FALSE; }
bool change_engine(subselect_engine *eng)
{
@@ -203,7 +203,7 @@ protected:
public:
Item_maxmin_subselect(THD *thd, Item_subselect *parent,
st_select_lex *select_lex, bool max);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void cleanup();
bool any_value() { return was_values; }
void register_value() { was_values= TRUE; }
@@ -234,7 +234,7 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
friend class select_exists_subselect;
friend class subselect_uniquesubquery_engine;
@@ -312,7 +312,7 @@ public:
void top_level_item() { abort_on_null=1; }
inline bool is_top_level_item() { return abort_on_null; }
bool test_limit(st_select_lex_unit *unit);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool fix_fields(THD *thd, Item **ref);
friend class Item_ref_null_helper;
@@ -335,7 +335,7 @@ public:
// only ALL subquery has upper not
subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
trans_res select_transformer(JOIN *join);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -399,7 +399,7 @@ public:
virtual bool may_be_null() { return maybe_null; };
virtual table_map upper_select_const_tables()= 0;
static table_map calc_const_tables(TABLE_LIST *);
- virtual void print(String *str)= 0;
+ virtual void print(String *str, enum_query_type query_type)= 0;
virtual bool change_result(Item_subselect *si, select_subselect *result)= 0;
virtual bool no_tables()= 0;
virtual bool is_executed() const { return FALSE; }
@@ -430,7 +430,7 @@ public:
uint8 uncacheable();
void exclude();
table_map upper_select_const_tables();
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
bool may_be_null();
@@ -454,7 +454,7 @@ public:
uint8 uncacheable();
void exclude();
table_map upper_select_const_tables();
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
bool is_executed() const;
@@ -511,7 +511,7 @@ public:
uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
void exclude();
table_map upper_select_const_tables() { return 0; }
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
int scan_table();
@@ -565,7 +565,7 @@ public:
having(having_arg)
{}
int exec();
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 27b964a9e15..264b53c780a 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -68,6 +68,7 @@ bool Item_sum::init_sum_func_check(THD *thd)
aggr_sel= NULL;
max_arg_level= -1;
max_sum_func_level= -1;
+ outer_fields.empty();
return FALSE;
}
@@ -176,6 +177,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
MYF(0));
return TRUE;
}
+
if (in_sum_func)
{
/*
@@ -196,6 +198,68 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
set_if_bigger(in_sum_func->max_sum_func_level, aggr_level);
set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level);
}
+
+ /*
+ Check that non-aggregated fields and sum functions aren't mixed in the
+ same select in the ONLY_FULL_GROUP_BY mode.
+ */
+ if (outer_fields.elements)
+ {
+ Item_field *field;
+ /*
+ Here we compare the nesting level of the select to which an outer field
+ belongs to with the aggregation level of the sum function. All fields in
+ the outer_fields list are checked.
+
+ If the nesting level is equal to the aggregation level then the field is
+ aggregated by this sum function.
+ If the nesting level is less than the aggregation level then the field
+ belongs to an outer select. In this case if there is an embedding sum
+ function add current field to functions outer_fields list. If there is
+ no embedding function then the current field treated as non aggregated
+ and the select it belongs to is marked accordingly.
+ If the nesting level is greater than the aggregation level then it means
+ that this field was added by an inner sum function.
+ Consider an example:
+
+ select avg ( <-- we are here, checking outer.f1
+ select (
+ select sum(outer.f1 + inner.f1) from inner
+ ) from outer)
+ from most_outer;
+
+ In this case we check that no aggregate functions are used in the
+ select the field belongs to. If there are some then an error is
+ raised.
+ */
+ List_iterator<Item_field> of(outer_fields);
+ while ((field= of++))
+ {
+ SELECT_LEX *sel= field->cached_table->select_lex;
+ if (sel->nest_level < aggr_level)
+ {
+ if (in_sum_func)
+ {
+ /*
+ Let upper function decide whether this field is a non
+ aggregated one.
+ */
+ in_sum_func->outer_fields.push_back(field);
+ }
+ else
+ sel->full_group_by_flag|= NON_AGG_FIELD_USED;
+ }
+ if (sel->nest_level > aggr_level &&
+ (sel->full_group_by_flag & SUM_FUNC_USED) &&
+ !sel->group_list.elements)
+ {
+ my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,
+ ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0));
+ return TRUE;
+ }
+ }
+ }
+ aggr_sel->full_group_by_flag|= SUM_FUNC_USED;
update_used_tables();
thd->lex->in_sum_func= in_sum_func;
return FALSE;
@@ -359,14 +423,14 @@ void Item_sum::make_field(Send_field *tmp_field)
}
-void Item_sum::print(String *str)
+void Item_sum::print(String *str, enum_query_type query_type)
{
str->append(func_name());
for (uint i=0 ; i < arg_count ; i++)
{
if (i)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
str->append(')');
}
@@ -600,6 +664,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
result_field=0;
null_value=1;
fix_length_and_dec();
+ item= item->real_item();
if (item->type() == Item::FIELD_ITEM)
hybrid_field_type= ((Item_field*) item)->field->type();
else
@@ -1227,7 +1292,15 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
null_value=1;
return NULL;
}
- sum_dec= Item_sum_sum::val_decimal(&sum_buff);
+
+ /*
+ For non-DECIMAL hybrid_type the division will be done in
+ Item_sum_avg::val_real().
+ */
+ if (hybrid_type != DECIMAL_RESULT)
+ return val_decimal_from_real(val);
+
+ sum_dec= dec_buffs + curr_dec_buff;
int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt);
my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, prec_increment);
return val;
@@ -2716,7 +2789,7 @@ void Item_udf_sum::cleanup()
}
-void Item_udf_sum::print(String *str)
+void Item_udf_sum::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -2724,7 +2797,7 @@ void Item_udf_sum::print(String *str)
{
if (i)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
str->append(')');
}
@@ -3460,7 +3533,7 @@ String* Item_func_group_concat::val_str(String* str)
}
-void Item_func_group_concat::print(String *str)
+void Item_func_group_concat::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("group_concat("));
if (distinct)
@@ -3469,7 +3542,7 @@ void Item_func_group_concat::print(String *str)
{
if (i)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
if (arg_count_order)
{
@@ -3478,7 +3551,7 @@ void Item_func_group_concat::print(String *str)
{
if (i)
str->append(',');
- (*order[i]->item)->print(str);
+ (*order[i]->item)->print(str, query_type);
if (order[i]->asc)
str->append(STRING_WITH_LEN(" ASC"));
else
@@ -3493,6 +3566,6 @@ void Item_func_group_concat::print(String *str)
Item_func_group_concat::~Item_func_group_concat()
{
- if (unique_filter)
+ if (!original && unique_filter)
delete unique_filter;
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index a3582967736..bee8792fbfa 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -239,6 +239,13 @@ public:
int8 max_arg_level; /* max level of unbound column references */
int8 max_sum_func_level;/* max level of aggregation for embedded functions */
bool quick_group; /* If incremental update of fields */
+ /*
+ This list is used by the check for mixing non aggregated fields and
+ sum functions in the ONLY_FULL_GROUP_BY_MODE. We save all outer fields
+ directly or indirectly used under this function it as it's unclear
+ at the moment of fixing outer field whether it's aggregated or not.
+ */
+ List<Item_field> outer_fields;
protected:
table_map used_tables_cache;
@@ -343,7 +350,7 @@ public:
}
virtual bool const_item() const { return forced_const; }
void make_field(Send_field *field);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void fix_num_length_and_dec();
/*
@@ -984,7 +991,7 @@ public:
void reset_field() {};
void update_field() {};
void cleanup();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1257,7 +1264,7 @@ public:
String* val_str(String* str);
Item *copy_or_same(THD* thd);
void no_rows_in_result() {}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
};
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index a0beadcd481..390f94945aa 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -2217,23 +2217,23 @@ static const char *interval_names[]=
"second_microsecond"
};
-void Item_date_add_interval::print(String *str)
+void Item_date_add_interval::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(date_sub_interval?" - interval ":" + interval ");
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(' ');
str->append(interval_names[int_type]);
str->append(')');
}
-void Item_extract::print(String *str)
+void Item_extract::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("extract("));
str->append(interval_names[int_type]);
str->append(STRING_WITH_LEN(" from "));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
@@ -2374,20 +2374,20 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const
return 1;
}
-void Item_typecast::print(String *str)
+void Item_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
str->append(cast_type());
str->append(')');
}
-void Item_char_typecast::print(String *str)
+void Item_char_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as char"));
if (cast_length >= 0)
{
@@ -2822,7 +2822,7 @@ null_date:
}
-void Item_func_add_time::print(String *str)
+void Item_func_add_time::print(String *str, enum_query_type query_type)
{
if (is_date)
{
@@ -2836,9 +2836,9 @@ void Item_func_add_time::print(String *str)
else
str->append(STRING_WITH_LEN("subtime("));
}
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(',');
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(')');
}
@@ -3083,7 +3083,7 @@ null_date:
}
-void Item_func_timestamp_diff::print(String *str)
+void Item_func_timestamp_diff::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -3123,7 +3123,7 @@ void Item_func_timestamp_diff::print(String *str)
for (uint i=0 ; i < 2 ; i++)
{
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
str->append(')');
}
@@ -3163,7 +3163,7 @@ String *Item_func_get_format::val_str(String *str)
}
-void Item_func_get_format::print(String *str)
+void Item_func_get_format::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -3181,7 +3181,7 @@ void Item_func_get_format::print(String *str)
default:
DBUG_ASSERT(0);
}
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 9be7e97db49..cfcf5fab080 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -694,7 +694,7 @@ public:
longlong val_int();
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
bool eq(const Item *item, bool binary_cmp) const;
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -711,7 +711,7 @@ class Item_extract :public Item_int_func
const char *func_name() const { return "extract"; }
void fix_length_and_dec();
bool eq(const Item *item, bool binary_cmp) const;
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -735,7 +735,7 @@ public:
max_length=args[0]->max_length;
}
virtual const char* cast_type() const= 0;
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -767,7 +767,7 @@ public:
const char* cast_type() const { return "char"; };
String *val_str(String *a);
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -901,7 +901,7 @@ public:
{
return tmp_table_field_from_field_type(table, 0);
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
const char *func_name() const { return "add_time"; }
double val_real() { return val_real_from_decimal(); }
my_decimal *val_decimal(my_decimal *decimal_value)
@@ -977,7 +977,7 @@ public:
decimals=0;
maybe_null=1;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1001,7 +1001,7 @@ public:
decimals=0;
max_length=17*MY_CHARSET_BIN_MB_MAXLEN;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
diff --git a/sql/key.cc b/sql/key.cc
index 7f075674ab6..47e5c5ebdd7 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -168,6 +168,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
}
for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
{
+ uchar used_uneven_bits= 0;
if (key_part->null_bit)
{
if (*from_key++)
@@ -186,6 +187,8 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
set_rec_bits(bits, to_record + key_part->null_offset +
(key_part->null_bit == 128),
field->bit_ofs, field->bit_len);
+ /* we have now used the byte with 'uneven' bits */
+ used_uneven_bits= 1;
}
}
if (key_part->key_part_flag & HA_BLOB_PART)
@@ -222,7 +225,9 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
else
{
length= min(key_length, key_part->length);
- memcpy(to_record + key_part->offset, from_key, (size_t) length);
+ /* skip the byte with 'uneven' bits, if used */
+ memcpy(to_record + key_part->offset, from_key + used_uneven_bits
+ , (size_t) length - used_uneven_bits);
}
from_key+= length;
key_length-= length;
diff --git a/sql/log.cc b/sql/log.cc
index 550285026b2..bb18d08f4db 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -52,8 +52,6 @@ LOGGER logger;
MYSQL_BIN_LOG mysql_bin_log;
ulong sync_binlog_counter= 0;
-static Muted_query_log_event invisible_commit;
-
static bool test_if_number(const char *str,
long *res, bool allow_wildcards);
static int binlog_init(void *p);
@@ -73,23 +71,28 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all);
*/
class Silence_log_table_errors : public Internal_error_handler
{
+ char m_message[MYSQL_ERRMSG_SIZE];
public:
Silence_log_table_errors()
- {}
+ {
+ m_message[0]= '\0';
+ }
virtual ~Silence_log_table_errors() {}
virtual bool handle_error(uint sql_errno, const char *message,
MYSQL_ERROR::enum_warning_level level,
THD *thd);
+ const char *message() const { return m_message; }
};
bool
Silence_log_table_errors::handle_error(uint /* sql_errno */,
- const char * /* message */,
+ const char *message_arg,
MYSQL_ERROR::enum_warning_level /* level */,
THD * /* thd */)
{
+ strmake(m_message, message_arg, sizeof(m_message)-1);
return TRUE;
}
@@ -150,7 +153,7 @@ private:
class binlog_trx_data {
public:
binlog_trx_data()
- : m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF)
+ : at_least_one_stmt(0), m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF)
{
trans_log.end_of_file= max_binlog_cache_size;
}
@@ -183,6 +186,16 @@ public:
reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0);
if (pos < before_stmt_pos)
before_stmt_pos= MY_OFF_T_UNDEF;
+
+ /*
+ The only valid positions that can be truncated to are at the
+ beginning of a statement. We are relying on this fact to be able
+ to set the at_least_one_stmt flag correctly. In other word, if
+ we are truncating to the beginning of the transaction cache,
+ there will be no statements in the cache, otherwhise, we will
+ have at least one statement in the transaction cache.
+ */
+ at_least_one_stmt= (pos > 0);
}
/*
@@ -208,6 +221,12 @@ public:
IO_CACHE trans_log; // The transaction cache
+ /**
+ Boolean that is true if there is at least one statement in the
+ transaction cache.
+ */
+ bool at_least_one_stmt;
+
private:
/*
Pending binrows event. This event is the event where the rows are
@@ -413,6 +432,7 @@ bool Log_to_csv_event_handler::
A positive return value in store() means truncation.
Still logging a message in the log in this case.
*/
+ table->field[5]->flags|= FIELDFLAG_HEX_ESCAPE;
if (table->field[5]->store(sql_text, sql_text_len, client_cs) < 0)
goto err;
@@ -436,8 +456,9 @@ bool Log_to_csv_event_handler::
result= FALSE;
err:
- if (result)
- sql_print_error("Failed to write to mysql.general_log");
+ if (result && !thd->killed)
+ sql_print_error("Failed to write to mysql.general_log: %s",
+ error_handler.message());
if (need_rnd_end)
{
@@ -495,11 +516,13 @@ bool Log_to_csv_event_handler::
bool result= TRUE;
bool need_close= FALSE;
bool need_rnd_end= FALSE;
+ Silence_log_table_errors error_handler;
Open_tables_state open_tables_backup;
CHARSET_INFO *client_cs= thd->variables.character_set_client;
bool save_time_zone_used;
DBUG_ENTER("Log_to_csv_event_handler::log_slow");
+ thd->push_internal_handler(& error_handler);
/*
CSV uses TIME_to_timestamp() internally if table needs to be repaired
which will set thd->time_zone_used
@@ -629,8 +652,11 @@ bool Log_to_csv_event_handler::
result= FALSE;
err:
- if (result)
- sql_print_error("Failed to write to mysql.slow_log");
+ thd->pop_internal_handler();
+
+ if (result && !thd->killed)
+ sql_print_error("Failed to write to mysql.slow_log: %s",
+ error_handler.message());
if (need_rnd_end)
{
@@ -994,7 +1020,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
current_time= my_time(0);
while (*current_handler)
- error+= (*current_handler++)->
+ error|= (*current_handler++)->
log_general(thd, current_time, user_host_buff,
user_host_len, id,
command_name[(uint) command].str,
@@ -1362,26 +1388,20 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
inside a stored function.
*/
thd->binlog_flush_pending_rows_event(TRUE);
+
+ error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev);
+ trx_data->reset();
+
/*
- We write the transaction cache to the binary log if either we're
- committing the entire transaction, or if we are doing an
- autocommit outside a transaction.
- */
- if (all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT)))
+ We need to step the table map version after writing the
+ transaction cache to disk.
+ */
+ mysql_bin_log.update_table_map_version();
+ statistic_increment(binlog_cache_use, &LOCK_status);
+ if (trans_log->disk_writes != 0)
{
- error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev);
- trx_data->reset();
- /*
- We need to step the table map version after writing the
- transaction cache to disk.
- */
- mysql_bin_log.update_table_map_version();
- statistic_increment(binlog_cache_use, &LOCK_status);
- if (trans_log->disk_writes != 0)
- {
- statistic_increment(binlog_cache_disk_use, &LOCK_status);
- trans_log->disk_writes= 0;
- }
+ statistic_increment(binlog_cache_disk_use, &LOCK_status);
+ trans_log->disk_writes= 0;
}
}
else
@@ -1420,6 +1440,8 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
return 0;
}
+#define YESNO(X) ((X) ? "yes" : "no")
+
/**
This function is called once after each statement.
@@ -1428,10 +1450,8 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
@param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
- @param all true if this is the last statement before a COMMIT
- statement; false if either this is a statement in a
- transaction but not the last, or if this is a statement
- not inside a BEGIN block and autocommit is on.
+ @param all This is @c true if this is a real transaction commit, and
+ @false otherwise.
@see handlerton::commit
*/
@@ -1447,26 +1467,86 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
trx_data->reset();
DBUG_RETURN(0);
}
+
/*
- Write commit event if at least one of the following holds:
- - the user sends an explicit COMMIT; or
- - the autocommit flag is on, and we are not inside a BEGIN.
- However, if the user has not sent an explicit COMMIT, and we are
- either inside a BEGIN or run with autocommit off, then this is not
- the end of a transaction and we should not write a commit event.
+ Decision table for committing a transaction. The top part, the
+ *conditions* represent different cases that can occur, and hte
+ bottom part, the *actions*, represent what should be done in that
+ particular case.
+
+ Real transaction 'all' was true
+
+ Statement in cache There were at least one statement in the
+ transaction cache
+
+ In transaction We are inside a transaction
+
+ Stmt modified non-trans The statement being committed modified a
+ non-transactional table
+
+ All modified non-trans Some statement before this one in the
+ transaction modified a non-transactional
+ table
+
+
+ ============================= = = = = = = = = = = = = = = = =
+ Real transaction N N N N N N N N N N N N N N N N
+ Statement in cache N N N N N N N N Y Y Y Y Y Y Y Y
+ In transaction N N N N Y Y Y Y N N N N Y Y Y Y
+ Stmt modified non-trans N N Y Y N N Y Y N N Y Y N N Y Y
+ All modified non-trans N Y N Y N Y N Y N Y N Y N Y N Y
+
+ Action: (C)ommit/(A)ccumulate C C - C A C - C - - - - A A - A
+ ============================= = = = = = = = = = = = = = = = =
+
+
+ ============================= = = = = = = = = = = = = = = = =
+ Real transaction Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
+ Statement in cache N N N N N N N N Y Y Y Y Y Y Y Y
+ In transaction N N N N Y Y Y Y N N N N Y Y Y Y
+ Stmt modified non-trans N N Y Y N N Y Y N N Y Y N N Y Y
+ All modified non-trans N Y N Y N Y N Y N Y N Y N Y N Y
+
+ (C)ommit/(A)ccumulate/(-) - - - - C C - C - - - - C C - C
+ ============================= = = = = = = = = = = = = = = = =
+
+ In other words, we commit the transaction if and only if both of
+ the following are true:
+ - We are not in a transaction and committing a statement
+
+ - We are in a transaction and one (or more) of the following are
+ true:
+
+ - A full transaction is committed
+
+ OR
+
+ - A non-transactional statement is committed and there is
+ no statement cached
+
+ Otherwise, we accumulate the statement
*/
- if (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ ulonglong const in_transaction=
+ thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
+ DBUG_PRINT("debug",
+ ("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+ all,
+ YESNO(trx_data->empty()),
+ YESNO(in_transaction),
+ YESNO(thd->transaction.all.modified_non_trans_table),
+ YESNO(thd->transaction.stmt.modified_non_trans_table)));
+ if (in_transaction &&
+ (all ||
+ (!trx_data->at_least_one_stmt &&
+ thd->transaction.stmt.modified_non_trans_table)) ||
+ !in_transaction && !all)
{
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
int error= binlog_end_trans(thd, trx_data, &qev, all);
DBUG_RETURN(error);
}
- else
- {
- int error= binlog_end_trans(thd, trx_data, &invisible_commit, all);
- DBUG_RETURN(error);
- }
+ DBUG_RETURN(0);
}
/**
@@ -1479,10 +1559,8 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
@param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
- @param all true if this is the last statement before a COMMIT
- statement; false if either this is a statement in a
- transaction but not the last, or if this is a statement
- not inside a BEGIN block and autocommit is on.
+ @param all This is @c true if this is a real transaction rollback, and
+ @false otherwise.
@see handlerton::rollback
*/
@@ -1498,21 +1576,36 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
DBUG_RETURN(0);
}
- /*
- Update the binary log with a BEGIN/ROLLBACK block if we have
- cached some queries and we updated some non-transactional
- table. Such cases should be rare (updating a
- non-transactional table inside a transaction...)
- */
- if (unlikely(thd->transaction.all.modified_non_trans_table ||
- (thd->options & OPTION_KEEP_LOG)))
+ DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+ YESNO(all),
+ YESNO(thd->transaction.all.modified_non_trans_table),
+ YESNO(thd->transaction.stmt.modified_non_trans_table)));
+ if (all && thd->transaction.all.modified_non_trans_table ||
+ !all && thd->transaction.stmt.modified_non_trans_table ||
+ (thd->options & OPTION_KEEP_LOG))
{
+ /*
+ We write the transaction cache with a rollback last if we have
+ modified any non-transactional table. We do this even if we are
+ committing a single statement that has modified a
+ non-transactional table since it can have modified a
+ transactional table in that statement as well, which needs to be
+ rolled back on the slave.
+ */
Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE);
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
error= binlog_end_trans(thd, trx_data, &qev, all);
}
- else
+ else if (all && !thd->transaction.all.modified_non_trans_table ||
+ !all && !thd->transaction.stmt.modified_non_trans_table)
+ {
+ /*
+ If we have modified only transactional tables, we can truncate
+ the transaction cache without writing anything to the binary
+ log.
+ */
error= binlog_end_trans(thd, trx_data, 0, all);
+ }
DBUG_RETURN(error);
}
@@ -2739,14 +2832,62 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
for (;;)
{
- my_delete_allow_opened(linfo.log_file_name, MYF(MY_WME));
+ if ((error= my_delete_allow_opened(linfo.log_file_name, MYF(0))) != 0)
+ {
+ if (my_errno == ENOENT)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ linfo.log_file_name);
+ sql_print_information("Failed to delete file '%s'",
+ linfo.log_file_name);
+ my_errno= 0;
+ error= 0;
+ }
+ else
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with deleting %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ linfo.log_file_name);
+ error= 1;
+ goto err;
+ }
+ }
if (find_next_log(&linfo, 0))
break;
}
/* Start logging with a new file */
close(LOG_CLOSE_INDEX);
- my_delete_allow_opened(index_file_name, MYF(MY_WME)); // Reset (open will update)
+ if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update)
+ {
+ if (my_errno == ENOENT)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ index_file_name);
+ sql_print_information("Failed to delete file '%s'",
+ index_file_name);
+ my_errno= 0;
+ error= 0;
+ }
+ else
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with deleting %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ index_file_name);
+ error= 1;
+ goto err;
+ }
+ }
if (!thd->slave_thread)
need_start_event=1;
if (!open_index_file(index_file_name, 0))
@@ -2906,6 +3047,9 @@ int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads
0 ok
@retval
LOG_INFO_EOF to_log not found
+ LOG_INFO_EMFILE too many files opened
+ LOG_INFO_FATAL if any other than ENOENT error from
+ my_stat() or my_delete()
*/
int MYSQL_BIN_LOG::purge_logs(const char *to_log,
@@ -2935,44 +3079,84 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
!log_in_use(log_info.log_file_name))
{
- ulong file_size= 0;
- if (decrease_log_space) //stat the file we want to delete
+ MY_STAT s;
+ if (!my_stat(log_info.log_file_name, &s, MYF(0)))
{
- MY_STAT s;
-
- /*
- If we could not stat, we can't know the amount
- of space that deletion will free. In most cases,
- deletion won't work either, so it's not a problem.
- */
- if (my_stat(log_info.log_file_name,&s,MYF(0)))
- file_size= s.st_size;
- else
- sql_print_information("Failed to execute my_stat on file '%s'",
+ if (my_errno == ENOENT)
+ {
+ /*
+ It's not fatal if we can't stat a log file that does not exist;
+ If we could not stat, we won't delete.
+ */
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ log_info.log_file_name);
+ sql_print_information("Failed to execute my_stat on file '%s'",
log_info.log_file_name);
+ my_errno= 0;
+ }
+ else
+ {
+ /*
+ Other than ENOENT are fatal
+ */
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with getting info on being purged %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ log_info.log_file_name);
+ error= LOG_INFO_FATAL;
+ goto err;
+ }
}
- /*
- It's not fatal if we can't delete a log file ;
- if we could delete it, take its size into account
- */
- DBUG_PRINT("info",("purging %s",log_info.log_file_name));
- if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space)
- *decrease_log_space-= file_size;
-
- ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
- if (current_thd->is_slave_error) {
- DBUG_PRINT("info",("slave error: %d", current_thd->is_slave_error));
- if (my_errno == EMFILE) {
- DBUG_PRINT("info",("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
- ret = LOG_INFO_EMFILE;
- break;
+ else
+ {
+ DBUG_PRINT("info",("purging %s",log_info.log_file_name));
+ if (!my_delete(log_info.log_file_name, MYF(0)))
+ {
+ if (decrease_log_space)
+ *decrease_log_space-= s.st_size;
+ }
+ else
+ {
+ if (my_errno == ENOENT)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ log_info.log_file_name);
+ sql_print_information("Failed to delete file '%s'",
+ log_info.log_file_name);
+ my_errno= 0;
+ }
+ else
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with deleting %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ log_info.log_file_name);
+ if (my_errno == EMFILE)
+ {
+ DBUG_PRINT("info",
+ ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
+ error= LOG_INFO_EMFILE;
+ }
+ error= LOG_INFO_FATAL;
+ goto err;
+ }
}
}
+ ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
+
if (find_next_log(&log_info, 0) || exit_loop)
break;
}
-
+
/*
If we get killed -9 here, the sysadmin would have to edit
the log index file after restart - otherwise, this should be safe
@@ -3003,6 +3187,8 @@ err:
0 ok
@retval
LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated
+ LOG_INFO_FATAL if any other than ENOENT error from
+ my_stat() or my_delete()
*/
int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
@@ -3026,14 +3212,67 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
while (strcmp(log_file_name, log_info.log_file_name) &&
!log_in_use(log_info.log_file_name))
{
- /* It's not fatal even if we can't delete a log file */
- if (!my_stat(log_info.log_file_name, &stat_area, MYF(0)) ||
- stat_area.st_mtime >= purge_time)
- break;
- my_delete(log_info.log_file_name, MYF(0));
-
- ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
-
+ if (!my_stat(log_info.log_file_name, &stat_area, MYF(0)))
+ {
+ if (my_errno == ENOENT)
+ {
+ /*
+ It's not fatal if we can't stat a log file that does not exist.
+ */
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ log_info.log_file_name);
+ sql_print_information("Failed to execute my_stat on file '%s'",
+ log_info.log_file_name);
+ my_errno= 0;
+ }
+ else
+ {
+ /*
+ Other than ENOENT are fatal
+ */
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with getting info on being purged %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ log_info.log_file_name);
+ error= LOG_INFO_FATAL;
+ goto err;
+ }
+ }
+ else
+ {
+ if (stat_area.st_mtime >= purge_time)
+ break;
+ if (my_delete(log_info.log_file_name, MYF(0)))
+ {
+ if (my_errno == ENOENT)
+ {
+ /* It's not fatal even if we can't delete a log file */
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ log_info.log_file_name);
+ sql_print_information("Failed to delete file '%s'",
+ log_info.log_file_name);
+ my_errno= 0;
+ }
+ else
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with deleting %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ log_info.log_file_name);
+ error= LOG_INFO_FATAL;
+ goto err;
+ }
+ }
+ ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
+ }
if (find_next_log(&log_info, 0))
break;
}
@@ -3377,6 +3616,16 @@ THD::binlog_start_trans_and_stmt()
if (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
trans_register_ha(this, TRUE, binlog_hton);
trans_register_ha(this, FALSE, binlog_hton);
+ /*
+ Mark statement transaction as read/write. We never start
+ a binary log transaction and keep it read-only,
+ therefore it's best to mark the transaction read/write just
+ at the same time we start it.
+ Not necessary to mark the normal transaction read/write
+ since the statement-level flag will be propagated automatically
+ inside ha_commit_trans.
+ */
+ ha_data[binlog_hton->slot].ha_info[0].set_trx_read_write();
}
DBUG_VOID_RETURN;
}
@@ -3397,35 +3646,6 @@ void THD::binlog_set_stmt_begin() {
trx_data->before_stmt_pos= pos;
}
-int THD::binlog_flush_transaction_cache()
-{
- DBUG_ENTER("binlog_flush_transaction_cache");
- binlog_trx_data *trx_data= (binlog_trx_data*)
- thd_get_ha_data(this, binlog_hton);
- DBUG_PRINT("enter", ("trx_data=0x%lu", (ulong) trx_data));
- if (trx_data)
- DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%lu",
- (ulong) trx_data->before_stmt_pos));
-
- /*
- Write the transaction cache to the binary log. We don't flush and
- sync the log file since we don't know if more will be written to
- it. If the caller want the log file sync:ed, the caller has to do
- it.
-
- The transaction data is only reset upon a successful write of the
- cache to the binary log.
- */
-
- if (trx_data && likely(mysql_bin_log.is_open())) {
- if (int error= mysql_bin_log.write_cache(&trx_data->trans_log, true, true))
- DBUG_RETURN(error);
- trx_data->reset();
- }
-
- DBUG_RETURN(0);
-}
-
/*
Write a table map to the binary log.
@@ -4432,15 +4652,7 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
return an error (e.g. logging to the log tables)
*/
-#ifdef EMBEDDED_LIBRARY
-int vprint_msg_to_log(enum loglevel level __attribute__((unused)),
- const char *format __attribute__((unused)),
- va_list argsi __attribute__((unused)))
-{
- DBUG_ENTER("vprint_msg_to_log");
- DBUG_RETURN(0);
-}
-#else /*!EMBEDDED_LIBRARY*/
+#ifndef EMBEDDED_LIBRARY
static void print_buffer_to_file(enum loglevel level, const char *buffer)
{
time_t skr;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 33a74a3ce31..944d99d7a74 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -14,7 +14,11 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_CLIENT
+
+#include "mysql_priv.h"
+
+#else
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -28,7 +32,9 @@
#include "rpl_utility.h"
#include "rpl_record.h"
#include <my_dir.h>
+
#endif /* MYSQL_CLIENT */
+
#include <base64.h>
#include <my_bitmap.h>
@@ -137,7 +143,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
" %s, Error_code: %d;", err->msg, err->code);
}
- rli->report(level, thd->net.client_last_errno,
+ rli->report(level, thd->is_error()? thd->main_da.sql_errno() : 0,
"Could not execute %s event on table %s.%s;"
"%s handler error %s; "
"the event's master log %s, end_log_pos %lu",
@@ -603,6 +609,7 @@ Log_event::Log_event(const char* buf,
#endif
when = uint4korr(buf);
server_id = uint4korr(buf + SERVER_ID_OFFSET);
+ data_written= uint4korr(buf + EVENT_LEN_OFFSET);
if (description_event->binlog_version==1)
{
log_pos= 0;
@@ -635,7 +642,7 @@ Log_event::Log_event(const char* buf,
binlog, so which will cause problems if the user uses this value
in CHANGE MASTER).
*/
- log_pos+= uint4korr(buf + EVENT_LEN_OFFSET);
+ log_pos+= data_written; /* purecov: inspected */
}
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
@@ -1589,7 +1596,7 @@ bool Query_log_event::write(IO_CACHE* file)
recognize Q_CATALOG_CODE and have no problem.
*/
}
- if (auto_increment_increment != 1)
+ if (auto_increment_increment != 1 || auto_increment_offset != 1)
{
*start++= Q_AUTO_INCREMENT;
int2store(start, auto_increment_increment);
@@ -2102,9 +2109,17 @@ void Query_log_event::print_query_header(IO_CACHE* file,
end= strmov(end, print_event_info->delimiter);
*end++='\n';
my_b_write(file, (uchar*) buff, (uint) (end-buff));
- if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
+ if ((!print_event_info->thread_id_printed ||
+ ((flags & LOG_EVENT_THREAD_SPECIFIC_F) &&
+ thread_id != print_event_info->thread_id)))
+ {
+ // If --short-form, print deterministic value instead of pseudo_thread_id.
my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
- (ulong)thread_id, print_event_info->delimiter);
+ short_form ? 999999999 : (ulong)thread_id,
+ print_event_info->delimiter);
+ print_event_info->thread_id= thread_id;
+ print_event_info->thread_id_printed= 1;
+ }
/*
If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to
@@ -2151,20 +2166,14 @@ void Query_log_event::print_query_header(IO_CACHE* file,
gracefully). So this code should always be good.
*/
- if (likely(sql_mode_inited))
+ if (likely(sql_mode_inited) &&
+ (unlikely(print_event_info->sql_mode != sql_mode ||
+ !print_event_info->sql_mode_inited)))
{
- if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */
- {
- print_event_info->sql_mode_inited= 1;
- /* force a difference to force write */
- print_event_info->sql_mode= ~sql_mode;
- }
- if (unlikely(print_event_info->sql_mode != sql_mode))
- {
- my_b_printf(file,"SET @@session.sql_mode=%lu%s\n",
- (ulong)sql_mode, print_event_info->delimiter);
- print_event_info->sql_mode= sql_mode;
- }
+ my_b_printf(file,"SET @@session.sql_mode=%lu%s\n",
+ (ulong)sql_mode, print_event_info->delimiter);
+ print_event_info->sql_mode= sql_mode;
+ print_event_info->sql_mode_inited= 1;
}
if (print_event_info->auto_increment_increment != auto_increment_increment ||
print_event_info->auto_increment_offset != auto_increment_offset)
@@ -2178,33 +2187,28 @@ void Query_log_event::print_query_header(IO_CACHE* file,
/* TODO: print the catalog when we feature SET CATALOG */
- if (likely(charset_inited))
+ if (likely(charset_inited) &&
+ (unlikely(!print_event_info->charset_inited ||
+ bcmp((uchar*) print_event_info->charset, (uchar*) charset, 6))))
{
- if (unlikely(!print_event_info->charset_inited)) /* first Query event */
+ CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
+ if (cs_info)
{
- print_event_info->charset_inited= 1;
- print_event_info->charset[0]= ~charset[0]; // force a difference to force write
- }
- if (unlikely(bcmp((uchar*) print_event_info->charset, (uchar*) charset, 6)))
- {
- CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
- if (cs_info)
- {
- /* for mysql client */
- my_b_printf(file, "/*!\\C %s */%s\n",
- cs_info->csname, print_event_info->delimiter);
- }
- my_b_printf(file,"SET "
- "@@session.character_set_client=%d,"
- "@@session.collation_connection=%d,"
- "@@session.collation_server=%d"
- "%s\n",
- uint2korr(charset),
- uint2korr(charset+2),
- uint2korr(charset+4),
- print_event_info->delimiter);
- memcpy(print_event_info->charset, charset, 6);
+ /* for mysql client */
+ my_b_printf(file, "/*!\\C %s */%s\n",
+ cs_info->csname, print_event_info->delimiter);
}
+ my_b_printf(file,"SET "
+ "@@session.character_set_client=%d,"
+ "@@session.collation_connection=%d,"
+ "@@session.collation_server=%d"
+ "%s\n",
+ uint2korr(charset),
+ uint2korr(charset+2),
+ uint2korr(charset+4),
+ print_event_info->delimiter);
+ memcpy(print_event_info->charset, charset, 6);
+ print_event_info->charset_inited= 1;
}
if (time_zone_len)
{
@@ -2603,21 +2607,6 @@ Query_log_event::do_shall_skip(Relay_log_info *rli)
/**************************************************************************
- Muted_query_log_event methods
-**************************************************************************/
-
-#ifndef MYSQL_CLIENT
-/*
- Muted_query_log_event::Muted_query_log_event()
-*/
-Muted_query_log_event::Muted_query_log_event()
- :Query_log_event()
-{
-}
-#endif
-
-
-/**************************************************************************
Start_log_event_v3 methods
**************************************************************************/
@@ -3116,7 +3105,7 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
original place when it comes to us; we'll know this by checking
log_pos ("artificial" events have log_pos == 0).
*/
- if (!artificial_event && created && thd->transaction.all.nht)
+ if (!artificial_event && created && thd->transaction.all.ha_list)
{
/* This is not an error (XA is safe), just an information */
rli->report(INFORMATION_LEVEL, 0,
@@ -7057,7 +7046,7 @@ int Table_map_log_event::save_field_metadata()
#if !defined(MYSQL_CLIENT)
Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
bool is_transactional, uint16 flags)
- : Log_event(thd, 0, is_transactional),
+ : Log_event(thd, 0, true),
m_table(tbl),
m_dbnam(tbl->s->db.str),
m_dblen(m_dbnam ? tbl->s->db.length : 0),
@@ -8267,7 +8256,6 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == HA_ERR_RECORD_DELETED || error == 0);
goto err;
}
-
ok:
table->default_column_bitmaps();
DBUG_RETURN(0);
@@ -8634,3 +8622,34 @@ Incident_log_event::write_data_body(IO_CACHE *file)
DBUG_ENTER("Incident_log_event::write_data_body");
DBUG_RETURN(write_str(file, m_message.str, m_message.length));
}
+
+
+#ifdef MYSQL_CLIENT
+/**
+ The default values for these variables should be values that are
+ *incorrect*, i.e., values that cannot occur in an event. This way,
+ they will always be printed for the first event.
+*/
+st_print_event_info::st_print_event_info()
+ :flags2_inited(0), sql_mode_inited(0),
+ auto_increment_increment(0),auto_increment_offset(0), charset_inited(0),
+ lc_time_names_number(~0),
+ charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER),
+ thread_id(0), thread_id_printed(false),
+ base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
+{
+ /*
+ Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
+ program's startup, but these explicit bzero() is for the day someone
+ creates dynamic instances.
+ */
+ bzero(db, sizeof(db));
+ bzero(charset, sizeof(charset));
+ bzero(time_zone_str, sizeof(time_zone_str));
+ delimiter[0]= ';';
+ delimiter[1]= 0;
+ myf const flags = MYF(MY_WME | MY_NABP);
+ open_cached_file(&head_cache, NULL, NULL, 0, flags);
+ open_cached_file(&body_cache, NULL, NULL, 0, flags);
+}
+#endif
diff --git a/sql/log_event.h b/sql/log_event.h
index 4e151d6cde9..76d92b23189 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -592,8 +592,9 @@ typedef struct st_print_event_info
{
/*
Settings for database, sql_mode etc that comes from the last event
- that was printed.
- */
+ that was printed. We cache these so that we don't have to print
+ them if they are unchanged.
+ */
// TODO: have the last catalog here ??
char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is
bool flags2_inited;
@@ -606,26 +607,10 @@ typedef struct st_print_event_info
char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH];
uint lc_time_names_number;
uint charset_database_number;
- st_print_event_info()
- :flags2_inited(0), sql_mode_inited(0),
- auto_increment_increment(1),auto_increment_offset(1), charset_inited(0),
- lc_time_names_number(0), charset_database_number(0),
- base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
- {
- /*
- Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
- program's startup, but these explicit bzero() is for the day someone
- creates dynamic instances.
- */
- bzero(db, sizeof(db));
- bzero(charset, sizeof(charset));
- bzero(time_zone_str, sizeof(time_zone_str));
- delimiter[0]= ';';
- delimiter[1]= 0;
- myf const flags = MYF(MY_WME | MY_NABP);
- open_cached_file(&head_cache, NULL, NULL, 0, flags);
- open_cached_file(&body_cache, NULL, NULL, 0, flags);
- }
+ uint thread_id;
+ bool thread_id_printed;
+
+ st_print_event_info();
~st_print_event_info() {
close_cached_file(&head_cache);
@@ -670,18 +655,18 @@ typedef struct st_print_event_info
Any @c Log_event saved on disk consists of the following three
components.
- * Common-Header
- * Post-Header
- * Body
+ - Common-Header
+ - Post-Header
+ - Body
The Common-Header, documented in the table @ref Table_common_header
"below", always has the same form and length within one version of
- MySQL. Each event type specifies a form and length of the
- Post-Header common to all events of the type. The Body may be of
- different form and length even for different events of the same
- type. The binary formats of Post-Header and Body are documented
- separately in each subclass. The binary format of Common-Header is
- as follows.
+ MySQL. Each event type specifies a format and length of the
+ Post-Header. The length of the Common-Header is the same for all
+ events of the same type. The Body may be of different format and
+ length even for different events of the same type. The binary
+ formats of Post-Header and Body are documented separately in each
+ subclass. The binary format of Common-Header is as follows.
<table>
<caption>Common-Header</caption>
@@ -750,8 +735,8 @@ typedef struct st_print_event_info
- Some events use a special format for efficient representation of
unsigned integers, called Packed Integer. A Packed Integer has the
capacity of storing up to 8-byte integers, while small integers
- still can use 1, 3, or 4 bytes. The first byte indicates how many
- bytes are used by the integer, according to the following table:
+ still can use 1, 3, or 4 bytes. The value of the first byte
+ determines how to read the number, according to the following table:
<table>
<caption>Format of Packed Integer</caption>
@@ -763,7 +748,7 @@ typedef struct st_print_event_info
<tr>
<td>0-250</td>
- <td>The first byte is the number (in range 0-250), and no more
+ <td>The first byte is the number (in the range 0-250), and no more
bytes are used.</td>
</tr>
@@ -1174,6 +1159,10 @@ protected:
@section Query_log_event_binary_format Binary format
+ See @ref Log_event_binary_format "Binary format for log events" for
+ a general discussion and introduction to the binary format of binlog
+ events.
+
The Post-Header has five components:
<table>
@@ -1407,7 +1396,7 @@ protected:
query "SELECT id, character_set_name, collation_name FROM
COLLATIONS".
- Cf. Q_CHARSET_DATABASE_NUMBER below.
+ Cf. Q_CHARSET_DATABASE_CODE below.
This field is always written.
</td>
@@ -1442,7 +1431,7 @@ protected:
<tr>
<td>charset_database_number</td>
- <td>Q_CHARSET_DATABASE_NUMBER == 8</td>
+ <td>Q_CHARSET_DATABASE_CODE == 8</td>
<td>2 byte integer</td>
<td>The value of the collation_database system variable (in the
@@ -1457,11 +1446,11 @@ protected:
In newer versions, "CREATE TABLE" has been changed to take the
character set from the database of the created table, rather than
- the database of the current database. This makes a difference
- when creating a table in another database than the current one.
- "LOAD DATA INFILE" has not yet changed to do this, but there are
- plans to eventually do it, and to make collation_database
- read-only.
+ the character set of the current database. This makes a
+ difference when creating a table in another database than the
+ current one. "LOAD DATA INFILE" has not yet changed to do this,
+ but there are plans to eventually do it, and to make
+ collation_database read-only.
This field is written if it is not 0.
</td>
@@ -1480,7 +1469,7 @@ protected:
Q_CATALOG_CODE will never be written by a new master, but can still
be understood by a new slave.
- * See Q_CHARSET_DATABASE_NUMBER in the table above.
+ * See Q_CHARSET_DATABASE_CODE in the table above.
*/
class Query_log_event: public Log_event
@@ -1610,31 +1599,6 @@ public: /* !!! Public in this patch to allow old usage */
};
-/**
- @class Muted_query_log_event
-
- Pretends to log SQL queries, but doesn't actually do so. This is
- used internally only and never written to any binlog.
-
- @section Muted_query_log_event_binary_format Binary Format
-
- This log event is not stored, and thus the binary format is 0 bytes
- long. Note that not even the Common-Header is stored.
-*/
-class Muted_query_log_event: public Query_log_event
-{
-public:
-#ifndef MYSQL_CLIENT
- Muted_query_log_event();
-
- bool write(IO_CACHE* file) { return(false); };
- virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
-#else
- Muted_query_log_event() {}
-#endif
-};
-
-
#ifdef HAVE_REPLICATION
/**
@@ -1919,6 +1883,8 @@ private:
@subsection Load_log_event_notes_on_previous_versions Notes on Previous Versions
+ This event type is understood by current versions, but only
+ generated by MySQL 3.23 and earlier.
*/
class Load_log_event: public Log_event
{
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 13f9763debe..808356a05c7 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1529,10 +1529,10 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
- uint actual_error= thd->net.client_last_errno;
+ uint actual_error= thd->net.last_errno;
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' in %s event: when locking tables",
- (actual_error ? thd->net.client_last_error :
+ (actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
get_type_str());
thd->is_fatal_error= 1;
@@ -1573,10 +1573,10 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
- uint actual_error= thd->net.client_last_errno;
+ uint actual_error= thd->net.last_errno;
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' on reopening tables",
- (actual_error ? thd->net.client_last_error :
+ (actual_error ? thd->net.last_error :
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
@@ -1729,10 +1729,10 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
break;
default:
- rli->report(ERROR_LEVEL, thd->net.client_last_errno,
+ rli->report(ERROR_LEVEL, thd->net.last_errno,
"Error in %s event: row application failed. %s",
get_type_str(),
- thd->net.client_last_error ? thd->net.client_last_error : "");
+ thd->net.last_error ? thd->net.last_error : "");
thd->is_slave_error= 1;
break;
}
@@ -1779,12 +1779,12 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
if (error)
{ /* error has occured during the transaction */
- rli->report(ERROR_LEVEL, thd->net.client_last_errno,
+ rli->report(ERROR_LEVEL, thd->net.last_errno,
"Error in %s event: error during transaction execution "
"on table %s.%s. %s",
get_type_str(), table->s->db.str,
table->s->table_name.str,
- thd->net.client_last_error ? thd->net.client_last_error : "");
+ thd->net.last_error ? thd->net.last_error : "");
/*
If one day we honour --skip-slave-errors in row-based replication, and
@@ -1913,7 +1913,7 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli)
example "no key found" (as this is allowed). This is a safety
measure; apparently those errors (e.g. when executing a
Delete_rows_log_event_old of a non-existing row, like in
- rpl_row_mystery22.test, thd->net.client_last_error = "Can't
+ rpl_row_mystery22.test, thd->net.last_error = "Can't
find record in 't1'" and last_errno=1032) do not become
visible. We still prefer to wipe them out.
*/
@@ -2647,8 +2647,8 @@ Write_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
DBUG_ASSERT(m_table != NULL);
int error= write_row(rli, TRUE /* overwrite */);
- if (error && !thd->net.client_last_errno)
- thd->net.client_last_errno= error;
+ if (error && !thd->net.last_errno)
+ thd->net.last_errno= error;
return error;
}
diff --git a/sql/matherr.c b/sql/matherr.c
deleted file mode 100644
index 4998d8b4961..00000000000
--- a/sql/matherr.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (C) 2000-2001 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
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/* Fix that we got POSTFIX_ERROR when doing unreasonable math (not core) */
-
-#include <my_global.h>
-#include <errno.h>
-
- /* Fix that we gets POSTFIX_ERROR when error in math */
-
-#if defined(HAVE_MATHERR)
-int matherr(struct exception *x)
-{
- if (x->type != PLOSS)
- x->retval=POSTFIX_ERROR;
- switch (x->type) {
- case DOMAIN:
- case SING:
- my_errno=EDOM;
- break;
- case OVERFLOW:
- case UNDERFLOW:
- my_errno=ERANGE;
- break;
- default:
- break;
- }
- return(1); /* Take no other action */
-}
-#endif
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 11978345e27..84604706326 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -44,6 +44,19 @@
#include "sql_plugin.h"
#include "scheduler.h"
+/**
+ Query type constants.
+
+ QT_ORDINARY -- ordinary SQL query.
+ QT_IS -- SQL query to be shown in INFORMATION_SCHEMA (in utf8 and without
+ character set introducers).
+*/
+enum enum_query_type
+{
+ QT_ORDINARY,
+ QT_IS
+};
+
/* TODO convert all these three maps to Bitmap classes */
typedef ulonglong table_map; /* Used for table bits in join */
#if MAX_INDEXES <= 64
@@ -726,8 +739,8 @@ inline bool check_some_routine_access(THD *thd, const char *db,
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
-bool mysql_multi_update_prepare(THD *thd);
-bool mysql_multi_delete_prepare(THD *thd);
+int mysql_multi_update_prepare(THD *thd);
+int mysql_multi_delete_prepare(THD *thd);
bool mysql_insert_select_prepare(THD *thd);
bool update_precheck(THD *thd, TABLE_LIST *tables);
bool delete_precheck(THD *thd, TABLE_LIST *tables);
@@ -746,6 +759,7 @@ bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
bool check_string_char_length(LEX_STRING *str, const char *err_msg,
uint max_char_length, CHARSET_INFO *cs,
bool no_error);
+bool test_if_data_home_dir(const char *dir);
bool parse_sql(THD *thd,
class Lex_input_stream *lip,
@@ -959,10 +973,8 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
void decrease_user_connections(USER_CONN *uc);
void thd_init_client_charset(THD *thd, uint cs_number);
bool setup_connection_thread_globals(THD *thd);
-bool login_connection(THD *thd);
-void end_connection(THD *thd);
-bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
+int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db);
@@ -1006,7 +1018,7 @@ void init_max_user_conn(void);
void init_update_queries(void);
void free_max_user_conn(void);
pthread_handler_t handle_bootstrap(void *arg);
-bool mysql_execute_command(THD *thd);
+int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length);
@@ -1032,8 +1044,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
bool no_grant, bool no_errors, bool schema_db);
bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
- bool no_errors);
-bool check_global_access(THD *thd, ulong want_access);
+ uint number, bool no_errors);
#else
inline bool check_access(THD *thd, ulong access, const char *db,
ulong *save_priv, bool no_grant, bool no_errors,
@@ -1044,12 +1055,16 @@ inline bool check_access(THD *thd, ulong access, const char *db,
return false;
}
inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
- bool no_errors)
-{ return false; }
-inline bool check_global_access(THD *thd, ulong want_access)
+ uint number, bool no_errors)
{ return false; }
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+bool check_global_access(THD *thd, ulong want_access);
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
+
/*
Support routine for SQL parser on partitioning syntax
*/
@@ -1179,7 +1194,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
void prepare_triggers_for_insert_stmt(TABLE *table);
-bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SQL_LIST *order, ha_rows rows, ulonglong options,
bool reset_auto_increment);
@@ -1390,6 +1405,13 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables,
extern Item **not_found_item;
/*
+ A set of constants used for checking non aggregated fields and sum
+ functions mixture in the ONLY_FULL_GROUP_BY_MODE.
+*/
+#define NON_AGG_FIELD_USED 1
+#define SUM_FUNC_USED 2
+
+/*
This enumeration type is used only by the function find_item_in_list
to return the info on how an item has been resolved against a list
of possibly aliased items.
@@ -1453,14 +1475,14 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
pthread_cond_t *cond);
int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
/* open_and_lock_tables with optional derived handling */
-bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
/* simple open_and_lock_tables without derived handling */
-inline bool simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
+inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
{
return open_and_lock_tables_derived(thd, tables, FALSE);
}
/* open_and_lock_tables with derived handling */
-inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{
return open_and_lock_tables_derived(thd, tables, TRUE);
}
@@ -1633,6 +1655,7 @@ extern pthread_mutex_t LOCK_gdl;
#define WFRM_WRITE_SHADOW 1
#define WFRM_INSTALL_SHADOW 2
#define WFRM_PACK_FRM 4
+#define WFRM_KEEP_SHARE 8
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt);
void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt);
@@ -1689,7 +1712,7 @@ inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table,
bool eval_const_cond(COND *cond);
/* sql_load.cc */
-bool mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
+int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
List<Item> &fields_vars, List<Item> &set_fields,
List<Item> &set_values_list,
enum enum_duplicates handle_duplicates, bool ignore,
@@ -1706,7 +1729,7 @@ bool mysql_manager_submit(void (*action)());
/* sql_test.cc */
#ifndef DBUG_OFF
-void print_where(COND *cond,const char *info);
+void print_where(COND *cond,const char *info, enum_query_type query_type);
void print_cached_tables(void);
void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
void print_plan(JOIN* join,uint idx, double record_count, double read_time,
@@ -1787,7 +1810,8 @@ extern time_t server_start_time, flush_status_time;
#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
extern uint mysql_data_home_len;
extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH],
- mysql_real_data_home[];
+ mysql_real_data_home[], mysql_unpacked_real_data_home[];
+extern CHARSET_INFO *character_set_filesystem;
#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
#ifdef MYSQL_SERVER
extern char *opt_mysql_tmpdir, mysql_charsets_dir[],
@@ -1877,6 +1901,7 @@ extern bool opt_disable_networking, opt_skip_show_db;
extern my_bool opt_character_set_client_handshake;
extern bool volatile abort_loop, shutdown_in_progress;
extern uint volatile thread_count, thread_running, global_read_lock;
+extern uint connection_count;
extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
@@ -1915,7 +1940,8 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db,
LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count,
- LOCK_bytes_sent, LOCK_bytes_received, LOCK_uuid_short;
+ LOCK_bytes_sent, LOCK_bytes_received, LOCK_connection_count,
+ LOCK_uuid_short;
#ifdef HAVE_OPENSSL
extern pthread_mutex_t LOCK_des_key_file;
#endif
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index a706fa8b0e9..9dadd15bfbe 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -122,6 +122,13 @@ extern "C" { // Because of SCO 3.2V4.2
#include <sys/mman.h>
#endif
+#ifdef __WIN__
+#include <crtdbg.h>
+#define SIGNAL_FMT "exception 0x%x"
+#else
+#define SIGNAL_FMT "signal %d"
+#endif
+
#ifdef __NETWARE__
#define zVOLSTATE_ACTIVE 6
#define zVOLSTATE_DEACTIVE 2
@@ -218,10 +225,16 @@ inline void set_proper_floating_point_mode()
extern "C" int gethostname(char *name, int namelen);
#endif
+extern "C" sig_handler handle_segfault(int sig);
/* Constants */
const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"};
+/*
+ WARNING: When adding new SQL modes don't forget to update the
+ tables definitions that stores it's value.
+ (ie: mysql.event, mysql.proc)
+*/
static const char *sql_mode_names[]=
{
"REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
@@ -507,6 +520,7 @@ char mysql_real_data_home[FN_REFLEN],
language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN],
*opt_init_file, *opt_tc_log_file,
def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
+char mysql_unpacked_real_data_home[FN_REFLEN];
uint reg_ext_length;
const key_map key_map_empty(0);
key_map key_map_full(0); // Will be initialized later
@@ -573,7 +587,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
LOCK_global_system_variables,
- LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
+ LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
+ LOCK_connection_count;
/**
The below lock protects access to two global server variables:
max_prepared_stmt_count and prepared_stmt_count. These variables
@@ -709,6 +724,11 @@ char *des_key_file;
struct st_VioSSLFd *ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */
+/**
+ Number of currently active user connections. The variable is protected by
+ LOCK_connection_count.
+*/
+uint connection_count= 0;
/* Function declarations */
@@ -1082,9 +1102,7 @@ static void __cdecl kill_server(int sig_ptr)
close_connections();
if (sig != MYSQL_KILL_SIGNAL &&
-#ifdef __WIN__
sig != SIGINT && /* Bug#18235 */
-#endif
sig != 0)
unireg_abort(1); /* purecov: inspected */
else
@@ -1334,6 +1352,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_bytes_sent);
(void) pthread_mutex_destroy(&LOCK_bytes_received);
(void) pthread_mutex_destroy(&LOCK_user_conn);
+ (void) pthread_mutex_destroy(&LOCK_connection_count);
Events::destroy_mutexes();
#ifdef HAVE_OPENSSL
(void) pthread_mutex_destroy(&LOCK_des_key_file);
@@ -1654,8 +1673,7 @@ static void network_init(void)
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
- MessageBox(NULL, (LPTSTR) lpMsgBuf, "Error from CreateNamedPipe",
- MB_OK|MB_ICONINFORMATION);
+ sql_perror((char *)lpMsgBuf);
LocalFree(lpMsgBuf);
unireg_abort(1);
}
@@ -1777,6 +1795,11 @@ void unlink_thd(THD *thd)
DBUG_ENTER("unlink_thd");
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
thd->cleanup();
+
+ pthread_mutex_lock(&LOCK_connection_count);
+ --connection_count;
+ pthread_mutex_unlock(&LOCK_connection_count);
+
(void) pthread_mutex_lock(&LOCK_thread_count);
thread_count--;
delete thd;
@@ -1867,9 +1890,9 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
/* It's safe to broadcast outside a lock (COND... is not deleted here) */
DBUG_PRINT("signal", ("Broadcasting COND_thread_count"));
+ my_thread_end();
(void) pthread_cond_broadcast(&COND_thread_count);
- my_thread_end();
pthread_exit(0);
DBUG_RETURN(0); // Impossible
}
@@ -1914,16 +1937,162 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
******************************************************************************/
#if defined(__WIN__)
+
+
+/*
+ On Windows, we use native SetConsoleCtrlHandler for handle events like Ctrl-C
+ with graceful shutdown.
+ Also, we do not use signal(), but SetUnhandledExceptionFilter instead - as it
+ provides possibility to pass the exception to just-in-time debugger, collect
+ dumps and potentially also the exception and thread context used to output
+ callstack.
+*/
+
+static BOOL WINAPI console_event_handler( DWORD type )
+{
+ DBUG_ENTER("console_event_handler");
+ if(type == CTRL_C_EVENT)
+ {
+ /*
+ Do not shutdown before startup is finished and shutdown
+ thread is initialized. Otherwise there is a race condition
+ between main thread doing initialization and CTRL-C thread doing
+ cleanup, which can result into crash.
+ */
+ if(hEventShutdown)
+ kill_mysql();
+ else
+ sql_print_warning("CTRL-C ignored during startup");
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ In Visual Studio 2005 and later, default SIGABRT handler will overwrite
+ any unhandled exception filter set by the application and will try to
+ call JIT debugger. This is not what we want, this we calling __debugbreak
+ to stop in debugger, if process is being debugged or to generate
+ EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
+*/
+
+#if (_MSC_VER >= 1400)
+static void my_sigabrt_handler(int sig)
+{
+ __debugbreak();
+}
+#endif /*_MSC_VER >=1400 */
+
+void win_install_sigabrt_handler(void)
+{
+#if (_MSC_VER >=1400)
+ /*abort() should not override our exception filter*/
+ _set_abort_behavior(0,_CALL_REPORTFAULT);
+ signal(SIGABRT,my_sigabrt_handler);
+#endif /* _MSC_VER >=1400 */
+}
+
+#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
+#define DEBUGGER_ATTACH_TIMEOUT 120
+/*
+ Wait for debugger to attach and break into debugger. If debugger is not attached,
+ resume after timeout.
+*/
+static void wait_for_debugger(int timeout_sec)
+{
+ if(!IsDebuggerPresent())
+ {
+ int i;
+ printf("Waiting for debugger to attach, pid=%u\n",GetCurrentProcessId());
+ fflush(stdout);
+ for(i= 0; i < timeout_sec; i++)
+ {
+ Sleep(1000);
+ if(IsDebuggerPresent())
+ {
+ /* Break into debugger */
+ __debugbreak();
+ return;
+ }
+ }
+ printf("pid=%u, debugger not attached after %d seconds, resuming\n",GetCurrentProcessId(),
+ timeout_sec);
+ fflush(stdout);
+ }
+}
+#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
+
+LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
+{
+ static BOOL first_time= TRUE;
+ if(!first_time)
+ {
+ /*
+ This routine can be called twice, typically
+ when detaching in JIT debugger.
+ Return EXCEPTION_EXECUTE_HANDLER to terminate process.
+ */
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+ first_time= FALSE;
+#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
+ /*
+ Unfortunately there is no clean way to debug unhandled exception filters,
+ as debugger does not stop there(also documented in MSDN)
+ To overcome, one could put a MessageBox, but this will not work in service.
+ Better solution is to print error message and sleep some minutes
+ until debugger is attached
+ */
+ wait_for_debugger(DEBUGGER_ATTACH_TIMEOUT);
+#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
+ __try
+ {
+ set_exception_pointers(ex_pointers);
+ handle_segfault(ex_pointers->ExceptionRecord->ExceptionCode);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ DWORD written;
+ const char msg[] = "Got exception in exception handler!\n";
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),msg, sizeof(msg)-1,
+ &written,NULL);
+ }
+ /*
+ Return EXCEPTION_CONTINUE_SEARCH to give JIT debugger
+ (drwtsn32 or vsjitdebugger) possibility to attach,
+ if JIT debugger is configured.
+ Windows Error reporting might generate a dump here.
+ */
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
static void init_signals(void)
{
- int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT } ;
- for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++)
- signal(signals[i], kill_server) ;
-#if defined(__WIN__)
- signal(SIGBREAK,SIG_IGN); //ignore SIGBREAK for NT
-#else
- signal(SIGBREAK, kill_server);
-#endif
+ win_install_sigabrt_handler();
+ if(opt_console)
+ SetConsoleCtrlHandler(console_event_handler,TRUE);
+ else
+ {
+ /* Avoid MessageBox()es*/
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+
+ /*
+ Do not use SEM_NOGPFAULTERRORBOX in the following SetErrorMode (),
+ because it would prevent JIT debugger and Windows error reporting
+ from working. We need WER or JIT-debugging, since our own unhandled
+ exception filter is not guaranteed to work in all situation
+ (like heap corruption or stack overflow)
+ */
+ SetErrorMode(SetErrorMode(0)|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
+ }
+ SetUnhandledExceptionFilter(my_unhandler_exception_filter);
}
@@ -2176,12 +2345,22 @@ static void check_data_home(const char *path)
{
}
-#else /* if ! __WIN__ */
+#endif /*__WIN__ || __NETWARE */
#ifdef HAVE_LINUXTHREADS
#define UNSAFE_DEFAULT_LINUX_THREADS 200
#endif
+
+#if BACKTRACE_DEMANGLE
+#include <cxxabi.h>
+extern "C" char *my_demangle(const char *mangled_name, int *status)
+{
+ return abi::__cxa_demangle(mangled_name, NULL, NULL, status);
+}
+#endif
+
+
extern "C" sig_handler handle_segfault(int sig)
{
time_t curr_time;
@@ -2196,7 +2375,7 @@ extern "C" sig_handler handle_segfault(int sig)
*/
if (segfaulted)
{
- fprintf(stderr, "Fatal signal %d while backtracing\n", sig);
+ fprintf(stderr, "Fatal " SIGNAL_FMT " while backtracing\n", sig);
exit(1);
}
@@ -2206,7 +2385,7 @@ extern "C" sig_handler handle_segfault(int sig)
localtime_r(&curr_time, &tm);
fprintf(stderr,"\
-%02d%02d%02d %2d:%02d:%02d - mysqld got signal %d;\n\
+%02d%02d%02d %2d:%02d:%02d - mysqld got " SIGNAL_FMT " ;\n\
This could be because you hit a bug. It is also possible that this binary\n\
or one of the libraries it was linked against is corrupt, improperly built,\n\
or misconfigured. This error can also be caused by malfunctioning hardware.\n",
@@ -2248,15 +2427,38 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n",
if (!(test_flags & TEST_NO_STACKTRACE))
{
fprintf(stderr,"thd: 0x%lx\n",(long) thd);
+ fprintf(stderr,"\
+Attempting backtrace. You can use the following information to find out\n\
+where mysqld died. If you see no messages after this, something went\n\
+terribly wrong...\n");
print_stacktrace(thd ? (uchar*) thd->thread_stack : (uchar*) 0,
my_thread_stack_size);
}
if (thd)
{
+ const char *kreason= "UNKNOWN";
+ switch (thd->killed) {
+ case THD::NOT_KILLED:
+ kreason= "NOT_KILLED";
+ break;
+ case THD::KILL_BAD_DATA:
+ kreason= "KILL_BAD_DATA";
+ break;
+ case THD::KILL_CONNECTION:
+ kreason= "KILL_CONNECTION";
+ break;
+ case THD::KILL_QUERY:
+ kreason= "KILL_QUERY";
+ break;
+ case THD::KILLED_NO_VALUE:
+ kreason= "KILLED_NO_VALUE";
+ break;
+ }
fprintf(stderr, "Trying to get some variables.\n\
Some pointers may be invalid and cause the dump to abort...\n");
safe_print_str("thd->query", thd->query, 1024);
fprintf(stderr, "thd->thread_id=%lu\n", (ulong) thd->thread_id);
+ fprintf(stderr, "thd->killed=%s\n", kreason);
}
fprintf(stderr, "\
The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\
@@ -2296,15 +2498,22 @@ of those buggy OS calls. You should consider whether you really need the\n\
bugs.\n");
}
+#ifdef HAVE_WRITE_CORE
if (test_flags & TEST_CORE_ON_SIGNAL)
{
fprintf(stderr, "Writing a core file\n");
fflush(stderr);
write_core(sig);
}
+#endif
+
+#ifndef __WIN__
+ /* On Windows, do not terminate, but pass control to exception filter */
exit(1);
+#endif
}
+#if !defined(__WIN__) && !defined(__NETWARE__)
#ifndef SA_RESETHAND
#define SA_RESETHAND 0
#endif
@@ -2356,7 +2565,6 @@ static void init_signals(void)
(void) sigemptyset(&set);
my_sigset(SIGPIPE,SIG_IGN);
sigaddset(&set,SIGPIPE);
- sigaddset(&set,SIGINT);
#ifndef IGNORE_SIGHUP_SIGQUIT
sigaddset(&set,SIGQUIT);
sigaddset(&set,SIGHUP);
@@ -2384,6 +2592,9 @@ static void init_signals(void)
my_sigset(thr_kill_signal, end_thread_signal);
my_sigset(SIGINT, end_thread_signal);
}
+ else
+ sigaddset(&set,SIGINT);
+
sigprocmask(SIG_SETMASK,&set,NULL);
pthread_sigmask(SIG_SETMASK,&set,NULL);
DBUG_VOID_RETURN;
@@ -2541,8 +2752,18 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
(TABLE_LIST*) 0, &not_used); // Flush logs
}
/* reenable logs after the options were reloaded */
- logger.set_handlers(LOG_FILE, opt_slow_log ? LOG_TABLE:LOG_NONE,
- opt_log ? LOG_TABLE:LOG_NONE);
+ if (log_output_options & LOG_NONE)
+ {
+ logger.set_handlers(LOG_FILE,
+ opt_slow_log ? LOG_TABLE : LOG_NONE,
+ opt_log ? LOG_TABLE : LOG_NONE);
+ }
+ else
+ {
+ logger.set_handlers(LOG_FILE,
+ opt_slow_log ? log_output_options : LOG_NONE,
+ opt_log ? log_output_options : LOG_NONE);
+ }
break;
#ifdef USE_ONE_SIGNAL_HAND
case THR_SERVER_ALARM:
@@ -2707,18 +2928,6 @@ pthread_handler_t handle_shutdown(void *arg)
kill_server(MYSQL_KILL_SIGNAL);
return 0;
}
-
-
-int STDCALL handle_kill(ulong ctrl_type)
-{
- if (ctrl_type == CTRL_CLOSE_EVENT ||
- ctrl_type == CTRL_SHUTDOWN_EVENT)
- {
- kill_server(MYSQL_KILL_SIGNAL);
- return TRUE;
- }
- return FALSE;
-}
#endif
#if !defined(EMBEDDED_LIBRARY)
@@ -3285,6 +3494,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_uuid_short, MY_MUTEX_INIT_FAST);
+ (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST);
#ifdef HAVE_OPENSSL
(void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
#ifndef HAVE_YASSL
@@ -4097,11 +4307,6 @@ we force server id to 2, but this MySQL server will not act as a slave.");
freopen(log_error_file,"a+",stderr);
FreeConsole(); // Remove window
}
- else
- {
- /* Don't show error dialog box when on foreground: it stops the server */
- SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
- }
#endif
/*
@@ -4551,6 +4756,11 @@ void create_thread_to_handle_connection(THD *thd)
thread_count--;
thd->killed= THD::KILL_CONNECTION; // Safety
(void) pthread_mutex_unlock(&LOCK_thread_count);
+
+ pthread_mutex_lock(&LOCK_connection_count);
+ --connection_count;
+ pthread_mutex_unlock(&LOCK_connection_count);
+
statistic_increment(aborted_connects,&LOCK_status);
/* Can't use my_error() since store_globals has not been called. */
my_snprintf(error_message_buff, sizeof(error_message_buff),
@@ -4590,15 +4800,34 @@ static void create_new_thread(THD *thd)
if (protocol_version > 9)
net->return_errno=1;
- /* don't allow too many connections */
- if (thread_count - delayed_insert_threads >= max_connections+1 || abort_loop)
+ /*
+ Don't allow too many connections. We roughly check here that we allow
+ only (max_connections + 1) connections.
+ */
+
+ pthread_mutex_lock(&LOCK_connection_count);
+
+ if (connection_count >= max_connections + 1 || abort_loop)
{
+ pthread_mutex_unlock(&LOCK_connection_count);
+
DBUG_PRINT("error",("Too many connections"));
close_connection(thd, ER_CON_COUNT_ERROR, 1);
delete thd;
DBUG_VOID_RETURN;
}
+
+ ++connection_count;
+
+ if (connection_count > max_used_connections)
+ max_used_connections= connection_count;
+
+ pthread_mutex_unlock(&LOCK_connection_count);
+
+ /* Start a new thread to handle connection. */
+
pthread_mutex_lock(&LOCK_thread_count);
+
/*
The initialization of thread_id is done in create_embedded_thd() for
the embedded library.
@@ -4606,13 +4835,10 @@ static void create_new_thread(THD *thd)
*/
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- /* Start a new thread to handle connection */
thread_count++;
- if (thread_count - delayed_insert_threads > max_used_connections)
- max_used_connections= thread_count - delayed_insert_threads;
-
thread_scheduler.add_connection(thd);
+
DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
@@ -7061,6 +7287,7 @@ SHOW_VAR status_vars[]= {
{"Open_tables", (char*) &show_open_tables, SHOW_FUNC},
{"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH},
{"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
+ {"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
{"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC},
#ifdef HAVE_QUERY_CACHE
{"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
@@ -8176,12 +8403,15 @@ static void fix_paths(void)
pos[1]= 0;
}
convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS);
+ (void) fn_format(buff, mysql_real_data_home, "", "",
+ (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
+ (void) unpack_dirname(mysql_unpacked_real_data_home, buff);
convert_dirname(language,language,NullS);
(void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir
(void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home);
(void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home);
(void) my_load_path(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr :
- get_relative_path(LIBDIR), mysql_home);
+ get_relative_path(PLUGINDIR), mysql_home);
opt_plugin_dir_ptr= opt_plugin_dir;
char *sharedir=get_relative_path(SHAREDIR);
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index ad653a2267d..1098e8e6832 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -127,10 +127,10 @@ my_bool my_net_init(NET *net, Vio* vio)
net->error=0; net->return_errno=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff;
- net->client_last_error[0]=0;
+ net->last_error[0]=0;
net->compress=0; net->reading_or_writing=0;
net->where_b = net->remain_in_buf=0;
- net->client_last_errno=0;
+ net->last_errno=0;
#ifdef USE_QUERY_CACHE
query_cache_init_query(net);
#else
@@ -177,7 +177,7 @@ my_bool net_realloc(NET *net, size_t length)
net->max_packet_size));
/* @todo: 1 and 2 codes are identical. */
net->error= 1;
- net->client_last_errno= ER_NET_PACKET_TOO_LARGE;
+ net->last_errno= ER_NET_PACKET_TOO_LARGE;
#ifdef MYSQL_SERVER
my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
#endif
@@ -194,7 +194,7 @@ my_bool net_realloc(NET *net, size_t length)
{
/* @todo: 1 and 2 codes are identical. */
net->error= 1;
- net->client_last_errno= ER_OUT_OF_RESOURCES;
+ net->last_errno= ER_OUT_OF_RESOURCES;
/* In the server the error is reported by MY_WME flag. */
DBUG_RETURN(1);
}
@@ -579,7 +579,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
COMP_HEADER_SIZE, MYF(MY_WME))))
{
net->error= 2;
- net->client_last_errno= ER_OUT_OF_RESOURCES;
+ net->last_errno= ER_OUT_OF_RESOURCES;
/* In the server, the error is reported by MY_WME flag. */
net->reading_or_writing= 0;
DBUG_RETURN(1);
@@ -632,7 +632,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
my_progname,vio_errno(net->vio));
#endif /* EXTRA_DEBUG */
net->error= 2; /* Close socket */
- net->client_last_errno= ER_NET_PACKET_TOO_LARGE;
+ net->last_errno= ER_NET_PACKET_TOO_LARGE;
#ifdef MYSQL_SERVER
my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
#endif
@@ -662,10 +662,10 @@ net_real_write(NET *net,const uchar *packet, size_t len)
}
#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
net->error= 2; /* Close socket */
- net->client_last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
+ net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
ER_NET_ERROR_ON_WRITE);
#ifdef MYSQL_SERVER
- my_error(net->client_last_errno, MYF(0));
+ my_error(net->last_errno, MYF(0));
#endif /* MYSQL_SERVER */
break;
}
@@ -844,7 +844,7 @@ my_real_read(NET *net, size_t *complen)
#endif /* EXTRA_DEBUG */
len= packet_error;
net->error= 2; /* Close socket */
- net->client_last_errno= ER_NET_FCNTL_ERROR;
+ net->last_errno= ER_NET_FCNTL_ERROR;
#ifdef MYSQL_SERVER
my_error(ER_NET_FCNTL_ERROR, MYF(0));
#endif
@@ -876,11 +876,11 @@ my_real_read(NET *net, size_t *complen)
remain, vio_errno(net->vio), (long) length));
len= packet_error;
net->error= 2; /* Close socket */
- net->client_last_errno= (vio_was_interrupted(net->vio) ?
+ net->last_errno= (vio_was_interrupted(net->vio) ?
ER_NET_READ_INTERRUPTED :
ER_NET_READ_ERROR);
#ifdef MYSQL_SERVER
- my_error(net->client_last_errno, MYF(0));
+ my_error(net->last_errno, MYF(0));
#endif
goto end;
}
@@ -1100,7 +1100,7 @@ my_net_read(NET *net)
&complen))
{
net->error= 2; /* caller will close socket */
- net->client_last_errno= ER_NET_UNCOMPRESS_ERROR;
+ net->last_errno= ER_NET_UNCOMPRESS_ERROR;
#ifdef MYSQL_SERVER
my_error(ER_NET_UNCOMPRESS_ERROR, MYF(0));
#endif
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index a1ad097f12c..e5709f418f7 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -5722,52 +5722,70 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
field->type() == MYSQL_TYPE_DATETIME))
field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES;
err= value->save_in_field_no_warnings(field, 1);
- if (err > 0 && field->cmp_type() != value->result_type())
+ if (err > 0)
{
- if ((type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) &&
- value->result_type() == item_cmp_type(field->result_type(),
- value->result_type()))
-
+ if (field->cmp_type() != value->result_type())
{
- tree= new (alloc) SEL_ARG(field, 0, 0);
- tree->type= SEL_ARG::IMPOSSIBLE;
- goto end;
- }
- else
- {
- /*
- TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE
- for the cases like int_field > 999999999999999999999999 as well.
- */
- tree= 0;
- if (err == 3 && field->type() == FIELD_TYPE_DATE &&
- (type == Item_func::GT_FUNC || type == Item_func::GE_FUNC ||
- type == Item_func::LT_FUNC || type == Item_func::LE_FUNC) )
+ if ((type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) &&
+ value->result_type() == item_cmp_type(field->result_type(),
+ value->result_type()))
+ {
+ tree= new (alloc) SEL_ARG(field, 0, 0);
+ tree->type= SEL_ARG::IMPOSSIBLE;
+ goto end;
+ }
+ else
{
/*
- We were saving DATETIME into a DATE column, the conversion went ok
- but a non-zero time part was cut off.
+ TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE
+ for the cases like int_field > 999999999999999999999999 as well.
+ */
+ tree= 0;
+ if (err == 3 && field->type() == FIELD_TYPE_DATE &&
+ (type == Item_func::GT_FUNC || type == Item_func::GE_FUNC ||
+ type == Item_func::LT_FUNC || type == Item_func::LE_FUNC) )
+ {
+ /*
+ We were saving DATETIME into a DATE column, the conversion went ok
+ but a non-zero time part was cut off.
- In MySQL's SQL dialect, DATE and DATETIME are compared as datetime
- values. Index over a DATE column uses DATE comparison. Changing
- from one comparison to the other is possible:
+ In MySQL's SQL dialect, DATE and DATETIME are compared as datetime
+ values. Index over a DATE column uses DATE comparison. Changing
+ from one comparison to the other is possible:
- datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10'
- datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10'
+ datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10'
+ datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10'
- datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10'
- datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10'
+ datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10'
+ datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10'
- but we'll need to convert '>' to '>=' and '<' to '<='. This will
- be done together with other types at the end of this function
- (grep for field_is_equal_to_item)
- */
+ but we'll need to convert '>' to '>=' and '<' to '<='. This will
+ be done together with other types at the end of this function
+ (grep for field_is_equal_to_item)
+ */
+ }
+ else
+ goto end;
}
- else
- goto end;
}
- }
- if (err < 0)
+
+ /*
+ guaranteed at this point: err > 0; field and const of same type
+ If an integer got bounded (e.g. to within 0..255 / -128..127)
+ for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN)
+ */
+ else if (err == 1 && field->result_type() == INT_RESULT)
+ {
+ if (type == Item_func::LT_FUNC && (value->val_int() > 0))
+ type = Item_func::LE_FUNC;
+ else if (type == Item_func::GT_FUNC &&
+ !((Field_num*)field)->unsigned_flag &&
+ !((Item_int*)value)->unsigned_flag &&
+ (value->val_int() < 0))
+ type = Item_func::GE_FUNC;
+ }
+ }
+ else if (err < 0)
{
field->table->in_use->variables.sql_mode= orig_sql_mode;
/* This happens when we try to insert a NULL field in a not null column */
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 1ce5a0c2206..808f305db2c 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -419,41 +419,167 @@ char *partition_info::has_unique_names()
/*
- Check that all partitions use the same storage engine.
- This is currently a limitation in this version.
+ Check that the partition/subpartition is setup to use the correct
+ storage engine
+ SYNOPSIS
+ check_engine_condition()
+ p_elem Partition element
+ table_engine_set Have user specified engine on table level
+ inout::engine_type Current engine used
+ inout::first Is it first partition
+ RETURN VALUE
+ TRUE Failed check
+ FALSE Ok
+ DESCRIPTION
+ Specified engine for table and partitions p0 and pn
+ Must be correct both on CREATE and ALTER commands
+ table p0 pn res (0 - OK, 1 - FAIL)
+ - - - 0
+ - - x 1
+ - x - 1
+ - x x 0
+ x - - 0
+ x - x 0
+ x x - 0
+ x x x 0
+ i.e:
+ - All subpartitions must use the same engine
+ AND it must be the same as the partition.
+ - All partitions must use the same engine
+ AND it must be the same as the table.
+ - if one does NOT specify an engine on the table level
+ then one must either NOT specify any engine on any
+ partition/subpartition OR for ALL partitions/subpartitions
+ Note:
+ When ALTER a table, the engines are already set for all levels
+ (table, all partitions and subpartitions). So if one want to
+ change the storage engine, one must specify it on the table level
+
+*/
+
+static bool check_engine_condition(partition_element *p_elem,
+ bool table_engine_set,
+ handlerton **engine_type,
+ bool *first)
+{
+ DBUG_ENTER("check_engine_condition");
+
+ DBUG_PRINT("enter", ("p_eng %s t_eng %s t_eng_set %u first %u state %u",
+ ha_resolve_storage_engine_name(p_elem->engine_type),
+ ha_resolve_storage_engine_name(*engine_type),
+ table_engine_set, *first, p_elem->part_state));
+ if (*first && !table_engine_set)
+ {
+ *engine_type= p_elem->engine_type;
+ DBUG_PRINT("info", ("setting table_engine = %s",
+ ha_resolve_storage_engine_name(*engine_type)));
+ }
+ *first= FALSE;
+ if ((table_engine_set &&
+ (p_elem->engine_type != (*engine_type) &&
+ p_elem->engine_type)) ||
+ (!table_engine_set &&
+ p_elem->engine_type != (*engine_type)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ else
+ {
+ DBUG_RETURN(FALSE);
+ }
+}
+
+/*
+ Check engine mix that it is correct
+ Current limitation is that all partitions and subpartitions
+ must use the same storage engine.
SYNOPSIS
check_engine_mix()
- engine_array An array of engine identifiers
- no_parts Total number of partitions
-
+ inout::engine_type Current engine used
+ table_engine_set Have user specified engine on table level
RETURN VALUE
- TRUE Error, mixed engines
- FALSE Ok, no mixed engines
+ TRUE Error, mixed engines
+ FALSE Ok, no mixed engines
DESCRIPTION
Current check verifies only that all handlers are the same.
Later this check will be more sophisticated.
+ (specified partition handler ) specified table handler
+ (NDB, NDB) NDB OK
+ (MYISAM, MYISAM) - OK
+ (MYISAM, -) - NOT OK
+ (MYISAM, -) MYISAM OK
+ (- , MYISAM) - NOT OK
+ (- , -) MYISAM OK
+ (-,-) - OK
+ (NDB, MYISAM) * NOT OK
*/
-bool partition_info::check_engine_mix(handlerton **engine_array, uint no_parts)
+bool partition_info::check_engine_mix(handlerton *engine_type,
+ bool table_engine_set)
{
- uint i= 0;
+ handlerton *old_engine_type= engine_type;
+ bool first= TRUE;
+ uint no_parts= partitions.elements;
DBUG_ENTER("partition_info::check_engine_mix");
-
- do
+ DBUG_PRINT("info", ("in: engine_type = %s, table_engine_set = %u",
+ ha_resolve_storage_engine_name(engine_type),
+ table_engine_set));
+ if (no_parts)
{
- if (engine_array[i] != engine_array[0])
+ List_iterator<partition_element> part_it(partitions);
+ uint i= 0;
+ do
{
- my_error(ER_MIX_HANDLER_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
- } while (++i < no_parts);
- if (engine_array[0]->flags & HTON_NO_PARTITION)
+ partition_element *part_elem= part_it++;
+ DBUG_PRINT("info", ("part = %d engine = %s table_engine_set %u",
+ i, ha_resolve_storage_engine_name(part_elem->engine_type),
+ table_engine_set));
+ if (is_sub_partitioned() &&
+ part_elem->subpartitions.elements)
+ {
+ uint no_subparts= part_elem->subpartitions.elements;
+ uint j= 0;
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ DBUG_PRINT("info", ("sub = %d engine = %s table_engie_set %u",
+ j, ha_resolve_storage_engine_name(sub_elem->engine_type),
+ table_engine_set));
+ if (check_engine_condition(sub_elem, table_engine_set,
+ &engine_type, &first))
+ goto error;
+ } while (++j < no_subparts);
+ /* ensure that the partition also has correct engine */
+ if (check_engine_condition(part_elem, table_engine_set,
+ &engine_type, &first))
+ goto error;
+ }
+ else if (check_engine_condition(part_elem, table_engine_set,
+ &engine_type, &first))
+ goto error;
+ } while (++i < no_parts);
+ }
+ DBUG_PRINT("info", ("engine_type = %s",
+ ha_resolve_storage_engine_name(engine_type)));
+ if (!engine_type)
+ engine_type= old_engine_type;
+ if (engine_type->flags & HTON_NO_PARTITION)
{
my_error(ER_PARTITION_MERGE_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
+ DBUG_PRINT("info", ("out: engine_type = %s",
+ ha_resolve_storage_engine_name(engine_type)));
+ DBUG_ASSERT(engine_type != partition_hton);
DBUG_RETURN(FALSE);
+error:
+ /*
+ Mixed engines not yet supported but when supported it will need
+ the partition handler
+ */
+ DBUG_RETURN(TRUE);
}
@@ -726,13 +852,15 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
handler *file, HA_CREATE_INFO *info,
bool check_partition_function)
{
- handlerton **engine_array= NULL;
- uint part_count= 0;
+ handlerton *table_engine= default_engine_type;
uint i, tot_partitions;
- bool result= TRUE;
+ bool result= TRUE, table_engine_set;
char *same_name;
DBUG_ENTER("partition_info::check_partition_info");
+ DBUG_ASSERT(default_engine_type != partition_hton);
+ DBUG_PRINT("info", ("default table_engine = %s",
+ ha_resolve_storage_engine_name(table_engine)));
if (check_partition_function)
{
int err= 0;
@@ -777,44 +905,89 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
goto end;
}
+ /*
+ if NOT specified ENGINE = <engine>:
+ If Create, always use create_info->db_type
+ else, use previous tables db_type
+ either ALL or NONE partition should be set to
+ default_engine_type when not table_engine_set
+ Note: after a table is created its storage engines for
+ the table and all partitions/subpartitions are set.
+ So when ALTER it is already set on table level
+ */
+ if (info && info->used_fields & HA_CREATE_USED_ENGINE)
+ {
+ table_engine_set= TRUE;
+ table_engine= info->db_type;
+ /* if partition_hton, use thd->lex->create_info */
+ if (table_engine == partition_hton)
+ table_engine= thd->lex->create_info.db_type;
+ DBUG_ASSERT(table_engine != partition_hton);
+ DBUG_PRINT("info", ("Using table_engine = %s",
+ ha_resolve_storage_engine_name(table_engine)));
+ }
+ else
+ {
+ table_engine_set= FALSE;
+ if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
+ {
+ table_engine_set= TRUE;
+ DBUG_PRINT("info", ("No create, table_engine = %s",
+ ha_resolve_storage_engine_name(table_engine)));
+ DBUG_ASSERT(table_engine && table_engine != partition_hton);
+ }
+ }
+
if ((same_name= has_unique_names()))
{
my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name);
goto end;
}
- engine_array= (handlerton**)my_malloc(tot_partitions * sizeof(handlerton *),
- MYF(MY_WME));
- if (unlikely(!engine_array))
- goto end;
i= 0;
{
List_iterator<partition_element> part_it(partitions);
+ uint no_parts_not_set= 0;
+ uint prev_no_subparts_not_set= no_subparts + 1;
do
{
partition_element *part_elem= part_it++;
- if (part_elem->engine_type == NULL)
- part_elem->engine_type= default_engine_type;
- if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
- part_elem->data_file_name= part_elem->index_file_name= 0;
+#ifdef HAVE_READLINK
+ if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
+#endif
+ {
+ if (part_elem->data_file_name)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
+ "DATA DIRECTORY option ignored");
+ if (part_elem->index_file_name)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
+ "INDEX DIRECTORY option ignored");
+ part_elem->data_file_name= part_elem->index_file_name= NULL;
+ }
if (!is_sub_partitioned())
{
+ if (part_elem->engine_type == NULL)
+ {
+ no_parts_not_set++;
+ part_elem->engine_type= default_engine_type;
+ }
if (check_table_name(part_elem->partition_name,
strlen(part_elem->partition_name)))
{
my_error(ER_WRONG_PARTITION_NAME, MYF(0));
goto end;
}
- DBUG_PRINT("info", ("engine = %d",
- ha_legacy_type(part_elem->engine_type)));
- engine_array[part_count++]= part_elem->engine_type;
+ DBUG_PRINT("info", ("part = %d engine = %s",
+ i, ha_resolve_storage_engine_name(part_elem->engine_type)));
}
else
{
uint j= 0;
+ uint no_subparts_not_set= 0;
List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *sub_elem;
do
{
- partition_element *sub_elem= sub_it++;
+ sub_elem= sub_it++;
if (check_table_name(sub_elem->partition_name,
strlen(sub_elem->partition_name)))
{
@@ -822,19 +995,65 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
goto end;
}
if (sub_elem->engine_type == NULL)
- sub_elem->engine_type= default_engine_type;
- DBUG_PRINT("info", ("engine = %u",
- ha_legacy_type(sub_elem->engine_type)));
- engine_array[part_count++]= sub_elem->engine_type;
+ {
+ if (part_elem->engine_type != NULL)
+ sub_elem->engine_type= part_elem->engine_type;
+ else
+ {
+ sub_elem->engine_type= default_engine_type;
+ no_subparts_not_set++;
+ }
+ }
+ DBUG_PRINT("info", ("part = %d sub = %d engine = %s", i, j,
+ ha_resolve_storage_engine_name(sub_elem->engine_type)));
} while (++j < no_subparts);
+
+ if (prev_no_subparts_not_set == (no_subparts + 1) &&
+ (no_subparts_not_set == 0 || no_subparts_not_set == no_subparts))
+ prev_no_subparts_not_set= no_subparts_not_set;
+
+ if (!table_engine_set &&
+ prev_no_subparts_not_set != no_subparts_not_set)
+ {
+ DBUG_PRINT("info", ("no_subparts_not_set = %u no_subparts = %u",
+ no_subparts_not_set, no_subparts));
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ goto end;
+ }
+
+ if (part_elem->engine_type == NULL)
+ {
+ if (no_subparts_not_set == 0)
+ part_elem->engine_type= sub_elem->engine_type;
+ else
+ {
+ no_parts_not_set++;
+ part_elem->engine_type= default_engine_type;
+ }
+ }
}
} while (++i < no_parts);
+ if (!table_engine_set &&
+ no_parts_not_set != 0 &&
+ no_parts_not_set != no_parts)
+ {
+ DBUG_PRINT("info", ("no_parts_not_set = %u no_parts = %u",
+ no_parts_not_set, no_subparts));
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ goto end;
+ }
}
- if (unlikely(partition_info::check_engine_mix(engine_array, part_count)))
+ if (unlikely(check_engine_mix(table_engine, table_engine_set)))
+ {
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
goto end;
+ }
+ DBUG_ASSERT(table_engine != partition_hton &&
+ default_engine_type == table_engine);
if (eng_type)
- *eng_type= (handlerton*)engine_array[0];
+ *eng_type= table_engine;
+
/*
We need to check all constant expressions that they are of the correct
@@ -850,7 +1069,6 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
}
result= FALSE;
end:
- my_free((char*)engine_array,MYF(MY_ALLOW_ZERO_PTR));
DBUG_RETURN(result);
}
@@ -1045,4 +1263,60 @@ error:
mem_alloc_error(size);
DBUG_RETURN(TRUE);
}
+
+
+/*
+ Check if path does not contain mysql data home directory
+ for partition elements with data directory and index directory
+
+ SYNOPSIS
+ check_partition_dirs()
+ part_info partition_info struct
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+bool check_partition_dirs(partition_info *part_info)
+{
+ if (!part_info)
+ return 0;
+
+ partition_element *part_elem;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ while ((part_elem= part_it++))
+ {
+ if (part_elem->subpartitions.elements)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *subpart_elem;
+ while ((subpart_elem= sub_it++))
+ {
+ if (test_if_data_home_dir(subpart_elem->data_file_name))
+ goto dd_err;
+ if (test_if_data_home_dir(subpart_elem->index_file_name))
+ goto id_err;
+ }
+ }
+ else
+ {
+ if (test_if_data_home_dir(part_elem->data_file_name))
+ goto dd_err;
+ if (test_if_data_home_dir(part_elem->index_file_name))
+ goto id_err;
+ }
+ }
+ return 0;
+
+dd_err:
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"DATA DIRECTORY");
+ return 1;
+
+id_err:
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"INDEX DIRECTORY");
+ return 1;
+}
+
+
#endif /* WITH_PARTITION_STORAGE_ENGINE */
diff --git a/sql/partition_info.h b/sql/partition_info.h
index b7d13e188f3..2af7fa1717c 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -269,7 +269,7 @@ public:
bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info,
uint start_no);
char *has_unique_names();
- static bool check_engine_mix(handlerton **engine_array, uint no_parts);
+ bool check_engine_mix(handlerton *engine_type, bool default_engine);
bool check_range_constants();
bool check_list_constants();
bool check_partition_info(THD *thd, handlerton **eng_type,
@@ -290,6 +290,7 @@ private:
};
uint32 get_next_partition_id_range(struct st_partition_iter* part_iter);
+bool check_partition_dirs(partition_info *part_info);
/* Initialize the iterator to return a single partition with given part_id */
diff --git a/sql/procedure.h b/sql/procedure.h
index 4578fcb87f1..ceb586766b1 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -144,7 +144,7 @@ public:
virtual int send_row(List<Item> &fields)=0;
virtual bool change_columns(List<Item> &fields)=0;
virtual void update_refs(void) {}
- virtual bool end_of_records() { return 0; }
+ virtual int end_of_records() { return 0; }
};
Procedure *setup_procedure(THD *thd,ORDER *proc_param,select_result *result,
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 9e1b3c65538..5fe56724d08 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -112,8 +112,6 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
- message : Stored as packed length (1-9 bytes) + message.
Is not stored if no message.
- If net->no_send_ok return without sending packet.
-
@param thd Thread handler
@param affected_rows Number of rows changed by statement
@param id Auto_increment id for first row (if used)
@@ -128,7 +126,7 @@ net_send_ok(THD *thd,
{
NET *net= &thd->net;
uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
- DBUG_ENTER("send_ok");
+ DBUG_ENTER("my_ok");
if (! net->vio) // hack for re-parsing queries
{
@@ -425,8 +423,8 @@ void net_end_statement(THD *thd)
/****************************************************************************
- Functions used by the protocol functions (like send_ok) to store strings
- and numbers in the header result packet.
+ Functions used by the protocol functions (like net_send_ok) to store
+ strings and numbers in the header result packet.
****************************************************************************/
/* The following will only be used for short strings < 65K */
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 589ee8b2605..d7e783f534f 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -118,11 +118,14 @@ void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status)
}
-#define get_object(p, obj) \
+#define get_object(p, obj, msg) \
{\
uint len = (uint)*p++; \
if (p + len > p_end || len >= sizeof(obj)) \
+ {\
+ errmsg= msg;\
goto err; \
+ }\
strmake(obj,(char*) p,len); \
p+= len; \
}\
@@ -168,6 +171,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
int res;
SLAVE_INFO *si;
uchar *p= packet, *p_end= packet + packet_length;
+ const char *errmsg= "Wrong parameters to function register_slave";
if (check_access(thd, REPL_SLAVE_ACL, any_db,0,0,0,0))
return 1;
@@ -176,9 +180,9 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
thd->server_id= si->server_id= uint4korr(p);
p+= 4;
- get_object(p,si->host);
- get_object(p,si->user);
- get_object(p,si->password);
+ get_object(p,si->host, "Failed to register slave: too long 'report-host'");
+ get_object(p,si->user, "Failed to register slave: too long 'report-user'");
+ get_object(p,si->password, "Failed to register slave; too long 'report-password'");
if (p+10 > p_end)
goto err;
si->port= uint2korr(p);
@@ -197,8 +201,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
err:
my_free(si, MYF(MY_WME));
- my_message(ER_UNKNOWN_ERROR, "Wrong parameters to function register_slave",
- MYF(0));
+ my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
err2:
return 1;
}
@@ -475,7 +478,7 @@ bool show_new_master(THD* thd)
protocol->store((ulonglong) lex_mi->pos);
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
}
@@ -688,7 +691,7 @@ bool show_slave_hosts(THD* thd)
}
}
pthread_mutex_unlock(&LOCK_slave_list);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -699,7 +702,7 @@ int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi)
if (!mi->host || !*mi->host) /* empty host */
{
- strmov(mysql->net.client_last_error, "Master is not configured");
+ strmov(mysql->net.last_error, "Master is not configured");
DBUG_RETURN(1);
}
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
@@ -1011,7 +1014,7 @@ err:
mysql_close(&mysql); // safe to call since we always do mysql_init()
if (!error)
- send_ok(thd);
+ my_ok(thd);
return error;
}
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index 781a5c215d5..5132f51bef6 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -63,6 +63,26 @@ int injector::transaction::commit()
{
DBUG_ENTER("injector::transaction::commit()");
m_thd->binlog_flush_pending_rows_event(true);
+ /*
+ Cluster replication does not preserve statement or
+ transaction boundaries of the master. Instead, a new
+ transaction on replication slave is started when a new GCI
+ (global checkpoint identifier) is issued, and is committed
+ when the last event of the check point has been received and
+ processed. This ensures consistency of each cluster in
+ cluster replication, and there is no requirement for stronger
+ consistency: MySQL replication is asynchronous with other
+ engines as well.
+
+ A practical consequence of that is that row level replication
+ stream passed through the injector thread never contains
+ COMMIT events.
+ Here we should preserve the server invariant that there is no
+ outstanding statement transaction when the normal transaction
+ is committed by committing the statement transaction
+ explicitly.
+ */
+ ha_autocommit_or_rollback(m_thd, 0);
end_trans(m_thd, COMMIT);
DBUG_RETURN(0);
}
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 1165164fd88..5e46837e948 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -29,9 +29,9 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
Master_info::Master_info()
:Slave_reporting_capability("I/O"),
- ssl(0), fd(-1), io_thd(0), inited(0),
+ ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), inited(0),
abort_slave(0),slave_running(0),
- ssl_verify_server_cert(0), slave_run_id(0)
+ slave_run_id(0)
{
host[0] = 0; user[0] = 0; password[0] = 0;
ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 03f790b934f..b6dfd1a1dc8 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -33,12 +33,12 @@ Relay_log_info::Relay_log_info()
:Slave_reporting_capability("SQL"),
no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id),
info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
- group_relay_log_pos(0), event_relay_log_pos(0),
#if HAVE_purify
is_fake(FALSE),
#endif
- cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0),
- ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0),
+ cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0),
+ group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0),
+ last_master_timestamp(0), slave_skip_counter(0),
abort_pos_wait(0), slave_run_id(0), sql_thd(0),
inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
until_log_pos(0), retried_trans(0),
@@ -615,7 +615,7 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
DBUG_ENTER("Relay_log_info::wait_for_pos");
if (!inited)
- DBUG_RETURN(-1);
+ DBUG_RETURN(-2);
DBUG_PRINT("enter",("log_name: '%s' log_pos: %lu timeout: %lu",
log_name->c_ptr(), (ulong) log_pos, (ulong) timeout));
@@ -955,6 +955,11 @@ err:
Check if condition stated in UNTIL clause of START SLAVE is reached.
SYNOPSYS
Relay_log_info::is_until_satisfied()
+ master_beg_pos position of the beginning of to be executed event
+ (not log_pos member of the event that points to the
+ beginning of the following event)
+
+
DESCRIPTION
Checks if UNTIL condition is reached. Uses caching result of last
comparison of current log file name and target log file name. So cached
@@ -979,7 +984,7 @@ err:
false - condition not met
*/
-bool Relay_log_info::is_until_satisfied()
+bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos)
{
const char *log_name;
ulonglong log_pos;
@@ -990,7 +995,7 @@ bool Relay_log_info::is_until_satisfied()
if (until_condition == UNTIL_MASTER_POS)
{
log_name= group_master_log_name;
- log_pos= group_master_log_pos;
+ log_pos= master_beg_pos;
}
else
{ /* until_condition == UNTIL_RELAY_POS */
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 36daffae1af..e13ea93842c 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -296,7 +296,7 @@ public:
void close_temporary_tables();
/* Check if UNTIL condition is satisfied. See slave.cc for more. */
- bool is_until_satisfied();
+ bool is_until_satisfied(my_off_t master_beg_pos);
inline ulonglong until_pos()
{
return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 86bda15b15d..8b73855f40a 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -137,7 +137,8 @@ static void fix_trans_mem_root(THD *thd, enum_var_type type);
static void fix_server_id(THD *thd, enum_var_type type);
static ulonglong fix_unsigned(THD *, ulonglong, const struct my_option *);
static bool get_unsigned(THD *thd, set_var *var);
-static void throw_bounds_warning(THD *thd, const char *name, ulonglong num);
+bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
+ const char *name, longlong val);
static KEY_CACHE *create_key_cache(const char *name, uint length);
void fix_sql_mode_var(THD *thd, enum_var_type type);
static uchar *get_error_count(THD *thd);
@@ -161,10 +162,14 @@ static void sys_default_slow_log_path(THD *thd, enum_var_type type);
static sys_var_chain vars = { NULL, NULL };
-static sys_var_thd_ulong sys_auto_increment_increment(&vars, "auto_increment_increment",
- &SV::auto_increment_increment);
-static sys_var_thd_ulong sys_auto_increment_offset(&vars, "auto_increment_offset",
- &SV::auto_increment_offset);
+static sys_var_thd_ulong
+sys_auto_increment_increment(&vars, "auto_increment_increment",
+ &SV::auto_increment_increment, NULL, NULL,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_thd_ulong
+sys_auto_increment_offset(&vars, "auto_increment_offset",
+ &SV::auto_increment_offset, NULL, NULL,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_bool_ptr sys_automatic_sp_privileges(&vars, "automatic_sp_privileges",
&sp_automatic_privileges);
@@ -176,19 +181,25 @@ static sys_var_thd_binlog_format sys_binlog_format(&vars, "binlog_format",
&SV::binlog_format);
static sys_var_thd_ulong sys_bulk_insert_buff_size(&vars, "bulk_insert_buffer_size",
&SV::bulk_insert_buff_size);
-static sys_var_character_set_sv sys_character_set_server(&vars, "character_set_server",
- &SV::collation_server,
- &default_charset_info);
+static sys_var_character_set_sv
+sys_character_set_server(&vars, "character_set_server",
+ &SV::collation_server, &default_charset_info, 0,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
sys_var_const_str sys_charset_system(&vars, "character_set_system",
(char *)my_charset_utf8_general_ci.name);
-static sys_var_character_set_database sys_character_set_database(&vars, "character_set_database");
-static sys_var_character_set_client sys_character_set_client(&vars,
- "character_set_client",
- &SV::character_set_client,
- &default_charset_info);
-static sys_var_character_set_sv sys_character_set_connection(&vars, "character_set_connection",
- &SV::collation_connection,
- &default_charset_info);
+static sys_var_character_set_database
+sys_character_set_database(&vars, "character_set_database",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_character_set_client
+sys_character_set_client(&vars, "character_set_client",
+ &SV::character_set_client,
+ &default_charset_info,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_character_set_sv
+sys_character_set_connection(&vars, "character_set_connection",
+ &SV::collation_connection,
+ &default_charset_info, 0,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_character_set_sv sys_character_set_results(&vars, "character_set_results",
&SV::character_set_results,
&default_charset_info, true);
@@ -199,15 +210,18 @@ static sys_var_thd_ulong sys_completion_type(&vars, "completion_type",
&SV::completion_type,
check_completion_type,
fix_completion_type);
-static sys_var_collation_sv sys_collation_connection(&vars, "collation_connection",
- &SV::collation_connection,
- &default_charset_info);
-static sys_var_collation_sv sys_collation_database(&vars, "collation_database",
- &SV::collation_database,
- &default_charset_info);
-static sys_var_collation_sv sys_collation_server(&vars, "collation_server",
- &SV::collation_server,
- &default_charset_info);
+static sys_var_collation_sv
+sys_collation_connection(&vars, "collation_connection",
+ &SV::collation_connection, &default_charset_info,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_collation_sv
+sys_collation_database(&vars, "collation_database", &SV::collation_database,
+ &default_charset_info,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_collation_sv
+sys_collation_server(&vars, "collation_server", &SV::collation_server,
+ &default_charset_info,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_long_ptr sys_concurrent_insert(&vars, "concurrent_insert",
&myisam_concurrent_insert);
static sys_var_long_ptr sys_connect_timeout(&vars, "connect_timeout",
@@ -303,9 +317,10 @@ static sys_var_thd_ulong sys_max_error_count(&vars, "max_error_count",
&SV::max_error_count);
static sys_var_thd_ulonglong sys_max_heap_table_size(&vars, "max_heap_table_size",
&SV::max_heap_table_size);
-static sys_var_thd_ulong sys_pseudo_thread_id(&vars, "pseudo_thread_id",
- &SV::pseudo_thread_id,
- check_pseudo_thread_id, 0);
+static sys_var_thd_ulong sys_pseudo_thread_id(&vars, "pseudo_thread_id",
+ &SV::pseudo_thread_id,
+ check_pseudo_thread_id, 0,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_thd_ha_rows sys_max_join_size(&vars, "max_join_size",
&SV::max_join_size,
fix_max_join_size);
@@ -436,8 +451,14 @@ static sys_var_long_ptr sys_slow_launch_time(&vars, "slow_launch_time",
&slow_launch_time);
static sys_var_thd_ulong sys_sort_buffer(&vars, "sort_buffer_size",
&SV::sortbuff_size);
+/*
+ sql_mode should *not* have binlog_mode=SESSION_VARIABLE_IN_BINLOG:
+ even though it is written to the binlog, the slave ignores the
+ MODE_NO_DIR_IN_CREATE variable, so slave's value differs from
+ master's (see log_event.cc: Query_log_event::do_apply_event()).
+*/
static sys_var_thd_sql_mode sys_sql_mode(&vars, "sql_mode",
- &SV::sql_mode);
+ &SV::sql_mode);
#ifdef HAVE_OPENSSL
extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
*opt_ssl_key;
@@ -588,7 +609,8 @@ static sys_var_thd_bit sys_sql_notes(&vars, "sql_notes", 0,
OPTION_SQL_NOTES);
static sys_var_thd_bit sys_auto_is_null(&vars, "sql_auto_is_null", 0,
set_option_bit,
- OPTION_AUTO_IS_NULL);
+ OPTION_AUTO_IS_NULL, 0,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_thd_bit sys_safe_updates(&vars, "sql_safe_updates", 0,
set_option_bit,
OPTION_SAFE_UPDATES);
@@ -601,11 +623,12 @@ static sys_var_thd_bit sys_quote_show_create(&vars, "sql_quote_show_create", 0,
static sys_var_thd_bit sys_foreign_key_checks(&vars, "foreign_key_checks", 0,
set_option_bit,
OPTION_NO_FOREIGN_KEY_CHECKS,
- 1);
+ 1, sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_thd_bit sys_unique_checks(&vars, "unique_checks", 0,
set_option_bit,
OPTION_RELAXED_UNIQUE_CHECKS,
- 1);
+ 1,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
static sys_var_thd_bit sys_profiling(&vars, "profiling", NULL,
set_option_bit,
@@ -618,13 +641,41 @@ static sys_var_thd_ulong sys_profiling_history_size(&vars, "profiling_history_si
static sys_var_thd_ha_rows sys_select_limit(&vars, "sql_select_limit",
&SV::select_limit);
-static sys_var_timestamp sys_timestamp(&vars, "timestamp");
-static sys_var_last_insert_id sys_last_insert_id(&vars, "last_insert_id");
-static sys_var_last_insert_id sys_identity(&vars, "identity");
+static sys_var_timestamp sys_timestamp(&vars, "timestamp",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_last_insert_id
+sys_last_insert_id(&vars, "last_insert_id",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+/*
+ identity is an alias for last_insert_id(), so that we are compatible
+ with Sybase
+*/
+static sys_var_last_insert_id
+sys_identity(&vars, "identity", sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_lc_time_names sys_lc_time_names(&vars, "lc_time_names");
+static sys_var_thd_lc_time_names
+sys_lc_time_names(&vars, "lc_time_names", sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_insert_id sys_insert_id(&vars, "insert_id");
+/*
+ insert_id should *not* be marked as written to the binlog (i.e., it
+ should *not* have binlog_status==SESSION_VARIABLE_IN_BINLOG),
+ because we want any statement that refers to insert_id explicitly to
+ be unsafe. (By "explicitly", we mean using @@session.insert_id,
+ whereas insert_id is used "implicitly" when NULL value is inserted
+ into an auto_increment column).
+
+ We want statements referring explicitly to @@session.insert_id to be
+ unsafe, because insert_id is modified internally by the slave sql
+ thread when NULL values are inserted in an AUTO_INCREMENT column.
+ This modification interfers with the value of the
+ @@session.insert_id variable if @@session.insert_id is referred
+ explicitly by an insert statement (as is seen by executing "SET
+ @@session.insert_id=0; CREATE TABLE t (a INT, b INT KEY
+ AUTO_INCREMENT); INSERT INTO t(a) VALUES (@@session.insert_id);" in
+ statement-based logging mode: t will be different on master and
+ slave).
+*/
+static sys_var_insert_id sys_insert_id(&vars, "insert_id");
static sys_var_readonly sys_error_count(&vars, "error_count",
OPT_SESSION,
SHOW_LONG,
@@ -634,9 +685,10 @@ static sys_var_readonly sys_warning_count(&vars, "warning_count",
SHOW_LONG,
get_warning_count);
-/* alias for last_insert_id() to be compatible with Sybase */
-static sys_var_rand_seed1 sys_rand_seed1(&vars, "rand_seed1");
-static sys_var_rand_seed2 sys_rand_seed2(&vars, "rand_seed2");
+static sys_var_rand_seed1 sys_rand_seed1(&vars, "rand_seed1",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_rand_seed2 sys_rand_seed2(&vars, "rand_seed2",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
static sys_var_thd_ulong sys_default_week_format(&vars, "default_week_format",
&SV::default_week_format);
@@ -644,11 +696,27 @@ static sys_var_thd_ulong sys_default_week_format(&vars, "default_week_for
sys_var_thd_ulong sys_group_concat_max_len(&vars, "group_concat_max_len",
&SV::group_concat_max_len);
-sys_var_thd_time_zone sys_time_zone(&vars, "time_zone");
+sys_var_thd_time_zone sys_time_zone(&vars, "time_zone",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
/* Global read-only variable containing hostname */
static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname);
+#ifndef EMBEDDED_LIBRARY
+static sys_var_const_str_ptr sys_repl_report_host(&vars, "report_host", &report_host);
+static sys_var_const_str_ptr sys_repl_report_user(&vars, "report_user", &report_user);
+static sys_var_const_str_ptr sys_repl_report_password(&vars, "report_password", &report_password);
+
+static uchar *slave_get_report_port(THD *thd)
+{
+ thd->sys_var_tmp.long_value= report_port;
+ return (uchar*) &thd->sys_var_tmp.long_value;
+}
+
+static sys_var_readonly sys_repl_report_port(&vars, "report_port", OPT_GLOBAL, SHOW_INT, slave_get_report_port);
+
+#endif
+
sys_var_thd_bool sys_keep_files_on_create(&vars, "keep_files_on_create",
&SV::keep_files_on_create);
/* Read only variables */
@@ -1021,7 +1089,7 @@ bool sys_var_set::update(THD *thd, set_var *var)
{
*value= var->save_result.ulong_value;
return 0;
-};
+}
uchar *sys_var_set::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
@@ -1208,13 +1276,29 @@ static void fix_server_id(THD *thd, enum_var_type type)
}
-static void throw_bounds_warning(THD *thd, const char *name, ulonglong num)
+bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
+ const char *name, longlong val)
{
- char buf[22];
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER(ER_TRUNCATED_WRONG_VALUE), name,
- ullstr(num, buf));
+ if (fixed)
+ {
+ char buf[22];
+
+ if (unsignd)
+ ullstr((ulonglong) val, buf);
+ else
+ llstr(val, buf);
+
+ if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
+ {
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
+ return TRUE;
+ }
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE,
+ ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
+ }
+ return FALSE;
}
static ulonglong fix_unsigned(THD *thd, ulonglong num,
@@ -1223,8 +1307,7 @@ static ulonglong fix_unsigned(THD *thd, ulonglong num,
my_bool fixed= FALSE;
ulonglong out= getopt_ull_limit_value(num, option_limits, &fixed);
- if (fixed)
- throw_bounds_warning(thd, option_limits->name, num);
+ throw_bounds_warning(thd, fixed, TRUE, option_limits->name, (longlong) num);
return out;
}
@@ -1267,7 +1350,8 @@ bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
if (tmp > ULONG_MAX)
{
tmp= ULONG_MAX;
- throw_bounds_warning(thd, name, var->save_result.ulonglong_value);
+ throw_bounds_warning(thd, TRUE, TRUE, name,
+ (longlong) var->save_result.ulonglong_value);
}
#endif
*value= (ulong) tmp;
@@ -1355,7 +1439,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var)
/* Don't use bigger value than given with --maximum-variable-name=.. */
if ((ulong) tmp > max_system_variables.*offset)
{
- throw_bounds_warning(thd, name, tmp);
+ throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp);
tmp= max_system_variables.*offset;
}
@@ -1365,7 +1449,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var)
else if (tmp > ULONG_MAX)
{
tmp= ULONG_MAX;
- throw_bounds_warning(thd, name, var->save_result.ulonglong_value);
+ throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) var->save_result.ulonglong_value);
}
#endif
@@ -3925,10 +4009,8 @@ bool sys_var_thd_dbug::update(THD *thd, set_var *var)
if (var->type == OPT_GLOBAL)
DBUG_SET_INITIAL(var ? var->value->str_value.c_ptr() : "");
else
- {
- DBUG_POP();
- DBUG_PUSH(var ? var->value->str_value.c_ptr() : "");
- }
+ DBUG_SET(var ? var->value->str_value.c_ptr() : "");
+
return 0;
}
diff --git a/sql/set_var.h b/sql/set_var.h
index 634bb67760e..b33a3a968bb 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -48,6 +48,22 @@ struct sys_var_chain
class sys_var
{
public:
+
+ /**
+ Enumeration type to indicate for a system variable whether it will be written to the binlog or not.
+ */
+ enum Binlog_status_enum
+ {
+ /* The variable value is not in the binlog. */
+ NOT_IN_BINLOG,
+ /* The value of the @@session variable is in the binlog. */
+ SESSION_VARIABLE_IN_BINLOG
+ /*
+ Currently, no @@global variable is ever in the binlog, so we
+ don't need an enumeration value for that.
+ */
+ };
+
sys_var *next;
struct my_option *option_limits; /* Updated by by set_var_init() */
uint name_length; /* Updated by by set_var_init() */
@@ -55,9 +71,10 @@ public:
sys_after_update_func after_update;
bool no_support_one_shot;
- sys_var(const char *name_arg,sys_after_update_func func= NULL)
- :name(name_arg), after_update(func)
- , no_support_one_shot(1)
+ sys_var(const char *name_arg, sys_after_update_func func= NULL,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :name(name_arg), after_update(func), no_support_one_shot(1),
+ binlog_status(binlog_status_arg)
{}
virtual ~sys_var() {}
void chain_sys_var(sys_var_chain *chain_arg)
@@ -71,6 +88,11 @@ public:
virtual bool check(THD *thd, set_var *var);
bool check_enum(THD *thd, set_var *var, const TYPELIB *enum_names);
bool check_set(THD *thd, set_var *var, TYPELIB *enum_names);
+ bool is_written_to_binlog(enum_var_type type)
+ {
+ return (type == OPT_SESSION || type == OPT_DEFAULT) &&
+ (binlog_status == SESSION_VARIABLE_IN_BINLOG);
+ }
virtual bool update(THD *thd, set_var *var)=0;
virtual void set_default(THD *thd_arg, enum_var_type type) {}
virtual SHOW_TYPE show_type() { return SHOW_UNDEF; }
@@ -86,6 +108,9 @@ public:
virtual bool is_struct() { return 0; }
virtual bool is_readonly() const { return 0; }
virtual sys_var_pluginvar *cast_pluginvar() { return 0; }
+
+private:
+ const Binlog_status_enum binlog_status;
};
@@ -232,8 +257,9 @@ class sys_var_const_str :public sys_var
{
public:
char *value; // Pointer to const value
- sys_var_const_str(sys_var_chain *chain, const char *name_arg, const char *value_arg)
- :sys_var(name_arg),value((char*) value_arg)
+ sys_var_const_str(sys_var_chain *chain, const char *name_arg,
+ const char *value_arg)
+ :sys_var(name_arg), value((char*) value_arg)
{ chain_sys_var(chain); }
bool check(THD *thd, set_var *var)
{
@@ -328,8 +354,9 @@ class sys_var_thd :public sys_var
{
public:
sys_var_thd(const char *name_arg,
- sys_after_update_func func= NULL)
- :sys_var(name_arg,func)
+ sys_after_update_func func= NULL,
+ Binlog_status_enum binlog_status= NOT_IN_BINLOG)
+ :sys_var(name_arg, func, binlog_status)
{}
bool check_type(enum_var_type type) { return 0; }
bool check_default(enum_var_type type)
@@ -344,12 +371,13 @@ class sys_var_thd_ulong :public sys_var_thd
sys_check_func check_func;
public:
ulong SV::*offset;
- sys_var_thd_ulong(sys_var_chain *chain, const char *name_arg, ulong SV::*offset_arg)
- :sys_var_thd(name_arg), check_func(0), offset(offset_arg)
- { chain_sys_var(chain); }
- sys_var_thd_ulong(sys_var_chain *chain, const char *name_arg, ulong SV::*offset_arg,
- sys_check_func c_func, sys_after_update_func au_func)
- :sys_var_thd(name_arg,au_func), check_func(c_func), offset(offset_arg)
+ sys_var_thd_ulong(sys_var_chain *chain, const char *name_arg,
+ ulong SV::*offset_arg,
+ sys_check_func c_func= NULL,
+ sys_after_update_func au_func= NULL,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, au_func, binlog_status_arg), check_func(c_func),
+ offset(offset_arg)
{ chain_sys_var(chain); }
bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
@@ -440,22 +468,12 @@ protected:
TYPELIB *enum_names;
sys_check_func check_func;
public:
- sys_var_thd_enum(sys_var_chain *chain, const char *name_arg, ulong SV::*offset_arg,
- TYPELIB *typelib)
- :sys_var_thd(name_arg), offset(offset_arg), enum_names(typelib),
- check_func(0)
- { chain_sys_var(chain); }
- sys_var_thd_enum(sys_var_chain *chain, const char *name_arg, ulong SV::*offset_arg,
- TYPELIB *typelib,
- sys_after_update_func func)
- :sys_var_thd(name_arg,func), offset(offset_arg), enum_names(typelib),
- check_func(0)
- { chain_sys_var(chain); }
- sys_var_thd_enum(sys_var_chain *chain, const char *name_arg, ulong SV::*offset_arg,
- TYPELIB *typelib, sys_after_update_func func,
- sys_check_func check_arg)
- :sys_var_thd(name_arg,func), offset(offset_arg), enum_names(typelib),
- check_func(check_arg)
+ sys_var_thd_enum(sys_var_chain *chain, const char *name_arg,
+ ulong SV::*offset_arg, TYPELIB *typelib,
+ sys_after_update_func func= NULL,
+ sys_check_func check= NULL)
+ :sys_var_thd(name_arg, func), offset(offset_arg),
+ enum_names(typelib), check_func(check)
{ chain_sys_var(chain); }
bool check(THD *thd, set_var *var)
{
@@ -480,7 +498,7 @@ public:
sys_var_thd_sql_mode(sys_var_chain *chain, const char *name_arg,
ulong SV::*offset_arg)
:sys_var_thd_enum(chain, name_arg, offset_arg, &sql_mode_typelib,
- fix_sql_mode_var)
+ fix_sql_mode_var)
{}
bool check(THD *thd, set_var *var)
{
@@ -534,9 +552,10 @@ public:
bool reverse;
sys_var_thd_bit(sys_var_chain *chain, const char *name_arg,
sys_check_func c_func, sys_update_func u_func,
- ulonglong bit, bool reverse_arg=0)
- :sys_var_thd(name_arg), check_func(c_func), update_func(u_func),
- bit_flag(bit), reverse(reverse_arg)
+ ulonglong bit, bool reverse_arg=0,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg), check_func(c_func),
+ update_func(u_func), bit_flag(bit), reverse(reverse_arg)
{ chain_sys_var(chain); }
bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
@@ -567,8 +586,9 @@ public:
class sys_var_timestamp :public sys_var
{
public:
- sys_var_timestamp(sys_var_chain *chain, const char *name_arg)
- :sys_var(name_arg)
+ sys_var_timestamp(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
{ chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
@@ -582,8 +602,9 @@ public:
class sys_var_last_insert_id :public sys_var
{
public:
- sys_var_last_insert_id(sys_var_chain *chain, const char *name_arg)
- :sys_var(name_arg)
+ sys_var_last_insert_id(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
{ chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
@@ -608,8 +629,9 @@ public:
class sys_var_rand_seed1 :public sys_var
{
public:
- sys_var_rand_seed1(sys_var_chain *chain, const char *name_arg)
- :sys_var(name_arg)
+ sys_var_rand_seed1(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
{ chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
@@ -618,8 +640,9 @@ public:
class sys_var_rand_seed2 :public sys_var
{
public:
- sys_var_rand_seed2(sys_var_chain *chain, const char *name_arg)
- :sys_var(name_arg)
+ sys_var_rand_seed2(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
{ chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
@@ -629,11 +652,12 @@ public:
class sys_var_collation :public sys_var_thd
{
public:
- sys_var_collation(const char *name_arg)
- :sys_var_thd(name_arg)
- {
+ sys_var_collation(const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg)
+ {
no_support_one_shot= 0;
- }
+ }
bool check(THD *thd, set_var *var);
SHOW_TYPE show_type() { return SHOW_CHAR; }
bool check_update_type(Item_result type)
@@ -648,8 +672,9 @@ class sys_var_character_set :public sys_var_thd
{
public:
bool nullable;
- sys_var_character_set(const char *name_arg, bool is_nullable= 0) :
- sys_var_thd(name_arg), nullable(is_nullable)
+ sys_var_character_set(const char *name_arg, bool is_nullable= 0,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg), nullable(is_nullable)
{
/*
In fact only almost all variables derived from sys_var_character_set
@@ -678,8 +703,9 @@ public:
sys_var_character_set_sv(sys_var_chain *chain, const char *name_arg,
CHARSET_INFO *SV::*offset_arg,
CHARSET_INFO **global_default_arg,
- bool is_nullable= 0)
- : sys_var_character_set(name_arg, is_nullable),
+ bool is_nullable= 0,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ : sys_var_character_set(name_arg, is_nullable, binlog_status_arg),
offset(offset_arg), global_default(global_default_arg)
{ chain_sys_var(chain); }
void set_default(THD *thd, enum_var_type type);
@@ -693,9 +719,9 @@ public:
sys_var_character_set_client(sys_var_chain *chain, const char *name_arg,
CHARSET_INFO *SV::*offset_arg,
CHARSET_INFO **global_default_arg,
- bool is_nullable= 0)
+ Binlog_status_enum binlog_status_arg)
: sys_var_character_set_sv(chain, name_arg, offset_arg, global_default_arg,
- is_nullable)
+ 0, binlog_status_arg)
{ }
bool check(THD *thd, set_var *var);
};
@@ -704,8 +730,10 @@ public:
class sys_var_character_set_database :public sys_var_character_set
{
public:
- sys_var_character_set_database(sys_var_chain *chain, const char *name_arg) :
- sys_var_character_set(name_arg)
+ sys_var_character_set_database(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg=
+ NOT_IN_BINLOG)
+ : sys_var_character_set(name_arg, 0, binlog_status_arg)
{ chain_sys_var(chain); }
void set_default(THD *thd, enum_var_type type);
CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
@@ -718,8 +746,9 @@ class sys_var_collation_sv :public sys_var_collation
public:
sys_var_collation_sv(sys_var_chain *chain, const char *name_arg,
CHARSET_INFO *SV::*offset_arg,
- CHARSET_INFO **global_default_arg)
- :sys_var_collation(name_arg),
+ CHARSET_INFO **global_default_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_collation(name_arg, binlog_status_arg),
offset(offset_arg), global_default(global_default_arg)
{
chain_sys_var(chain);
@@ -946,8 +975,9 @@ public:
class sys_var_thd_time_zone :public sys_var_thd
{
public:
- sys_var_thd_time_zone(sys_var_chain *chain, const char *name_arg):
- sys_var_thd(name_arg)
+ sys_var_thd_time_zone(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg)
{
no_support_one_shot= 0;
chain_sys_var(chain);
@@ -1034,8 +1064,9 @@ public:
class sys_var_thd_lc_time_names :public sys_var_thd
{
public:
- sys_var_thd_lc_time_names(sys_var_chain *chain, const char *name_arg):
- sys_var_thd(name_arg)
+ sys_var_thd_lc_time_names(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ : sys_var_thd(name_arg, NULL, binlog_status_arg)
{
#if MYSQL_VERSION_ID < 50000
no_support_one_shot= 0;
@@ -1079,9 +1110,8 @@ public:
sys_var_thd_binlog_format(sys_var_chain *chain, const char *name_arg,
ulong SV::*offset_arg)
:sys_var_thd_enum(chain, name_arg, offset_arg,
- &binlog_format_typelib
- , fix_binlog_format_after_update
- )
+ &binlog_format_typelib,
+ fix_binlog_format_after_update)
{};
bool is_readonly() const;
};
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index ee3a7e6080a..894e2094968 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -2146,14 +2146,14 @@ ER_WRONG_SUB_KEY
cze "Chybn-Bá podèást klíèe -- není to øetìzec nebo je del¹í ne¾ délka èásti klíèe"
dan "Forkert indeksdel. Den anvendte nøgledel er ikke en streng eller længden er større end nøglelængden"
nla "Foutief sub-gedeelte van de zoeksleutel. De gebruikte zoeksleutel is geen onderdeel van een string of of de gebruikte lengte is langer dan de zoeksleutel"
- eng "Incorrect sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys"
+ eng "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys"
est "Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid"
fre "Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur dépasse celle définie dans la clef"
ger "Falscher Unterteilschlüssel. Der verwendete Schlüsselteil ist entweder kein String, die verwendete Länge ist länger als der Teilschlüssel oder die Speicher-Engine unterstützt keine Unterteilschlüssel"
greek "ÅóöáëìÝíï sub part key. Ôï ÷ñçóéìïðïéïýìåíï key part äåí åßíáé string Þ ôï ìÞêïò ôïõ åßíáé ìåãáëýôåñï"
hun "Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz"
ita "Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave."
- jpn "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part"
+ jpn "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part"
kor "ºÎÁ¤È®ÇÑ ¼­¹ö ÆÄÆ® Å°. »ç¿ëµÈ Å° ÆÄÆ®°¡ ½ºÆ®¸µÀÌ ¾Æ´Ï°Å³ª Å° ÆÄÆ®ÀÇ ±æÀÌ°¡ ³Ê¹« ±é´Ï´Ù."
nor "Feil delnøkkel. Den brukte delnøkkelen er ikke en streng eller den oppgitte lengde er lengre enn nøkkel lengden"
norwegian-ny "Feil delnykkel. Den brukte delnykkelen er ikkje ein streng eller den oppgitte lengda er lengre enn nykkellengden"
@@ -2162,7 +2162,7 @@ ER_WRONG_SUB_KEY
rum "Componentul cheii este incorrect. Componentul folosit al cheii nu este un sir sau lungimea folosita este mai lunga decit lungimea cheii"
rus "îÅËÏÒÒÅËÔÎÁÑ ÞÁÓÔØ ËÌÀÞÁ. éÓÐÏÌØÚÕÅÍÁÑ ÞÁÓÔØ ËÌÀÞÁ ÎÅ Ñ×ÌÑÅÔÓÑ ÓÔÒÏËÏÊ, ÕËÁÚÁÎÎÁÑ ÄÌÉÎÁ ÂÏÌØÛÅ, ÞÅÍ ÄÌÉÎÁ ÞÁÓÔÉ ËÌÀÞÁ, ÉÌÉ ÏÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÕÎÉËÁÌØÎÙÅ ÞÁÓÔÉ ËÌÀÞÁ"
serbian "Pogrešan pod-kljuè dela kljuèa. Upotrebljeni deo kljuèa nije string, upotrebljena dužina je veæa od dela kljuèa ili handler tabela ne podržava jedinstvene pod-kljuèeve"
- slo "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part"
+ slo "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part"
spa "Parte de la clave es erronea. Una parte de la clave no es una cadena o la longitud usada es tan grande como la parte de la clave"
swe "Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden"
ukr "îÅצÒÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ. ÷ÉËÏÒÉÓÔÁÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ ÎÅ ¤ ÓÔÒÏËÏÀ, ÚÁÄÏ×ÇÁ ÁÂÏ ×ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ ÕΦËÁÌØÎÉÈ ÞÁÓÔÉÎ ËÌÀÞÅÊ"
@@ -5523,6 +5523,7 @@ ER_M_BIGGER_THAN_D 42000 S1009
ger "Für FLOAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.192s')"
ER_WRONG_LOCK_OF_SYSTEM_TABLE
eng "You can't combine write-locking of system tables with other tables or lock types"
+ ger "Sie können Schreibsperren auf der Systemtabelle nicht mit anderen Tabellen kombinieren"
ER_CONNECT_TO_FOREIGN_DATA_SOURCE
eng "Unable to connect to foreign data source: %.64s"
ger "Kann nicht mit Fremddatenquelle verbinden: %.64s"
@@ -5584,8 +5585,7 @@ ER_VIEW_OTHER_USER
eng "You need the SUPER privilege for creation view with '%-.192s'@'%-.192s' definer"
ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.192s'@'%-.192s' zu erzeugen"
ER_NO_SUCH_USER
- eng "There is no '%-.64s'@'%-.64s' registered"
- ger "'%-.64s'@'%-.64s' ist nicht registriert"
+ eng "The user specified as a definer ('%-.64s'@'%-.64s') does not exist"
ER_FORBID_SCHEMA_CHANGE
eng "Changing schema from '%-.192s' to '%-.192s' is not allowed."
ger "Wechsel des Schemas von '%-.192s' auf '%-.192s' ist nicht erlaubt"
@@ -6121,3 +6121,8 @@ ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT
eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement."
ER_SLAVE_CORRUPT_EVENT
eng "Corrupted replication event was detected"
+ER_LOAD_DATA_INVALID_COLUMN
+ eng "Invalid column reference (%-.64s) in LOAD DATA"
+
+ER_LOG_PURGE_NO_FILE
+ eng "Being purged log %s was not found"
diff --git a/sql/slave.cc b/sql/slave.cc
index ea0dde942da..dcc808625c0 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -415,7 +415,20 @@ terminate_slave_thread(THD *thd,
while (*slave_running) // Should always be true
{
DBUG_PRINT("loop", ("killing slave thread"));
- KICK_SLAVE(thd);
+
+ pthread_mutex_lock(&thd->LOCK_delete);
+#ifndef DONT_USE_THR_ALARM
+ /*
+ Error codes from pthread_kill are:
+ EINVAL: invalid signal number (can't happen)
+ ESRCH: thread already killed (can happen, should be ignored)
+ */
+ IF_DBUG(int err= ) pthread_kill(thd->real_id, thr_client_alarm);
+ DBUG_ASSERT(err != EINVAL);
+#endif
+ thd->awake(THD::NOT_KILLED);
+ pthread_mutex_unlock(&thd->LOCK_delete);
+
/*
There is a small chance that slave thread might miss the first
alarm. To protect againts it, resend the signal until it reacts
@@ -747,7 +760,11 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
{
+ char err_buff[MAX_SLAVE_ERRMSG];
const char* errmsg= 0;
+ int err_code= 0;
+ MYSQL_RES *master_res= 0;
+ MYSQL_ROW master_row;
DBUG_ENTER("get_master_version_and_clock");
/*
@@ -758,7 +775,11 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
mi->rli.relay_log.description_event_for_queue= 0;
if (!my_isdigit(&my_charset_bin,*mysql->server_version))
+ {
errmsg = "Master reported unrecognized MySQL version";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
else
{
/*
@@ -770,6 +791,8 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
case '1':
case '2':
errmsg = "Master reported unrecognized MySQL version";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
break;
case '3':
mi->rli.relay_log.description_event_for_queue= new
@@ -802,26 +825,21 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
*/
if (errmsg)
- {
- sql_print_error(errmsg);
- DBUG_RETURN(1);
- }
+ goto err;
/* as we are here, we tried to allocate the event */
if (!mi->rli.relay_log.description_event_for_queue)
{
- mi->report(ERROR_LEVEL, ER_SLAVE_CREATE_EVENT_FAILURE,
- ER(ER_SLAVE_CREATE_EVENT_FAILURE),
- "default Format_description_log_event");
- DBUG_RETURN(1);
+ errmsg= "default Format_description_log_event";
+ err_code= ER_SLAVE_CREATE_EVENT_FAILURE;
+ sprintf(err_buff, ER(err_code), errmsg);
+ goto err;
}
/*
Compare the master and slave's clock. Do not die if master's clock is
unavailable (very old master not supporting UNIX_TIMESTAMP()?).
*/
- MYSQL_RES *master_res= 0;
- MYSQL_ROW master_row;
if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&
(master_res= mysql_store_result(mysql)) &&
@@ -858,11 +876,17 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
if ((master_row= mysql_fetch_row(master_res)) &&
(::server_id == strtoul(master_row[1], 0, 10)) &&
!mi->rli.replicate_same_server_id)
+ {
errmsg= "The slave I/O thread stops because master and slave have equal \
MySQL server ids; these ids must be different for replication to work (or \
the --replicate-same-server-id option must be used on slave but this does \
not always make sense; please check the manual before using it).";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
mysql_free_result(master_res);
+ if (errmsg)
+ goto err;
}
/*
@@ -893,10 +917,16 @@ not always make sense; please check the manual before using it).";
{
if ((master_row= mysql_fetch_row(master_res)) &&
strcmp(master_row[0], global_system_variables.collation_server->name))
+ {
errmsg= "The slave I/O thread stops because master and slave have \
different values for the COLLATION_SERVER global variable. The values must \
be equal for replication to work";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
mysql_free_result(master_res);
+ if (errmsg)
+ goto err;
}
/*
@@ -921,16 +951,24 @@ be equal for replication to work";
if ((master_row= mysql_fetch_row(master_res)) &&
strcmp(master_row[0],
global_system_variables.time_zone->get_name()->ptr()))
+ {
errmsg= "The slave I/O thread stops because master and slave have \
different values for the TIME_ZONE global variable. The values must \
be equal for replication to work";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
mysql_free_result(master_res);
+
+ if (errmsg)
+ goto err;
}
err:
if (errmsg)
{
- sql_print_error(errmsg);
+ DBUG_ASSERT(err_code != 0);
+ mi->report(ERROR_LEVEL, err_code, err_buff);
DBUG_RETURN(1);
}
@@ -1451,7 +1489,7 @@ bool show_master_info(THD* thd, Master_info* mi)
if (my_net_write(&thd->net, (uchar*) thd->packet.ptr(), packet->length()))
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -1507,6 +1545,9 @@ void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli)
static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{
DBUG_ENTER("init_slave_thread");
+#if !defined(DBUG_OFF)
+ int simulate_error= 0;
+#endif
thd->system_thread = (thd_type == SLAVE_THD_SQL) ?
SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
thd->security_ctx->skip_grants();
@@ -1526,10 +1567,17 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_EXECUTE_IF("simulate_io_slave_error_on_init",
+ simulate_error|= (1 << SLAVE_THD_IO););
+ DBUG_EXECUTE_IF("simulate_sql_slave_error_on_init",
+ simulate_error|= (1 << SLAVE_THD_SQL););
+#if !defined(DBUG_OFF)
+ if (init_thr_lock() || thd->store_globals() || simulate_error & (1<< thd_type))
+#else
if (init_thr_lock() || thd->store_globals())
+#endif
{
thd->cleanup();
- delete thd;
DBUG_RETURN(-1);
}
lex_start(thd);
@@ -1971,28 +2019,6 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
wait for something for example inside of next_event().
*/
pthread_mutex_lock(&rli->data_lock);
- /*
- This tests if the position of the end of the last previous executed event
- hits the UNTIL barrier.
- We would prefer to test if the position of the start (or possibly) end of
- the to-be-read event hits the UNTIL barrier, this is different if there
- was an event ignored by the I/O thread just before (BUG#13861 to be
- fixed).
- */
- if (rli->until_condition!=Relay_log_info::UNTIL_NONE &&
- rli->is_until_satisfied())
- {
- char buf[22];
- sql_print_information("Slave SQL thread stopped because it reached its"
- " UNTIL position %s", llstr(rli->until_pos(), buf));
- /*
- Setting abort_slave flag because we do not want additional message about
- error in query execution to be printed.
- */
- rli->abort_slave= 1;
- pthread_mutex_unlock(&rli->data_lock);
- DBUG_RETURN(1);
- }
Log_event * ev = next_event(rli);
@@ -2006,7 +2032,30 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
}
if (ev)
{
- int exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE);
+ int exec_res;
+
+ /*
+ This tests if the position of the beginning of the current event
+ hits the UNTIL barrier.
+ */
+ if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
+ rli->is_until_satisfied((rli->is_in_group() || !ev->log_pos) ?
+ rli->group_master_log_pos :
+ ev->log_pos - ev->data_written))
+ {
+ char buf[22];
+ sql_print_information("Slave SQL thread stopped because it reached its"
+ " UNTIL position %s", llstr(rli->until_pos(), buf));
+ /*
+ Setting abort_slave flag because we do not want additional message about
+ error in query execution to be printed.
+ */
+ rli->abort_slave= 1;
+ pthread_mutex_unlock(&rli->data_lock);
+ delete ev;
+ DBUG_RETURN(1);
+ }
+ exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE);
/*
Format_description_log_event should not be deleted because it will be
@@ -2229,6 +2278,7 @@ pthread_handler_t handle_slave_io(void *arg)
thd= new THD; // note that contructor of THD uses DBUG_ !
THD_CHECK_SENTRY(thd);
+ mi->io_thd = thd;
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd; // remember where our stack is
@@ -2239,7 +2289,6 @@ pthread_handler_t handle_slave_io(void *arg)
sql_print_error("Failed during slave I/O thread initialization");
goto err;
}
- mi->io_thd = thd;
pthread_mutex_lock(&LOCK_thread_count);
threads.append(thd);
pthread_mutex_unlock(&LOCK_thread_count);
@@ -2530,9 +2579,11 @@ pthread_handler_t handle_slave_sql(void *arg)
thd = new THD; // note that contructor of THD uses DBUG_ !
thd->thread_stack = (char*)&thd; // remember where our stack is
-
+ rli->sql_thd= thd;
+
/* Inform waiting threads that slave has started */
rli->slave_run_id++;
+ rli->slave_running = 1;
pthread_detach_this_thread();
if (init_slave_thread(thd, SLAVE_THD_SQL))
@@ -2547,7 +2598,6 @@ pthread_handler_t handle_slave_sql(void *arg)
goto err;
}
thd->init_for_queries();
- rli->sql_thd= thd;
thd->temporary_tables = rli->save_temporary_tables; // restore temp tables
pthread_mutex_lock(&LOCK_thread_count);
threads.append(thd);
@@ -2560,7 +2610,6 @@ pthread_handler_t handle_slave_sql(void *arg)
start receiving data so we realize we are not caught up and
Seconds_Behind_Master grows. No big deal.
*/
- rli->slave_running = 1;
rli->abort_slave = 0;
pthread_mutex_unlock(&rli->run_lock);
pthread_cond_broadcast(&rli->start_cond);
@@ -2642,6 +2691,22 @@ Slave SQL thread aborted. Can't execute init_slave query");
}
}
+ /*
+ First check until condition - probably there is nothing to execute. We
+ do not want to wait for next event in this case.
+ */
+ pthread_mutex_lock(&rli->data_lock);
+ if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
+ rli->is_until_satisfied(rli->group_master_log_pos))
+ {
+ char buf[22];
+ sql_print_information("Slave SQL thread stopped because it reached its"
+ " UNTIL position %s", llstr(rli->until_pos(), buf));
+ pthread_mutex_unlock(&rli->data_lock);
+ goto err;
+ }
+ pthread_mutex_unlock(&rli->data_lock);
+
/* Read queries from the IO/THREAD until this thread is killed */
while (!sql_slave_killed(thd,rli))
@@ -3991,9 +4056,10 @@ end:
has a certain bug.
@param rli Relay_log_info which tells the master's version
@param bug_id Number of the bug as found in bugs.mysql.com
+ @param report bool report error message, default TRUE
@return TRUE if master has the bug, FALSE if it does not.
*/
-bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id)
+bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id, bool report)
{
struct st_version_range_for_one_bug {
uint bug_id;
@@ -4003,7 +4069,9 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id)
static struct st_version_range_for_one_bug versions_for_all_bugs[]=
{
{24432, { 5, 0, 24 }, { 5, 0, 38 } },
- {24432, { 5, 1, 12 }, { 5, 1, 17 } }
+ {24432, { 5, 1, 12 }, { 5, 1, 17 } },
+ {33029, { 5, 0, 0 }, { 5, 0, 58 } },
+ {33029, { 5, 1, 0 }, { 5, 1, 12 } },
};
const uchar *master_ver=
rli->relay_log.description_event_for_exec->server_version_split;
@@ -4019,6 +4087,9 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id)
(memcmp(introduced_in, master_ver, 3) <= 0) &&
(memcmp(fixed_in, master_ver, 3) > 0))
{
+ if (!report)
+ return TRUE;
+
// a short message for SHOW SLAVE STATUS (message length constraints)
my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from"
" http://bugs.mysql.com/bug.php?id=%u"
@@ -4050,6 +4121,26 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id)
return FALSE;
}
+/**
+ BUG#33029, For all 5.0 up to 5.0.58 exclusive, and 5.1 up to 5.1.12
+ exclusive, if one statement in a SP generated AUTO_INCREMENT value
+ by the top statement, all statements after it would be considered
+ generated AUTO_INCREMENT value by the top statement, and a
+ erroneous INSERT_ID value might be associated with these statement,
+ which could cause duplicate entry error and stop the slave.
+
+ Detect buggy master to work around.
+ */
+bool rpl_master_erroneous_autoinc(THD *thd)
+{
+ if (active_mi && active_mi->rli.sql_thd == thd)
+ {
+ Relay_log_info *rli= &active_mi->rli;
+ return rpl_master_has_bug(rli, 33029, FALSE);
+ }
+ return FALSE;
+}
+
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
template class I_List_iterator<i_string>;
template class I_List_iterator<i_string_pair>;
diff --git a/sql/slave.h b/sql/slave.h
index b4f1b7f7467..80d267e5b27 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -165,7 +165,8 @@ int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
bool show_master_info(THD* thd, Master_info* mi);
bool show_binlog_info(THD* thd);
-bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id);
+bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id, bool report=TRUE);
+bool rpl_master_erroneous_autoinc(THD* thd);
const char *print_slave_db_safe(const char *db);
int check_expected_error(THD* thd, Relay_log_info const *rli, int error_code);
diff --git a/sql/sp.cc b/sql/sp.cc
index 99ffc18deea..69eae8de207 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -388,7 +388,8 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
uint length;
char buff[65];
String str(buff, sizeof(buff), &my_charset_bin);
- ulong sql_mode;
+ bool saved_time_zone_used= thd->time_zone_used;
+ ulong sql_mode, saved_mode= thd->variables.sql_mode;
Open_tables_state open_tables_state_backup;
Stored_program_creation_ctx *creation_ctx;
@@ -400,6 +401,9 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
goto done;
@@ -501,12 +505,43 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
sql_mode, params, returns, body, chistics,
definer, created, modified, creation_ctx);
done:
+ /*
+ Restore the time zone flag as the timezone usage in proc table
+ does not affect replication.
+ */
+ thd->time_zone_used= saved_time_zone_used;
if (table)
close_system_tables(thd, &open_tables_state_backup);
+ thd->variables.sql_mode= saved_mode;
DBUG_RETURN(ret);
}
+/**
+ Silence DEPRECATED SYNTAX warnings when loading a stored procedure
+ into the cache.
+*/
+struct Silence_deprecated_warning : public Internal_error_handler
+{
+public:
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+};
+
+bool
+Silence_deprecated_warning::handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ if (sql_errno == ER_WARN_DEPRECATED_SYNTAX &&
+ level == MYSQL_ERROR::WARN_LEVEL_WARN)
+ return TRUE;
+
+ return FALSE;
+}
+
+
static int
db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ulong sql_mode, const char *params, const char *returns,
@@ -523,7 +558,8 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ulong old_sql_mode= thd->variables.sql_mode;
ha_rows old_select_limit= thd->variables.select_limit;
sp_rcontext *old_spcont= thd->spcont;
-
+ Silence_deprecated_warning warning_handler;
+
char definer_user_name_holder[USERNAME_LENGTH + 1];
LEX_STRING definer_user_name= { definer_user_name_holder,
USERNAME_LENGTH };
@@ -583,7 +619,9 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
lex_start(thd);
+ thd->push_internal_handler(&warning_handler);
ret= parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL;
+ thd->pop_internal_handler();
/*
Force switching back to the saved current database (if changed),
@@ -665,8 +703,16 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
(TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION).
@param sp Stored routine object to store.
- @return Error code. SP_OK is returned on success. Other SP_ constants are
- used to indicate about errors.
+ @note Opens and closes the thread tables. Therefore assumes
+ that there are no locked tables in this thread at the time of
+ invocation.
+ Unlike some other DDL statements, *does* close the tables
+ in the end, since the call to this function is normally
+ followed by an implicit grant (sp_grant_privileges())
+ and this subsequent call opens and closes mysql.procs_priv.
+
+ @return Error code. SP_OK is returned on success. Other
+ SP_ constants are used to indicate about errors.
*/
int
@@ -675,6 +721,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
int ret;
TABLE *table;
char definer[USER_HOST_BUFF_SIZE];
+ ulong saved_mode= thd->variables.sql_mode;
CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
@@ -689,6 +736,9 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
type == TYPE_ENUM_FUNCTION);
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
@@ -790,7 +840,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_SQL_MODE]->
- store((longlong)thd->variables.sql_mode, TRUE);
+ store((longlong)saved_mode, TRUE);
if (sp->m_chistics->comment.str)
{
@@ -890,6 +940,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
done:
thd->count_cuted_fields= saved_count_cuted_fields;
+ thd->variables.sql_mode= saved_mode;
close_thread_tables(thd);
DBUG_RETURN(ret);
@@ -1214,7 +1265,7 @@ sp_show_status_routine(THD *thd, int type, const char *name_pattern)
}
err_case1:
- send_eof(thd);
+ my_eof(thd);
err_case:
table->file->ha_index_end();
close_thread_tables(thd);
@@ -1223,7 +1274,13 @@ done:
}
-/* Drop all routines in database 'db' */
+/**
+ Drop all routines in database 'db'
+
+ @note Close the thread tables, the calling code might want to
+ delete from other system tables afterwards.
+*/
+
int
sp_drop_db_routines(THD *thd, char *db)
{
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 0edb566ed9f..8bd10e00f15 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2309,7 +2309,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "proc";
- *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) ||
+ *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) ||
(!strcmp(sp->m_definer_user.str,
thd->security_ctx->priv_user) &&
!strcmp(sp->m_definer_host.str,
@@ -2419,7 +2419,7 @@ sp_head::show_create_routine(THD *thd, int type)
err_status= protocol->write();
if (!err_status)
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(err_status);
}
@@ -2611,7 +2611,7 @@ sp_head::show_routine_code(THD *thd)
}
if (!res)
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(res);
}
@@ -2700,6 +2700,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
m_lex->unit.cleanup();
thd_proc_info(thd, "closing tables");
+ /* Here we also commit or rollback the current statement. */
close_thread_tables(thd);
thd_proc_info(thd, 0);
@@ -2753,7 +2754,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Check whenever we have access to tables for this statement
and open and lock them before executing instructions core function.
*/
- if (check_table_access(thd, SELECT_ACL, tables, 0)
+ if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, tables))
result= -1;
else
@@ -2932,7 +2933,7 @@ sp_instr_set::print(String *str)
}
str->qs_append(m_offset);
str->qs_append(' ');
- m_value->print(str);
+ m_value->print(str, QT_ORDINARY);
}
@@ -2960,9 +2961,9 @@ void
sp_instr_set_trigger_field::print(String *str)
{
str->append(STRING_WITH_LEN("set_trigger_field "));
- trigger_field->print(str);
+ trigger_field->print(str, QT_ORDINARY);
str->append(STRING_WITH_LEN(":="));
- value->print(str);
+ value->print(str, QT_ORDINARY);
}
/*
@@ -3088,7 +3089,7 @@ sp_instr_jump_if_not::print(String *str)
str->qs_append('(');
str->qs_append(m_cont_dest);
str->qs_append(STRING_WITH_LEN(") "));
- m_expr->print(str);
+ m_expr->print(str, QT_ORDINARY);
}
@@ -3176,7 +3177,7 @@ sp_instr_freturn::print(String *str)
str->qs_append(STRING_WITH_LEN("freturn "));
str->qs_append((uint)m_type);
str->qs_append(' ');
- m_value->print(str);
+ m_value->print(str, QT_ORDINARY);
}
/*
@@ -3664,7 +3665,7 @@ sp_instr_set_case_expr::print(String *str)
str->qs_append(STRING_WITH_LEN(") "));
str->qs_append(m_case_expr_id);
str->qs_append(' ');
- m_case_expr->print(str);
+ m_case_expr->print(str, QT_ORDINARY);
}
uint
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 8babbd52ff6..414ea12cd7a 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -51,6 +51,8 @@ sp_cond_check(LEX_STRING *sqlstate)
(c < 'A' || 'Z' < c))
return FALSE;
}
+ if (strcmp(sqlstate->str, "00000") == 0)
+ return FALSE;
return TRUE;
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 5b64c684223..4694ca707b7 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -192,7 +192,7 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
const char *ip);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
-static my_bool grant_load(TABLE_LIST *tables);
+static my_bool grant_load(THD *thd, TABLE_LIST *tables);
/*
Convert scrambled password to binary form, according to scramble type,
@@ -314,8 +314,11 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
char tmp_name[NAME_LEN+1];
int password_length;
+ ulong old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("acl_load");
+ thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+
grant_version++; /* Privileges updated */
acl_cache->clear(1); // Clear locked hostname cache
@@ -622,6 +625,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
return_val=0;
end:
+ thd->variables.sql_mode= old_sql_mode;
DBUG_RETURN(return_val);
}
@@ -801,7 +805,9 @@ static ulong get_sort(uint count,...)
{
for (; *str ; str++)
{
- if (*str == wild_many || *str == wild_one || *str == wild_prefix)
+ if (*str == wild_prefix && str[1])
+ str++;
+ else if (*str == wild_many || *str == wild_one)
{
wild_pos= (uint) (str - start) + 1;
break;
@@ -1515,7 +1521,7 @@ bool acl_check_host(const char *host, const char *ip)
1 ERROR ; In this case the error is sent to the client.
*/
-bool check_change_password(THD *thd, const char *host, const char *user,
+int check_change_password(THD *thd, const char *host, const char *user,
char *new_password, uint new_password_len)
{
if (!initialized)
@@ -2281,7 +2287,7 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
const char *t, ulong p, ulong c)
:GRANT_NAME(h,d,u,t,p), cols(c)
{
- (void) hash_init(&hash_columns,system_charset_info,
+ (void) hash_init2(&hash_columns,4,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
}
@@ -2329,7 +2335,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
cols= (ulong) form->field[7]->val_int();
cols = fix_rights_for_column(cols);
- (void) hash_init(&hash_columns,system_charset_info,
+ (void) hash_init2(&hash_columns,4,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
if (cols)
{
@@ -2779,6 +2785,10 @@ table_error:
}
+/**
+ @retval 0 success
+ @retval -1 error
+*/
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
TABLE *table, const LEX_USER &combo,
const char *db, const char *routine_name,
@@ -2800,14 +2810,11 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
thd->security_ctx->host_or_ip, NullS);
/*
- The following should always succeed as new users are created before
- this function is called!
+ New users are created before this function is called.
+
+ There may be some cases where a routine's definer is removed but the
+ routine remains.
*/
- if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
- {
- my_error(ER_PASSWORD_NO_MATCH,MYF(0));
- DBUG_RETURN(-1);
- }
table->use_all_columns();
restore_record(table, s->default_values); // Get empty record
@@ -2916,7 +2923,7 @@ table_error:
TRUE error
*/
-bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
+int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
List <LEX_USER> &user_list,
List <LEX_COLUMN> &columns, ulong rights,
bool revoke_grant)
@@ -3041,6 +3048,12 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
#endif
+ /*
+ The lock api is depending on the thd->lex variable which needs to be
+ re-initialized.
+ */
+ Query_tables_list backup;
+ thd->lex->reset_n_backup_query_tables_list(&backup);
if (simple_open_n_lock_tables(thd,tables))
{ // Should never happen
close_thread_tables(thd); /* purecov: deadcode */
@@ -3170,9 +3183,10 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
rw_unlock(&LOCK_grant);
if (!result) /* success */
- send_ok(thd);
+ my_ok(thd);
/* Tables are automatically closed */
+ thd->lex->restore_backup_query_tables_list(&backup);
DBUG_RETURN(result);
}
@@ -3321,7 +3335,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
- db_name, table_name, is_proc, rights, revoke_grant))
+ db_name, table_name, is_proc, rights,
+ revoke_grant) != 0)
{
result= TRUE;
continue;
@@ -3337,7 +3352,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
rw_unlock(&LOCK_grant);
if (!result && !no_error)
- send_ok(thd);
+ my_ok(thd);
/* Tables are automatically closed */
DBUG_RETURN(result);
@@ -3453,7 +3468,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
close_thread_tables(thd);
if (!result)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(result);
}
@@ -3611,7 +3626,7 @@ end_unlock:
@retval TRUE Error
*/
-static my_bool grant_load(TABLE_LIST *tables)
+static my_bool grant_load(THD *thd, TABLE_LIST *tables)
{
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
@@ -3619,7 +3634,11 @@ static my_bool grant_load(TABLE_LIST *tables)
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
+ ulong old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("grant_load");
+
+ thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+
(void) hash_init(&column_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table,
(hash_free_key) free_grant_table,0);
@@ -3671,6 +3690,7 @@ static my_bool grant_load(TABLE_LIST *tables)
return_val=0; // Return ok
end_unlock:
+ thd->variables.sql_mode= old_sql_mode;
t_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
DBUG_RETURN(return_val);
@@ -3784,7 +3804,7 @@ my_bool grant_reload(THD *thd)
old_mem= memex;
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
- if ((return_val= grant_load(tables)))
+ if ((return_val= grant_load(thd, tables)))
{ // Error. Revert to old hash
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */
@@ -3862,7 +3882,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
of other queries). For simple queries first_not_own_table is 0.
*/
for (i= 0, table= tables;
- table != first_not_own_table && i < number;
+ i < number && table != first_not_own_table;
table= table->next_global, i++)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
@@ -4808,7 +4828,7 @@ end:
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(error);
}
@@ -5947,11 +5967,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
{
- if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
+ if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
grant_proc->db,
grant_proc->tname,
is_proc,
- ~(ulong)0, 1))
+ ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
@@ -5977,17 +5997,73 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
}
-/*
- Revoke privileges for all users on a stored procedure
- SYNOPSIS
- sp_revoke_privileges()
+
+/**
+ If the defining user for a routine does not exist, then the ACL lookup
+ code should raise two errors which we should intercept. We convert the more
+ descriptive error into a warning, and consume the other.
+
+ If any other errors are raised, then we set a flag that should indicate
+ that there was some failure we should complain at a higher level.
+*/
+class Silence_routine_definer_errors : public Internal_error_handler
+{
+public:
+ Silence_routine_definer_errors()
+ : is_grave(FALSE)
+ {}
+
+ virtual ~Silence_routine_definer_errors()
+ {}
+
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+
+ bool has_errors() { return is_grave; }
+
+private:
+ bool is_grave;
+};
+
+bool
+Silence_routine_definer_errors::handle_error(uint sql_errno,
+ const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ {
+ switch (sql_errno)
+ {
+ case ER_NONEXISTING_PROC_GRANT:
+ /* Convert the error into a warning. */
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, message);
+ return TRUE;
+ default:
+ is_grave= TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Revoke privileges for all users on a stored procedure. Use an error handler
+ that converts errors about missing grants into warnings.
+
+ @param
thd The current thread.
+ @param
db DB of the stored procedure
+ @param
name Name of the stored procedure
- RETURN
+ @retval
0 OK.
+ @retval
< 0 Error. Error message not yet sent.
*/
@@ -5998,11 +6074,15 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
int result;
TABLE_LIST tables[GRANT_TABLES];
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ Silence_routine_definer_errors error_handler;
DBUG_ENTER("sp_revoke_privileges");
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
+ /* Be sure to pop this before exiting this scope! */
+ thd->push_internal_handler(&error_handler);
+
rw_wrlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
@@ -6029,14 +6109,14 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
grant_proc->host.hostname : (char*)"";
lex_user.host.length= grant_proc->host.hostname ?
strlen(grant_proc->host.hostname) : 0;
- if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
- grant_proc->db, grant_proc->tname,
- is_proc, ~(ulong)0, 1))
+
+ if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
+ grant_proc->db, grant_proc->tname,
+ is_proc, ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
}
- result= -1; // Something went wrong
}
counter++;
}
@@ -6046,10 +6126,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
- if (result)
- my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
+ thd->pop_internal_handler();
- DBUG_RETURN(result);
+ DBUG_RETURN(error_handler.has_errors());
}
@@ -6067,7 +6146,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
< 0 Error. Error message not yet sent.
*/
-bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
+int sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
bool is_proc)
{
Security_context *sctx= thd->security_ctx;
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index a279c26c2e4..9ae17a4bf02 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -222,13 +222,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
char *ip, char *db);
bool acl_check_host(const char *host, const char *ip);
-bool check_change_password(THD *thd, const char *host, const char *user,
+int check_change_password(THD *thd, const char *host, const char *user,
char *password, uint password_len);
bool change_password(THD *thd, const char *host, const char *user,
char *password);
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
ulong rights, bool revoke);
-bool mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
+int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, ulong rights,
bool revoke);
bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc,
@@ -264,7 +264,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table);
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
bool is_proc);
-bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
+int sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
bool is_proc);
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
bool is_proc);
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index 490cc5e28c1..9ca6e0a0a2b 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -682,7 +682,7 @@ int analyse::send_row(List<Item> & /* field_list */)
} // analyse::send_row
-bool analyse::end_of_records()
+int analyse::end_of_records()
{
field_info **f = f_info;
char buff[MAX_FIELD_WIDTH];
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 827b6f4b217..8807b40857e 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -350,7 +350,7 @@ public:
virtual bool change_columns(List<Item> &fields);
virtual int send_row(List<Item> &field_list);
virtual void end_group(void) {}
- virtual bool end_of_records(void);
+ virtual int end_of_records(void);
friend Procedure *proc_analyse_init(THD *thd, ORDER *param,
select_result *result,
List<Item> &field_list);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 646ab8a4346..2c2404ee97d 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -747,6 +747,7 @@ void close_handle_and_leave_table_as_lock(TABLE *table)
table->db_stat= 0; // Mark file closed
release_table_share(table->s, RELEASE_NORMAL);
table->s= share;
+ table->file->change_table_ptr(table, table->s);
DBUG_VOID_RETURN;
}
@@ -799,7 +800,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
table_list.table_name= share->table_name.str;
table_list.grant.privilege=0;
- if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1))
+ if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE))
continue;
/* need to check if we haven't already listed it */
for (table= open_list ; table ; table=table->next)
@@ -1324,29 +1325,45 @@ void close_thread_tables(THD *thd)
Mark all temporary tables used by this statement as free for reuse.
*/
mark_temp_tables_as_free_for_reuse(thd);
-
- if (thd->locked_tables || prelocked_mode)
+ /*
+ Let us commit transaction for statement. Since in 5.0 we only have
+ one statement transaction and don't allow several nested statement
+ transactions this call will do nothing if we are inside of stored
+ function or trigger (i.e. statement transaction is already active and
+ does not belong to statement for which we do close_thread_tables()).
+ TODO: This should be fixed in later releases.
+ */
+ if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
{
+ thd->main_da.can_overwrite_status= TRUE;
+ ha_autocommit_or_rollback(thd, thd->is_error());
+ thd->main_da.can_overwrite_status= FALSE;
+
/*
- Let us commit transaction for statement. Since in 5.0 we only have
- one statement transaction and don't allow several nested statement
- transactions this call will do nothing if we are inside of stored
- function or trigger (i.e. statement transaction is already active and
- does not belong to statement for which we do close_thread_tables()).
- TODO: This should be fixed in later releases.
+ Reset transaction state, but only if we're not inside a
+ sub-statement of a prelocked statement.
*/
- ha_commit_stmt(thd);
+ if (! prelocked_mode || thd->lex->requires_prelocking())
+ thd->transaction.stmt.reset();
+ }
+
+ if (thd->locked_tables || prelocked_mode)
+ {
/* Ensure we are calling ha_reset() for all used tables */
mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
- /* We are under simple LOCK TABLES so should not do anything else. */
+ /*
+ We are under simple LOCK TABLES or we're inside a sub-statement
+ of a prelocked statement, so should not do anything else.
+ */
if (!prelocked_mode || !thd->lex->requires_prelocking())
DBUG_VOID_RETURN;
/*
- We are in prelocked mode, so we have to leave it now with doing
- implicit UNLOCK TABLES if need.
+ We are in the top-level statement of a prelocked statement,
+ so we have to leave the prelocked mode now with doing implicit
+ UNLOCK TABLES if needed.
*/
DBUG_PRINT("info",("thd->prelocked_mode= NON_PRELOCKED"));
thd->prelocked_mode= NON_PRELOCKED;
@@ -1375,19 +1392,6 @@ void close_thread_tables(THD *thd)
thd->lock=0;
}
/*
- assume handlers auto-commit (if some doesn't - transaction handling
- in MySQL should be redesigned to support it; it's a big change,
- and it's not worth it - better to commit explicitly only writing
- transactions, read-only ones should better take care of themselves.
- saves some work in 2pc too)
- see also sql_parse.cc - dispatch_command()
- */
- if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
- bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
- if (!thd->active_transaction())
- thd->transaction.xid_state.xid.null();
-
- /*
Note that we need to hold LOCK_open while changing the
open_tables list. Another thread may work on it.
(See: remove_table_from_cache(), mysql_wait_completed_table())
@@ -4596,8 +4600,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
}
if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
- tables->table->reginfo.lock_type= tables->lock_type == TL_WRITE_DEFAULT ?
- thd->update_lock_default : tables->lock_type;
+ {
+ if (tables->lock_type == TL_WRITE_DEFAULT)
+ tables->table->reginfo.lock_type= thd->update_lock_default;
+ else if (tables->table->s->tmp_table == NO_TMP_TABLE)
+ tables->table->reginfo.lock_type= tables->lock_type;
+ }
tables->table->grant= tables->grant;
/* Attach MERGE children if not locked already. */
@@ -4858,7 +4866,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
the third argument set appropriately.
*/
-bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
{
uint counter;
bool need_reopen;
@@ -5059,10 +5067,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables)
DBUG_PRINT("info", ("error: %d", error));
if (error)
- {
- ha_rollback_stmt(thd);
return -1;
- }
/*
We switch to row-based format if we are in mixed mode and one of
@@ -5216,7 +5221,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
table->table->query_id= thd->query_id;
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
- ha_rollback_stmt(thd);
mysql_unlock_tables(thd, thd->locked_tables);
thd->locked_tables= 0;
thd->options&= ~(OPTION_TABLE_LOCK);
@@ -5251,7 +5255,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
if (!table->placeholder() &&
check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
- ha_rollback_stmt(thd);
DBUG_RETURN(-1);
}
}
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index cff4ceeccf9..a6d0c8c9e9b 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -231,9 +231,10 @@ void mysql_client_binlog_statement(THD* thd)
DBUG_PRINT("info",("binlog base64 execution finished successfully"));
- send_ok(thd);
+ my_ok(thd);
end:
+ thd->rli_fake->clear_tables_to_lock();
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index 9a765120895..97accefe8aa 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -65,7 +65,7 @@ public:
my_bool is_clear_all() const { return bitmap_is_clear_all(&map); }
my_bool is_set_all() const { return bitmap_is_set_all(&map); }
my_bool is_subset(const Bitmap& map2) const { return bitmap_is_subset(&map, &map2.map); }
- my_bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, map2.map); }
+ my_bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, &map2.map); }
my_bool operator==(const Bitmap& map2) const { return bitmap_cmp(&map, &map2.map); }
char *print(char *buf) const
{
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 2ce9cb951fe..5d85202aed2 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1390,7 +1390,7 @@ def_week_frmt: %lu",
table_list.db = table->db();
table_list.alias= table_list.table_name= table->table();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_table_access(thd,SELECT_ACL,&table_list,1))
+ if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE))
{
DBUG_PRINT("qcache",
("probably no SELECT access to %s.%s => return to normal processing",
@@ -2004,6 +2004,7 @@ void Query_cache::make_disabled()
query_cache_size= 0;
queries_blocks= 0;
free_memory= 0;
+ free_memory_blocks= 0;
bins= 0;
steps= 0;
cache= 0;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 31641f78fbb..fb08b2e81b3 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -28,6 +28,7 @@
#include "mysql_priv.h"
#include "rpl_rli.h"
#include "rpl_record.h"
+#include "slave.h"
#include <my_bitmap.h>
#include "log_event.h"
#include <m_ctype.h>
@@ -253,7 +254,8 @@ const char *set_thd_proc_info(THD *thd, const char *info,
const unsigned int calling_line)
{
const char *old_info= thd->proc_info;
- DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, info));
+ DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line,
+ (info != NULL) ? info : "(null)"));
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.status_change(info, calling_function, calling_file, calling_line);
#endif
@@ -264,7 +266,7 @@ const char *set_thd_proc_info(THD *thd, const char *info,
extern "C"
void **thd_ha_data(const THD *thd, const struct handlerton *hton)
{
- return (void **) thd->ha_data + hton->slot;
+ return (void **) &thd->ha_data[hton->slot].ha_ptr;
}
extern "C"
@@ -394,8 +396,11 @@ Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg,
const char *message_arg)
{
DBUG_ASSERT(! is_set());
- /* Refuse to overwrite an error with an OK packet. */
- if (is_error())
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an OK packet.
+ */
+ if (is_error() || is_disabled())
return;
m_server_status= thd->server_status;
@@ -403,7 +408,7 @@ Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg,
m_affected_rows= affected_rows_arg;
m_last_insert_id= last_insert_id_arg;
if (message_arg)
- strmake(m_message, message_arg, sizeof(m_message));
+ strmake(m_message, message_arg, sizeof(m_message) - 1);
else
m_message[0]= '\0';
m_status= DA_OK;
@@ -420,8 +425,11 @@ Diagnostics_area::set_eof_status(THD *thd)
/** Only allowed to report eof if has not yet reported an error */
DBUG_ASSERT(! is_set());
- /* Refuse to overwrite an error with an EOF packet. */
- if (is_error())
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an EOF packet.
+ */
+ if (is_error() || is_disabled())
return;
m_server_status= thd->server_status;
@@ -449,9 +457,17 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
an error can happen during the flush.
*/
DBUG_ASSERT(! is_set() || can_overwrite_status);
+#ifdef DBUG_OFF
+ /*
+ In production, refuse to overwrite a custom response with an
+ ERROR packet.
+ */
+ if (is_disabled())
+ return;
+#endif
m_sql_errno= sql_errno_arg;
- strmake(m_message, message_arg, sizeof(m_message));
+ strmake(m_message, message_arg, sizeof(m_message) - 1);
m_status= DA_ERROR;
}
@@ -678,6 +694,7 @@ void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size)
return thd->memdup(str, size);
}
+extern "C"
void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid)
{
*xid = *(MYSQL_XID *) &thd->transaction.xid_state.xid;
@@ -765,6 +782,10 @@ void THD::init_for_queries()
void THD::change_user(void)
{
+ pthread_mutex_lock(&LOCK_status);
+ add_to_status(&global_status_var, &status_var);
+ pthread_mutex_unlock(&LOCK_status);
+
cleanup();
killed= NOT_KILLED;
cleanup_done= 0;
@@ -866,7 +887,10 @@ THD::~THD()
#endif
#ifndef EMBEDDED_LIBRARY
if (rli_fake)
+ {
delete rli_fake;
+ rli_fake= NULL;
+ }
#endif
free_root(&main_mem_root, MYF(0));
@@ -1537,7 +1561,7 @@ bool select_send::send_eof()
mysql_unlock_tables(thd, thd->lock);
thd->lock=0;
}
- ::send_eof(thd);
+ ::my_eof(thd);
is_result_set_started= 0;
return FALSE;
}
@@ -1572,7 +1596,7 @@ bool select_to_file::send_eof()
function, SELECT INTO has to have an own SQLCOM.
TODO: split from SQLCOM_SELECT
*/
- ::send_ok(thd,row_count);
+ ::my_ok(thd,row_count);
}
file= -1;
return error;
@@ -2509,7 +2533,7 @@ bool select_dumpvar::send_data(List<Item> &items)
suv->update();
}
}
- DBUG_RETURN(0);
+ DBUG_RETURN(thd->is_error());
}
bool select_dumpvar::send_eof()
@@ -2522,7 +2546,7 @@ bool select_dumpvar::send_eof()
function, SELECT INTO has to have an own SQLCOM.
TODO: split from SQLCOM_SELECT
*/
- ::send_ok(thd,row_count);
+ ::my_ok(thd,row_count);
return 0;
}
@@ -2756,6 +2780,17 @@ extern "C" int thd_killed(const MYSQL_THD thd)
return(thd->killed);
}
+/**
+ Return the thread id of a user thread
+ @param thd user thread
+ @return thread id
+*/
+extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
+{
+ return((unsigned long)thd->thread_id);
+}
+
+
#ifdef INNODB_COMPATIBILITY_HOOKS
extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd)
{
@@ -2820,6 +2855,18 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
void THD::reset_sub_statement_state(Sub_statement_state *backup,
uint new_state)
{
+#ifndef EMBEDDED_LIBRARY
+ /* BUG#33029, if we are replicating from a buggy master, reset
+ auto_inc_intervals_forced to prevent substatement
+ (triggers/functions) from using erroneous INSERT_ID value
+ */
+ if (rpl_master_erroneous_autoinc(this))
+ {
+ backup->auto_inc_intervals_forced= auto_inc_intervals_forced;
+ auto_inc_intervals_forced.empty();
+ }
+#endif
+
backup->options= options;
backup->in_sub_stmt= in_sub_stmt;
backup->enable_slow_log= enable_slow_log;
@@ -2857,6 +2904,18 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
void THD::restore_sub_statement_state(Sub_statement_state *backup)
{
+#ifndef EMBEDDED_LIBRARY
+ /* BUG#33029, if we are replicating from a buggy master, restore
+ auto_inc_intervals_forced so that the top statement can use the
+ INSERT_ID value set before this statement.
+ */
+ if (rpl_master_erroneous_autoinc(this))
+ {
+ auto_inc_intervals_forced= backup->auto_inc_intervals_forced;
+ backup->auto_inc_intervals_forced.empty();
+ }
+#endif
+
/*
To save resources we want to release savepoints which were created
during execution of function or trigger before leaving their savepoint
@@ -3561,17 +3620,24 @@ bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
{
/* it cannot, so need to add a new interval */
Discrete_interval *new_interval= new Discrete_interval(start, val, incr);
- if (unlikely(new_interval == NULL)) // out of memory
- DBUG_RETURN(1);
- DBUG_PRINT("info",("adding new auto_increment interval"));
- if (head == NULL)
- head= current= new_interval;
- else
- tail->next= new_interval;
- tail= new_interval;
- elements++;
+ DBUG_RETURN(append(new_interval));
}
DBUG_RETURN(0);
}
+bool Discrete_intervals_list::append(Discrete_interval *new_interval)
+{
+ DBUG_ENTER("Discrete_intervals_list::append");
+ if (unlikely(new_interval == NULL))
+ DBUG_RETURN(1);
+ DBUG_PRINT("info",("adding new auto_increment interval"));
+ if (head == NULL)
+ head= current= new_interval;
+ else
+ tail->next= new_interval;
+ tail= new_interval;
+ elements++;
+ DBUG_RETURN(0);
+}
+
#endif /* !defined(MYSQL_CLIENT) */
diff --git a/sql/sql_class.h b/sql/sql_class.h
index bda8ae43917..5d18d7f108a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -689,7 +689,8 @@ private:
struct st_savepoint {
struct st_savepoint *prev;
char *name;
- uint length, nht;
+ uint length;
+ Ha_trx_info *ha_list;
};
enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED};
@@ -911,6 +912,7 @@ public:
ulonglong first_successful_insert_id_in_prev_stmt;
ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row;
Discrete_interval auto_inc_interval_for_cur_row;
+ Discrete_intervals_list auto_inc_intervals_forced;
ulonglong limit_found_rows;
ha_rows cuted_fields, sent_row_count, examined_row_count;
ulong client_capabilities;
@@ -992,9 +994,9 @@ public:
{
/** The area is cleared at start of a statement. */
DA_EMPTY= 0,
- /** Set whenever one calls send_ok(). */
+ /** Set whenever one calls my_ok(). */
DA_OK,
- /** Set whenever one calls send_eof(). */
+ /** Set whenever one calls my_eof(). */
DA_EOF,
/** Set whenever one calls my_error() or my_message(). */
DA_ERROR,
@@ -1062,7 +1064,7 @@ private:
Copied from thd->server_status when the diagnostics area is assigned.
We need this member as some places in the code use the following pattern:
thd->server_status|= ...
- send_eof(thd);
+ my_eof(thd);
thd->server_status&= ~...
Assigned by OK, EOF or ERROR.
*/
@@ -1096,6 +1098,33 @@ private:
/**
+ Storage engine specific thread local data.
+*/
+
+struct Ha_data
+{
+ /**
+ Storage engine specific thread local data.
+ Lifetime: one user connection.
+ */
+ void *ha_ptr;
+ /**
+ 0: Life time: one statement within a transaction. If @@autocommit is
+ on, also represents the entire transaction.
+ @sa trans_register_ha()
+
+ 1: Life time: one transaction within a connection.
+ If the storage engine does not participate in a transaction,
+ this should not be used.
+ @sa trans_register_ha()
+ */
+ Ha_trx_info ha_info[2];
+
+ Ha_data() :ha_ptr(NULL) {}
+};
+
+
+/**
@class THD
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
@@ -1234,7 +1263,7 @@ public:
uint in_sub_stmt;
/* container for handler's private per-connection data */
- void *ha_data[MAX_HA];
+ Ha_data ha_data[MAX_HA];
#ifndef MYSQL_CLIENT
int binlog_setup_trx_data();
@@ -1243,7 +1272,6 @@ public:
Public interface to write RBR events to the binlog
*/
void binlog_start_trans_and_stmt();
- int binlog_flush_transaction_cache();
void binlog_set_stmt_begin();
int binlog_write_table_map(TABLE *table, bool is_transactional);
int binlog_write_row(TABLE* table, bool is_transactional,
@@ -2113,7 +2141,7 @@ private:
/** A short cut for thd->main_da.set_ok_status(). */
inline void
-send_ok(THD *thd, ha_rows affected_rows= 0, ulonglong id= 0,
+my_ok(THD *thd, ha_rows affected_rows= 0, ulonglong id= 0,
const char *message= NULL)
{
thd->main_da.set_ok_status(thd, affected_rows, id, message);
@@ -2123,7 +2151,7 @@ send_ok(THD *thd, ha_rows affected_rows= 0, ulonglong id= 0,
/** A short cut for thd->main_da.set_eof_status(). */
inline void
-send_eof(THD *thd)
+my_eof(THD *thd)
{
thd->main_da.set_eof_status(thd);
}
@@ -2585,7 +2613,7 @@ class user_var_entry
bool unsigned_flag;
double val_real(my_bool *null_value);
- longlong val_int(my_bool *null_value);
+ longlong val_int(my_bool *null_value) const;
String *val_str(my_bool *null_value, String *str, uint decimals);
my_decimal *val_decimal(my_bool *null_value, my_decimal *result);
DTCollation collation;
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 309a1c7ab5d..d9fb586eb77 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -39,22 +39,7 @@
#endif /* HAVE_OPENSSL */
#ifdef __WIN__
-static void test_signal(int sig_ptr)
-{
-#if !defined( DBUG_OFF)
- MessageBox(NULL,"Test signal","DBUG",MB_OK);
-#endif
-#if defined(OS2)
- fprintf(stderr, "Test signal %d\n", sig_ptr);
- fflush(stderr);
-#endif
-}
-static void init_signals(void)
-{
- int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
- for (int i=0 ; i < 7 ; i++)
- signal( signals[i], test_signal) ;
-}
+extern void win_install_sigabrt_handler();
#endif
/*
@@ -334,7 +319,7 @@ check_user(THD *thd, enum enum_server_command command,
if (mysql_change_db(thd, &db_str, FALSE))
DBUG_RETURN(1);
}
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
#else
@@ -417,10 +402,11 @@ check_user(THD *thd, enum enum_server_command command,
if (check_count)
{
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- bool count_ok= thread_count <= max_connections + delayed_insert_threads
- || (thd->main_security_ctx.master_access & SUPER_ACL);
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ pthread_mutex_lock(&LOCK_connection_count);
+ bool count_ok= connection_count <= max_connections ||
+ (thd->main_security_ctx.master_access & SUPER_ACL);
+ VOID(pthread_mutex_unlock(&LOCK_connection_count));
+
if (!count_ok)
{ // too many connections
my_error(ER_CON_COUNT_ERROR, MYF(0));
@@ -483,7 +469,7 @@ check_user(THD *thd, enum enum_server_command command,
DBUG_RETURN(1);
}
}
- send_ok(thd);
+ my_ok(thd);
thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */
DBUG_RETURN(0);
@@ -626,7 +612,7 @@ bool init_new_connection_handler_thread()
{
pthread_detach_this_thread();
#if defined(__WIN__)
- init_signals();
+ win_install_sigabrt_handler();
#else
/* Win32 calls this in pthread_create */
if (my_thread_init())
@@ -715,20 +701,24 @@ static int check_connection(THD *thd)
bzero((char*) &thd->remote, sizeof(thd->remote));
}
vio_keepalive(net->vio, TRUE);
+
+ ulong server_capabilites;
{
/* buff[] needs to big enough to hold the server_version variable */
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
- ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
- CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
+ server_capabilites= CLIENT_BASIC_FLAGS;
if (opt_using_transactions)
- client_flags|=CLIENT_TRANSACTIONS;
+ server_capabilites|= CLIENT_TRANSACTIONS;
#ifdef HAVE_COMPRESS
- client_flags |= CLIENT_COMPRESS;
+ server_capabilites|= CLIENT_COMPRESS;
#endif /* HAVE_COMPRESS */
#ifdef HAVE_OPENSSL
if (ssl_acceptor_fd)
- client_flags |= CLIENT_SSL; /* Wow, SSL is available! */
+ {
+ server_capabilites |= CLIENT_SSL; /* Wow, SSL is available! */
+ server_capabilites |= CLIENT_SSL_VERIFY_SERVER_CERT;
+ }
#endif /* HAVE_OPENSSL */
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
@@ -747,7 +737,7 @@ static int check_connection(THD *thd)
*/
end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
- int2store(end, client_flags);
+ int2store(end, server_capabilites);
/* write server characteristics: up to 16 bytes allowed */
end[2]=(char) default_charset_info->number;
int2store(end+3, thd->server_status);
@@ -777,7 +767,7 @@ static int check_connection(THD *thd)
if (thd->packet.alloc(thd->variables.net_buffer_length))
return 1; /* The error is set by alloc(). */
- thd->client_capabilities=uint2korr(net->read_pos);
+ thd->client_capabilities= uint2korr(net->read_pos);
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
@@ -792,6 +782,11 @@ static int check_connection(THD *thd)
thd->max_client_packet_length= uint3korr(net->read_pos+2);
end= (char*) net->read_pos+5;
}
+ /*
+ Disable those bits which are not supported by the server.
+ This is a precautionary measure, if the client lies. See Bug#27944.
+ */
+ thd->client_capabilities&= server_capabilites;
if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
thd->variables.sql_mode|= MODE_IGNORE_SPACE;
@@ -945,7 +940,7 @@ bool setup_connection_thread_globals(THD *thd)
*/
-bool login_connection(THD *thd)
+static bool login_connection(THD *thd)
{
NET *net= &thd->net;
int error;
@@ -983,7 +978,7 @@ bool login_connection(THD *thd)
This mainly updates status variables
*/
-void end_connection(THD *thd)
+static void end_connection(THD *thd)
{
NET *net= &thd->net;
plugin_thdvar_cleanup(thd);
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 2301b561797..5c4e93d4c74 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -88,6 +88,7 @@ class Materialized_cursor: public Server_side_cursor
public:
Materialized_cursor(select_result *result, TABLE *table);
+ int fill_item_list(THD *thd, List<Item> &send_fields);
virtual bool is_open() const { return table != 0; }
virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
@@ -109,6 +110,7 @@ class Select_materialize: public select_union
{
select_result *result; /**< the result object of the caller (PS or SP) */
public:
+ Materialized_cursor *materialized_cursor;
Select_materialize(select_result *result_arg) :result(result_arg) {}
virtual bool send_fields(List<Item> &list, uint flags);
};
@@ -151,7 +153,7 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result,
if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result)))
{
- delete result;
+ delete result_materialize;
return 1;
}
@@ -173,13 +175,13 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result,
/*
Possible options here:
- a sensitive cursor is open. In this case rc is 0 and
- result_materialize->table is NULL, or
+ result_materialize->materialized_cursor is NULL, or
- a materialized cursor is open. In this case rc is 0 and
- result_materialize->table is not NULL
- - an error occured during materializaton.
- result_materialize->table is not NULL, but rc != 0
+ result_materialize->materialized is not NULL
+ - an error occurred during materialization.
+ result_materialize->materialized_cursor is not NULL, but rc != 0
- successful completion of mysql_execute_command without
- a cursor: rc is 0, result_materialize->table is NULL,
+ a cursor: rc is 0, result_materialize->materialized_cursor is NULL,
sensitive_cursor is not open.
This is possible if some command writes directly to the
network, bypassing select_result mechanism. An example of
@@ -190,7 +192,7 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result,
if (sensitive_cursor->is_open())
{
- DBUG_ASSERT(!result_materialize->table);
+ DBUG_ASSERT(!result_materialize->materialized_cursor);
/*
It's safer if we grab THD state after mysql_execute_command
is finished and not in Sensitive_cursor::open(), because
@@ -201,18 +203,10 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result,
*pcursor= sensitive_cursor;
goto end;
}
- else if (result_materialize->table)
+ else if (result_materialize->materialized_cursor)
{
- Materialized_cursor *materialized_cursor;
- TABLE *table= result_materialize->table;
- MEM_ROOT *mem_root= &table->mem_root;
-
- if (!(materialized_cursor= new (mem_root)
- Materialized_cursor(result, table)))
- {
- rc= 1;
- goto err_open;
- }
+ Materialized_cursor *materialized_cursor=
+ result_materialize->materialized_cursor;
if ((rc= materialized_cursor->open(0)))
{
@@ -228,8 +222,6 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result,
err_open:
DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open()));
delete sensitive_cursor;
- if (result_materialize->table)
- free_tmp_table(thd, result_materialize->table);
end:
delete result_materialize;
return rc;
@@ -322,9 +314,10 @@ Sensitive_cursor::post_open(THD *thd)
close_at_commit= FALSE; /* reset in case we're reusing the cursor */
info= &ht_info[0];
- for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++)
+ for (Ha_trx_info *ha_trx_info= thd->transaction.stmt.ha_list;
+ ha_trx_info; ha_trx_info= ha_trx_info->next())
{
- handlerton *ht= *pht;
+ handlerton *ht= ha_trx_info->ht();
close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT);
if (ht->create_cursor_read_view)
{
@@ -553,6 +546,51 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg,
}
+/**
+ Preserve the original metadata that would be sent to the client.
+
+ @param thd Thread identifier.
+ @param send_fields List of fields that would be sent.
+*/
+
+int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
+{
+ Query_arena backup_arena;
+ int rc;
+ List_iterator_fast<Item> it_org(send_fields);
+ List_iterator_fast<Item> it_dst(item_list);
+ Item *item_org;
+ Item *item_dst;
+
+ thd->set_n_backup_active_arena(this, &backup_arena);
+
+ if ((rc= table->fill_item_list(&item_list)))
+ goto end;
+
+ DBUG_ASSERT(send_fields.elements == item_list.elements);
+
+ /*
+ Unless we preserve the original metadata, it will be lost,
+ since new fields describe columns of the temporary table.
+ Allocate a copy of the name for safety only. Currently
+ items with original names are always kept in memory,
+ but in case this changes a memory leak may be hard to notice.
+ */
+ while ((item_dst= it_dst++, item_org= it_org++))
+ {
+ Send_field send_field;
+ Item_ident *ident= static_cast<Item_ident *>(item_dst);
+ item_org->make_field(&send_field);
+
+ ident->db_name= thd->strdup(send_field.db_name);
+ ident->table_name= thd->strdup(send_field.table_name);
+ }
+end:
+ thd->restore_active_arena(this, &backup_arena);
+ /* Check for thd->is_error() in case of OOM */
+ return rc || thd->is_error();
+}
+
int Materialized_cursor::open(JOIN *join __attribute__((unused)))
{
THD *thd= fake_unit.thd;
@@ -561,8 +599,7 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
thd->set_n_backup_active_arena(this, &backup_arena);
/* Create a list of fields and start sequential scan */
- rc= (table->fill_item_list(&item_list) ||
- result->prepare(item_list, &fake_unit) ||
+ rc= (result->prepare(item_list, &fake_unit) ||
table->file->ha_rnd_init(TRUE));
thd->restore_active_arena(this, &backup_arena);
if (rc == 0)
@@ -668,6 +705,24 @@ bool Select_materialize::send_fields(List<Item> &list, uint flags)
if (create_result_table(unit->thd, unit->get_unit_column_types(),
FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, ""))
return TRUE;
+
+ materialized_cursor= new (&table->mem_root)
+ Materialized_cursor(result, table);
+
+ if (! materialized_cursor)
+ {
+ free_tmp_table(table->in_use, table);
+ table= 0;
+ return TRUE;
+ }
+ if (materialized_cursor->fill_item_list(unit->thd, list))
+ {
+ delete materialized_cursor;
+ table= 0;
+ materialized_cursor= 0;
+ return TRUE;
+ }
+
return FALSE;
}
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index b5f49b97ec9..2c7e0e82b3c 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -595,7 +595,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
In this case the entry should not be logged.
SIDE-EFFECTS
- 1. Report back to client that command succeeded (send_ok)
+ 1. Report back to client that command succeeded (my_ok)
2. Report errors to client
3. Log event to binary log
(The 'silent' flags turns off 1 and 3.)
@@ -606,7 +606,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
*/
-bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
+int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
bool silent)
{
char path[FN_REFLEN+16];
@@ -660,7 +660,7 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db);
if (!silent)
- send_ok(thd);
+ my_ok(thd);
error= 0;
goto exit;
}
@@ -749,7 +749,7 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
/* These DDL methods and logging protected with LOCK_mysql_create_db */
mysql_bin_log.write(&qinfo);
}
- send_ok(thd, result);
+ my_ok(thd, result);
}
exit:
@@ -826,7 +826,7 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
/* These DDL methods and logging protected with LOCK_mysql_create_db */
mysql_bin_log.write(&qinfo);
}
- send_ok(thd, result);
+ my_ok(thd, result);
exit:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
@@ -960,7 +960,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
}
thd->clear_error();
thd->server_status|= SERVER_STATUS_DB_DROPPED;
- send_ok(thd, (ulong) deleted);
+ my_ok(thd, (ulong) deleted);
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
}
else if (mysql_bin_log.is_open())
@@ -1111,12 +1111,17 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
/* Drop the table nicely */
*extension= 0; // Remove extension
TABLE_LIST *table_list=(TABLE_LIST*)
- thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+2);
+ thd->calloc(sizeof(*table_list) +
+ strlen(db) + 1 +
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH +
+ strlen(file->name) + 1);
+
if (!table_list)
- goto err;
+ goto err;
table_list->db= (char*) (table_list+1);
table_list->table_name= strmov(table_list->db, db) + 1;
VOID(filename_to_tablename(file->name, table_list->table_name,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH +
strlen(file->name) + 1));
table_list->alias= table_list->table_name; // If lower_case_table_names=2
table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index adaf4d537f1..225c23525ec 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -24,6 +24,14 @@
#include "sp_head.h"
#include "sql_trigger.h"
+/**
+ Implement DELETE SQL word.
+
+ @note Like implementations of other DDL/DML in MySQL, this function
+ relies on the caller to close the thread tables. This is done in the
+ end of dispatch_command().
+*/
+
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SQL_LIST *order, ha_rows limit, ulonglong options,
bool reset_auto_increment)
@@ -37,6 +45,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool transactional_table, safe_update, const_cond;
bool const_cond_result;
ha_rows deleted= 0;
+ bool triggers_applicable;
uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
THD::killed_state killed_status= THD::NOT_KILLED;
@@ -94,6 +103,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
/* Error evaluating val_int(). */
DBUG_RETURN(TRUE);
}
+
/*
Test if the user wants to delete all rows and deletion doesn't have
any side-effects (because of triggers), so we can use optimized
@@ -150,7 +160,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
- send_ok(thd, (ha_rows) thd->row_count_func); // No matching records
+ my_ok(thd, (ha_rows) thd->row_count_func); // No matching records
DBUG_RETURN(0);
}
#endif
@@ -167,7 +177,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select;
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
- send_ok(thd, (ha_rows) thd->row_count_func);
+ my_ok(thd, (ha_rows) thd->row_count_func);
/*
We don't need to call reset_auto_increment in this case, because
mysql_truncate always gives a NULL conds argument, hence we never
@@ -241,7 +251,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
init_ftfuncs(thd, select_lex, 1);
thd_proc_info(thd, "updating");
- if (table->triggers &&
+
+ /* NOTE: TRUNCATE must not invoke triggers. */
+
+ triggers_applicable= table->triggers &&
+ thd->lex->sql_command != SQLCOM_TRUNCATE;
+
+ if (triggers_applicable &&
table->triggers->has_triggers(TRG_EVENT_DELETE,
TRG_ACTION_AFTER))
{
@@ -266,7 +282,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(select && select->skip_record())&& ! thd->is_error() )
{
- if (table->triggers &&
+ if (triggers_applicable &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
{
@@ -277,7 +293,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
- if (table->triggers &&
+ if (triggers_applicable &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
{
@@ -380,21 +396,10 @@ cleanup:
}
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
- if (transactional_table)
- {
- if (ha_autocommit_or_rollback(thd,error >= 0))
- error=1;
- }
-
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error))
{
thd->row_count_func= deleted;
- send_ok(thd, (ha_rows) thd->row_count_func);
+ my_ok(thd, (ha_rows) thd->row_count_func);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
DBUG_RETURN(error >= 0 || thd->is_error());
@@ -414,13 +419,26 @@ cleanup:
FALSE OK
TRUE error
*/
-bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
{
Item *fake_conds= 0;
SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_delete");
List<Item> all_fields;
+ /*
+ Statement-based replication of DELETE ... LIMIT is not safe as order of
+ rows is not defined, so in mixed mode we go to row-based.
+
+ Note that we may consider a statement as safe if ORDER BY primary_key
+ is present. However it may confuse users to see very similiar statements
+ replicated differently.
+ */
+ if (thd->lex->current_select->select_limit)
+ {
+ thd->lex->set_stmt_unsafe();
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
thd->lex->allow_sum_func= 0;
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
@@ -477,7 +495,7 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
TRUE Error
*/
-bool mysql_multi_delete_prepare(THD *thd)
+int mysql_multi_delete_prepare(THD *thd)
{
LEX *lex= thd->lex;
TABLE_LIST *aux_tables= (TABLE_LIST *)lex->auxiliary_table_list.first;
@@ -751,11 +769,9 @@ void multi_delete::abort()
The same if all tables are transactional, regardless of where we are.
In all other cases do attempt deletes ...
*/
- if ((table_being_deleted == delete_tables &&
- table_being_deleted->table->file->has_transactions()) ||
- !normal_tables)
- ha_rollback_stmt(thd);
- else if (do_delete)
+ if (do_delete && normal_tables &&
+ (table_being_deleted != delete_tables ||
+ !table_being_deleted->table->file->has_transactions()))
{
/*
We have to execute the recorded do_deletes() and write info into the
@@ -780,8 +796,6 @@ void multi_delete::abort()
}
thd->transaction.all.modified_non_trans_table= true;
}
- DBUG_ASSERT(!normal_tables || !deleted ||
- thd->transaction.stmt.modified_non_trans_table);
DBUG_VOID_RETURN;
}
@@ -899,8 +913,6 @@ bool multi_delete::send_eof()
{
query_cache_invalidate3(thd, delete_tables, 1);
}
- DBUG_ASSERT(!normal_tables || !deleted ||
- thd->transaction.stmt.modified_non_trans_table);
if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table)
{
if (mysql_bin_log.is_open())
@@ -921,15 +933,10 @@ bool multi_delete::send_eof()
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
- /* Commit or rollback the current SQL statement */
- if (transactional_tables)
- if (ha_autocommit_or_rollback(thd,local_error > 0))
- local_error=1;
-
if (!local_error)
{
thd->row_count_func= deleted;
- ::send_ok(thd, (ha_rows) thd->row_count_func);
+ ::my_ok(thd, (ha_rows) thd->row_count_func);
}
return 0;
}
@@ -985,7 +992,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
my_free((char*) table,MYF(0));
/*
If we return here we will not have logged the truncation to the bin log
- and we will not send_ok() to the client.
+ and we will not my_ok() to the client.
*/
goto end;
}
@@ -1034,7 +1041,7 @@ end:
we don't test current_stmt_binlog_row_based.
*/
write_bin_log(thd, TRUE, thd->query, thd->query_length);
- send_ok(thd); // This should return record count
+ my_ok(thd); // This should return record count
}
VOID(pthread_mutex_lock(&LOCK_open));
unlock_table_name(thd, table_list);
@@ -1063,6 +1070,12 @@ trunc_by_del:
error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
HA_POS_ERROR, LL(0), TRUE);
ha_enable_transaction(thd, TRUE);
+ /*
+ Safety, in case the engine ignored ha_enable_transaction(FALSE)
+ above. Also clears thd->transaction.*.
+ */
+ error= ha_autocommit_or_rollback(thd, error);
+ ha_commit(thd);
thd->options= save_options;
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(error);
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index a3eb93f87da..8406a9eaf45 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -28,7 +28,17 @@ bool mysql_do(THD *thd, List<Item> &values)
while ((value = li++))
value->val_int();
free_underlaid_joins(thd, &thd->lex->select_lex);
- thd->clear_error(); // DO always is OK
- send_ok(thd);
+
+ if (thd->is_error())
+ {
+ /*
+ Rollback the effect of the statement, since next instruction
+ will clear the error and the rollback in the end of
+ dispatch_command() won't work.
+ */
+ ha_autocommit_or_rollback(thd, thd->is_error());
+ thd->clear_error(); // DO always is OK
+ }
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 89cff73d153..0b74e3455eb 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -249,6 +249,6 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index a4ba6f1140b..28a9fb5c78e 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -298,7 +298,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
tables->table->open_by_handler= 1;
if (! reopen)
- send_ok(thd);
+ my_ok(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
@@ -350,7 +350,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(TRUE);
}
- send_ok(thd);
+ my_ok(thd);
DBUG_PRINT("exit", ("OK"));
DBUG_RETURN(FALSE);
}
@@ -658,7 +658,7 @@ retry:
}
ok:
mysql_unlock_tables(thd,lock);
- send_eof(thd);
+ my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 0d633ce86ac..e424425272e 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -779,7 +779,7 @@ bool mysqld_help(THD *thd, const char *mask)
if (send_variant_2_list(mem_root,protocol, &categories_list, "Y", 0))
goto error;
}
- send_eof(thd);
+ my_eof(thd);
close_system_tables(thd, &open_tables_state_backup);
DBUG_RETURN(FALSE);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index cb0728490b7..394a3d2e7b3 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -541,6 +541,10 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
/**
INSERT statement implementation
+
+ @note Like implementations of other DDL/DML in MySQL, this function
+ relies on the caller to close the thread tables. This is done in the
+ end of dispatch_command().
*/
bool mysql_insert(THD *thd,TABLE_LIST *table_list,
@@ -697,8 +701,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error=0;
thd_proc_info(thd, "update");
- if (duplic != DUP_ERROR || ignore)
- table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (duplic == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -716,8 +718,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
values_list.elements, and - if nothing else - to initialize
the code to make the call of end_bulk_insert() below safe.
*/
- if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode)
- table->file->ha_start_bulk_insert(values_list.elements);
+#ifndef EMBEDDED_LIBRARY
+ if (lock_type != TL_WRITE_DELAYED)
+#endif /* EMBEDDED_LIBRARY */
+ {
+ if (duplic != DUP_ERROR || ignore)
+ table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ if (!thd->prelocked_mode)
+ table->file->ha_start_bulk_insert(values_list.elements);
+ }
thd->abort_on_warning= (!ignore && (thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
@@ -836,6 +845,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
table->file->print_error(my_errno,MYF(0));
error=1;
}
+ if (duplic != DUP_ERROR || ignore)
+ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+
transactional_table= table->file->has_transactions();
if ((changed= (info.copied || info.deleted || info.updated)))
@@ -893,12 +905,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
DBUG_ASSERT(transactional_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
- if (transactional_table)
- error=ha_autocommit_or_rollback(thd,error);
-
+
if (thd->lock)
{
- mysql_unlock_tables(thd, thd->lock);
/*
Invalidate the table in the query cache if something changed
after unlocking when changes become fisible.
@@ -909,7 +918,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
{
query_cache_invalidate3(thd, table_list, 1);
}
- thd->lock=0;
}
}
thd_proc_info(thd, "end");
@@ -932,8 +940,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
table->next_number_field=0;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
table->auto_increment_field_not_null= FALSE;
- if (duplic != DUP_ERROR || ignore)
- table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
if (duplic == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
@@ -946,7 +952,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->row_count_func= info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
info.touched : info.updated);
- send_ok(thd, (ulong) thd->row_count_func, id);
+ my_ok(thd, (ulong) thd->row_count_func, id);
}
else
{
@@ -961,7 +967,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.deleted + updated), (ulong) thd->cuted_fields);
thd->row_count_func= info.copied + info.deleted + updated;
- ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
+ ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
}
thd->abort_on_warning= 0;
DBUG_RETURN(FALSE);
@@ -1267,7 +1273,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds);
select_lex->first_execution= 0;
}
- if (duplic == DUP_UPDATE || duplic == DUP_REPLACE)
+ /*
+ Only call prepare_for_posistion() if we are not performing a DELAYED
+ operation. It will instead be executed by delayed insert thread.
+ */
+ if ((duplic == DUP_UPDATE || duplic == DUP_REPLACE) &&
+ (table->reginfo.lock_type != TL_WRITE_DELAYED))
table->prepare_for_position();
DBUG_RETURN(FALSE);
}
@@ -2426,6 +2437,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
*/
di->table->file->ha_release_auto_increment();
mysql_unlock_tables(thd, lock);
+ ha_autocommit_or_rollback(thd, 0);
di->group_count=0;
pthread_mutex_lock(&di->mutex);
}
@@ -2445,7 +2457,7 @@ err:
first call to ha_*_row() instead. Remove code that are used to
cover for the case outlined above.
*/
- ha_rollback_stmt(thd);
+ ha_autocommit_or_rollback(thd, 1);
#ifndef __WIN__
end:
@@ -2764,6 +2776,19 @@ bool mysql_insert_select_prepare(THD *thd)
DBUG_ENTER("mysql_insert_select_prepare");
/*
+ Statement-based replication of INSERT ... SELECT ... LIMIT is not safe
+ as order of rows is not defined, so in mixed mode we go to row-based.
+
+ Note that we may consider a statement as safe if ORDER BY primary_key
+ is present or we SELECT a constant. However it may confuse users to
+ see very similiar statements replicated differently.
+ */
+ if (lex->current_select->select_limit)
+ {
+ lex->set_stmt_unsafe();
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
+ /*
SELECT_LEX do not belong to INSERT statement, so we can't add WHERE
clause if table is VIEW
*/
@@ -3139,18 +3164,6 @@ bool select_insert::send_eof()
thd->query, thd->query_length,
trans_table, FALSE, killed_status);
}
- /*
- We will call ha_autocommit_or_rollback() also for
- non-transactional tables under row-based replication: there might
- be events in the binary logs transaction, and we need to write
- them to the binary log.
- */
- if (trans_table || thd->current_stmt_binlog_row_based)
- {
- int error2= ha_autocommit_or_rollback(thd, error);
- if (error2 && !error)
- error= error2;
- }
table->file->ha_release_auto_increment();
if (error)
@@ -3174,7 +3187,7 @@ bool select_insert::send_eof()
(thd->arg_of_last_insert_id_function ?
thd->first_successful_insert_id_in_prev_stmt :
(info.copied ? autoinc_value_of_last_inserted_row : 0));
- ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
+ ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
DBUG_RETURN(0);
}
@@ -3228,7 +3241,6 @@ void select_insert::abort() {
table->file->ha_release_auto_increment();
}
- ha_rollback_stmt(thd);
DBUG_VOID_RETURN;
}
@@ -3678,7 +3690,10 @@ bool select_create::send_eof()
nevertheless.
*/
if (!table->s->tmp_table)
- ha_commit(thd); // Can fail, but we proceed anyway
+ {
+ ha_autocommit_or_rollback(thd, 0);
+ end_active_trans(thd);
+ }
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
@@ -3697,21 +3712,15 @@ void select_create::abort()
{
DBUG_ENTER("select_create::abort");
- /*
- Disable binlog, because we "roll back" partial inserts in ::abort
- by removing the table, even for non-transactional tables.
- */
- tmp_disable_binlog(thd);
- select_insert::abort();
- reenable_binlog(thd);
-
if ((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0)
ha_enable_transaction(thd, TRUE);
/*
- We roll back the statement, including truncating the transaction
- cache of the binary log, if the statement failed.
-
+ In select_insert::abort() we roll back the statement, including
+ truncating the transaction cache of the binary log. To do this, we
+ pretend that the statement is transactional, even though it might
+ be the case that it was not.
+
We roll back the statement prior to deleting the table and prior
to releasing the lock on the table, since there might be potential
for failure if the rollback is executed after the drop or after
@@ -3721,8 +3730,11 @@ void select_create::abort()
of the table succeeded or not, since we need to reset the binary
log state.
*/
- if (thd->current_stmt_binlog_row_based)
- ha_rollback_stmt(thd);
+ tmp_disable_binlog(thd);
+ select_insert::abort();
+ thd->transaction.stmt.modified_non_trans_table= FALSE;
+ reenable_binlog(thd);
+
if (m_plock)
{
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 5352f8efbbb..449c2fccb0b 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1601,6 +1601,7 @@ void st_select_lex::init_select()
non_agg_fields.empty();
cond_value= having_value= Item::COND_UNDEF;
inner_refs_list.empty();
+ full_group_by_flag= 0;
}
/*
@@ -1836,8 +1837,6 @@ bool st_select_lex::test_limit()
"LIMIT & IN/ALL/ANY/SOME subquery");
return(1);
}
- // no sense in ORDER BY without LIMIT
- order_list.empty();
return(0);
}
@@ -1947,7 +1946,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
}
-void st_select_lex_unit::print(String *str)
+void st_select_lex_unit::print(String *str, enum_query_type query_type)
{
bool union_all= !union_distinct;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
@@ -1962,7 +1961,7 @@ void st_select_lex_unit::print(String *str)
}
if (sl->braces)
str->append('(');
- sl->print(thd, str);
+ sl->print(thd, str, query_type);
if (sl->braces)
str->append(')');
}
@@ -1971,16 +1970,19 @@ void st_select_lex_unit::print(String *str)
if (fake_select_lex->order_list.elements)
{
str->append(STRING_WITH_LEN(" order by "));
- fake_select_lex->print_order(str,
- (ORDER *) fake_select_lex->
- order_list.first);
+ fake_select_lex->print_order(
+ str,
+ (ORDER *) fake_select_lex->order_list.first,
+ query_type);
}
- fake_select_lex->print_limit(thd, str);
+ fake_select_lex->print_limit(thd, str, query_type);
}
}
-void st_select_lex::print_order(String *str, ORDER *order)
+void st_select_lex::print_order(String *str,
+ ORDER *order,
+ enum_query_type query_type)
{
for (; order; order= order->next)
{
@@ -1991,7 +1993,7 @@ void st_select_lex::print_order(String *str, ORDER *order)
str->append(buffer, length);
}
else
- (*order->item)->print(str);
+ (*order->item)->print(str, query_type);
if (!order->asc)
str->append(STRING_WITH_LEN(" desc"));
if (order->next)
@@ -2000,7 +2002,9 @@ void st_select_lex::print_order(String *str, ORDER *order)
}
-void st_select_lex::print_limit(THD *thd, String *str)
+void st_select_lex::print_limit(THD *thd,
+ String *str,
+ enum_query_type query_type)
{
SELECT_LEX_UNIT *unit= master_unit();
Item_subselect *item= unit->item;
@@ -2019,10 +2023,10 @@ void st_select_lex::print_limit(THD *thd, String *str)
str->append(STRING_WITH_LEN(" limit "));
if (offset_limit)
{
- offset_limit->print(str);
+ offset_limit->print(str, query_type);
str->append(',');
}
- select_limit->print(str);
+ select_limit->print(str, query_type);
}
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index ef0a9bb11ef..443c85b4854 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -543,7 +543,7 @@ public:
inline void unclean() { cleaned= 0; }
void reinit_exec_mechanism();
- void print(String *str);
+ void print(String *str, enum_query_type query_type);
bool add_fake_select_lex(THD *thd);
void init_prepare_fake_select_lex(THD *thd);
@@ -692,6 +692,16 @@ public:
joins on the right.
*/
List<String> *prev_join_using;
+ /*
+ Bitmap used in the ONLY_FULL_GROUP_BY_MODE to prevent mixture of aggregate
+ functions and non aggregated fields when GROUP BY list is absent.
+ Bits:
+ 0 - non aggregated fields are used in this select,
+ defined as NON_AGG_FIELD_USED.
+ 1 - aggregate functions are used in this select,
+ defined as SUM_FUNC_USED.
+ */
+ uint8 full_group_by_flag;
void init_query();
void init_select();
st_select_lex_unit* master_unit();
@@ -762,9 +772,11 @@ public:
init_select();
}
bool setup_ref_array(THD *thd, uint order_group_num);
- void print(THD *thd, String *str);
- static void print_order(String *str, ORDER *order);
- void print_limit(THD *thd, String *str);
+ void print(THD *thd, String *str, enum_query_type query_type);
+ static void print_order(String *str,
+ ORDER *order,
+ enum_query_type query_type);
+ void print_limit(THD *thd, String *str, enum_query_type query_type);
void fix_prepare_information(THD *thd, Item **conds, Item **having_conds);
/*
Destroy the used execution plan (JOIN) of this subtree (this
@@ -1513,10 +1525,8 @@ typedef struct st_lex : public Query_tables_list
/* store original leaf_tables for INSERT SELECT and PS/SP */
TABLE_LIST *leaf_tables_insert;
- /** Start of SELECT of CREATE VIEW statement */
- const char* create_view_select_start;
- /** End of SELECT of CREATE VIEW statement */
- const char* create_view_select_end;
+ /** SELECT of CREATE VIEW statement */
+ LEX_STRING create_view_select;
/** Start of 'ON table', in trigger statements. */
const char* raw_trg_on_table_name_begin;
@@ -1710,8 +1720,6 @@ typedef struct st_lex : public Query_tables_list
*/
bool use_only_table_context;
- LEX_STRING view_body_utf8;
-
/*
Reference to a struct that contains information in various commands
to add/create/drop/change table spaces.
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index d27244ee5c6..5c77848722d 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -110,7 +110,7 @@ static bool write_execute_load_query_log_event(THD *thd,
TRUE - error / FALSE - success
*/
-bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
+int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
List<Item> &fields_vars, List<Item> &set_fields,
List<Item> &set_values,
enum enum_duplicates handle_duplicates, bool ignore,
@@ -237,9 +237,11 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
while ((item= it++))
{
- if (item->type() == Item::FIELD_ITEM)
+ Item *real_item= item->real_item();
+
+ if (real_item->type() == Item::FIELD_ITEM)
{
- Field *field= ((Item_field*)item)->field;
+ Field *field= ((Item_field*)real_item)->field;
if (field->flags & BLOB_FLAG)
{
use_blobs= 1;
@@ -248,7 +250,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
else
tot_length+= field->field_length;
}
- else
+ else if (item->type() == Item::STRING_ITEM)
use_vars= 1;
}
if (use_blobs && !ex->line_term->length() && !field_term->length())
@@ -470,9 +472,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
}
#endif /*!EMBEDDED_LIBRARY*/
- if (transactional_table)
- ha_autocommit_or_rollback(thd,error);
-
error= -1; // Error on read
goto err;
}
@@ -510,20 +509,13 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
}
#endif /*!EMBEDDED_LIBRARY*/
- if (transactional_table)
- error=ha_autocommit_or_rollback(thd,error);
/* ok to client sent only after binlog write and engine commit */
- send_ok(thd, info.copied + info.deleted, 0L, name);
+ my_ok(thd, info.copied + info.deleted, 0L, name);
err:
DBUG_ASSERT(transactional_table || !(info.copied || info.deleted) ||
thd->transaction.stmt.modified_non_trans_table);
table->file->ha_release_auto_increment();
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
table->auto_increment_field_not_null= FALSE;
thd->abort_on_warning= 0;
DBUG_RETURN(error);
@@ -711,6 +703,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
{
uint length;
uchar *pos;
+ Item *real_item;
if (read_info.read_field())
break;
@@ -722,14 +715,17 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
pos=read_info.row_start;
length=(uint) (read_info.row_end-pos);
+ real_item= item->real_item();
+
if (!read_info.enclosed &&
(enclosed_length && length == 4 &&
!memcmp(pos, STRING_WITH_LEN("NULL"))) ||
(length == 1 && read_info.found_null))
{
- if (item->type() == Item::FIELD_ITEM)
+
+ if (real_item->type() == Item::FIELD_ITEM)
{
- Field *field= ((Item_field *)item)->field;
+ Field *field= ((Item_field *)real_item)->field;
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name,
@@ -746,25 +742,39 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
ER_WARN_NULL_TO_NOTNULL, 1);
}
}
- else
+ else if (item->type() == Item::STRING_ITEM)
+ {
((Item_user_var_as_out_param *)item)->set_null_value(
read_info.read_charset);
+ }
+ else
+ {
+ my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name());
+ DBUG_RETURN(1);
+ }
+
continue;
}
- if (item->type() == Item::FIELD_ITEM)
+ if (real_item->type() == Item::FIELD_ITEM)
{
-
- Field *field= ((Item_field *)item)->field;
+ Field *field= ((Item_field *)real_item)->field;
field->set_notnull();
read_info.row_end[0]=0; // Safe to change end marker
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char*) pos, length, read_info.read_charset);
}
- else
+ else if (item->type() == Item::STRING_ITEM)
+ {
((Item_user_var_as_out_param *)item)->set_value((char*) pos, length,
- read_info.read_charset);
+ read_info.read_charset);
+ }
+ else
+ {
+ my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name());
+ DBUG_RETURN(1);
+ }
}
if (read_info.error)
break;
@@ -780,9 +790,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
break;
for (; item ; item= it++)
{
- if (item->type() == Item::FIELD_ITEM)
+ Item *real_item= item->real_item();
+ if (real_item->type() == Item::FIELD_ITEM)
{
- Field *field= ((Item_field *)item)->field;
+ Field *field= ((Item_field *)real_item)->field;
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name,
@@ -802,9 +813,16 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count);
}
- else
+ else if (item->type() == Item::STRING_ITEM)
+ {
((Item_user_var_as_out_param *)item)->set_null_value(
read_info.read_charset);
+ }
+ else
+ {
+ my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name());
+ DBUG_RETURN(1);
+ }
}
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index fa7f8dad6bb..712bdc85ae3 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -510,7 +510,7 @@ static bool check_merge_table_access(THD *thd, char *db,
tlist->db= db; /* purecov: inspected */
}
error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- table_list,0);
+ table_list, UINT_MAX, FALSE);
}
return error;
}
@@ -921,7 +921,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (!mysql_change_db(thd, &tmp, FALSE))
{
general_log_write(thd, command, thd->db, thd->db_length);
- send_ok(thd);
+ my_ok(thd);
}
break;
}
@@ -929,7 +929,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_REGISTER_SLAVE:
{
if (!register_slave(thd, (uchar*)packet, packet_length))
- send_ok(thd);
+ my_ok(thd);
break;
}
#endif
@@ -1330,7 +1330,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
general_log_print(thd, command, NullS);
if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
- send_ok(thd);
+ my_ok(thd);
break;
}
#ifndef EMBEDDED_LIBRARY
@@ -1356,7 +1356,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
general_log_print(thd, command, NullS);
- send_eof(thd);
+ my_eof(thd);
close_thread_tables(thd); // Free before kill
kill_mysql();
error=TRUE;
@@ -1409,7 +1409,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
#ifdef EMBEDDED_LIBRARY
/* Store the buffer in permanent memory */
- send_ok(thd, 0, 0, buff);
+ my_ok(thd, 0, 0, buff);
#else
VOID(my_net_write(net, (uchar*) buff, length));
VOID(net_flush(net));
@@ -1419,7 +1419,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_PING:
status_var_increment(thd->status_var.com_other);
- send_ok(thd); // Tell client we are alive
+ my_ok(thd); // Tell client we are alive
break;
case COM_PROCESS_INFO:
status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]);
@@ -1446,11 +1446,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
switch (opt_command) {
case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
- send_eof(thd);
+ my_eof(thd);
break;
case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
- send_eof(thd);
+ my_eof(thd);
break;
default:
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
@@ -1464,7 +1464,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break; /* purecov: inspected */
mysql_print_status();
general_log_print(thd, command, NullS);
- send_eof(thd);
+ my_eof(thd);
break;
case COM_SLEEP:
case COM_CONNECT: // Impossible here
@@ -1476,21 +1476,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
- thd_proc_info(thd, "closing tables");
- /* Free tables */
- close_thread_tables(thd);
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->main_da.can_overwrite_status= TRUE;
+ ha_autocommit_or_rollback(thd, thd->is_error());
+ thd->main_da.can_overwrite_status= FALSE;
+
+ thd->transaction.stmt.reset();
- /*
- assume handlers auto-commit (if some doesn't - transaction handling
- in MySQL should be redesigned to support it; it's a big change,
- and it's not worth it - better to commit explicitly only writing
- transactions, read-only ones should better take care of themselves.
- saves some work in 2pc too)
- see also sql_base.cc - close_thread_tables()
- */
- bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
- if (!thd->active_transaction())
- thd->transaction.xid_state.xid.null();
/* report error issued during command execution */
if (thd->killed_errno())
@@ -1507,6 +1499,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
net_end_statement(thd);
query_cache_end_of_result(thd);
+ thd->proc_info= "closing tables";
+ /* Free tables */
+ close_thread_tables(thd);
+
log_slow_statement(thd);
thd_proc_info(thd, "cleaning up");
@@ -1872,10 +1868,10 @@ bool sp_process_definer(THD *thd)
TRUE Error
*/
-bool
+int
mysql_execute_command(THD *thd)
{
- bool res= FALSE;
+ int res= FALSE;
bool need_start_waiting= FALSE; // have protection against global read lock
int up_result= 0;
LEX *lex= thd->lex;
@@ -2058,7 +2054,7 @@ mysql_execute_command(THD *thd)
res= check_table_access(thd,
lex->exchange ? SELECT_ACL | FILE_ACL :
SELECT_ACL,
- all_tables, 0);
+ all_tables, UINT_MAX, FALSE);
}
else
res= check_access(thd,
@@ -2083,7 +2079,7 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_DO:
- if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables))
goto error;
@@ -2091,7 +2087,7 @@ mysql_execute_command(THD *thd)
break;
case SQLCOM_EMPTY_QUERY:
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_HELP:
@@ -2192,7 +2188,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_BACKUP_TABLE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
@@ -2204,7 +2200,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_RESTORE_TABLE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, INSERT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
@@ -2257,7 +2253,7 @@ mysql_execute_command(THD *thd)
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
"the master info structure does not exist");
- send_ok(thd);
+ my_ok(thd);
}
pthread_mutex_unlock(&LOCK_active_mi);
break;
@@ -2281,14 +2277,14 @@ mysql_execute_command(THD *thd)
#endif /* HAVE_REPLICATION */
case SQLCOM_SHOW_ENGINE_STATUS:
{
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PROCESS_ACL))
goto error;
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
break;
}
case SQLCOM_SHOW_ENGINE_MUTEX:
{
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PROCESS_ACL))
goto error;
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
break;
@@ -2315,7 +2311,7 @@ mysql_execute_command(THD *thd)
if (!fetch_master_table(thd, first_table->db, first_table->table_name,
active_mi, 0, 0))
{
- send_ok(thd);
+ my_ok(thd);
}
pthread_mutex_unlock(&LOCK_active_mi);
break;
@@ -2366,15 +2362,7 @@ mysql_execute_command(THD *thd)
/* Might have been updated in create_table_precheck */
create_info.alias= create_table->alias;
-#ifndef HAVE_READLINK
- if (create_info.data_file_name)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "DATA DIRECTORY option ignored");
- if (create_info.index_file_name)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "INDEX DIRECTORY option ignored");
- create_info.data_file_name= create_info.index_file_name= NULL;
-#else
+#ifdef HAVE_READLINK
/* Fix names if symlinked tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
create_table->table_name) ||
@@ -2526,7 +2514,7 @@ mysql_execute_command(THD *thd)
&alter_info, 0, 0);
}
if (!res)
- send_ok(thd);
+ my_ok(thd);
}
/* put tables back for PS rexecuting */
@@ -2753,7 +2741,8 @@ end_with_restore_list:
case SQLCOM_CHECKSUM:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
@@ -2761,7 +2750,8 @@ end_with_restore_list:
case SQLCOM_REPAIR:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res= mysql_repair_table(thd, first_table, &lex->check_opt);
@@ -2780,7 +2770,8 @@ end_with_restore_list:
case SQLCOM_CHECK:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res = mysql_check_table(thd, first_table, &lex->check_opt);
@@ -2791,7 +2782,8 @@ end_with_restore_list:
case SQLCOM_ANALYZE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res= mysql_analyze_table(thd, first_table, &lex->check_opt);
@@ -2811,7 +2803,8 @@ end_with_restore_list:
case SQLCOM_OPTIMIZE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
@@ -3016,10 +3009,8 @@ end_with_restore_list:
/* INSERT ... SELECT should invalidate only the very first table */
TABLE_LIST *save_table= first_table->next_local;
first_table->next_local= 0;
- mysql_unlock_tables(thd, thd->lock);
query_cache_invalidate3(thd, first_table, 1);
first_table->next_local= save_table;
- thd->lock=0;
}
delete sel_result;
}
@@ -3140,7 +3131,7 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (!lex->drop_temporary)
{
- if (check_table_access(thd, DROP_ACL, all_tables, 0))
+ if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
goto error;
@@ -3206,7 +3197,7 @@ end_with_restore_list:
LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };
if (!mysql_change_db(thd, &db_str, FALSE))
- send_ok(thd);
+ my_ok(thd);
break;
}
@@ -3244,7 +3235,7 @@ end_with_restore_list:
if (lex->autocommit && end_active_trans(thd))
goto error;
- if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables)))
goto error;
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
@@ -3259,8 +3250,20 @@ end_with_restore_list:
about the ONE_SHOT property of that SET. So we use a |= instead of = .
*/
thd->one_shot_set|= lex->one_shot_set;
- send_ok(thd);
+ my_ok(thd);
}
+ else
+ {
+ /*
+ We encountered some sort of error, but no message was sent.
+ Send something semi-generic here since we don't know which
+ assignment in the list caused the error.
+ */
+ if (!thd->is_error())
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"SET");
+ goto error;
+ }
+
break;
}
@@ -3279,14 +3282,15 @@ end_with_restore_list:
}
if (thd->global_read_lock)
unlock_global_read_lock(thd);
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_LOCK_TABLES:
unlock_locked_tables(thd);
/* we must end the trasaction first, regardless of anything */
if (end_active_trans(thd))
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
@@ -3299,7 +3303,7 @@ end_with_restore_list:
#endif /*HAVE_QUERY_CACHE*/
thd->locked_tables=thd->lock;
thd->lock=0;
- send_ok(thd);
+ my_ok(thd);
}
else
{
@@ -3436,7 +3440,7 @@ end_with_restore_list:
res= mysql_upgrade_db(thd, db);
if (!res)
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_ALTER_DB:
@@ -3521,7 +3525,7 @@ end_with_restore_list:
}
DBUG_PRINT("info",("DDL error code=%d", res));
if (!res)
- send_ok(thd);
+ my_ok(thd);
} while (0);
/* Don't do it, if we are inside a SP */
@@ -3540,7 +3544,7 @@ end_with_restore_list:
if (!(res= Events::drop_event(thd,
lex->spname->m_db, lex->spname->m_name,
lex->drop_if_exists)))
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_CREATE_FUNCTION: // UDF function
{
@@ -3548,7 +3552,7 @@ end_with_restore_list:
break;
#ifdef HAVE_DLOPEN
if (!(res = mysql_create_function(thd, &lex->udf)))
- send_ok(thd);
+ my_ok(thd);
#else
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
res= TRUE;
@@ -3565,7 +3569,7 @@ end_with_restore_list:
goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_create_user(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_DROP_USER:
@@ -3577,7 +3581,7 @@ end_with_restore_list:
goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_drop_user(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_RENAME_USER:
@@ -3589,7 +3593,7 @@ end_with_restore_list:
goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_rename_user(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_REVOKE_ALL:
@@ -3601,7 +3605,7 @@ end_with_restore_list:
break;
/* Conditionally writes to binlog */
if (!(res = mysql_revoke_all(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_REVOKE:
@@ -3738,7 +3742,7 @@ end_with_restore_list:
{
write_bin_log(thd, FALSE, thd->query, thd->query_length);
}
- send_ok(thd);
+ my_ok(thd);
}
break;
@@ -3780,7 +3784,7 @@ end_with_restore_list:
#endif
case SQLCOM_HA_OPEN:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))
goto error;
res= mysql_ha_open(thd, first_table, 0);
break;
@@ -3810,19 +3814,19 @@ end_with_restore_list:
}
if (begin_trans(thd))
goto error;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_COMMIT:
if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
goto error;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_ROLLBACK:
if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
goto error;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_RELEASE_SAVEPOINT:
{
@@ -3839,7 +3843,7 @@ end_with_restore_list:
if (ha_release_savepoint(thd, sv))
res= TRUE; // cannot happen
else
- send_ok(thd);
+ my_ok(thd);
thd->transaction.savepoints=sv->prev;
}
else
@@ -3868,7 +3872,7 @@ end_with_restore_list:
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
- send_ok(thd);
+ my_ok(thd);
}
thd->transaction.savepoints=sv;
}
@@ -3879,7 +3883,7 @@ end_with_restore_list:
case SQLCOM_SAVEPOINT:
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
thd->in_sub_stmt) || !opt_using_transactions)
- send_ok(thd);
+ my_ok(thd);
else
{
SAVEPOINT **sv, *newsv;
@@ -3916,7 +3920,7 @@ end_with_restore_list:
{
newsv->prev=thd->transaction.savepoints;
thd->transaction.savepoints=newsv;
- send_ok(thd);
+ my_ok(thd);
}
}
break;
@@ -3989,7 +3993,6 @@ end_with_restore_list:
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_PROC_AUTO_GRANT_FAIL,
ER(ER_PROC_AUTO_GRANT_FAIL));
- close_thread_tables(thd);
}
#endif
break;
@@ -4017,7 +4020,7 @@ end_with_restore_list:
create_sp_error:
if (sp_result != SP_OK )
goto error;
- send_ok(thd);
+ my_ok(thd);
break; /* break super switch */
} /* end case group bracket */
case SQLCOM_CALL:
@@ -4028,7 +4031,7 @@ create_sp_error:
This will cache all SP and SF and open and lock all tables
required for execution.
*/
- if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables))
goto error;
@@ -4110,8 +4113,8 @@ create_sp_error:
thd->server_status&= ~bits_to_be_cleared;
if (!res)
- send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
- thd->row_count_func));
+ my_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
+ thd->row_count_func));
else
{
DBUG_ASSERT(thd->is_error() || thd->killed);
@@ -4188,7 +4191,7 @@ create_sp_error:
switch (sp_result)
{
case SP_OK:
- send_ok(thd);
+ my_ok(thd);
break;
case SP_KEY_NOT_FOUND:
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
@@ -4253,7 +4256,7 @@ create_sp_error:
if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
{
- send_ok(thd);
+ my_ok(thd);
break;
}
}
@@ -4270,7 +4273,7 @@ create_sp_error:
res= sp_result;
switch (sp_result) {
case SP_OK:
- send_ok(thd);
+ my_ok(thd);
break;
case SP_KEY_NOT_FOUND:
if (lex->drop_if_exists)
@@ -4279,7 +4282,7 @@ create_sp_error:
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
SP_COM_STRING(lex), lex->spname->m_name.str);
res= FALSE;
- send_ok(thd);
+ my_ok(thd);
break;
}
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
@@ -4375,7 +4378,7 @@ create_sp_error:
}
case SQLCOM_DROP_VIEW:
{
- if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
+ if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) ||
end_active_trans(thd))
goto error;
/* Conditionally writes to binlog. */
@@ -4411,7 +4414,7 @@ create_sp_error:
break;
}
thd->transaction.xid_state.xa_state=XA_ACTIVE;
- send_ok(thd);
+ my_ok(thd);
break;
}
if (thd->lex->xa_opt != XA_NONE)
@@ -4442,7 +4445,7 @@ create_sp_error:
thd->transaction.all.modified_non_trans_table= FALSE;
thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_XA_END:
/* fake it */
@@ -4463,7 +4466,7 @@ create_sp_error:
break;
}
thd->transaction.xid_state.xa_state=XA_IDLE;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_XA_PREPARE:
if (thd->transaction.xid_state.xa_state != XA_IDLE)
@@ -4485,7 +4488,7 @@ create_sp_error:
break;
}
thd->transaction.xid_state.xa_state=XA_PREPARED;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_XA_COMMIT:
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
@@ -4497,7 +4500,7 @@ create_sp_error:
{
ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
xid_cache_delete(xs);
- send_ok(thd);
+ my_ok(thd);
}
break;
}
@@ -4508,7 +4511,7 @@ create_sp_error:
if ((r= ha_commit(thd)))
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
else
- send_ok(thd);
+ my_ok(thd);
}
else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
thd->lex->xa_opt == XA_NONE)
@@ -4523,7 +4526,7 @@ create_sp_error:
if (ha_commit_one_phase(thd, 1))
my_error(ER_XAER_RMERR, MYF(0));
else
- send_ok(thd);
+ my_ok(thd);
start_waiting_global_read_lock(thd);
}
}
@@ -4549,7 +4552,7 @@ create_sp_error:
{
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
xid_cache_delete(xs);
- send_ok(thd);
+ my_ok(thd);
}
break;
}
@@ -4563,7 +4566,7 @@ create_sp_error:
if (ha_rollback(thd))
my_error(ER_XAER_RMERR, MYF(0));
else
- send_ok(thd);
+ my_ok(thd);
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
@@ -4577,16 +4580,16 @@ create_sp_error:
if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0, thd->db ? is_schema_db(thd->db) : 0))
break;
if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_INSTALL_PLUGIN:
if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
&thd->lex->ident)))
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_UNINSTALL_PLUGIN:
if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_BINLOG_BASE64_EVENT:
{
@@ -4613,7 +4616,7 @@ create_sp_error:
my_error(error, MYF(0), lex->server_options.server_name);
break;
}
- send_ok(thd, 1);
+ my_ok(thd, 1);
break;
}
case SQLCOM_ALTER_SERVER:
@@ -4632,7 +4635,7 @@ create_sp_error:
my_error(error, MYF(0), lex->server_options.server_name);
break;
}
- send_ok(thd, 1);
+ my_ok(thd, 1);
break;
}
case SQLCOM_DROP_SERVER:
@@ -4654,18 +4657,18 @@ create_sp_error:
}
else
{
- send_ok(thd, 0);
+ my_ok(thd, 0);
}
break;
}
- send_ok(thd, 1);
+ my_ok(thd, 1);
break;
}
default:
#ifndef EMBEDDED_LIBRARY
DBUG_ASSERT(0); /* Impossible */
#endif
- send_ok(thd);
+ my_ok(thd);
break;
}
thd_proc_info(thd, "query end");
@@ -4741,7 +4744,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
char buff[1024];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.length(0);
- thd->lex->unit.print(&str);
+ thd->lex->unit.print(&str, QT_ORDINARY);
str.append('\0');
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_YES, str.ptr());
@@ -4851,7 +4854,7 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
subselects_tables= subselects_tables->next_global;
}
if (subselects_tables &&
- (check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
+ (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE)))
return 1;
}
return 0;
@@ -4993,35 +4996,6 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
-/**
- check for global access and give descriptive error message if it fails.
-
- @param thd Thread handler
- @param want_access Use should have any of these global rights
-
- @warning
- One gets access right if one has ANY of the rights in want_access.
- This is useful as one in most cases only need one global right,
- but in some case we want to check if the user has SUPER or
- REPL_CLIENT_ACL rights.
-
- @retval
- 0 ok
- @retval
- 1 Access denied. In this case an error is sent to the client
-*/
-
-bool check_global_access(THD *thd, ulong want_access)
-{
- char command[128];
- if ((thd->security_ctx->master_access & want_access))
- return 0;
- get_privilege_desc(command, sizeof(command), want_access);
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
- return 1;
-}
-
-
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
switch (get_schema_table_idx(table->schema_table)) {
@@ -5084,11 +5058,12 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
/**
Check the privilege for all used tables.
- @param thd Thread context
- @param want_access Privileges requested
- @param tables List of tables to be checked
- @param no_errors FALSE/TRUE - report/don't report error to
- the client (using my_error() call).
+ @param thd Thread context
+ @param want_access Privileges requested
+ @param tables List of tables to be checked
+ @param number Check at most this number of tables.
+ @param no_errors FALSE/TRUE - report/don't report error to
+ the client (using my_error() call).
@note
Table privileges are cached in the table list for GRANT checking.
@@ -5097,25 +5072,25 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
(the latter should be either 0 or point to next_global member
of one of elements of this table list).
- @retval
- FALSE OK
- @retval
- TRUE Access denied
+ @retval FALSE OK
+ @retval TRUE Access denied
*/
bool
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
- bool no_errors)
+ uint number, bool no_errors)
{
TABLE_LIST *org_tables= tables;
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+ uint i= 0;
Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
/*
The check that first_not_own_table is not reached is for the case when
the given table list refers to the list for prelocking (contains tables
of other queries). For simple queries first_not_own_table is 0.
*/
- for (; tables != first_not_own_table; tables= tables->next_global)
+ for (; i < number && tables != first_not_own_table;
+ tables= tables->next_global, i++)
{
if (tables->security_ctx)
sctx= tables->security_ctx;
@@ -5165,7 +5140,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
}
thd->security_ctx= backup_ctx;
return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
- test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
+ test(want_access & EXTRA_ACL), number, no_errors);
deny:
thd->security_ctx= backup_ctx;
return TRUE;
@@ -5263,6 +5238,39 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
+
+/**
+ check for global access and give descriptive error message if it fails.
+
+ @param thd Thread handler
+ @param want_access Use should have any of these global rights
+
+ @warning
+ One gets access right if one has ANY of the rights in want_access.
+ This is useful as one in most cases only need one global right,
+ but in some case we want to check if the user has SUPER or
+ REPL_CLIENT_ACL rights.
+
+ @retval
+ 0 ok
+ @retval
+ 1 Access denied. In this case an error is sent to the client
+*/
+
+bool check_global_access(THD *thd, ulong want_access)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ char command[128];
+ if ((thd->security_ctx->master_access & want_access))
+ return 0;
+ get_privilege_desc(command, sizeof(command), want_access);
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ return 1;
+#else
+ return 0;
+#endif
+}
+
/****************************************************************************
Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/
@@ -6459,6 +6467,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
{
thd->thread_stack= (char*) &tmp_thd;
thd->store_globals();
+ lex_start(thd);
}
if (thd)
{
@@ -6466,6 +6475,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
result= 1;
if (grant_reload(thd))
result= 1;
+ if (servers_reload(thd))
+ result= 1; /* purecov: inspected */
}
if (tmp_thd)
{
@@ -6671,7 +6682,7 @@ void sql_kill(THD *thd, ulong id, bool only_kill_query)
{
uint error;
if (!(error= kill_one_thread(thd, id, only_kill_query)))
- send_ok(thd);
+ my_ok(thd);
else
my_error(error, MYF(0), id);
}
@@ -6895,7 +6906,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
/* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0);
- if (check_table_access(thd, SELECT_ACL, tables, 0))
+ if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
/*
@@ -6904,7 +6915,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
call check_table_access() safely.
*/
thd->lex->query_tables_own_last= 0;
- if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
+ if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE))
{
thd->lex->query_tables_own_last= save_query_tables_own_last;
DBUG_RETURN(TRUE);
@@ -7143,7 +7154,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
}
}
#endif
- if (tables && check_table_access(thd, SELECT_ACL, tables,0))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
goto err;
}
else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
@@ -7346,6 +7357,49 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
}
+/*
+ Check if path does not contain mysql data home directory
+ SYNOPSIS
+ test_if_data_home_dir()
+ dir directory
+ conv_home_dir converted data home directory
+ home_dir_len converted data home directory length
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+bool test_if_data_home_dir(const char *dir)
+{
+ char path[FN_REFLEN], conv_path[FN_REFLEN];
+ uint dir_len, home_dir_len= strlen(mysql_unpacked_real_data_home);
+ DBUG_ENTER("test_if_data_home_dir");
+
+ if (!dir)
+ DBUG_RETURN(0);
+
+ (void) fn_format(path, dir, "", "",
+ (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
+ dir_len= unpack_dirname(conv_path, dir);
+
+ if (home_dir_len < dir_len)
+ {
+ if (lower_case_file_system)
+ {
+ if (!my_strnncoll(character_set_filesystem,
+ (const uchar*) conv_path, home_dir_len,
+ (const uchar*) mysql_unpacked_real_data_home,
+ home_dir_len))
+ DBUG_RETURN(1);
+ }
+ else if (!memcmp(conv_path, mysql_unpacked_real_data_home, home_dir_len))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
extern int MYSQLparse(void *thd); // from sql_yacc.cc
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index eabf4526f7b..037da87be7f 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -3819,9 +3819,9 @@ bool mysql_unpack_partition(THD *thd,
DBUG_PRINT("info", ("Successful parse"));
part_info= lex.part_info;
- DBUG_PRINT("info", ("default engine = %d, default_db_type = %d",
- ha_legacy_type(part_info->default_engine_type),
- ha_legacy_type(default_db_type)));
+ DBUG_PRINT("info", ("default engine = %s, default_db_type = %s",
+ ha_resolve_storage_engine_name(part_info->default_engine_type),
+ ha_resolve_storage_engine_name(default_db_type)));
if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
{
if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
@@ -3863,6 +3863,8 @@ bool mysql_unpack_partition(THD *thd,
if (!part_info->default_engine_type)
part_info->default_engine_type= default_db_type;
DBUG_ASSERT(part_info->default_engine_type == default_db_type);
+ DBUG_ASSERT(part_info->default_engine_type->db_type != DB_TYPE_UNKNOWN);
+ DBUG_ASSERT(part_info->default_engine_type != partition_hton);
{
/*
@@ -3968,84 +3970,38 @@ static int fast_end_partition(THD *thd, ulonglong copied,
bool written_bin_log)
{
int error;
+ char tmp_name[80];
DBUG_ENTER("fast_end_partition");
thd->proc_info="end";
+
if (!is_empty)
query_cache_invalidate3(thd, table_list, 0);
- error= ha_commit_stmt(thd);
- if (ha_commit(thd))
- error= 1;
- if (!error || is_empty)
- {
- char tmp_name[80];
- if ((!is_empty) && (!written_bin_log) &&
- (!thd->lex->no_write_to_binlog))
- write_bin_log(thd, FALSE, thd->query, thd->query_length);
- close_thread_tables(thd);
- my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
- (ulong) (copied + deleted),
- (ulong) deleted,
- (ulong) 0);
- send_ok(thd, (ha_rows) (copied+deleted),0L,tmp_name);
- DBUG_RETURN(FALSE);
- }
- table->file->print_error(error, MYF(0));
- close_thread_tables(thd);
- DBUG_RETURN(TRUE);
-}
-
-
-/*
- Check engine mix that it is correct
- SYNOPSIS
- check_engine_condition()
- p_elem Partition element
- default_engine Have user specified engine on table level
- inout::engine_type Current engine used
- inout::first Is it first partition
- RETURN VALUE
- TRUE Failed check
- FALSE Ok
- DESCRIPTION
- (specified partition handler ) specified table handler
- (NDB, NDB) NDB OK
- (MYISAM, MYISAM) - OK
- (MYISAM, -) - NOT OK
- (MYISAM, -) MYISAM OK
- (- , MYISAM) - NOT OK
- (- , -) MYISAM OK
- (-,-) - OK
- (NDB, MYISAM) * NOT OK
-*/
-static bool check_engine_condition(partition_element *p_elem,
- bool default_engine,
- handlerton **engine_type,
- bool *first)
-{
- DBUG_ENTER("check_engine_condition");
+ error= ha_autocommit_or_rollback(thd, 0);
+ if (end_active_trans(thd))
+ error= 1;
- DBUG_PRINT("enter", ("def_eng = %u, first = %u", default_engine, *first));
- if (*first && default_engine)
- {
- *engine_type= p_elem->engine_type;
- }
- *first= FALSE;
- if ((!default_engine &&
- (p_elem->engine_type != (*engine_type) &&
- p_elem->engine_type)) ||
- (default_engine &&
- p_elem->engine_type != (*engine_type)))
+ if (error)
{
+ /* If error during commit, no need to rollback, it's done. */
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(TRUE);
}
- else
- {
- DBUG_RETURN(FALSE);
- }
+
+ if ((!is_empty) && (!written_bin_log) &&
+ (!thd->lex->no_write_to_binlog))
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
+
+ my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
+ (ulong) (copied + deleted),
+ (ulong) deleted,
+ (ulong) 0);
+ my_ok(thd, (ha_rows) (copied+deleted),0L, tmp_name);
+ DBUG_RETURN(FALSE);
}
+
/*
We need to check if engine used by all partitions can handle
partitioning natively.
@@ -4070,52 +4026,30 @@ static bool check_engine_condition(partition_element *p_elem,
static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val,
partition_info *part_info, THD *thd)
{
- List_iterator<partition_element> part_it(part_info->partitions);
- bool first= TRUE;
- bool default_engine;
- handlerton *engine_type= create_info->db_type;
+ bool table_engine_set;
+ handlerton *engine_type= part_info->default_engine_type;
handlerton *old_engine_type= engine_type;
- uint i= 0;
- uint no_parts= part_info->partitions.elements;
DBUG_ENTER("check_native_partitioned");
- default_engine= (create_info->used_fields & HA_CREATE_USED_ENGINE) ?
- FALSE : TRUE;
- DBUG_PRINT("info", ("engine_type = %u, default = %u",
- ha_legacy_type(engine_type),
- default_engine));
- if (no_parts)
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE)
{
- do
+ table_engine_set= TRUE;
+ engine_type= create_info->db_type;
+ }
+ else
+ {
+ table_engine_set= FALSE;
+ if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
{
- partition_element *part_elem= part_it++;
- if (part_info->is_sub_partitioned() &&
- part_elem->subpartitions.elements)
- {
- uint no_subparts= part_elem->subpartitions.elements;
- uint j= 0;
- List_iterator<partition_element> sub_it(part_elem->subpartitions);
- do
- {
- partition_element *sub_elem= sub_it++;
- if (check_engine_condition(sub_elem, default_engine,
- &engine_type, &first))
- goto error;
- } while (++j < no_subparts);
- /*
- In case of subpartitioning and defaults we allow that only
- subparts have specified engines, as long as the parts haven't
- specified the wrong engine it's ok.
- */
- if (check_engine_condition(part_elem, FALSE,
- &engine_type, &first))
- goto error;
- }
- else if (check_engine_condition(part_elem, default_engine,
- &engine_type, &first))
- goto error;
- } while (++i < no_parts);
+ table_engine_set= TRUE;
+ DBUG_ASSERT(engine_type && engine_type != partition_hton);
+ }
}
+ DBUG_PRINT("info", ("engine_type = %s, table_engine_set = %u",
+ ha_resolve_storage_engine_name(engine_type),
+ table_engine_set));
+ if (part_info->check_engine_mix(engine_type, table_engine_set))
+ goto error;
/*
All engines are of the same type. Check if this engine supports
@@ -4212,7 +4146,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
- if (alter_info->flags == ALTER_TABLE_REORG)
+ if (alter_info->flags & ALTER_TABLE_REORG)
{
uint new_part_no, curr_part_no;
if (tab_part_info->part_type != HASH_PARTITION ||
@@ -4313,7 +4247,12 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
{
my_error(ER_NO_BINLOG_ERROR, MYF(0));
DBUG_RETURN(TRUE);
- }
+ }
+ if (tab_part_info->defined_max_value)
+ {
+ my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
if (no_new_partitions == 0)
{
my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0));
@@ -4538,7 +4477,7 @@ that are reorganised.
tab_part_info->is_auto_partitioned= FALSE;
}
}
- else if (alter_info->flags == ALTER_DROP_PARTITION)
+ else if (alter_info->flags & ALTER_DROP_PARTITION)
{
/*
Drop a partition from a range partition and list partitioning is
@@ -4742,7 +4681,7 @@ state of p1.
tab_part_info->is_auto_partitioned= FALSE;
}
}
- else if (alter_info->flags == ALTER_REORGANIZE_PARTITION)
+ else if (alter_info->flags & ALTER_REORGANIZE_PARTITION)
{
/*
Reorganise partitions takes a number of partitions that are next
@@ -4923,8 +4862,8 @@ the generated partition syntax in a correct manner.
}
*partition_changed= TRUE;
thd->work_part_info= tab_part_info;
- if (alter_info->flags == ALTER_ADD_PARTITION ||
- alter_info->flags == ALTER_REORGANIZE_PARTITION)
+ if (alter_info->flags & ALTER_ADD_PARTITION ||
+ alter_info->flags & ALTER_REORGANIZE_PARTITION)
{
if (tab_part_info->use_default_subpartitions &&
!alt_part_info->use_default_subpartitions)
@@ -5051,13 +4990,21 @@ the generated partition syntax in a correct manner.
DBUG_PRINT("info", ("partition changed"));
*partition_changed= TRUE;
}
- if (create_info->db_type == partition_hton)
+ /*
+ Set up partition default_engine_type either from the create_info
+ or from the previus table
+ */
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE)
+ part_info->default_engine_type= create_info->db_type;
+ else
{
- if (!part_info->default_engine_type)
+ if (table->part_info)
part_info->default_engine_type= table->part_info->default_engine_type;
+ else
+ part_info->default_engine_type= create_info->db_type;
}
- else
- part_info->default_engine_type= create_info->db_type;
+ DBUG_ASSERT(part_info->default_engine_type &&
+ part_info->default_engine_type != partition_hton);
if (check_native_partitioned(create_info, &is_native_partitioned,
part_info, thd))
{
@@ -5838,32 +5785,42 @@ static void release_log_entries(partition_info *part_info)
/*
- Get a lock on table name to avoid that anyone can open the table in
- a critical part of the ALTER TABLE.
- SYNOPSIS
- get_name_lock()
+ Final part of partition changes to handle things when under
+ LOCK TABLES.
+ SYNPOSIS
+ alter_partition_lock_handling()
lpt Struct carrying parameters
RETURN VALUES
- FALSE Success
- TRUE Failure
+ NONE
*/
-
-static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
+static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
{
- int error= 0;
- DBUG_ENTER("get_name_lock");
-
- bzero(&lpt->table_list, sizeof(lpt->table_list));
- lpt->table_list.db= (char*)lpt->db;
- lpt->table_list.table= lpt->table;
- lpt->table_list.table_name= (char*)lpt->table_name;
- pthread_mutex_lock(&LOCK_open);
- error= lock_table_name(lpt->thd, &lpt->table_list, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(error);
+ int err;
+ if (lpt->thd->locked_tables)
+ {
+ /*
+ When we have the table locked, it is necessary to reopen the table
+ since all table objects were closed and removed as part of the
+ ALTER TABLE of partitioning structure.
+ */
+ pthread_mutex_lock(&LOCK_open);
+ lpt->thd->in_lock_tables= 1;
+ err= reopen_tables(lpt->thd, 1, 1);
+ lpt->thd->in_lock_tables= 0;
+ if (err)
+ {
+ /*
+ Issue a warning since we weren't able to regain the lock again.
+ We also need to unlink table from thread's open list and from
+ table_cache
+ */
+ unlink_open_table(lpt->thd, lpt->table, FALSE);
+ sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ }
}
-
/*
Unlock and close table before renaming and dropping partitions
SYNOPSIS
@@ -5876,35 +5833,16 @@ static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
{
THD *thd= lpt->thd;
- TABLE *table= lpt->table;
+ const char *db= lpt->db;
+ const char *table_name= lpt->table_name;
DBUG_ENTER("alter_close_tables");
/*
We need to also unlock tables and close all handlers.
We set lock to zero to ensure we don't do this twice
and we set db_stat to zero to ensure we don't close twice.
*/
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
- table->file->close();
- table->db_stat= 0;
- DBUG_RETURN(0);
-}
-
-
-/*
- Release a lock name
- SYNOPSIS
- release_name_lock()
- lpt
- RETURN VALUES
- 0
-*/
-
-static int release_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- DBUG_ENTER("release_name_lock");
pthread_mutex_lock(&LOCK_open);
- unlock_table_name(lpt->thd, &lpt->table_list);
+ close_data_files_and_morph_locks(thd, db, table_name);
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(0);
}
@@ -6113,7 +6051,19 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
((alter_info->flags & ALTER_REPAIR_PARTITION) &&
(error= table->file->ha_repair_partitions(thd))))
{
- table->file->print_error(error, MYF(0));
+ if (error == HA_ADMIN_NOT_IMPLEMENTED) {
+ if (alter_info->flags & ALTER_OPTIMIZE_PARTITION)
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "optimize partition");
+ else if (alter_info->flags & ALTER_ANALYZE_PARTITION)
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "analyze partition");
+ else if (alter_info->flags & ALTER_CHECK_PARTITION)
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "check partition");
+ else if (alter_info->flags & ALTER_REPAIR_PARTITION)
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "repair partition");
+ else
+ table->file->print_error(error, MYF(0));
+ } else
+ table->file->print_error(error, MYF(0));
goto err;
}
}
@@ -6164,7 +6114,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
goto err;
}
}
- else if (alter_info->flags == ALTER_DROP_PARTITION)
+ else if (alter_info->flags & ALTER_DROP_PARTITION)
{
/*
Now after all checks and setting state on dropped partitions we can
@@ -6202,7 +6152,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
name lock.
5) Close all tables that have already been opened but didn't stumble on
the abort locked previously. This is done as part of the
- get_name_lock call.
+ close_data_files_and_morph_locks call.
6) We are now ready to release all locks we got in this thread.
7) Write the bin log
Unfortunately the writing of the binlog is not synchronised with
@@ -6219,8 +6169,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
9) Prepare handlers for drop of partitions
10) Drop the partitions
11) Remove entries from ddl log
- 12) Release name lock so that all other threads can access the table
- again.
+ 12) Reopen table if under lock tables
13) Complete query
We insert Error injections at all places where it could be interesting
@@ -6235,23 +6184,21 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
(not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
- get_name_lock(lpt) ||
- ERROR_INJECT_CRASH("crash_drop_partition_5") ||
alter_close_tables(lpt) ||
- ERROR_INJECT_CRASH("crash_drop_partition_6") ||
+ ERROR_INJECT_CRASH("crash_drop_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query, thd->query_length), FALSE)) ||
- ERROR_INJECT_CRASH("crash_drop_partition_7") ||
+ ERROR_INJECT_CRASH("crash_drop_partition_6") ||
((frm_install= TRUE), FALSE) ||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
((frm_install= FALSE), FALSE) ||
- ERROR_INJECT_CRASH("crash_drop_partition_8") ||
+ ERROR_INJECT_CRASH("crash_drop_partition_7") ||
mysql_drop_partitions(lpt) ||
- ERROR_INJECT_CRASH("crash_drop_partition_9") ||
+ ERROR_INJECT_CRASH("crash_drop_partition_8") ||
(write_log_completed(lpt, FALSE), FALSE) ||
- ERROR_INJECT_CRASH("crash_drop_partition_10") ||
- (release_name_lock(lpt), FALSE))
+ ERROR_INJECT_CRASH("crash_drop_partition_9") ||
+ (alter_partition_lock_handling(lpt), FALSE))
{
handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
goto err;
@@ -6283,7 +6230,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
name lock.
5) Close all tables that have already been opened but didn't stumble on
the abort locked previously. This is done as part of the
- get_name_lock call.
+ close_data_files_and_morph_locks call.
6) Close all table handlers and unlock all handlers but retain name lock
7) Write binlog
8) Now the change is completed except for the installation of the
@@ -6293,7 +6240,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
added to the table.
10)Wait until all accesses using the old frm file has completed
11)Remove entries from ddl log
- 12)Release name lock
+ 12)Reopen tables if under lock tables
13)Complete query
*/
if (write_log_add_change_partition(lpt) ||
@@ -6303,8 +6250,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
- ERROR_INJECT_CRASH("crash_add_partition_3") ||
- get_name_lock(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
@@ -6320,7 +6265,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_8") ||
(write_log_completed(lpt, FALSE), FALSE) ||
ERROR_INJECT_CRASH("crash_add_partition_9") ||
- (release_name_lock(lpt), FALSE))
+ (alter_partition_lock_handling(lpt), FALSE))
{
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
goto err;
@@ -6374,15 +6319,15 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
7) Close all tables opened but not yet locked, after this call we are
certain that no other thread is in the lock wait queue or has
opened the table. The name lock will ensure that they are blocked
- on the open call. This is achieved also by get_name_lock call.
+ on the open call.
+ This is achieved also by close_data_files_and_morph_locks call.
8) Close all partitions opened by this thread, but retain name lock.
9) Write bin log
10) Prepare handlers for rename and delete of partitions
11) Rename and drop the reorged partitions such that they are no
longer used and rename those added to their real new names.
12) Install the shadow frm file
- 13) Release the name lock to enable other threads to start using the
- table again.
+ 13) Reopen the table if under lock tables
14) Complete query
*/
if (write_log_add_change_partition(lpt) ||
@@ -6396,24 +6341,22 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
(not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
ERROR_INJECT_CRASH("crash_change_partition_5") ||
- get_name_lock(lpt) ||
- ERROR_INJECT_CRASH("crash_change_partition_6") ||
alter_close_tables(lpt) ||
- ERROR_INJECT_CRASH("crash_change_partition_7") ||
+ ERROR_INJECT_CRASH("crash_change_partition_6") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query, thd->query_length), FALSE)) ||
- ERROR_INJECT_CRASH("crash_change_partition_8") ||
+ ERROR_INJECT_CRASH("crash_change_partition_7") ||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
- ERROR_INJECT_CRASH("crash_change_partition_9") ||
+ ERROR_INJECT_CRASH("crash_change_partition_8") ||
mysql_drop_partitions(lpt) ||
- ERROR_INJECT_CRASH("crash_change_partition_10") ||
+ ERROR_INJECT_CRASH("crash_change_partition_9") ||
mysql_rename_partitions(lpt) ||
((frm_install= TRUE), FALSE) ||
- ERROR_INJECT_CRASH("crash_change_partition_11") ||
+ ERROR_INJECT_CRASH("crash_change_partition_10") ||
(write_log_completed(lpt, FALSE), FALSE) ||
- ERROR_INJECT_CRASH("crash_change_partition_12") ||
- (release_name_lock(lpt), FALSE))
+ ERROR_INJECT_CRASH("crash_change_partition_11") ||
+ (alter_partition_lock_handling(lpt), FALSE))
{
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
goto err;
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 225008e6b3a..e3ee1305e01 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -210,6 +210,8 @@ static void reap_plugins(void);
/* declared in set_var.cc */
extern sys_var *intern_find_sys_var(const char *str, uint length, bool no_error);
+extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
+ const char *name, longlong val);
#ifdef EMBEDDED_LIBRARY
/* declared in sql_base.cc */
@@ -1181,9 +1183,8 @@ int plugin_init(int *argc, char **argv, int flags)
/* Register all dynamic plugins */
if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
{
- if (opt_plugin_load &&
- plugin_load_list(&tmp_root, argc, argv, opt_plugin_load))
- goto err;
+ if (opt_plugin_load)
+ plugin_load_list(&tmp_root, argc, argv, opt_plugin_load);
if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
plugin_load(&tmp_root, argc, argv);
}
@@ -1412,7 +1413,11 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
while (list)
{
if (p == buffer + sizeof(buffer) - 1)
- break;
+ {
+ sql_print_error("plugin-load parameter too long");
+ DBUG_RETURN(TRUE);
+ }
+
switch ((*(p++)= *(list++))) {
case '\0':
list= NULL; /* terminate the loop */
@@ -1421,10 +1426,17 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
case ':': /* can't use this as delimiter as it may be drive letter */
#endif
case ';':
- name.str[name.length]= '\0';
- if (str != &dl) // load all plugins in named module
+ str->str[str->length]= '\0';
+ if (str == &name) // load all plugins in named module
{
+ if (!name.length)
+ {
+ p--; /* reset pointer */
+ continue;
+ }
+
dl= name;
+ pthread_mutex_lock(&LOCK_plugin);
if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG)))
{
for (plugin= plugin_dl->plugins; plugin->info; plugin++)
@@ -1442,9 +1454,11 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
else
{
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
+ pthread_mutex_lock(&LOCK_plugin);
if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG))
goto error;
}
+ pthread_mutex_unlock(&LOCK_plugin);
name.length= dl.length= 0;
dl.str= NULL; name.str= p= buffer;
str= &name;
@@ -1453,6 +1467,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
case '#':
if (str == &name)
{
+ name.str[name.length]= '\0';
str= &dl;
str->str= p;
continue;
@@ -1464,6 +1479,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
}
DBUG_RETURN(FALSE);
error:
+ pthread_mutex_unlock(&LOCK_plugin);
sql_print_error("Couldn't load plugin named '%s' with soname '%s'.",
name.str, dl.str);
DBUG_RETURN(TRUE);
@@ -1619,7 +1635,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
bzero(&tables, sizeof(tables));
tables.db= (char *)"mysql";
tables.table_name= tables.alias= (char *)"plugin";
- if (check_table_access(thd, INSERT_ACL, &tables, 0))
+ if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE))
DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */
@@ -1888,16 +1904,8 @@ static int check_func_int(THD *thd, struct st_mysql_sys_var *var,
else
*(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed);
- if (fixed)
- {
- char buf[22];
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER(ER_TRUNCATED_WRONG_VALUE), var->name,
- ullstr(tmp, buf));
- }
- return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) &&
- (*(int *)save != (int) tmp);
+ return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
+ var->name, (longlong) tmp);
}
@@ -1916,16 +1924,8 @@ static int check_func_long(THD *thd, struct st_mysql_sys_var *var,
else
*(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed);
- if (fixed)
- {
- char buf[22];
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER(ER_TRUNCATED_WRONG_VALUE), var->name,
- ullstr(tmp, buf));
- }
- return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) &&
- (*(long *)save != (long) tmp);
+ return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
+ var->name, (longlong) tmp);
}
@@ -1944,16 +1944,8 @@ static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
else
*(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed);
- if (fixed)
- {
- char buf[22];
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER(ER_TRUNCATED_WRONG_VALUE), var->name,
- ullstr(tmp, buf));
- }
- return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) &&
- (*(long long *)save != tmp);
+ return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
+ var->name, (longlong) tmp);
}
static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
@@ -3024,7 +3016,8 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
DBUG_RETURN(-1);
}
- if (opt->flags & PLUGIN_VAR_NOCMDOPT)
+ if ((opt->flags & (PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_THDLOCAL))
+ == PLUGIN_VAR_NOCMDOPT)
continue;
if (!opt->name)
@@ -3034,7 +3027,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
DBUG_RETURN(-1);
}
- if (!(v= find_bookmark(name, opt->name, opt->flags)))
+ if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
{
optnamelen= strlen(opt->name);
optname= (char*) alloc_root(mem_root, namelen + optnamelen + 2);
@@ -3042,7 +3035,23 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
optnamelen= namelen + optnamelen + 1;
}
else
- optname= (char*) memdup_root(mem_root, v->key + 1, (optnamelen= v->name_len) + 1);
+ {
+ /* this should not fail because register_var should create entry */
+ if (!(v= find_bookmark(name, opt->name, opt->flags)))
+ {
+ sql_print_error("Thread local variable '%s' not allocated "
+ "in plugin '%s'.", opt->name, plugin_name);
+ DBUG_RETURN(-1);
+ }
+
+ *(int*)(opt + 1)= offset= v->offset;
+
+ if (opt->flags & PLUGIN_VAR_NOCMDOPT)
+ continue;
+
+ optname= (char*) memdup_root(mem_root, v->key + 1,
+ (optnamelen= v->name_len) + 1);
+ }
/* convert '_' to '-' */
for (p= optname; *p; p++)
@@ -3054,20 +3063,13 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
options->app_type= opt;
options->id= (options-1)->id + 1;
- if (opt->flags & PLUGIN_VAR_THDLOCAL)
- *(int*)(opt + 1)= offset= v->offset;
-
plugin_opt_set_limits(options, opt);
- if ((opt->flags & PLUGIN_VAR_TYPEMASK) != PLUGIN_VAR_ENUM &&
- (opt->flags & PLUGIN_VAR_TYPEMASK) != PLUGIN_VAR_SET)
- {
- if (opt->flags & PLUGIN_VAR_THDLOCAL)
- options->value= options->u_max_value= (uchar**)
- (global_system_variables.dynamic_variables_ptr + offset);
- else
- options->value= options->u_max_value= *(uchar***) (opt + 1);
- }
+ if (opt->flags & PLUGIN_VAR_THDLOCAL)
+ options->value= options->u_max_value= (uchar**)
+ (global_system_variables.dynamic_variables_ptr + offset);
+ else
+ options->value= options->u_max_value= *(uchar***) (opt + 1);
options[1]= options[0];
options[1].name= p= (char*) alloc_root(mem_root, optnamelen + 8);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 9d1865becff..a1cdd0742d3 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1280,7 +1280,7 @@ static int mysql_test_select(Prepared_statement *stmt,
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (tables)
{
- if (check_table_access(thd, privilege, tables,0))
+ if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE))
goto error;
}
else if (check_access(thd, privilege, any_db,0,0,0,0))
@@ -1349,7 +1349,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt,
THD *thd= stmt->thd;
DBUG_ENTER("mysql_test_do_fields");
- if (tables && check_table_access(thd, SELECT_ACL, tables, 0))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
if (open_normal_and_derived_tables(thd, tables, 0))
@@ -1380,7 +1380,7 @@ static bool mysql_test_set_fields(Prepared_statement *stmt,
THD *thd= stmt->thd;
set_var_base *var;
- if (tables && check_table_access(thd, SELECT_ACL, tables, 0) ||
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) ||
open_normal_and_derived_tables(thd, tables, 0))
goto error;
@@ -1415,7 +1415,7 @@ error:
*/
static bool select_like_stmt_test(Prepared_statement *stmt,
- bool (*specific_prepare)(THD *thd),
+ int (*specific_prepare)(THD *thd),
ulong setup_tables_done_option)
{
DBUG_ENTER("select_like_stmt_test");
@@ -1452,7 +1452,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
static bool
select_like_stmt_test_with_open(Prepared_statement *stmt,
TABLE_LIST *tables,
- bool (*specific_prepare)(THD *thd),
+ int (*specific_prepare)(THD *thd),
ulong setup_tables_done_option)
{
DBUG_ENTER("select_like_stmt_test_with_open");
@@ -1524,6 +1524,44 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
/**
+ @brief Validate and prepare for execution CREATE VIEW statement
+
+ @param stmt prepared statement
+
+ @note This function handles create view commands.
+
+ @retval FALSE Operation was a success.
+ @retval TRUE An error occured.
+*/
+
+static bool mysql_test_create_view(Prepared_statement *stmt)
+{
+ DBUG_ENTER("mysql_test_create_view");
+ THD *thd= stmt->thd;
+ LEX *lex= stmt->lex;
+ bool res= TRUE;
+ /* Skip first table, which is the view we are creating */
+ bool link_to_local;
+ TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *tables= lex->query_tables;
+
+ if (create_view_precheck(thd, tables, view, lex->create_view_mode))
+ goto err;
+
+ if (open_normal_and_derived_tables(thd, tables, 0))
+ goto err;
+
+ lex->view_prepare_mode= 1;
+ res= select_like_stmt_test(stmt, 0, 0);
+
+err:
+ /* put view back for PS rexecuting */
+ lex->link_first_table_back(view, link_to_local);
+ DBUG_RETURN(res);
+}
+
+
+/*
Validate and prepare for execution a multi update statement.
@param stmt prepared statement
@@ -1600,7 +1638,7 @@ error:
uses local tables lists.
*/
-static bool mysql_insert_select_prepare_tester(THD *thd)
+static int mysql_insert_select_prepare_tester(THD *thd)
{
SELECT_LEX *first_select= &thd->lex->select_lex;
TABLE_LIST *second_table= ((TABLE_LIST*)first_select->table_list.first)->
@@ -1735,6 +1773,7 @@ static bool check_prepared_statement(Prepared_statement *stmt,
my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
goto error;
}
+ res= mysql_test_create_view(stmt);
break;
case SQLCOM_DO:
res= mysql_test_do_fields(stmt, tables, lex->insert_list);
@@ -2111,7 +2150,7 @@ void mysql_sql_stmt_prepare(THD *thd)
thd->stmt_map.erase(stmt);
}
else
- send_ok(thd, 0L, 0L, "Statement prepared");
+ my_ok(thd, 0L, 0L, "Statement prepared");
DBUG_VOID_RETURN;
}
@@ -2493,7 +2532,9 @@ void mysql_stmt_reset(THD *thd, char *packet)
stmt->state= Query_arena::PREPARED;
- send_ok(thd);
+ general_log_print(thd, thd->command, NullS);
+
+ my_ok(thd);
DBUG_VOID_RETURN;
}
@@ -2513,6 +2554,8 @@ void mysql_stmt_close(THD *thd, char *packet)
Prepared_statement *stmt;
DBUG_ENTER("mysql_stmt_close");
+ thd->main_da.disable_status();
+
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close")))
DBUG_VOID_RETURN;
@@ -2522,8 +2565,7 @@ void mysql_stmt_close(THD *thd, char *packet)
*/
DBUG_ASSERT(! (stmt->flags & (uint) Prepared_statement::IS_IN_USE));
(void) stmt->deallocate();
-
- thd->main_da.disable_status();
+ general_log_print(thd, thd->command, NullS);
DBUG_VOID_RETURN;
}
@@ -2556,7 +2598,7 @@ void mysql_sql_stmt_close(THD *thd)
}
if (stmt->deallocate() == 0)
- send_ok(thd);
+ my_ok(thd);
}
/**
@@ -2629,6 +2671,9 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
stmt->last_errno= ER_OUTOFMEMORY;
sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
}
+
+ general_log_print(thd, thd->command, NullS);
+
DBUG_VOID_RETURN;
}
@@ -2661,7 +2706,7 @@ bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
bool Select_fetch_protocol_binary::send_eof()
{
- ::send_eof(thd);
+ ::my_eof(thd);
return FALSE;
}
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 1922fa3bc2b..c62cf6401ba 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -456,7 +456,7 @@ bool PROFILING::show_profiles()
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 9dd8e1b70d4..fc87356e452 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -178,7 +178,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
if (!silent && !error)
{
write_bin_log(thd, TRUE, thd->query, thd->query_length);
- send_ok(thd);
+ my_ok(thd);
}
if (!error)
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 280598e8557..d15b1fc58a3 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -252,7 +252,7 @@ bool purge_error_message(THD* thd, int res)
my_message(errmsg, ER(errmsg), MYF(0));
return TRUE;
}
- send_ok(thd);
+ my_ok(thd);
return FALSE;
}
@@ -262,7 +262,7 @@ bool purge_master_logs(THD* thd, const char* to_log)
char search_file_name[FN_REFLEN];
if (!mysql_bin_log.is_open())
{
- send_ok(thd);
+ my_ok(thd);
return FALSE;
}
@@ -277,7 +277,7 @@ bool purge_master_logs_before_date(THD* thd, time_t purge_time)
{
if (!mysql_bin_log.is_open())
{
- send_ok(thd);
+ my_ok(thd);
return 0;
}
return purge_error_message(thd,
@@ -738,7 +738,7 @@ end:
end_io_cache(&log);
(void)my_close(file, MYF(MY_WME));
- send_eof(thd);
+ my_eof(thd);
thd_proc_info(thd, "Waiting to finalize termination");
pthread_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
@@ -884,7 +884,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
DBUG_RETURN(1);
}
else if (net_report)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
}
@@ -936,7 +936,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
DBUG_RETURN(1);
}
else if (net_report)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
}
@@ -1279,7 +1279,7 @@ bool change_master(THD* thd, Master_info* mi)
unlock_slave_threads(mi);
thd_proc_info(thd, 0);
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
@@ -1453,7 +1453,7 @@ err:
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
"SHOW BINLOG EVENTS", errmsg);
else
- send_eof(thd);
+ my_eof(thd);
pthread_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
@@ -1490,7 +1490,7 @@ bool show_binlog_info(THD* thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -1572,7 +1572,7 @@ bool show_binlogs(THD* thd)
goto err;
}
mysql_bin_log.unlock_index();
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
err:
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index cf5201f17b1..d5c9040f8dc 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -36,12 +36,6 @@ extern bool server_id_supplied;
extern int max_binlog_dump_events;
extern my_bool opt_sporadic_binlog_dump_fail;
-#define KICK_SLAVE(thd) do { \
- pthread_mutex_lock(&(thd)->LOCK_delete); \
- (thd)->awake(THD::NOT_KILLED); \
- pthread_mutex_unlock(&(thd)->LOCK_delete); \
- } while(0)
-
int start_slave(THD* thd, Master_info* mi, bool net_report);
int stop_slave(THD* thd, Master_info* mi, bool net_report);
bool change_master(THD* thd, Master_info* mi);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 326fb39cc17..a6e7cc33b51 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -383,10 +383,10 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
}
}
new_ref= direct_ref ?
- new Item_direct_ref(ref->context, item_ref, ref->field_name,
- ref->table_name, ref->alias_name_used) :
- new Item_ref(ref->context, item_ref, ref->field_name,
- ref->table_name, ref->alias_name_used);
+ new Item_direct_ref(ref->context, item_ref, ref->table_name,
+ ref->field_name, ref->alias_name_used) :
+ new Item_ref(ref->context, item_ref, ref->table_name,
+ ref->field_name, ref->alias_name_used);
if (!new_ref)
return TRUE;
ref->outer_ref= new_ref;
@@ -597,37 +597,13 @@ JOIN::prepare(Item ***rref_pointer_array,
/*
Check if there are references to un-aggregated columns when computing
aggregate functions with implicit grouping (there is no GROUP BY).
- TODO: Add check of calculation of GROUP functions and fields:
- SELECT COUNT(*)+table.col1 from table1;
*/
- if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
+ if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list &&
+ select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED))
{
- if (!group_list)
- {
- uint flag=0;
- List_iterator_fast<Item> it(fields_list);
- Item *item;
- while ((item= it++))
- {
- if (item->with_sum_func)
- flag|=1;
- else if (!(flag & 2) && !item->const_during_execution())
- flag|=2;
- }
- if (having)
- {
- if (having->with_sum_func)
- flag |= 1;
- else if (!having->const_during_execution())
- flag |= 2;
- }
- if (flag == 3)
- {
- my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,
- ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0));
- DBUG_RETURN(-1);
- }
- }
+ my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,
+ ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0));
+ DBUG_RETURN(-1);
}
{
/* Caclulate the number of groups */
@@ -957,7 +933,8 @@ JOIN::optimize()
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0);
DBUG_EXECUTE("where",
print_where(table_independent_conds,
- "where after opt_sum_query()"););
+ "where after opt_sum_query()",
+ QT_ORDINARY););
conds= table_independent_conds;
}
}
@@ -1037,7 +1014,10 @@ JOIN::optimize()
{
conds= substitute_for_best_equal_field(conds, cond_equal, map2table);
conds->update_used_tables();
- DBUG_EXECUTE("where", print_where(conds, "after substitute_best_equal"););
+ DBUG_EXECUTE("where",
+ print_where(conds,
+ "after substitute_best_equal",
+ QT_ORDINARY););
}
/*
@@ -2100,12 +2080,14 @@ JOIN::exec()
curr_table->select_cond= curr_table->select->cond;
curr_table->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(curr_table->select->cond,
- "select and having"););
+ "select and having",
+ QT_ORDINARY););
curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
~ (table_map) 0,
~used_tables);
DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
- "having after sort"););
+ "having after sort",
+ QT_ORDINARY););
}
}
{
@@ -2995,7 +2977,9 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
}
}
else if (old->eq_func && new_fields->eq_func &&
- old->val->eq(new_fields->val, old->field->binary()))
+ old->val->eq_by_collation(new_fields->val,
+ old->field->binary(),
+ old->field->charset()))
{
old->level= and_level;
@@ -5849,7 +5833,8 @@ static void add_not_null_conds(JOIN *join)
if (notnull->fix_fields(join->thd, &notnull))
DBUG_VOID_RETURN;
DBUG_EXECUTE("where",print_where(notnull,
- referred_tab->table->alias););
+ referred_tab->table->alias,
+ QT_ORDINARY););
add_cond_and_fix(&referred_tab->select_cond, notnull);
}
}
@@ -6007,7 +5992,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
make_cond_for_table(cond,
join->const_table_map,
(table_map) 0);
- DBUG_EXECUTE("where",print_where(const_cond,"constants"););
+ DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
for (JOIN_TAB *tab= join->join_tab+join->const_tables;
tab < join->join_tab+join->tables ; tab++)
{
@@ -6108,7 +6093,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
}
if (tmp || !cond)
{
- DBUG_EXECUTE("where",print_where(tmp,tab->table->alias););
+ DBUG_EXECUTE("where",print_where(tmp,tab->table->alias, QT_ORDINARY););
SQL_SELECT *sel= tab->select= ((SQL_SELECT*)
thd->memdup((uchar*) select,
sizeof(*select)));
@@ -6148,7 +6133,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tab->select_cond= sel->cond= NULL;
sel->head=tab->table;
- DBUG_EXECUTE("where",print_where(tmp,tab->table->alias););
+ DBUG_EXECUTE("where",print_where(tmp,tab->table->alias, QT_ORDINARY););
if (tab->quick)
{
/* Use quick key read if it's a constant and it's not used
@@ -6260,7 +6245,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
current_map,
current_map)))
{
- DBUG_EXECUTE("where",print_where(tmp,"cache"););
+ DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
tab->cache.select=(SQL_SELECT*)
thd->memdup((uchar*) sel, sizeof(SQL_SELECT));
tab->cache.select->cond=tmp;
@@ -8884,10 +8869,10 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
predicate. Substitute a constant instead of this field if the
multiple equality contains a constant.
*/
- DBUG_EXECUTE("where", print_where(conds, "original"););
+ DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY););
conds= build_equal_items(join->thd, conds, NULL, join_list,
&join->cond_equal);
- DBUG_EXECUTE("where",print_where(conds,"after equal_items"););
+ DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
/* change field = field to field = const for each found field = const */
propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds);
@@ -8895,9 +8880,9 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
Remove all instances of item == item
Remove all and-levels where CONST item != CONST item
*/
- DBUG_EXECUTE("where",print_where(conds,"after const change"););
+ DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
conds= remove_eq_conds(thd, conds, cond_value) ;
- DBUG_EXECUTE("info",print_where(conds,"after remove"););
+ DBUG_EXECUTE("info",print_where(conds,"after remove", QT_ORDINARY););
}
DBUG_RETURN(conds);
}
@@ -11354,7 +11339,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
we found a row, as no new rows can be added to the result.
*/
if (not_used_in_distinct && found_records != join->found_records)
- return NESTED_LOOP_OK;
+ return NESTED_LOOP_NO_MORE_ROWS;
}
else
join_tab->read_record.file->unlock_row();
@@ -11739,21 +11724,42 @@ join_read_key(JOIN_TAB *tab)
}
+/*
+ ref access method implementation: "read_first" function
+
+ SYNOPSIS
+ join_read_always_key()
+ tab JOIN_TAB of the accessed table
+
+ DESCRIPTION
+ This is "read_fist" function for the "ref" access method.
+
+ The functon must leave the index initialized when it returns.
+ ref_or_null access implementation depends on that.
+
+ RETURN
+ 0 - Ok
+ -1 - Row not found
+ 1 - Error
+*/
+
static int
join_read_always_key(JOIN_TAB *tab)
{
int error;
TABLE *table= tab->table;
+ /* Initialize the index first */
+ if (!table->file->inited)
+ table->file->ha_index_init(tab->ref.key, tab->sorted);
+
+ /* Perform "Late NULLs Filtering" (see internals manual for explanations) */
for (uint i= 0 ; i < tab->ref.key_parts ; i++)
{
if ((tab->ref.null_rejecting & 1 << i) && tab->ref.items[i]->is_null())
return -1;
- }
- if (!table->file->inited)
- {
- table->file->ha_index_init(tab->ref.key, tab->sorted);
}
+
if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
return -1;
if ((error=table->file->index_read_map(table->record[0],
@@ -13306,6 +13312,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
tab->read_first_record= best_key_direction > 0 ?
join_read_first:join_read_last;
tab->type=JT_NEXT; // Read with index_first(), index_next()
+ if (select && select->quick)
+ {
+ delete select->quick;
+ select->quick= 0;
+ }
if (table->covering_keys.is_set(best_key))
{
table->key_read=1;
@@ -13316,15 +13327,27 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
{
tab->ref.key= -1;
tab->ref.key_parts= 0;
- if (select && select->quick)
- {
- delete select->quick;
- select->quick= 0;
- }
if (select_limit < table_records)
tab->limit= select_limit;
}
}
+ else if (tab->type != JT_ALL)
+ {
+ /*
+ We're about to use a quick access to the table.
+ We need to change the access method so as the quick access
+ method is actually used.
+ */
+ DBUG_ASSERT(tab->select->quick);
+ tab->type=JT_ALL;
+ tab->use_quick=1;
+ tab->ref.key= -1;
+ tab->ref.key_parts=0; // Don't use ref key.
+ tab->read_first_record= join_init_read_record;
+ /*
+ TODO: update the number of records in join->best_positions[tablenr]
+ */
+ }
}
used_key_parts= best_key_parts;
order_direction= best_key_direction;
@@ -13535,7 +13558,7 @@ static bool fix_having(JOIN *join, Item **having)
JOIN_TAB *table=&join->join_tab[join->const_tables];
table_map used_tables= join->const_table_map | table->table->map;
- DBUG_EXECUTE("where",print_where(*having,"having"););
+ DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
Item* sort_table_cond=make_cond_for_table(*having,used_tables,used_tables);
if (sort_table_cond)
{
@@ -13552,9 +13575,11 @@ static bool fix_having(JOIN *join, Item **having)
table->select_cond=table->select->cond;
table->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(table->select_cond,
- "select and having"););
+ "select and having",
+ QT_ORDINARY););
*having=make_cond_for_table(*having,~ (table_map) 0,~used_tables);
- DBUG_EXECUTE("where",print_where(*having,"having after make_cond"););
+ DBUG_EXECUTE("where",
+ print_where(*having,"having after make_cond", QT_ORDINARY););
}
return 0;
}
@@ -15222,7 +15247,7 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
char buff[256];
String str(buff,sizeof(buff),&my_charset_bin);
str.length(0);
- item->print(&str);
+ item->print(&str, QT_ORDINARY);
item_field->name= sql_strmake(str.ptr(),str.length());
}
#endif
@@ -16263,7 +16288,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
extra.append(STRING_WITH_LEN(": "));
- ((COND *)pushed_cond)->print(&extra);
+ ((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
}
}
else
@@ -16412,9 +16437,13 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
@param thd thread handler
@param str string where table should be printed
@param tables list of tables in join
+ @query_type type of the query is being generated
*/
-static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
+static void print_join(THD *thd,
+ String *str,
+ List<TABLE_LIST> *tables,
+ enum_query_type query_type)
{
/* List is reversed => we should reverse it before using */
List_iterator_fast<TABLE_LIST> ti(*tables);
@@ -16427,7 +16456,7 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
*t= ti++;
DBUG_ASSERT(tables->elements >= 1);
- (*table)->print(thd, str);
+ (*table)->print(thd, str, query_type);
TABLE_LIST **end= table + tables->elements;
for (TABLE_LIST **tbl= table + 1; tbl < end; tbl++)
@@ -16442,11 +16471,11 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
str->append(STRING_WITH_LEN(" straight_join "));
else
str->append(STRING_WITH_LEN(" join "));
- curr->print(thd, str);
+ curr->print(thd, str, query_type);
if (curr->on_expr)
{
str->append(STRING_WITH_LEN(" on("));
- curr->on_expr->print(str);
+ curr->on_expr->print(str, query_type);
str->append(')');
}
}
@@ -16493,15 +16522,15 @@ Index_hint::print(THD *thd, String *str)
/**
Print table as it should be in join list.
- @param str string where table should bbe printed
+ @param str string where table should be printed
*/
-void TABLE_LIST::print(THD *thd, String *str)
+void TABLE_LIST::print(THD *thd, String *str, enum_query_type query_type)
{
if (nested_join)
{
str->append('(');
- print_join(thd, str, &nested_join->join_list);
+ print_join(thd, str, &nested_join->join_list, query_type);
str->append(')');
}
else
@@ -16524,7 +16553,7 @@ void TABLE_LIST::print(THD *thd, String *str)
{
// A derived table
str->append('(');
- derived->print(str);
+ derived->print(str, query_type);
str->append(')');
cmp_name= ""; // Force printing of alias
}
@@ -16584,7 +16613,7 @@ void TABLE_LIST::print(THD *thd, String *str)
}
-void st_select_lex::print(THD *thd, String *str)
+void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{
/* QQ: thd may not be set for sub queries, but this should be fixed */
if (!thd)
@@ -16632,7 +16661,7 @@ void st_select_lex::print(THD *thd, String *str)
first= 0;
else
str->append(',');
- item->print_item_w_name(str);
+ item->print_item_w_name(str, query_type);
}
/*
@@ -16643,7 +16672,15 @@ void st_select_lex::print(THD *thd, String *str)
{
str->append(STRING_WITH_LEN(" from "));
/* go through join tree */
- print_join(thd, str, &top_join_list);
+ print_join(thd, str, &top_join_list, query_type);
+ }
+ else if (where)
+ {
+ /*
+ "SELECT 1 FROM DUAL WHERE 2" should not be printed as
+ "SELECT 1 WHERE 2": the 1st syntax is valid, but the 2nd is not.
+ */
+ str->append(STRING_WITH_LEN(" from DUAL "));
}
// Where
@@ -16654,7 +16691,7 @@ void st_select_lex::print(THD *thd, String *str)
{
str->append(STRING_WITH_LEN(" where "));
if (cur_where)
- cur_where->print(str);
+ cur_where->print(str, query_type);
else
str->append(cond_value != Item::COND_FALSE ? "1" : "0");
}
@@ -16663,7 +16700,7 @@ void st_select_lex::print(THD *thd, String *str)
if (group_list.elements)
{
str->append(STRING_WITH_LEN(" group by "));
- print_order(str, (ORDER *) group_list.first);
+ print_order(str, (ORDER *) group_list.first, query_type);
switch (olap)
{
case CUBE_TYPE:
@@ -16686,7 +16723,7 @@ void st_select_lex::print(THD *thd, String *str)
{
str->append(STRING_WITH_LEN(" having "));
if (cur_having)
- cur_having->print(str);
+ cur_having->print(str, query_type);
else
str->append(having_value != Item::COND_FALSE ? "1" : "0");
}
@@ -16694,11 +16731,11 @@ void st_select_lex::print(THD *thd, String *str)
if (order_list.elements)
{
str->append(STRING_WITH_LEN(" order by "));
- print_order(str, (ORDER *) order_list.first);
+ print_order(str, (ORDER *) order_list.first, query_type);
}
// limit
- print_limit(thd, str);
+ print_limit(thd, str, query_type);
// PROCEDURE unsupported here
}
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 8203ca92eed..4390919f8c7 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -55,8 +55,8 @@ static bool get_server_from_table_to_cache(TABLE *table);
static int insert_server(THD *thd, FOREIGN_SERVER *server_options);
static int insert_server_record(TABLE *table, FOREIGN_SERVER *server);
static int insert_server_record_into_cache(FOREIGN_SERVER *server);
-static void prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options,
- FOREIGN_SERVER *server);
+static FOREIGN_SERVER *
+prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options);
/* drop functions */
static int delete_server_record(TABLE *table,
char *server_name,
@@ -166,6 +166,9 @@ end:
RETURN VALUES
FALSE Success
TRUE Error
+
+ TODO
+ Revert back to old list if we failed to load new one.
*/
static bool servers_load(THD *thd, TABLE_LIST *tables)
@@ -175,10 +178,9 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
bool return_val= TRUE;
DBUG_ENTER("servers_load");
- /* first, send all cached rows to sleep with the fishes, oblivion!
- I expect this crappy comment replaced */
- free_root(&mem, MYF(MY_MARK_BLOCKS_FREE));
my_hash_reset(&servers_cache);
+ free_root(&mem, MYF(0));
+ init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0);
while (!(read_record_info.read_record(&read_record_info)))
@@ -966,10 +968,14 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
server_options->server_name_length))
goto end;
- server= (FOREIGN_SERVER *)alloc_root(&mem,
- sizeof(FOREIGN_SERVER));
- prepare_server_struct_for_insert(server_options, server);
+ if (!(server= prepare_server_struct_for_insert(server_options)))
+ {
+ /* purecov: begin inspected */
+ error= ER_OUT_OF_RESOURCES;
+ goto end;
+ /* purecov: end */
+ }
error= insert_server(thd, server);
@@ -1040,52 +1046,64 @@ end:
SYNOPSIS
prepare_server_struct_for_insert()
LEX_SERVER_OPTIONS *server_options
- FOREIGN_SERVER *server
NOTES
+ As FOREIGN_SERVER members are allocated on mem_root, we do not need to
+ free them in case of error.
RETURN VALUE
- none
+ On success filled FOREIGN_SERVER, or NULL in case out of memory.
*/
-static void
-prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options,
- FOREIGN_SERVER *server)
+static FOREIGN_SERVER *
+prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options)
{
char *unset_ptr= (char*)"";
+ FOREIGN_SERVER *server;
DBUG_ENTER("prepare_server_struct");
+ if (!(server= (FOREIGN_SERVER *)alloc_root(&mem, sizeof(FOREIGN_SERVER))))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
/* these two MUST be set */
- server->server_name= strdup_root(&mem, server_options->server_name);
+ if (!(server->server_name= strdup_root(&mem, server_options->server_name)))
+ DBUG_RETURN(NULL); /* purecov: inspected */
server->server_name_length= server_options->server_name_length;
- server->host= server_options->host ?
- strdup_root(&mem, server_options->host) : unset_ptr;
+ if (!(server->host= server_options->host ?
+ strdup_root(&mem, server_options->host) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
- server->db= server_options->db ?
- strdup_root(&mem, server_options->db) : unset_ptr;
+ if (!(server->db= server_options->db ?
+ strdup_root(&mem, server_options->db) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
- server->username= server_options->username ?
- strdup_root(&mem, server_options->username) : unset_ptr;
+ if (!(server->username= server_options->username ?
+ strdup_root(&mem, server_options->username) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
- server->password= server_options->password ?
- strdup_root(&mem, server_options->password) : unset_ptr;
+ if (!(server->password= server_options->password ?
+ strdup_root(&mem, server_options->password) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
/* set to 0 if not specified */
server->port= server_options->port > -1 ?
server_options->port : 0;
- server->socket= server_options->socket ?
- strdup_root(&mem, server_options->socket) : unset_ptr;
+ if (!(server->socket= server_options->socket ?
+ strdup_root(&mem, server_options->socket) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
- server->scheme= server_options->scheme ?
- strdup_root(&mem, server_options->scheme) : unset_ptr;
+ if (!(server->scheme= server_options->scheme ?
+ strdup_root(&mem, server_options->scheme) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
- server->owner= server_options->owner ?
- strdup_root(&mem, server_options->owner) : unset_ptr;
+ if (!(server->owner= server_options->owner ?
+ strdup_root(&mem, server_options->owner) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ DBUG_RETURN(server);
}
/*
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 57562f4d982..5b2fe04f1e2 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -227,7 +227,7 @@ bool mysqld_show_authors(THD *thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -261,7 +261,7 @@ bool mysqld_show_contributors(THD *thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -334,7 +334,7 @@ bool mysqld_show_privileges(THD *thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -422,7 +422,7 @@ bool mysqld_show_column_types(THD *thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -669,7 +669,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -750,7 +750,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -792,7 +792,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
table->use_all_columns();
if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS))
DBUG_VOID_RETURN;
- send_eof(thd);
+ my_eof(thd);
DBUG_VOID_RETURN;
}
@@ -1595,7 +1595,7 @@ view_store_create_info(THD *thd, TABLE_LIST *table, String *buff)
We can't just use table->query, because our SQL_MODE may trigger
a different syntax, like when ANSI_QUOTES is defined.
*/
- table->view->unit.print(buff);
+ table->view->unit.print(buff, QT_ORDINARY);
if (table->with_check != VIEW_CHECK_NONE)
{
@@ -1751,7 +1751,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
if (protocol->write())
break; /* purecov: inspected */
}
- send_eof(thd);
+ my_eof(thd);
DBUG_VOID_RETURN;
}
@@ -3443,7 +3443,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
/*
there was errors during opening tables
*/
- const char *error= thd->main_da.message();
+ const char *error= thd->is_error() ? thd->main_da.message() : "";
if (tables->view)
table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
else if (tables->schema_table)
@@ -3651,8 +3651,9 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS
rather than in SHOW COLUMNS
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ if (thd->is_error())
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->main_da.sql_errno(), thd->main_da.message());
thd->clear_error();
res= 0;
}
@@ -4079,7 +4080,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.table_name= proc_tables.alias= (char*) "proc";
proc_tables.table_name_length= 4;
proc_tables.lock_type= TL_READ;
- full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
+ full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE);
if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
{
DBUG_RETURN(1);
@@ -4467,10 +4468,8 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
Table_triggers_list *triggers= tables->table->triggers;
int event, timing;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_table_access(thd, TRIGGER_ACL, tables, 1))
+ if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE))
goto ret;
-#endif
for (event= 0; event < (int)TRG_EVENT_MAX; event++)
{
@@ -4508,9 +4507,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
}
}
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
ret:
-#endif
DBUG_RETURN(0);
}
@@ -5300,8 +5297,14 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
f_key_info->referenced_db->length, cs);
table->field[10]->store(f_key_info->referenced_table->str,
f_key_info->referenced_table->length, cs);
- table->field[5]->store(f_key_info->referenced_key_name->str,
- f_key_info->referenced_key_name->length, cs);
+ if (f_key_info->referenced_key_name)
+ {
+ table->field[5]->store(f_key_info->referenced_key_name->str,
+ f_key_info->referenced_key_name->length, cs);
+ table->field[5]->set_notnull();
+ }
+ else
+ table->field[5]->set_null();
table->field[6]->store(STRING_WITH_LEN("NONE"), cs);
table->field[7]->store(f_key_info->update_method->str,
f_key_info->update_method->length, cs);
@@ -5408,8 +5411,9 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
@param
thd thread handler
- @param
- schema_table pointer to 'shema_tables' element
+
+ @param table_list Used to pass I_S table information(fields info, tables
+ parameters etc) and table name.
@retval \# Pointer to created table
@retval NULL Can't create table
@@ -5460,6 +5464,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(NULL);
break;
case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
if (!(item= new Item_decimal((longlong) fields_info->value, false)))
{
DBUG_RETURN(0);
@@ -5809,7 +5814,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
{
ST_SCHEMA_TABLE *schema_table= get_schema_table(schema_table_idx);
LEX_STRING db, table;
- DBUG_ENTER("mysql_schema_select");
+ DBUG_ENTER("make_schema_select");
DBUG_PRINT("enter", ("mysql_schema_select: %s", schema_table->table_name));
/*
We have to make non const db_name & table_name
@@ -5904,9 +5909,11 @@ bool get_schema_tables_result(JOIN *join,
{
result= 1;
join->error= 1;
+ tab->read_record.file= table_list->table->file;
table_list->schema_table_state= executed_place;
break;
}
+ tab->read_record.file= table_list->table->file;
table_list->schema_table_state= executed_place;
}
}
@@ -6502,8 +6509,8 @@ ST_FIELD_INFO referential_constraints_fields_info[]=
OPEN_FULL_TABLE},
{"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
- {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
+ {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0,
+ MY_I_S_MAYBE_NULL, 0, OPEN_FULL_TABLE},
{"MATCH_OPTION", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"UPDATE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"DELETE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
@@ -6784,7 +6791,7 @@ static bool show_create_trigger_impl(THD *thd,
ret_code= p->write();
if (!ret_code)
- send_eof(thd);
+ my_eof(thd);
return ret_code != 0;
}
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 7fa3786c382..34b310931d6 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -840,6 +840,68 @@ outp:
}
+/**
+ Copy string with HEX-encoding of "bad" characters.
+
+ @details This functions copies the string pointed by "src"
+ to the string pointed by "dst". Not more than "srclen" bytes
+ are read from "src". Any sequences of bytes representing
+ a not-well-formed substring (according to cs) are hex-encoded,
+ and all well-formed substrings (according to cs) are copied as is.
+ Not more than "dstlen" bytes are written to "dst". The number
+ of bytes written to "dst" is returned.
+
+ @param cs character set pointer of the destination string
+ @param[out] dst destination string
+ @param dstlen size of dst
+ @param src source string
+ @param srclen length of src
+
+ @retval result length
+*/
+
+size_t
+my_copy_with_hex_escaping(CHARSET_INFO *cs,
+ char *dst, size_t dstlen,
+ const char *src, size_t srclen)
+{
+ const char *srcend= src + srclen;
+ char *dst0= dst;
+
+ for ( ; src < srcend ; )
+ {
+ size_t chlen;
+ if ((chlen= my_ismbchar(cs, src, srcend)))
+ {
+ if (dstlen < chlen)
+ break; /* purecov: inspected */
+ memcpy(dst, src, chlen);
+ src+= chlen;
+ dst+= chlen;
+ dstlen-= chlen;
+ }
+ else if (*src & 0x80)
+ {
+ if (dstlen < 4)
+ break; /* purecov: inspected */
+ *dst++= '\\';
+ *dst++= 'x';
+ *dst++= _dig_vec_upper[((unsigned char) *src) >> 4];
+ *dst++= _dig_vec_upper[((unsigned char) *src) & 15];
+ src++;
+ dstlen-= 4;
+ }
+ else
+ {
+ if (dstlen < 1)
+ break; /* purecov: inspected */
+ *dst++= *src++;
+ dstlen--;
+ }
+ }
+ return dst - dst0;
+}
+
/*
copy a string,
with optional character set conversion,
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 128ed749b5f..b4d76a1779a 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -37,6 +37,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs,
const char **well_formed_error_pos,
const char **cannot_convert_error_pos,
const char **from_end_pos);
+size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
+ char *dst, size_t dstlen,
+ const char *src, size_t srclen);
class String
{
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 83200f1f380..bc4e6dfbbcc 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -42,7 +42,7 @@ static int copy_data_between_tables(TABLE *from,TABLE *to,
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
-static bool
+static int
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
Alter_info *alter_info,
bool tmp_table,
@@ -1226,8 +1226,12 @@ uint build_table_shadow_filename(char *buff, size_t bufflen,
flags Flags as defined below
WFRM_INITIAL_WRITE If set we need to prepare table before
creating the frm file
- WFRM_CREATE_HANDLER_FILES If set we need to create the handler file as
- part of the creation of the frm file
+ WFRM_INSTALL_SHADOW If set we should install the new frm
+ WFRM_KEEP_SHARE If set we know that the share is to be
+ retained and thus we should ensure share
+ object is correct, if not set we don't
+ set the new partition syntax string since
+ we know the share object is destroyed.
WFRM_PACK_FRM If set we should pack the frm file and delete
the frm file
@@ -1370,7 +1374,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
goto err;
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (part_info)
+ if (part_info && (flags & WFRM_KEEP_SHARE))
{
TABLE_SHARE *share= lpt->table->s;
char *tmp_part_syntax_str;
@@ -1497,7 +1501,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
if (error)
DBUG_RETURN(TRUE);
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
@@ -2173,7 +2177,7 @@ int prepare_create_field(Create_field *sql_field,
TRUE error
*/
-static bool
+static int
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
Alter_info *alter_info,
bool tmp_table,
@@ -3001,6 +3005,37 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
(qsort_cmp) sort_keys);
create_info->null_bits= null_fields;
+ /* Check fields. */
+ it.rewind();
+ while ((sql_field=it++))
+ {
+ Field::utype type= (Field::utype) MTYP_TYPENR(sql_field->unireg_check);
+
+ if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
+ !sql_field->def &&
+ sql_field->sql_type == MYSQL_TYPE_TIMESTAMP &&
+ (sql_field->flags & NOT_NULL_FLAG) &&
+ (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
+ {
+ /*
+ An error should be reported if:
+ - NO_ZERO_DATE SQL mode is active;
+ - there is no explicit DEFAULT clause (default column value);
+ - this is a TIMESTAMP column;
+ - the column is not NULL;
+ - this is not the DEFAULT CURRENT_TIMESTAMP column.
+
+ In other words, an error should be reported if
+ - NO_ZERO_DATE SQL mode is active;
+ - the column definition is equivalent to
+ 'column_name TIMESTAMP DEFAULT 0'.
+ */
+
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ }
+
DBUG_RETURN(FALSE);
}
@@ -3292,8 +3327,9 @@ bool mysql_create_table_no_lock(THD *thd,
}
}
}
- DBUG_PRINT("info", ("db_type = %d",
- ha_legacy_type(part_info->default_engine_type)));
+ DBUG_PRINT("info", ("db_type = %s create_info->db_type = %s",
+ ha_resolve_storage_engine_name(part_info->default_engine_type),
+ ha_resolve_storage_engine_name(create_info->db_type)));
if (part_info->check_partition_info(thd, &engine_type, file,
create_info, TRUE))
goto err;
@@ -3317,8 +3353,8 @@ bool mysql_create_table_no_lock(THD *thd,
The handler assigned to the table cannot handle partitioning.
Assign the partition handler as the handler of the table.
*/
- DBUG_PRINT("info", ("db_type: %d",
- ha_legacy_type(create_info->db_type)));
+ DBUG_PRINT("info", ("db_type: %s",
+ ha_resolve_storage_engine_name(create_info->db_type)));
delete file;
create_info->db_type= partition_hton;
if (!(file= get_ha_partition(part_info)))
@@ -3488,8 +3524,36 @@ bool mysql_create_table_no_lock(THD *thd,
thd_proc_info(thd, "creating table");
create_info->table_existed= 0; // Mark that table is created
- if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
+#ifdef HAVE_READLINK
+ if (test_if_data_home_dir(create_info->data_file_name))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
+ goto unlock_and_end;
+ }
+ if (test_if_data_home_dir(create_info->index_file_name))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
+ goto unlock_and_end;
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (check_partition_dirs(thd->lex->part_info))
+ {
+ goto unlock_and_end;
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
+
+ if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
+#endif /* HAVE_READLINK */
+ {
+ if (create_info->data_file_name)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
+ "DATA DIRECTORY option ignored");
+ if (create_info->index_file_name)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
+ "INDEX DIRECTORY option ignored");
create_info->data_file_name= create_info->index_file_name= 0;
+ }
create_info->table_options=db_options;
path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
@@ -3752,9 +3816,9 @@ mysql_rename_table(handlerton *base, const char *old_db,
wait_while_table_is_used()
thd Thread handler
table Table to remove from cache
- function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
- HA_EXTRA_FORCE_REOPEN if table is not be used
- HA_EXTRA_PREPARE_FOR_REANME if table is to be renamed
+ function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
+ HA_EXTRA_FORCE_REOPEN if table is not be used
+ HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
NOTES
When returning, the table will be unusable for other threads until
the table is closed.
@@ -4141,6 +4205,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
switch ((*prepare_func)(thd, table, check_opt)) {
case 1: // error, message written to net
ha_autocommit_or_rollback(thd, 1);
+ end_trans(thd, ROLLBACK);
close_thread_tables(thd);
DBUG_PRINT("admin", ("simple error, admin next table"));
continue;
@@ -4199,6 +4264,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
table_name);
protocol->store(buff, length, system_charset_info);
ha_autocommit_or_rollback(thd, 0);
+ end_trans(thd, COMMIT);
close_thread_tables(thd);
lex->reset_query_tables_list(FALSE);
table->table=0; // For query cache
@@ -4471,17 +4537,19 @@ send_result_message:
}
}
ha_autocommit_or_rollback(thd, 0);
+ end_trans(thd, COMMIT);
close_thread_tables(thd);
table->table=0; // For query cache
if (protocol->write())
goto err;
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
- err:
+err:
ha_autocommit_or_rollback(thd, 1);
+ end_trans(thd, ROLLBACK);
close_thread_tables(thd); // Shouldn't be needed
if (table)
table->table=0;
@@ -5004,8 +5072,8 @@ mysql_discard_or_import_tablespace(THD *thd,
query_cache_invalidate3(thd, table_list, 0);
/* The ALTER TABLE is always in its own transaction */
- error = ha_commit_stmt(thd);
- if (ha_commit(thd))
+ error = ha_autocommit_or_rollback(thd, 0);
+ if (end_active_trans(thd))
error=1;
if (error)
goto err;
@@ -5013,12 +5081,11 @@ mysql_discard_or_import_tablespace(THD *thd,
err:
ha_autocommit_or_rollback(thd, error);
- close_thread_tables(thd);
thd->tablespace_op=FALSE;
if (error == 0)
{
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
}
@@ -5985,7 +6052,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
- send_ok(thd);
+ my_ok(thd);
}
unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
@@ -6223,7 +6290,7 @@ view_err:
if (!error)
{
write_bin_log(thd, TRUE, thd->query, thd->query_length);
- send_ok(thd);
+ my_ok(thd);
}
else if (error > 0)
{
@@ -6539,8 +6606,8 @@ view_err:
VOID(pthread_mutex_unlock(&LOCK_open));
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
- error= ha_commit_stmt(thd);
- if (ha_commit(thd))
+ error= ha_autocommit_or_rollback(thd, 0);
+ if (end_active_trans(thd))
error= 1;
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -6628,7 +6695,7 @@ view_err:
/* Need to commit before a table is unlocked (NDB requirement). */
DBUG_PRINT("info", ("Committing before unlocking table"));
- if (ha_commit_stmt(thd) || ha_commit(thd))
+ if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
goto err1;
}
/*end of if (! new_table) for add/drop index*/
@@ -6858,7 +6925,7 @@ end_temporary:
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
(ulong) (copied + deleted), (ulong) deleted,
(ulong) thd->cuted_fields);
- send_ok(thd, copied + deleted, 0L, tmp_name);
+ my_ok(thd, copied + deleted, 0L, tmp_name);
thd->some_tables_deleted=0;
DBUG_RETURN(FALSE);
@@ -7126,9 +7193,9 @@ err:
Ensure that the new table is saved properly to disk so that we
can do a rename
*/
- if (ha_commit_stmt(thd))
+ if (ha_autocommit_or_rollback(thd, 0))
error=1;
- if (ha_commit(thd))
+ if (end_active_trans(thd))
error=1;
thd->variables.sql_mode= save_sql_mode;
@@ -7293,7 +7360,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
goto err;
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
err:
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index f1d7e4a7312..1a522b852e9 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -49,14 +49,14 @@ static const char *lock_descriptions[] =
#ifndef DBUG_OFF
void
-print_where(COND *cond,const char *info)
+print_where(COND *cond,const char *info, enum_query_type query_type)
{
if (cond)
{
char buff[256];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.length(0);
- cond->print(&str);
+ cond->print(&str, query_type);
str.append('\0');
DBUG_LOCK_FILE;
(void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
@@ -143,7 +143,7 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
else
{
str.length(0);
- sortorder->item->print(&str);
+ sortorder->item->print(&str, QT_ORDINARY);
out.append(str);
}
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 4bf23dc52f9..d426904e4b2 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -422,7 +422,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
thd->lex->query_tables_own_last= 0;
- err_status= check_table_access(thd, TRIGGER_ACL, tables, 0);
+ err_status= check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE);
thd->lex->query_tables_own_last= save_query_tables_own_last;
@@ -516,7 +516,7 @@ end:
start_waiting_global_read_lock(thd);
if (!result)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(result);
}
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 30e8829d764..ae3f43eba5b 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -382,6 +382,14 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
}
+/**
+ Create a user defined function.
+
+ @note Like implementations of other DDL/DML in MySQL, this function
+ relies on the caller to close the thread tables. This is done in the
+ end of dispatch_command().
+*/
+
int mysql_create_function(THD *thd,udf_func *udf)
{
int error;
@@ -489,7 +497,6 @@ int mysql_create_function(THD *thd,udf_func *udf)
table->field[3]->store((longlong) u_d->type, TRUE);
error = table->file->ha_write_row(table->record[0]);
- close_thread_tables(thd);
if (error)
{
my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index eb44a13826c..0c6e9a89a17 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -319,7 +319,7 @@ int mysql_update(THD *thd,
if (prune_partitions(thd, table, conds))
{
free_underlaid_joins(thd, select_lex);
- send_ok(thd); // No matching records
+ my_ok(thd); // No matching records
DBUG_RETURN(0);
}
#endif
@@ -336,7 +336,7 @@ int mysql_update(THD *thd,
{
DBUG_RETURN(1); // Error in where
}
- send_ok(thd); // No matching records
+ my_ok(thd); // No matching records
DBUG_RETURN(0);
}
if (!select && limit != HA_POS_ERROR)
@@ -643,14 +643,6 @@ int mysql_update(THD *thd,
updated++;
else
error= 0;
-
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
- {
- error= 1;
- break;
- }
}
else if (!ignore ||
table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
@@ -669,6 +661,14 @@ int mysql_update(THD *thd,
}
}
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE))
+ {
+ error= 1;
+ break;
+ }
+
if (!--limit && using_limit)
{
/*
@@ -803,17 +803,6 @@ int mysql_update(THD *thd,
}
DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
- if (transactional_table)
- {
- if (ha_autocommit_or_rollback(thd, error >= 0))
- error=1;
- }
-
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
/* If LAST_INSERT_ID(X) was used, report X */
id= thd->arg_of_last_insert_id_function ?
@@ -826,7 +815,7 @@ int mysql_update(THD *thd,
(ulong) thd->cuted_fields);
thd->row_count_func=
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- send_ok(thd, (ulong) thd->row_count_func, id, buff);
+ my_ok(thd, (ulong) thd->row_count_func, id, buff);
DBUG_PRINT("info",("%ld records updated", (long) updated));
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
@@ -870,6 +859,19 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_update");
+ /*
+ Statement-based replication of UPDATE ... LIMIT is not safe as order of
+ rows is not defined, so in mixed mode we go to row-based.
+
+ Note that we may consider a statement as safe if ORDER BY primary_key
+ is present. However it may confuse users to see very similiar statements
+ replicated differently.
+ */
+ if (thd->lex->current_select->select_limit)
+ {
+ thd->lex->set_stmt_unsafe();
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege=
(SELECT_ACL & ~table->grant.privilege);
@@ -941,7 +943,7 @@ static table_map get_table_map(List<Item> *items)
TRUE Error
*/
-bool mysql_multi_update_prepare(THD *thd)
+int mysql_multi_update_prepare(THD *thd)
{
LEX *lex= thd->lex;
TABLE_LIST *table_list= lex->query_tables;
@@ -1203,7 +1205,7 @@ multi_update::multi_update(TABLE_LIST *table_list,
tmp_tables(0), updated(0), found(0), fields(field_list),
values(value_list), table_count(0), copy_field(0),
handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1),
- transactional_tables(1), ignore(ignore_arg), error_handled(0)
+ transactional_tables(0), ignore(ignore_arg), error_handled(0)
{}
@@ -1644,12 +1646,12 @@ bool multi_update::send_data(List<Item> &not_used_values)
trans_safe= 0;
thd->transaction.stmt.modified_non_trans_table= TRUE;
}
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
- DBUG_RETURN(1);
}
}
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE))
+ DBUG_RETURN(1);
}
else
{
@@ -1716,13 +1718,8 @@ void multi_update::abort()
If not attempt to do remaining updates.
*/
- if (trans_safe)
+ if (! trans_safe)
{
- DBUG_ASSERT(transactional_tables);
- (void) ha_autocommit_or_rollback(thd, 1);
- }
- else
- {
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table);
if (do_update && table_count > 1)
{
@@ -1754,11 +1751,6 @@ void multi_update::abort()
thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table);
-
- if (transactional_tables)
- {
- (void) ha_autocommit_or_rollback(thd, 1);
- }
}
@@ -1881,12 +1873,12 @@ int multi_update::do_updates()
updated++;
else
local_error= 0;
-
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
- goto err2;
}
+
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE))
+ goto err2;
}
if (updated != org_updated)
@@ -1996,12 +1988,6 @@ bool multi_update::send_eof()
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
- if (transactional_tables)
- {
- if (ha_autocommit_or_rollback(thd, local_error != 0))
- local_error=1;
- }
-
if (local_error > 0) // if the above log write did not fail ...
{
/* Safety: If we haven't got an error before (can happen in do_updates) */
@@ -2016,6 +2002,6 @@ bool multi_update::send_eof()
(ulong) thd->cuted_fields);
thd->row_count_func=
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
+ ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index da301b37484..a654721de37 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -182,10 +182,33 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
TABLE_LIST decoy;
memcpy (&decoy, view, sizeof (TABLE_LIST));
- if (!open_table(thd, &decoy, thd->mem_root, &not_used, OPEN_VIEW_NO_PARSE) &&
- !decoy.view)
+
+ /*
+ Let's reset decoy.view before calling open_table(): when we start
+ supporting ALTER VIEW in PS/SP that may save us from a crash.
+ */
+
+ decoy.view= NULL;
+
+ /*
+ open_table() will return NULL if 'decoy' is idenitifying a view *and*
+ there is no TABLE object for that view in the table cache. However,
+ decoy.view will be set to 1.
+
+ If there is a TABLE-instance for the oject identified by 'decoy',
+ open_table() will return that instance no matter if it is a table or
+ a view.
+
+ Thus, there is no need to check for the return value of open_table(),
+ since the return value itself does not mean anything.
+ */
+
+ open_table(thd, &decoy, thd->mem_root, &not_used, OPEN_VIEW_NO_PARSE);
+
+ if (!decoy.view)
{
- /* It's a table */
+ /* It's a table. */
+ my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
return TRUE;
}
@@ -204,104 +227,31 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
return FALSE;
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
- Creating/altering VIEW procedure
+ @brief CREATE VIEW privileges pre-check.
@param thd thread handler
+ @param tables tables used in the view
@param views views to create
@param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
- @note This function handles both create and alter view commands.
-
@retval FALSE Operation was a success.
@retval TRUE An error occured.
*/
-bool mysql_create_view(THD *thd, TABLE_LIST *views,
- enum_view_create_mode mode)
+bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
+ enum_view_create_mode mode)
{
LEX *lex= thd->lex;
- bool link_to_local;
/* first table in list is target VIEW name => cut off it */
- TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
- TABLE_LIST *tables= lex->query_tables;
TABLE_LIST *tbl;
SELECT_LEX *select_lex= &lex->select_lex;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
SELECT_LEX *sl;
-#endif
- SELECT_LEX_UNIT *unit= &lex->unit;
- bool res= FALSE;
- DBUG_ENTER("mysql_create_view");
-
- /* This is ensured in the parser. */
- DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
- !lex->param_list.elements && !lex->derived_tables);
-
- if (mode != VIEW_CREATE_NEW)
- {
- if (mode == VIEW_ALTER &&
- fill_defined_view_parts(thd, view))
- {
- res= TRUE;
- goto err;
- }
- sp_cache_invalidate();
- }
-
- if (!lex->definer)
- {
- /*
- DEFINER-clause is missing; we have to create default definer in
- persistent arena to be PS/SP friendly.
- If this is an ALTER VIEW then the current user should be set as
- the definer.
- */
- Query_arena original_arena;
- Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
-
- if (!(lex->definer= create_default_definer(thd)))
- res= TRUE;
-
- if (ps_arena)
- thd->restore_active_arena(ps_arena, &original_arena);
+ bool res= TRUE;
+ DBUG_ENTER("create_view_precheck");
- if (res)
- goto err;
- }
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /*
- check definer of view:
- - same as current user
- - current user has SUPER_ACL
- */
- if (lex->definer &&
- (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 ||
- my_strcasecmp(system_charset_info,
- lex->definer->host.str,
- thd->security_ctx->priv_host) != 0))
- {
- if (!(thd->security_ctx->master_access & SUPER_ACL))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- res= TRUE;
- goto err;
- }
- else
- {
- if (!is_acl_user(lex->definer->host.str,
- lex->definer->user.str))
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_NO_SUCH_USER,
- ER(ER_NO_SUCH_USER),
- lex->definer->user.str,
- lex->definer->host.str);
- }
- }
- }
/*
Privilege check for view creation:
- user has CREATE VIEW privilege on view table
@@ -323,10 +273,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
(check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
check_grant(thd, DROP_ACL, view, 0, 1, 0))))
- {
- res= TRUE;
goto err;
- }
+
for (sl= select_lex; sl; sl= sl->next_select())
{
for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
@@ -340,7 +288,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"ANY", thd->security_ctx->priv_user,
thd->security_ctx->priv_host, tbl->table_name);
- res= TRUE;
goto err;
}
/*
@@ -376,10 +323,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (check_access(thd, SELECT_ACL, tbl->db,
&tbl->grant.privilege, 0, 0, test(tbl->schema_table)) ||
check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
- {
- res= TRUE;
goto err;
- }
}
}
}
@@ -403,8 +347,126 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
}
}
+
+ res= FALSE;
+
+err:
+ DBUG_RETURN(res || thd->is_error());
+}
+
+#else
+
+bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
+ enum_view_create_mode mode)
+{
+ return FALSE;
+}
+
#endif
+
+/**
+ @brief Creating/altering VIEW procedure
+
+ @param thd thread handler
+ @param views views to create
+ @param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
+
+ @note This function handles both create and alter view commands.
+
+ @retval FALSE Operation was a success.
+ @retval TRUE An error occured.
+*/
+
+bool mysql_create_view(THD *thd, TABLE_LIST *views,
+ enum_view_create_mode mode)
+{
+ LEX *lex= thd->lex;
+ bool link_to_local;
+ /* first table in list is target VIEW name => cut off it */
+ TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *tables= lex->query_tables;
+ TABLE_LIST *tbl;
+ SELECT_LEX *select_lex= &lex->select_lex;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ SELECT_LEX *sl;
+#endif
+ SELECT_LEX_UNIT *unit= &lex->unit;
+ bool res= FALSE;
+ DBUG_ENTER("mysql_create_view");
+
+ /* This is ensured in the parser. */
+ DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
+ !lex->param_list.elements);
+
+ if (mode != VIEW_CREATE_NEW)
+ {
+ if (mode == VIEW_ALTER &&
+ fill_defined_view_parts(thd, view))
+ {
+ res= TRUE;
+ goto err;
+ }
+ sp_cache_invalidate();
+ }
+
+ if (!lex->definer)
+ {
+ /*
+ DEFINER-clause is missing; we have to create default definer in
+ persistent arena to be PS/SP friendly.
+ If this is an ALTER VIEW then the current user should be set as
+ the definer.
+ */
+ Query_arena original_arena;
+ Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
+
+ if (!(lex->definer= create_default_definer(thd)))
+ res= TRUE;
+
+ if (ps_arena)
+ thd->restore_active_arena(ps_arena, &original_arena);
+
+ if (res)
+ goto err;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ check definer of view:
+ - same as current user
+ - current user has SUPER_ACL
+ */
+ if (lex->definer &&
+ (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 ||
+ my_strcasecmp(system_charset_info,
+ lex->definer->host.str,
+ thd->security_ctx->priv_host) != 0))
+ {
+ if (!(thd->security_ctx->master_access & SUPER_ACL))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ res= TRUE;
+ goto err;
+ }
+ else
+ {
+ if (!is_acl_user(lex->definer->host.str,
+ lex->definer->user.str))
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_NO_SUCH_USER,
+ ER(ER_NO_SUCH_USER),
+ lex->definer->user.str,
+ lex->definer->host.str);
+ }
+ }
+ }
+#endif
+
+ if ((res= create_view_precheck(thd, tables, view, mode)))
+ goto err;
+
if (open_and_lock_tables(thd, tables))
{
res= TRUE;
@@ -599,7 +661,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (res)
goto err;
- send_ok(thd);
+ my_ok(thd);
lex->link_first_table_back(view, link_to_local);
DBUG_RETURN(0);
@@ -696,8 +758,42 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
enum_view_create_mode mode)
{
LEX *lex= thd->lex;
- char buff[4096];
- String view_query(buff, sizeof (buff), thd->charset());
+
+ /*
+ View definition query -- a SELECT statement that fully defines view. It
+ is generated from the Item-tree built from the original (specified by
+ the user) query. The idea is that generated query should eliminates all
+ ambiguities and fix view structure at CREATE-time (once for all).
+ Item::print() virtual operation is used to generate view definition
+ query.
+
+ INFORMATION_SCHEMA query (IS query) -- a SQL statement describing a
+ view that is shown in INFORMATION_SCHEMA. Basically, it is 'view
+ definition query' with text literals converted to UTF8 and without
+ character set introducers.
+
+ For example:
+ Let's suppose we have:
+ CREATE TABLE t1(a INT, b INT);
+ User specified query:
+ CREATE VIEW v1(x, y) AS SELECT * FROM t1;
+ Generated query:
+ SELECT a AS x, b AS y FROM t1;
+ IS query:
+ SELECT a AS x, b AS y FROM t1;
+
+ View definition query is stored in the client character set.
+ */
+ char view_query_buff[4096];
+ String view_query(view_query_buff,
+ sizeof (view_query_buff),
+ thd->charset());
+
+ char is_query_buff[4096];
+ String is_query(is_query_buff,
+ sizeof (is_query_buff),
+ system_charset_info);
+
char md5[MD5_BUFF_LENGTH];
bool can_be_merged;
char dir_buff[FN_REFLEN], path_buff[FN_REFLEN];
@@ -705,12 +801,16 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
int error= 0;
DBUG_ENTER("mysql_register_view");
- /* print query */
+ /* Generate view definition and IS queries. */
view_query.length(0);
+ is_query.length(0);
{
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
- lex->unit.print(&view_query);
+
+ lex->unit.print(&view_query, QT_ORDINARY);
+ lex->unit.print(&is_query, QT_IS);
+
thd->variables.sql_mode|= sql_mode;
}
DBUG_PRINT("info", ("View: %s", view_query.ptr()));
@@ -718,11 +818,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
/* fill structure */
view->select_stmt.str= view_query.c_ptr_safe();
view->select_stmt.length= view_query.length();
-
- view->source.str= (char*) thd->lex->create_view_select_start;
- view->source.length= (thd->lex->create_view_select_end
- - thd->lex->create_view_select_start);
- trim_whitespace(thd->charset(), & view->source);
+ view->source= thd->lex->create_view_select;
view->file_version= 1;
view->calc_md5(md5);
@@ -853,7 +949,8 @@ loop_out:
lex_string_set(&view->view_connection_cl_name,
view->view_creation_ctx->get_connection_cl()->name);
- view->view_body_utf8= lex->view_body_utf8;
+ view->view_body_utf8.str= is_query.c_ptr_safe();
+ view->view_body_utf8.length= is_query.length();
/*
Check that table of main select do not used in subqueries.
@@ -1123,8 +1220,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (!table->prelocking_placeholder &&
(old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
{
- if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
- check_table_access(thd, SHOW_VIEW_ACL, table, 1))
+ if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) &&
+ check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE))
{
my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
goto err;
@@ -1134,7 +1231,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
(old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
!table->belong_to_view)
{
- if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
+ if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE))
goto err;
}
@@ -1552,7 +1649,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
{
DBUG_RETURN(TRUE);
}
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
@@ -1581,7 +1678,7 @@ frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
DBUG_RETURN(FRMTYPE_ERROR);
- error= my_read(file, (uchar*) header, sizeof(header), MYF(MY_WME | MY_NABP));
+ error= my_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
my_close(file, MYF(MY_WME));
if (error)
diff --git a/sql/sql_view.h b/sql/sql_view.h
index d3c83c82f44..b8138663489 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -15,6 +15,9 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
+ enum_view_create_mode mode);
+
bool mysql_create_view(THD *thd, TABLE_LIST *view,
enum_view_create_mode mode);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0d5908ccc1e..58483a33582 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1217,6 +1217,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <interval_time_st> interval_time_st
+%type <interval_time_st> interval_time_stamp
+
%type <db_type> storage_engines known_storage_engines
%type <row_type> row_types
@@ -4370,7 +4372,7 @@ create_table_option:
Lex->create_info.row_type= $3;
Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
}
- | UNION_SYM opt_equal '(' table_list ')'
+ | UNION_SYM opt_equal '(' opt_table_list ')'
{
/* Move the union list to the merge_list */
LEX *lex=Lex;
@@ -7007,9 +7009,9 @@ function_call_nonkeyword:
$$= new (YYTHD->mem_root) Item_func_now_local($3);
Lex->safe_to_cache_query=0;
}
- | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')'
+ | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')'
{ $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0); }
- | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')'
+ | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')'
{ $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3); }
| UTC_DATE_SYM optional_braces
{
@@ -7403,6 +7405,7 @@ variable_aux:
}
| '@' opt_var_ident_type ident_or_text opt_component
{
+ /* disallow "SELECT @@global.global.variable" */
if ($3.str && $4.str && check_reserved_words(&$3))
{
my_parse_error(ER(ER_SYNTAX_ERROR));
@@ -7410,6 +7413,8 @@ variable_aux:
}
if (!($$= get_system_var(YYTHD, $2, $3, $4)))
MYSQL_YYABORT;
+ if (!((Item_func_get_system_var*) $$)->is_written_to_binlog())
+ Lex->set_stmt_unsafe();
}
;
@@ -7974,22 +7979,41 @@ interval:
| HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
| HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
| HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
- | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
| MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
| SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
| YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
;
+interval_time_stamp:
+ interval_time_st {}
+ | FRAC_SECOND_SYM {
+ $$=INTERVAL_MICROSECOND;
+ /*
+ FRAC_SECOND was mistakenly implemented with
+ a wrong resolution. According to the ODBC
+ standard it should be nanoseconds, not
+ microseconds. Changing it to nanoseconds
+ in MySQL would mean making TIMESTAMPDIFF
+ and TIMESTAMPADD to return DECIMAL, since
+ the return value would be too big for BIGINT
+ Hence we just deprecate the incorrect
+ implementation without changing its
+ resolution.
+ */
+ WARN_DEPRECATED(yythd, "6.2", "FRAC_SECOND", "MICROSECOND");
+ }
+ ;
+
interval_time_st:
DAY_SYM { $$=INTERVAL_DAY; }
| WEEK_SYM { $$=INTERVAL_WEEK; }
| HOUR_SYM { $$=INTERVAL_HOUR; }
- | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_SYM { $$=INTERVAL_MINUTE; }
| MONTH_SYM { $$=INTERVAL_MONTH; }
| QUARTER_SYM { $$=INTERVAL_QUARTER; }
| SECOND_SYM { $$=INTERVAL_SECOND; }
+ | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
| YEAR_SYM { $$=INTERVAL_YEAR; }
;
@@ -8233,10 +8257,10 @@ limit_options:
;
limit_option:
- param_marker
- {
- ((Item_param *) $1)->set_strict_type(INT_RESULT);
- }
+ param_marker
+ {
+ ((Item_param *) $1)->limit_clause_param= TRUE;
+ }
| ULONGLONG_NUM { $$= new Item_uint($1.str, $1.length); }
| LONG_NUM { $$= new Item_uint($1.str, $1.length); }
| NUM { $$= new Item_uint($1.str, $1.length); }
@@ -9842,8 +9866,11 @@ text_literal:
}
| UNDERSCORE_CHARSET TEXT_STRING
{
- $$= new Item_string($2.str, $2.length, $1);
- ((Item_string*) $$)->set_repertoire_from_value();
+ Item_string *str= new Item_string($2.str, $2.length, $1);
+ str->set_repertoire_from_value();
+ str->set_cs_specified(TRUE);
+
+ $$= str;
}
| text_literal TEXT_STRING_literal
{
@@ -9946,15 +9973,22 @@ literal:
String *str= tmp ?
tmp->quick_fix_field(), tmp->val_str((String*) 0) :
(String*) 0;
- $$= new Item_string(NULL, /* name will be set in select_item */
- str ? str->ptr() : "",
- str ? str->length() : 0,
- $1);
- if (!$$ || !$$->check_well_formed_result(&$$->str_value, TRUE))
+
+ Item_string *item_str=
+ new Item_string(NULL, /* name will be set in select_item */
+ str ? str->ptr() : "",
+ str ? str->length() : 0,
+ $1);
+ if (!item_str ||
+ !item_str->check_well_formed_result(&item_str->str_value, TRUE))
{
MYSQL_YYABORT;
}
- ((Item_string *) $$)->set_repertoire_from_value();
+
+ item_str->set_repertoire_from_value();
+ item_str->set_cs_specified(TRUE);
+
+ $$= item_str;
}
| UNDERSCORE_CHARSET BIN_NUM
{
@@ -9966,14 +10000,21 @@ literal:
String *str= tmp ?
tmp->quick_fix_field(), tmp->val_str((String*) 0) :
(String*) 0;
- $$= new Item_string(NULL, /* name will be set in select_item */
- str ? str->ptr() : "",
- str ? str->length() : 0,
- $1);
- if (!$$ || !$$->check_well_formed_result(&$$->str_value, TRUE))
+
+ Item_string *item_str=
+ new Item_string(NULL, /* name will be set in select_item */
+ str ? str->ptr() : "",
+ str ? str->length() : 0,
+ $1);
+ if (!item_str ||
+ !item_str->check_well_formed_result(&item_str->str_value, TRUE))
{
MYSQL_YYABORT;
}
+
+ item_str->set_cs_specified(TRUE);
+
+ $$= item_str;
}
| DATE_SYM text_literal { $$ = $2; }
| TIME_SYM text_literal { $$ = $2; }
@@ -10314,12 +10355,6 @@ TEXT_STRING_filesystem:
ident:
IDENT_sys { $$=$1; }
- | READ_ONLY_SYM
- {
- THD *thd= YYTHD;
- $$.str= thd->strmake("read_only",9);
- $$.length= 9;
- }
| keyword
{
THD *thd= YYTHD;
@@ -10625,6 +10660,7 @@ keyword_sp:
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
+ | READ_ONLY_SYM {}
| REBUILD_SYM {}
| RECOVER_SYM {}
| REDO_BUFFER_SIZE_SYM {}
@@ -12009,27 +12045,7 @@ view_tail:
if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING))
MYSQL_YYABORT;
}
- view_list_opt AS
- {
- THD *thd= YYTHD;
- Lex_input_stream *lip= thd->m_lip;
-
- lip->body_utf8_start(thd, lip->get_cpp_ptr());
- }
- view_select
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= thd->m_lip;
-
- lip->body_utf8_append(lip->get_cpp_ptr());
-
- lex->view_body_utf8.str= thd->strmake(lip->get_body_utf8_str(),
- lip->get_body_utf8_length());
- lex->view_body_utf8.length= lip->get_body_utf8_length();
-
- trim_whitespace(&my_charset_utf8_general_ci, &lex->view_body_utf8);
- }
+ view_list_opt AS view_select
;
view_list_opt:
@@ -12060,18 +12076,22 @@ view_select:
lex->parsing_options.allows_select_into= FALSE;
lex->parsing_options.allows_select_procedure= FALSE;
lex->parsing_options.allows_derived= FALSE;
- lex->create_view_select_start= lip->get_cpp_ptr();
+ lex->create_view_select.str= (char *) lip->get_cpp_ptr();
}
view_select_aux view_check_option
{
THD *thd= YYTHD;
LEX *lex= Lex;
Lex_input_stream *lip= thd->m_lip;
+ uint len= lip->get_cpp_ptr() - lex->create_view_select.str;
+ void *create_view_select= thd->memdup(lex->create_view_select.str, len);
+ lex->create_view_select.length= len;
+ lex->create_view_select.str= (char *) create_view_select;
+ trim_whitespace(thd->charset(), &lex->create_view_select);
lex->parsing_options.allows_variable= TRUE;
lex->parsing_options.allows_select_into= TRUE;
lex->parsing_options.allows_select_procedure= TRUE;
lex->parsing_options.allows_derived= TRUE;
- lex->create_view_select_end= lip->get_cpp_ptr();
}
;
diff --git a/sql/stacktrace.c b/sql/stacktrace.c
index b1267e20774..5c3411aa8b1 100644
--- a/sql/stacktrace.c
+++ b/sql/stacktrace.c
@@ -13,15 +13,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* Workaround for Bug#32082: VOID redefinition on Win results in compile errors*/
+#define DONT_DEFINE_VOID 1
+
#include <my_global.h>
#include "stacktrace.h"
+
+#ifndef __WIN__
#include <signal.h>
#include <my_pthread.h>
-
+#include <m_string.h>
#ifdef HAVE_STACKTRACE
#include <unistd.h>
#include <strings.h>
+#if HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)
char *heap_start;
@@ -93,9 +102,68 @@ inline uint32* find_prev_pc(uint32* pc, uchar** fp)
}
#endif /* defined(__alpha__) && defined(__GNUC__) */
+#if BACKTRACE_DEMANGLE
+static void my_demangle_symbols(char **addrs, int n)
+{
+ int status, i;
+ char *begin, *end, *demangled;
+
+ for (i= 0; i < n; i++)
+ {
+ demangled= NULL;
+ begin= strchr(addrs[i], '(');
+ end= begin ? strchr(begin, '+') : NULL;
+
+ if (begin && end)
+ {
+ *begin++= *end++= '\0';
+ demangled= my_demangle(begin, &status);
+ if (!demangled || status)
+ {
+ demangled= NULL;
+ begin[-1]= '(';
+ end[-1]= '+';
+ }
+ }
+
+ if (demangled)
+ fprintf(stderr, "%s(%s+%s\n", addrs[i], demangled, end);
+ else
+ fprintf(stderr, "%s\n", addrs[i]);
+ }
+}
+#endif
+
+
+#if HAVE_BACKTRACE
+static void backtrace_current_thread(void)
+{
+ void *addrs[128];
+ char **strings= NULL;
+ int n = backtrace(addrs, array_elements(addrs));
+#if BACKTRACE_DEMANGLE
+ if ((strings= backtrace_symbols(addrs, n)))
+ {
+ my_demangle_symbols(strings, n);
+ free(strings);
+ }
+#endif
+#if HAVE_BACKTRACE_SYMBOLS_FD
+ if (!strings)
+ {
+ backtrace_symbols_fd(addrs, n, fileno(stderr));
+ }
+#endif
+}
+#endif
+
void print_stacktrace(uchar* stack_bottom, ulong thread_stack)
{
+#if HAVE_BACKTRACE
+ backtrace_current_thread();
+ return;
+#endif
uchar** fp;
uint frame_count = 0, sigreturn_frame_count;
#if defined(__alpha__) && defined(__GNUC__)
@@ -103,10 +171,7 @@ void print_stacktrace(uchar* stack_bottom, ulong thread_stack)
#endif
LINT_INIT(fp);
- fprintf(stderr,"\
-Attempting backtrace. You can use the following information to find out\n\
-where mysqld died. If you see no messages after this, something went\n\
-terribly wrong...\n");
+
#ifdef __i386__
__asm __volatile__ ("movl %%ebp,%0"
:"=r"(fp)
@@ -244,3 +309,267 @@ void write_core(int sig)
#endif
}
#endif
+#else /* __WIN__*/
+
+#include <dbghelp.h>
+
+/*
+ Stack tracing on Windows is implemented using Debug Helper library(dbghelp.dll)
+ We do not redistribute dbghelp and the one comes with older OS (up to Windows 2000)
+ is missing some important functions like functions StackWalk64 or MinidumpWriteDump.
+ Hence, we have to load functions at runtime using LoadLibrary/GetProcAddress.
+*/
+
+typedef DWORD (WINAPI *SymSetOptions_FctType)(DWORD dwOptions);
+typedef BOOL (WINAPI *SymGetModuleInfo64_FctType)
+ (HANDLE,DWORD64,PIMAGEHLP_MODULE64) ;
+typedef BOOL (WINAPI *SymGetSymFromAddr64_FctType)
+ (HANDLE,DWORD64,PDWORD64,PIMAGEHLP_SYMBOL64) ;
+typedef BOOL (WINAPI *SymGetLineFromAddr64_FctType)
+ (HANDLE,DWORD64,PDWORD,PIMAGEHLP_LINE64);
+typedef BOOL (WINAPI *SymInitialize_FctType)
+ (HANDLE,PSTR,BOOL);
+typedef BOOL (WINAPI *StackWalk64_FctType)
+ (DWORD,HANDLE,HANDLE,LPSTACKFRAME64,PVOID,PREAD_PROCESS_MEMORY_ROUTINE64,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64,PGET_MODULE_BASE_ROUTINE64 ,
+ PTRANSLATE_ADDRESS_ROUTINE64);
+typedef BOOL (WINAPI *MiniDumpWriteDump_FctType)(
+ IN HANDLE hProcess,
+ IN DWORD ProcessId,
+ IN HANDLE hFile,
+ IN MINIDUMP_TYPE DumpType,
+ IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
+ IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
+ IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
+ );
+
+static SymSetOptions_FctType pSymSetOptions;
+static SymGetModuleInfo64_FctType pSymGetModuleInfo64;
+static SymGetSymFromAddr64_FctType pSymGetSymFromAddr64;
+static SymInitialize_FctType pSymInitialize;
+static StackWalk64_FctType pStackWalk64;
+static SymGetLineFromAddr64_FctType pSymGetLineFromAddr64;
+static MiniDumpWriteDump_FctType pMiniDumpWriteDump;
+
+static EXCEPTION_POINTERS *exception_ptrs;
+
+#define MODULE64_SIZE_WINXP 576
+#define STACKWALK_MAX_FRAMES 64
+
+/*
+ Dynamically load dbghelp functions
+*/
+BOOL init_dbghelp_functions()
+{
+ static BOOL first_time= TRUE;
+ static BOOL rc;
+ HMODULE hDbghlp;
+
+ if(first_time)
+ {
+ first_time= FALSE;
+ hDbghlp= LoadLibrary("dbghelp");
+ if(!hDbghlp)
+ {
+ rc= FALSE;
+ return rc;
+ }
+ pSymSetOptions= (SymSetOptions_FctType)
+ GetProcAddress(hDbghlp,"SymSetOptions");
+ pSymInitialize= (SymInitialize_FctType)
+ GetProcAddress(hDbghlp,"SymInitialize");
+ pSymGetModuleInfo64= (SymGetModuleInfo64_FctType)
+ GetProcAddress(hDbghlp,"SymGetModuleInfo64");
+ pSymGetLineFromAddr64= (SymGetLineFromAddr64_FctType)
+ GetProcAddress(hDbghlp,"SymGetLineFromAddr64");
+ pSymGetSymFromAddr64=(SymGetSymFromAddr64_FctType)
+ GetProcAddress(hDbghlp,"SymGetSymFromAddr64");
+ pStackWalk64= (StackWalk64_FctType)
+ GetProcAddress(hDbghlp,"StackWalk64");
+ pMiniDumpWriteDump = (MiniDumpWriteDump_FctType)
+ GetProcAddress(hDbghlp,"MiniDumpWriteDump");
+
+ rc = (BOOL)(pSymSetOptions && pSymInitialize && pSymGetModuleInfo64
+ && pSymGetLineFromAddr64 && pSymGetSymFromAddr64 && pStackWalk64);
+ }
+ return rc;
+}
+
+void set_exception_pointers(EXCEPTION_POINTERS *ep)
+{
+ exception_ptrs = ep;
+}
+
+/* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
+#ifndef SYMOPT_NO_PROMPTS
+#define SYMOPT_NO_PROMPTS 0
+#endif
+
+void print_stacktrace(uchar* unused1, ulong unused2)
+{
+ HANDLE hProcess= GetCurrentProcess();
+ HANDLE hThread= GetCurrentThread();
+ static IMAGEHLP_MODULE64 module= {sizeof(module)};
+ static IMAGEHLP_SYMBOL64_PACKAGE package;
+ DWORD64 addr;
+ DWORD machine;
+ int i;
+ CONTEXT context;
+ STACKFRAME64 frame={0};
+
+ if(!exception_ptrs || !init_dbghelp_functions())
+ return;
+
+ /* Copy context, as stackwalking on original will unwind the stack */
+ context = *(exception_ptrs->ContextRecord);
+ /*Initialize symbols.*/
+ pSymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
+ pSymInitialize(hProcess,NULL,TRUE);
+
+ /*Prepare stackframe for the first StackWalk64 call*/
+ frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat;
+#if (defined _M_IX86)
+ machine= IMAGE_FILE_MACHINE_I386;
+ frame.AddrFrame.Offset= context.Ebp;
+ frame.AddrPC.Offset= context.Eip;
+ frame.AddrStack.Offset= context.Esp;
+#elif (defined _M_X64)
+ machine = IMAGE_FILE_MACHINE_AMD64;
+ frame.AddrFrame.Offset= context.Rbp;
+ frame.AddrPC.Offset= context.Rip;
+ frame.AddrStack.Offset= context.Rsp;
+#else
+ /*There is currently no need to support IA64*/
+#pragma error ("unsupported architecture")
+#endif
+
+ package.sym.SizeOfStruct= sizeof(package.sym);
+ package.sym.MaxNameLength= sizeof(package.name);
+
+ /*Walk the stack, output useful information*/
+ for(i= 0; i< STACKWALK_MAX_FRAMES;i++)
+ {
+ DWORD64 function_offset= 0;
+ DWORD line_offset= 0;
+ IMAGEHLP_LINE64 line= {sizeof(line)};
+ BOOL have_module= FALSE;
+ BOOL have_symbol= FALSE;
+ BOOL have_source= FALSE;
+
+ if(!pStackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0))
+ break;
+ addr= frame.AddrPC.Offset;
+
+ have_module= pSymGetModuleInfo64(hProcess,addr,&module);
+#ifdef _M_IX86
+ if(!have_module)
+ {
+ /*
+ ModuleInfo structure has been "compatibly" extended in releases after XP,
+ and its size was increased. To make XP dbghelp.dll function
+ happy, pretend passing the old structure.
+ */
+ module.SizeOfStruct= MODULE64_SIZE_WINXP;
+ have_module= pSymGetModuleInfo64(hProcess, addr, &module);
+ }
+#endif
+
+ have_symbol= pSymGetSymFromAddr64(hProcess, addr, &function_offset,
+ &(package.sym));
+ have_source= pSymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
+
+ fprintf(stderr, "%p ", addr);
+ if(have_module)
+ {
+ char *base_image_name= strrchr(module.ImageName, '\\');
+ if(base_image_name)
+ base_image_name++;
+ else
+ base_image_name= module.ImageName;
+ fprintf(stderr, "%s!", base_image_name);
+ }
+ if(have_symbol)
+ fprintf(stderr, "%s()", package.sym.Name);
+ else if(have_module)
+ fprintf(stderr, "???");
+
+ if(have_source)
+ {
+ char *base_file_name= strrchr(line.FileName, '\\');
+ if(base_file_name)
+ base_file_name++;
+ else
+ base_file_name= line.FileName;
+ fprintf(stderr,"[%s:%u]", base_file_name, line.LineNumber);
+ }
+ fprintf(stderr, "\n");
+ }
+ fflush(stderr);
+}
+
+
+/*
+ Write dump. The dump is created in current directory,
+ file name is constructed from executable name plus
+ ".dmp" extension
+*/
+void write_core(int unused)
+{
+ char path[MAX_PATH];
+ char dump_fname[MAX_PATH]= "core.dmp";
+ MINIDUMP_EXCEPTION_INFORMATION info;
+ HANDLE hFile;
+
+ if(!exception_ptrs || !init_dbghelp_functions() || !pMiniDumpWriteDump)
+ return;
+
+ info.ExceptionPointers= exception_ptrs;
+ info.ClientPointers= FALSE;
+ info.ThreadId= GetCurrentThreadId();
+
+ if(GetModuleFileName(NULL, path, sizeof(path)))
+ {
+ _splitpath(path, NULL, NULL,dump_fname,NULL);
+ strncat(dump_fname, ".dmp", sizeof(dump_fname));
+ }
+
+ hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if(hFile)
+ {
+ /* Create minidump */
+ if(pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
+ hFile, MiniDumpNormal, &info, 0, 0))
+ {
+ fprintf(stderr, "Minidump written to %s\n",
+ _fullpath(path, dump_fname, sizeof(path)) ? path : dump_fname);
+ }
+ else
+ {
+ fprintf(stderr,"MiniDumpWriteDump() failed, last error %u\n",
+ GetLastError());
+ }
+ CloseHandle(hFile);
+ }
+ else
+ {
+ fprintf(stderr, "CreateFile(%s) failed, last error %u\n", dump_fname,
+ GetLastError());
+ }
+ fflush(stderr);
+}
+
+
+void safe_print_str(const char *name, const char *val, int len)
+{
+ fprintf(stderr,"%s at %p", name, val);
+ __try
+ {
+ fprintf(stderr,"=%.*s\n", len, val);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fprintf(stderr,"is an invalid string pointer\n");
+ }
+}
+#endif /*__WIN__*/
diff --git a/sql/stacktrace.h b/sql/stacktrace.h
index 1a0b80c88d3..498f4f197fc 100644
--- a/sql/stacktrace.h
+++ b/sql/stacktrace.h
@@ -17,6 +17,14 @@
extern "C" {
#endif
+#if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS && HAVE_CXXABI_H && HAVE_ABI_CXA_DEMANGLE
+#define BACKTRACE_DEMANGLE 1
+#endif
+
+#if BACKTRACE_DEMANGLE
+char *my_demangle(const char *mangled_name, int *status);
+#endif
+
#ifdef TARGET_OS_LINUX
#if defined(HAVE_STACKTRACE) || (defined (__x86_64__) || defined (__i386__) || (defined(__alpha__) && defined(__GNUC__)))
#undef HAVE_STACKTRACE
@@ -28,19 +36,33 @@ extern char* heap_start;
#define init_stacktrace() do { \
heap_start = (char*) &__bss_start; \
} while(0);
+void check_thread_lib(void);
+#endif /* defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */
+#elif defined (__WIN__)
+#define HAVE_STACKTRACE
+extern void set_exception_pointers(EXCEPTION_POINTERS *ep);
+#define init_stacktrace() {}
+#endif
+
+#ifdef HAVE_STACKTRACE
void print_stacktrace(uchar* stack_bottom, ulong thread_stack);
void safe_print_str(const char* name, const char* val, int max_len);
-#endif /* (defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */
-#endif /* TARGET_OS_LINUX */
-
+#else
/* Define empty prototypes for functions that are not implemented */
-#ifndef HAVE_STACKTRACE
#define init_stacktrace() {}
#define print_stacktrace(A,B) {}
#define safe_print_str(A,B,C) {}
#endif /* HAVE_STACKTRACE */
+
+#if !defined(__NETWARE__)
+#define HAVE_WRITE_CORE
+#endif
+
+#ifdef HAVE_WRITE_CORE
void write_core(int sig);
+#endif
+
#ifdef __cplusplus
}
diff --git a/sql/structs.h b/sql/structs.h
index f14cca3c5db..809175fdde4 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -314,8 +314,27 @@ private:
*/
Discrete_interval *current;
uint elements; // number of elements
+
+ /* helper function for copy construct and assignment operator */
+ void copy_(const Discrete_intervals_list& from)
+ {
+ for (Discrete_interval *i= from.head; i; i= i->next)
+ {
+ Discrete_interval j= *i;
+ append(&j);
+ }
+ }
public:
Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {};
+ Discrete_intervals_list(const Discrete_intervals_list& from)
+ {
+ copy_(from);
+ }
+ void operator=(const Discrete_intervals_list& from)
+ {
+ empty();
+ copy_(from);
+ }
void empty_no_free()
{
head= current= NULL;
@@ -331,6 +350,7 @@ public:
}
empty_no_free();
}
+
const Discrete_interval* get_next()
{
Discrete_interval *tmp= current;
@@ -340,6 +360,7 @@ public:
}
~Discrete_intervals_list() { empty(); };
bool append(ulonglong start, ulonglong val, ulonglong incr);
+ bool append(Discrete_interval *interval);
ulonglong minimum() const { return (head ? head->minimum() : 0); };
ulonglong maximum() const { return (head ? tail->maximum() : 0); };
uint nb_elements() const { return elements; }
diff --git a/sql/table.cc b/sql/table.cc
index 3d03d05d209..7f1e0412103 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -892,26 +892,31 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
ha_legacy_type(share->db_type())));
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
- else
+ else if (str_db_type_length == 9 &&
+ !strncmp((char *) next_chunk + 2, "partition", 9))
{
- LEX_STRING pname= { C_STRING_WITH_LEN( "partition" ) };
- if (str_db_type_length == pname.length &&
- !strncmp((char *) next_chunk + 2, pname.str, pname.length))
- {
- /*
- Use partition handler
- tmp_plugin is locked with a local lock.
- we unlock the old value of share->db_plugin before
- replacing it with a globally locked version of tmp_plugin
- */
- plugin_unlock(NULL, share->db_plugin);
- share->db_plugin= ha_lock_engine(NULL, partition_hton);
- DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
- str_db_type_length, next_chunk + 2,
- ha_legacy_type(share->db_type())));
- }
+ /*
+ Use partition handler
+ tmp_plugin is locked with a local lock.
+ we unlock the old value of share->db_plugin before
+ replacing it with a globally locked version of tmp_plugin
+ */
+ plugin_unlock(NULL, share->db_plugin);
+ share->db_plugin= ha_lock_engine(NULL, partition_hton);
+ DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
+ str_db_type_length, next_chunk + 2,
+ ha_legacy_type(share->db_type())));
}
#endif
+ else if (!tmp_plugin)
+ {
+ /* purecov: begin inspected */
+ error= 8;
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
+ my_free(buff, MYF(0));
+ goto err;
+ /* purecov: end */
+ }
next_chunk+= str_db_type_length + 2;
}
if (next_chunk + 5 < buff_end)
@@ -2200,6 +2205,8 @@ void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg)
"of MySQL and cannot be read",
MYF(0), buff);
break;
+ case 8:
+ break;
default: /* Better wrong error than none */
case 4:
strxmov(buff, share->normalized_path.str, reg_ext, NullS);
diff --git a/sql/table.h b/sql/table.h
index 24948d0a076..0e0a35b022d 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -760,12 +760,36 @@ enum enum_schema_tables
typedef struct st_field_info
{
+ /**
+ This is used as column name.
+ */
const char* field_name;
+ /**
+ For string-type columns, this is the maximum number of
+ characters. Otherwise, it is the 'display-length' for the column.
+ */
uint field_length;
+ /**
+ This denotes data type for the column. For the most part, there seems to
+ be one entry in the enum for each SQL data type, although there seem to
+ be a number of additional entries in the enum.
+ */
enum enum_field_types field_type;
int value;
+ /**
+ This is used to set column attributes. By default, columns are @c NOT
+ @c NULL and @c SIGNED, and you can deviate from the default
+ by setting the appopriate flags. You can use either one of the flags
+ @c MY_I_S_MAYBE_NULL and @cMY_I_S_UNSIGNED or
+ combine them using the bitwise or operator @c |. Both flags are
+ defined in table.h.
+ */
uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.)
const char* old_name;
+ /**
+ This should be one of @c SKIP_OPEN_TABLE,
+ @c OPEN_FRM_ONLY or @c OPEN_FULL_TABLE.
+ */
uint open_method;
} ST_FIELD_INFO;
@@ -1149,7 +1173,7 @@ struct TABLE_LIST
return derived || view || schema_table || create && !table->db_stat ||
!table;
}
- void print(THD *thd, String *str);
+ void print(THD *thd, String *str, enum_query_type query_type);
bool check_single_table(TABLE_LIST **table, table_map map,
TABLE_LIST *view);
bool set_insert_values(MEM_ROOT *mem_root);
diff --git a/sql/unireg.cc b/sql/unireg.cc
index c4d656e6e9c..10446c036ec 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -980,9 +980,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
type= (Field::utype) MTYP_TYPENR(field->unireg_check);
- if (field->def &&
- (regfield->real_type() != MYSQL_TYPE_YEAR ||
- field->def->val_int() != 0))
+ if (field->def)
{
int res= field->def->save_in_field(regfield, 1);
/* If not ok or warning of level 'note' */
diff --git a/sql/unireg.h b/sql/unireg.h
index 2e0172ce31d..18c3ab16f6a 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -35,8 +35,8 @@
#ifndef SHAREDIR
#define SHAREDIR "share/"
#endif
-#ifndef LIBDIR
-#define LIBDIR "lib/"
+#ifndef PLUGINDIR
+#define PLUGINDIR "lib/plugin"
#endif
#define ER(X) errmesg[(X) - ER_ERROR_FIRST]
@@ -108,7 +108,6 @@
#define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */
#define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */
-#define POSTFIX_ERROR DBL_MAX
#define ME_INFO (ME_HOLDTANG+ME_OLDWIN+ME_NOREFRESH)
#define ME_ERROR (ME_BELL+ME_OLDWIN+ME_NOREFRESH)